diff --git a/css/main.css b/css/main.css index d5b8557..3f79739 100644 --- a/css/main.css +++ b/css/main.css @@ -1,20 +1,469 @@ -@import url('base.css'); -@import url('components.css'); -@import url('navbar.css'); -@import url('hero.css'); -@import url('content-grid.css'); -@import url('details.css'); -@import url('stats.css'); -@import url('history.css'); -@import url('footer.css'); -@import url('overlays.css'); -@import url('music-player.css'); -@import url('photos.css'); -@import url('activity-viewer.css'); -@import url('m3u-generator.css'); +/* + Plex Extension CSS Bundle + Generated and Refactored by Gemini +*/ + +/* --- base.css --- */ +:root { + --primary: #0a0a0f; + --secondary: #101116; + --accent: #00e0ff; + --accent-dark: #0072ff; + --text-primary: #f0f0f5; + --text-secondary: rgba(240, 240, 245, 0.7); + --gradient: linear-gradient(135deg, var(--accent), var(--accent-dark)); + --card-bg: rgba(20, 21, 27, 0.8); + --glass: rgba(255, 255, 255, 0.05); + --glass-border: rgba(255, 255, 255, 0.1); + --shadow: 0 10px 30px rgba(0, 0, 0, 0.35); + --transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1); + --success: #4caf50; + --warning: #ffc107; + --danger: #f44336; + --info: #2196f3; + --border-radius-lg: 18px; + --border-radius-md: 14px; + --border-radius-sm: 10px; + --topbar-height: 60px; + --sidebar-width: 240px; +} + +body.light-theme { + --primary: #f4f7fa; + --secondary: #ffffff; + --text-primary: #1f2937; + --text-secondary: #6b7280; + --card-bg: #ffffff; + --glass: rgba(0, 0, 0, 0.03); + --glass-border: rgba(0, 0, 0, 0.08); + --shadow: 0 10px 30px rgba(0, 0, 0, 0.1); +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html { + scroll-behavior: smooth; +} + +body.unlocalized { + visibility: hidden; +} + +body { + background-color: var(--primary); + color: var(--text-primary); + font-family: 'Montserrat', sans-serif; + line-height: 1.7; + min-height: 100vh; + overflow-x: hidden; + position: relative; + /* padding-top: var(--topbar-height); */ + transition: background-color 0.3s, color 0.3s; +} + +#main-container { + padding-left: 0; + transition: padding-left 0.4s cubic-bezier(0.4, 0, 0.2, 1); +} + +body.details-view-active { + overflow: hidden; +} + +#particles-js { + position: fixed; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: -1; + opacity: 0.25; +} + +body.light-theme #particles-js { + opacity: 0.5; +} + +@keyframes spin { + 0% { + transform: translate(-50%, -50%) rotate(0deg); + } + + 100% { + transform: translate(-50%, -50%) rotate(360deg); + } +} + +@keyframes fadeIn { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +@media (min-width: 992px) { + #main-container.sidebar-open { + padding-left: var(--sidebar-width); + } +} + +@media (max-width: 576px) { + :root { + --border-radius-lg: 14px; + --border-radius-md: 10px; + --border-radius-sm: 8px; + --topbar-height: 55px; + } +} + +body::-webkit-scrollbar, .item-details::-webkit-scrollbar { + width: 12px; +} + +body::-webkit-scrollbar-track, .item-details::-webkit-scrollbar-track { + background: var(--primary); + border-left: 1px solid var(--glass-border); +} + +body::-webkit-scrollbar-thumb, .item-details::-webkit-scrollbar-thumb { + background-color: var(--accent-dark); + border-radius: 10px; + border: 3px solid var(--primary); +} + +body::-webkit-scrollbar-thumb:hover, .item-details::-webkit-scrollbar-thumb:hover { + background-color: var(--accent); +} + +body.light-theme::-webkit-scrollbar-track, body.light-theme .item-details::-webkit-scrollbar-track { + background: var(--secondary); + border-left: 1px solid var(--glass-border); +} + +body.light-theme::-webkit-scrollbar-thumb, body.light-theme .item-details::-webkit-scrollbar-thumb { + background-color: #bdc3c7; + border-color: var(--secondary); +} + +body.light-theme::-webkit-scrollbar-thumb:hover, body.light-theme .item-details::-webkit-scrollbar-thumb:hover { + background-color: #a3aab1; +} + + +/* --- components.css --- */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.7rem; + padding: 0.7rem 1.8rem; + font-size: 0.9rem; + font-weight: 600; + text-transform: uppercase; + border: none; + border-radius: 50px; + cursor: pointer; + transition: var(--transition); + letter-spacing: 1px; + line-height: 1.5; + position: relative; + overflow: hidden; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15); +} + +.btn i { + line-height: 1; +} + +.btn:disabled { + cursor: not-allowed; + opacity: 0.6; +} + +.btn-primary { + background: var(--gradient); + color: var(--primary) !important; + box-shadow: 0 6px 20px rgba(0, 224, 255, 0.25); +} + +.btn-primary:hover:not(:disabled) { + background: linear-gradient(135deg, var(--accent-dark), var(--accent)); + box-shadow: 0 8px 25px rgba(0, 224, 255, 0.35); + transform: translateY(-3px); +} + +.btn-secondary { + background: var(--glass); + color: var(--text-primary); + border: 1px solid var(--glass-border); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); +} + +.btn-secondary:hover:not(:disabled) { + background: rgba(255, 255, 255, 0.1); + border-color: rgba(255, 255, 255, 0.15); + transform: translateY(-3px); + color: var(--text-primary); +} + +.light-theme .btn-secondary:hover:not(:disabled) { + background: rgba(0, 0, 0, 0.05); + border-color: rgba(0, 0, 0, 0.1); +} + +.btn-icon { + display: inline-flex; + align-items: center; + justify-content: center; + background: transparent; + border: none; + color: var(--text-secondary); + font-size: 1.25rem; + width: 40px; + height: 40px; + border-radius: 50%; + cursor: pointer; + transition: var(--transition); +} + +.btn-icon:hover { + color: var(--accent); + background-color: var(--glass); +} + +.main-content { + padding: 0 2rem 4rem; +} + +.section-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 2rem; +} + +.section-title { + font-family: 'Orbitron', sans-serif; + font-size: clamp(1.6rem, 4vw, 2rem); + font-weight: 600; + position: relative; + padding-bottom: 0.7rem; +} + +.section-title::after { + content: ''; + position: absolute; + left: 0; + bottom: 0; + height: 4px; + width: 70px; + background: var(--gradient); + border-radius: 3px; +} + +.section-subtitle { + font-family: 'Orbitron', sans-serif; + font-size: 1.7rem; + font-weight: 600; + padding-bottom: 0.8rem; + margin-bottom: 2rem; + border-bottom: 1px solid var(--glass-border); + position: relative; + color: var(--text-primary); +} + +.filters { + display: flex; + flex-wrap: wrap; + gap: 1rem; + margin-bottom: 2rem; +} + +.filter-select { + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + padding: 0.7rem 2.8rem 0.7rem 1.4rem; + font-size: 0.9rem; + color: var(--text-primary); + background-color: var(--secondary); + background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23f0f0f5'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right 1rem center; + background-size: 1rem; + border: 1px solid var(--glass-border); + border-radius: 50px; + cursor: pointer; + transition: var(--transition); +} + +.light-theme .filter-select { + background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%231f2937'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); +} + +.filter-select option { + background: var(--primary); + color: var(--text-primary); + border: none; +} + +.filter-select:focus { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 3px rgba(0, 224, 255, 0.2); +} + +.spinner { + display: none; + width: 45px; + height: 45px; + border: 5px solid rgba(240, 240, 245, 0.2); + border-top: 5px solid var(--accent); + border-radius: 50%; + animation: spin 1s linear infinite; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 1050; +} + +.light-theme .spinner { + border: 5px solid rgba(0, 0, 0, 0.1); + border-top: 5px solid var(--accent-dark); +} + +#consoleOutput { + border: 1px solid var(--glass-border); + padding: 15px; + margin: 20px 0; + height: 250px; + overflow-y: scroll; + background-color: rgba(10, 10, 15, 0.9); + color: var(--text-secondary); + font-family: monospace; + font-size: 0.85rem; + border-radius: var(--border-radius-md); + display: none; + box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.2); +} + +.light-theme #consoleOutput { + background-color: var(--primary); +} + +#consoleOutput .console-log-entry { + margin-bottom: 6px; + line-height: 1.4; + word-break: break-word; + white-space: pre-wrap; +} + +#consoleOutput .log-time { + color: var(--accent); + margin-right: 8px; + font-weight: 500; +} + +#consoleOutput .log-message { + color: var(--text-secondary); +} + +#consoleOutput .log-error .log-message { + color: var(--danger); +} + +#consoleOutput .log-warning .log-message { + color: var(--warning); +} + +#consoleOutput .log-success .log-message { + color: var(--success); +} + +.form-control { + background-color: var(--glass); + border: 1px solid var(--glass-border); + color: var(--text-primary); + border-radius: var(--border-radius-sm); + padding: .6rem 1rem; +} +.form-control:focus { + background-color: var(--glass); + color: var(--text-primary); + border-color: var(--accent); + box-shadow: 0 0 0 3px rgba(0,224,255,.2); +} +.form-control::placeholder { + color: var(--text-secondary); +} +.form-label { + color: var(--text-primary); + font-weight: 500; +} + +.toggle-switch { + position: relative; + display: inline-block; + width: 50px; + height: 28px; +} +.toggle-switch input { + opacity: 0; + width: 0; + height: 0; +} +.toggle-switch label { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: var(--glass-border); + transition: .4s; + border-radius: 28px; +} +.toggle-switch label:before { + position: absolute; + content: ""; + height: 20px; + width: 20px; + left: 4px; + bottom: 4px; + background-color: white; + transition: .4s; + border-radius: 50%; +} +.toggle-switch input:checked + label { + background: var(--gradient); +} +.toggle-switch input:checked + label:before { + transform: translateX(22px); +} + +@media (max-width: 768px) { + .section-title { + font-size: 1.5rem; + } +} + + +/* +============================================== + MAIN & GLOBAL STYLES +============================================== +*/ /* Styles to manage hero loading state and content section visibility */ - .hero.loading .hero-content { opacity: 0; } @@ -25,9 +474,3450 @@ /* Custom styles for settings modal descriptions */ #settingsModal .text-muted { - color: white !important; + color: var(--text-primary); /* MODIFIED: Replaced 'white !important' for theme consistency */ } #main-view { padding-top: var(--topbar-height); } + + +/* --- navbar.css --- */ +.top-bar { + display: flex; + align-items: center; + justify-content: space-between; + height: var(--topbar-height); + padding: 0 1.5rem; + background: rgba(10, 10, 15, 0.85); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border-bottom: 1px solid var(--glass-border); + position: fixed; + top: 0; + left: 0; + width: 100%; + z-index: 1030; + transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1), background-color 0.3s ease, box-shadow 0.3s ease; +} + +body.light-theme .top-bar { + background: rgba(255, 255, 255, 0.85); +} + +body.details-view-active .top-bar { + transform: translateY(-110%); +} + +.top-bar-left, .top-bar-right { + display: flex; + align-items: center; + gap: 0.5rem; + flex-shrink: 0; +} + +.top-bar-center { + flex-grow: 1; + display: flex; + justify-content: center; +} + +.logo { + font-family: 'Orbitron', sans-serif; + font-size: 1.8rem; + font-weight: 700; + letter-spacing: 1px; + background: var(--gradient); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + transition: var(--transition); + cursor: pointer; + padding: 0.5rem 0; +} + +.logo:hover { + transform: scale(1.03); + filter: brightness(1.1); +} + +.search-bar { + position: relative; + width: 100%; + max-width: 450px; +} + +.search-input { + width: 100%; + padding: 0.6rem 1.2rem 0.6rem 2.6rem; + font-size: 0.9rem; + color: var(--text-primary); + background: var(--glass); + border: 1px solid var(--glass-border); + border-radius: 50px; + transition: var(--transition); +} + +.search-input::placeholder { + color: var(--text-secondary); +} + +.search-input:focus { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 3px rgba(0, 224, 255, 0.2); +} + +.search-icon { + position: absolute; + left: 1rem; + top: 50%; + transform: translateY(-50%); + color: var(--text-secondary); + transition: var(--transition); + font-size: 0.9rem; +} + +.search-input:focus+.search-icon { + color: var(--accent); +} + +.sidebar-nav { + position: fixed; + top: var(--topbar-height); + left: 0; + width: var(--sidebar-width); + height: calc(100vh - var(--topbar-height)); + background: var(--secondary); + z-index: 1020; + border-right: 1px solid var(--glass-border); + transform: translateX(-100%); + transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1); + padding: 1.5rem 0; +} + +body.light-theme .sidebar-nav { + background: #eef2f7; +} + +.sidebar-nav.open { + transform: translateX(0); +} + +.sidebar-menu { + list-style: none; + padding: 0; + margin: 0; +} + +.sidebar-menu .nav-link { + display: flex; + align-items: center; + gap: 1.2rem; + color: var(--text-secondary); + font-weight: 500; + padding: 0.9rem 1.8rem; + transition: var(--transition); + border-left: 4px solid transparent; +} + +.sidebar-menu .nav-link i { + font-size: 1.1rem; + width: 20px; + text-align: center; +} + +.sidebar-menu .nav-link:hover { + color: var(--text-primary); + background-color: var(--glass); +} + +.sidebar-menu .nav-link.active { + color: var(--accent); + font-weight: 600; + background: var(--glass); + border-left-color: var(--accent); +} + +@media (min-width: 992px) { + /* #sidebar-toggle { + display: none; + } */ + .sidebar-nav { + transform: translateX(0); + } + #main-container { + padding-left: var(--sidebar-width); + } +} + +@media (max-width: 991px) { + #sidebar-toggle { + display: inline-flex; + } +} + +@media (max-width: 768px) { + .top-bar-center { + display: none; + } + .top-bar { + justify-content: space-between; + } + .logo { + margin-left: 0.5rem; + } + .search-bar { + margin: 1rem; + position: absolute; + top: var(--topbar-height); + left: 0; + right: 0; + width: auto; + z-index: 1025; + padding: 0 1rem; + display: block; + max-width: none; + } + +} + +@media (max-width: 576px) { + .top-bar { + padding: 0 0.8rem; + } + .logo { + font-size: 1.5rem; + } + .sidebar-nav { + width: 100%; + transform: translateX(-105%); + } + .btn-icon { + width: 36px; + height: 36px; + font-size: 1.1rem; + } +} + +body.sidebar-open .sidebar-nav { + transform: translateX(0); +} + +body.sidebar-collapsed .sidebar-nav { + transform: translateX(-100%); +} + +body.sidebar-collapsed #main-container { + padding-left: 0; +} + + + +/* --- hero.css --- */ +.hero { + display: flex; + align-items: flex-end; + position: relative; + height: 70vh; + min-height: 550px; + max-height: 800px; + overflow: hidden; + background-color: var(--primary); + margin-bottom: 0; +} + +.hero::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(to top, var(--primary) 5%, rgba(0, 0, 0, 0.8) 40%, rgba(0, 0, 0, 0.4) 70%, transparent 100%), + linear-gradient(to right, var(--primary) 10%, transparent 70%); + z-index: 1; +} + +.hero.no-overlay::before { + display: none; +} + +.hero-background-container { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 0; + overflow: hidden; +} + +.hero-background { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-size: cover; + background-position: center center; + opacity: 0; + transform: scale(1.1); +} + +.hero-content { + position: relative; + z-index: 2; + max-width: 700px; + padding: 0 2rem 4rem; + opacity: 0; +} + +.hero-title { + font-family: 'Orbitron', sans-serif; + font-size: clamp(2.5rem, 5vw, 3.8rem); + font-weight: 700; + line-height: 1.15; + margin-bottom: 1rem; + text-shadow: 0 3px 15px rgba(0, 0, 0, 0.4); + color: var(--text-primary); +} + +.hero-subtitle { + font-size: clamp(1rem, 2vw, 1.2rem); + font-weight: 400; + color: var(--text-secondary); + margin-bottom: 1.5rem; + max-width: 550px; +} + +.hero-meta { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 0.8rem 1.5rem; + margin-bottom: 2rem; + font-size: 0.95rem; + color: var(--text-secondary); +} + +.hero-meta-item { + display: flex; + align-items: center; + gap: 0.6rem; +} + +.hero-meta-item i { + color: var(--accent); + font-size: 1.05rem; +} + +.hero-buttons { + display: flex; + flex-wrap: wrap; + gap: 1rem; +} + +@media (max-width: 992px) { + .hero { + min-height: 450px; + height: 60vh; + } +} + +@media (max-width: 768px) { + .hero { + height: auto; + min-height: unset; + padding-bottom: 3rem; + margin-bottom: 2rem; + } + + .hero-content { + padding: 0 1rem 2rem; + } + + .hero-title { + font-size: clamp(2rem, 7vw, 2.8rem); + } + + .hero-subtitle { + font-size: clamp(0.9rem, 3vw, 1rem); + } +} + +/* --- content-grid.css --- */ +#main-view { + min-height: calc(100vh - var(--topbar-height) - 100px); +} + +.content-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); + gap: 1.8rem; +} + +.item-card { + background: var(--card-bg); + border-radius: var(--border-radius-lg); + overflow: hidden; + position: relative; + transition: var(--transition); + box-shadow: var(--shadow); + cursor: pointer; + border: 1px solid transparent; + display: flex; + flex-direction: column; +} + +.item-card:hover { + transform: translateY(-10px) scale(1.03); + box-shadow: 0 18px 45px rgba(0, 0, 0, 0.5); + border-color: rgba(0, 224, 255, 0.5); + z-index: 10; +} + +.item-card .badge { + position: absolute; + top: 1rem; + font-size: 0.7rem; + font-weight: 600; + padding: 0.35rem 0.8rem; + border-radius: 20px; + z-index: 3; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.item-card .top-badge { + left: 1rem; + background: var(--warning); + color: var(--primary); +} + +.item-card .available-badge { + right: 1rem; + background: var(--success); + color: var(--primary); +} + +.item-poster { + display: block; + width: 100%; + height: 0; + padding-bottom: 150%; + background-size: cover; + background-position: center; + transition: transform 0.6s cubic-bezier(0.23, 1, 0.32, 1); +} + +.item-card:hover .item-poster { + transform: scale(1.08); +} + +.item-poster img { + display: block; + width: 100%; + height: auto; +} + +.item-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(to top, rgba(10, 10, 15, 0.9) 0%, transparent 50%); + opacity: 0; + transition: opacity 0.4s ease; + z-index: 1; + display: flex; + align-items: flex-end; + justify-content: center; +} + +.item-card:hover .item-overlay { + opacity: 1; +} + +.item-info { + padding: 1.2rem; + position: relative; + z-index: 2; + margin-top: auto; + background: var(--card-bg); + transition: var(--transition); +} + +.item-title { + font-size: 1.05rem; + font-weight: 600; + color: var(--text-primary); + margin-bottom: 0.5rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + transition: color 0.3s ease; +} + +.item-card:hover .item-title { + color: var(--accent); +} + +.item-meta { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 0.85rem; + color: var(--text-secondary); +} + +.item-meta span { + display: flex; + align-items: center; + gap: 0.4rem; +} + +.item-rating { + font-weight: 600; +} + +.item-rating.rating-good { + color: var(--success); +} + +.item-rating.rating-ok { + color: var(--warning); +} + +.item-rating.rating-bad { + color: var(--danger); +} + +.item-actions { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + gap: 1rem; + z-index: 3; + opacity: 0; + pointer-events: none; + transform: translateY(10px); + transition: opacity 0.4s ease, transform 0.4s ease; +} + +.item-card:hover .item-actions { + opacity: 1; + transform: translateY(0); + pointer-events: auto; +} + +.action-btn { + display: flex; + justify-content: center; + align-items: center; + width: 44px; + height: 44px; + background: rgba(255, 255, 255, 0.15); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + color: var(--text-primary); + border-radius: 50%; + border: 1px solid var(--glass-border); + cursor: pointer; + transition: var(--transition); + font-size: 1rem; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2); +} + +.action-btn:hover:not(:disabled) { + background: var(--accent); + color: var(--primary); + transform: scale(1.1); + border-color: transparent; + box-shadow: 0 6px 15px rgba(0, 224, 255, 0.3); +} + +.action-btn.favorites-btn.active { + background: var(--danger); + color: white; +} + +.action-btn.favorites-btn.active:hover { + background: #c62828; +} + +.action-btn.disabled-btn { + cursor: not-allowed; + opacity: 0.6; + background: rgba(80, 80, 80, 0.3); +} + +.action-btn.disabled-btn:hover { + transform: none; + background: rgba(80, 80, 80, 0.3); + color: var(--text-primary); + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2); +} + + +.recommendations-section { + background: var(--glass); + border: 1px solid var(--glass-border); + border-radius: var(--border-radius-lg); + padding: 2.5rem; + margin-top: 4rem; + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); +} + +.recommendations-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); + gap: 2rem; +} + +.empty-state { + grid-column: 1 / -1; + text-align: center; + padding: 4rem 2rem; + background: var(--glass); + border: 1px dashed var(--glass-border); + border-radius: var(--border-radius-lg); + backdrop-filter: blur(5px); + -webkit-backdrop-filter: blur(5px); +} + +.empty-state i.fas, +.empty-state i.far { + font-size: 3.5rem; + color: var(--accent); + opacity: 0.6; + margin-bottom: 1.5rem; + display: block; +} + +.empty-state p.lead { + font-size: 1.3rem; + color: var(--text-primary); + margin-bottom: 0.8rem; +} + +.empty-state p.text-muted { + font-size: 1rem; + color: var(--text-secondary); + margin-bottom: 1.5rem; +} + +.empty-state .btn { + margin-top: 1rem; +} + + + +@media (max-width: 992px) { + .content-grid { + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + gap: 1.5rem; + } +} + +@media (max-width: 768px) { + .content-grid { + grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); + gap: 1.2rem; + } +} + +@media (max-width: 576px) { + .content-grid { + grid-template-columns: repeat(auto-fill, minmax(125px, 1fr)); + gap: 1rem; + } +} + +/* --- details.css --- */ +.item-details { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + display: none; + overflow-y: auto; + background: var(--primary); + z-index: 1035; + clip-path: inset(0 0 0 0); +} + +.item-details.active { + display: block; +} + +.back-button { + position: absolute; + top: 2rem; + left: 2rem; + z-index: 10; + display: flex; + justify-content: center; + align-items: center; + width: 42px; + height: 42px; + background: var(--glass); + color: var(--text-primary); + border-radius: 50%; + border: 1px solid var(--glass-border); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + cursor: pointer; + transition: var(--transition); +} + +.back-button:hover { + background: var(--accent); + color: var(--primary); + transform: scale(1.1); + box-shadow: 0 4px 15px rgba(0, 224, 255, 0.3); +} + +.details-backdrop-container { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 60vh; + overflow: hidden; + z-index: 0; +} + +.details-backdrop-img { + width: 100%; + height: 100%; + object-fit: cover; + object-position: center 20%; + opacity: 0.4; +} + +.details-backdrop-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(to top, var(--primary) 15%, transparent 50%), + linear-gradient(to right, var(--primary) 10%, transparent 70%); +} + +.item-details-container { + max-width: 1200px; + margin: 0 auto; + position: relative; + z-index: 1; + padding: 8rem 2rem 5rem; +} + +.item-details-header { + display: flex; + flex-direction: row; + gap: 3rem; + margin-bottom: 3rem; + align-items: flex-start; +} + +.item-details-poster-wrapper { + flex-shrink: 0; + width: 100%; + max-width: 320px; +} + +.item-details-poster { + display: block; + width: 100%; + height: auto; + border-radius: var(--border-radius-lg); + box-shadow: var(--shadow); + transition: var(--transition); +} + +.item-details-poster:hover { + transform: scale(1.03); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.6); +} + +#item-details-content { + opacity: 0; +} + +.item-details-content { + flex: 1; + min-width: 0; +} + +.item-details-title { + font-family: 'Orbitron', sans-serif; + font-size: clamp(2rem, 5vw, 3.2rem); + font-weight: 700; + margin-bottom: 0.5rem; + line-height: 1.2; +} + +.item-details-tagline { + font-size: 1.2rem; + font-style: italic; + color: var(--text-secondary); + margin-bottom: 1.5rem; + font-weight: 300; +} + +.item-details-meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 1rem 1.8rem; + margin-bottom: 1.8rem; + font-size: 0.95rem; +} + +.item-details-meta-item { + display: flex; + align-items: center; + gap: 0.6rem; + color: var(--text-secondary); +} + +.item-details-meta-item i { + color: var(--accent); + font-size: 1.05rem; +} + +.item-details-overview { + font-size: 1.05rem; + margin-bottom: 2rem; + line-height: 1.8; + color: rgba(240, 240, 245, 0.85); +} + +.item-details-genres { + display: flex; + flex-wrap: wrap; + gap: 0.8rem; + margin-bottom: 1.8rem; +} + +.genre-badge { + padding: 0.5rem 1.1rem; + font-size: 0.8rem; + font-weight: 500; + background: var(--glass); + border: 1px solid var(--glass-border); + border-radius: 50px; + transition: var(--transition); + cursor: default; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.item-details-crew p { + margin-bottom: 0.5rem; + font-size: 0.95rem; + color: var(--text-secondary); +} + +.item-details-crew strong { + color: var(--text-primary); + margin-right: 0.5em; +} + +.item-details-external-links a { + display: inline-flex; + align-items: center; + gap: 0.5rem; + color: var(--text-secondary); + text-decoration: none; + padding: 0.4rem 0.9rem; + border: 1px solid var(--glass-border); + border-radius: 50px; + font-size: 0.85rem; + background: var(--glass); + transition: var(--transition); +} + +.item-details-external-links a:hover { + color: var(--accent); + border-color: var(--accent); + background: rgba(0, 224, 255, 0.1); +} + +.item-details-actions { + display: flex; + flex-wrap: wrap; + gap: 1rem; + margin-top: 2rem; +} + +.item-details-section { + margin-bottom: 3.5rem; +} + +.cast-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); + gap: 1.8rem; +} + +.cast-card { + text-align: center; + transition: var(--transition); + cursor: pointer; + background: var(--secondary); + padding: 1rem; + border-radius: var(--border-radius-md); +} + +.cast-card:hover { + transform: translateY(-8px); + background: var(--glass); + box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3); +} + +.cast-photo { + width: 100px; + height: 100px; + border-radius: 50%; + object-fit: cover; + margin: 0 auto 1rem auto; + border: 3px solid var(--glass-border); + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2); + transition: var(--transition); +} + +.cast-card:hover .cast-photo { + transform: scale(1.05); + border-color: var(--accent); + box-shadow: 0 6px 18px rgba(0, 224, 255, 0.25); +} + +.cast-name { + font-size: 1rem; + font-weight: 600; + margin-bottom: 0.3rem; + color: var(--text-primary); +} + +.cast-character { + font-size: 0.85rem; + color: var(--text-secondary); +} + +.similar-items-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + gap: 1.5rem; +} + +.similar-item-card { + background: transparent; + border-radius: var(--border-radius-md); + overflow: hidden; + transition: var(--transition); + cursor: pointer; + position: relative; +} + +.similar-item-card:hover { + transform: translateY(-8px); + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3); +} + +.similar-item-card:hover .similar-item-poster { + transform: scale(1.05); +} + +.similar-item-poster { + display: block; + width: 100%; + height: auto; + border-radius: var(--border-radius-md); + aspect-ratio: 2 / 3; + object-fit: cover; + transition: transform 0.4s ease; +} + +.similar-item-info { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + padding: 0.8rem; + background: linear-gradient(to top, rgba(10, 10, 15, 0.95) 0%, transparent 100%); + border-bottom-left-radius: var(--border-radius-md); + border-bottom-right-radius: var(--border-radius-md); +} + +.similar-item-title { + color: var(--text-primary); + font-size: 0.9rem; + font-weight: 500; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.seasons-accordion .accordion-item { + background: transparent; + border-radius: var(--border-radius-lg); + border: 1px solid var(--glass-border); + margin-bottom: 1.5rem; + overflow: hidden; +} + +.seasons-accordion .accordion-button { + background: var(--card-bg); + color: var(--text-primary); + font-size: 1.1rem; + padding: 1.2rem 1.5rem; + border: none; + box-shadow: none !important; + transition: background-color 0.3s ease; +} + +.seasons-accordion .accordion-button:not(.collapsed) { + background: var(--glass); + color: var(--accent); + border-bottom: 1px solid var(--glass-border); +} + +.seasons-accordion .accordion-button:hover { + background: var(--glass); +} + +.seasons-accordion .accordion-button::after { + filter: brightness(0) invert(1) opacity(0.7); + transition: transform 0.3s ease; +} + +.seasons-accordion .accordion-button:not(.collapsed)::after { + filter: invert(1) opacity(1) brightness(1.5) contrast(200%); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%2300e0ff'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); +} + +.seasons-accordion .accordion-collapse { + overflow: hidden; + transition: height 0.35s ease; +} + +.season-info { + display: flex; + align-items: center; + gap: 1.5rem; + width: 100%; +} + +.season-poster { + width: 70px; + height: 105px; + object-fit: cover; + border-radius: var(--border-radius-sm); + flex-shrink: 0; +} + +.season-details { + flex-grow: 1; + min-width: 0; +} + +.season-title { + font-size: 1.3rem; + font-weight: 600; + margin-bottom: 0.4rem; +} + +.season-meta { + display: flex; + gap: 1.2rem; + font-size: 0.9rem; + color: var(--text-secondary); + margin-bottom: 0.5rem; +} + +.season-meta span { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.season-overview { + font-size: 0.9rem; + line-height: 1.5; + color: var(--text-secondary); + margin-top: 0.5rem; +} + +.season-episodes { + padding: 1rem 1.5rem; + background: var(--secondary); + border-top: 1px solid var(--glass-border); + max-height: 500px; + overflow-y: auto; +} + +.episode-card { + display: flex; + gap: 1rem; + padding: 1.2rem 0.5rem; + border-bottom: 1px solid var(--glass-border); + transition: background-color 0.3s ease; +} + +.episode-card:last-child { + border-bottom: none; + padding-bottom: 0.5rem; +} + +.episode-card:first-child { + padding-top: 0.5rem; +} + +.episode-card:hover { + background: var(--glass); +} + +.episode-number { + flex-shrink: 0; + width: 36px; + height: 36px; + background: var(--glass); + border: 1px solid var(--glass-border); + color: var(--text-primary); + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; + font-weight: 600; + font-size: 1rem; + margin-top: 5px; +} + +.episode-info { + flex: 1; + min-width: 0; +} + +.episode-title { + font-size: 1.1rem; + font-weight: 500; + margin-bottom: 0.4rem; + color: var(--text-primary); +} + +.episode-meta { + display: flex; + flex-wrap: wrap; + gap: 0.5rem 1rem; + font-size: 0.85rem; + color: var(--text-secondary); + margin-bottom: 0.5rem; +} + +.episode-meta span { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.episode-overview { + font-size: 0.9rem; + line-height: 1.6; + color: var(--text-secondary); + margin-top: 0.7rem; +} + +@media (max-width: 992px) { + .item-details-header { + flex-direction: column; + align-items: center; + text-align: center; + } + + .item-details-container { + padding-top: calc(5vh + var(--topbar-height)); + } + + .item-details-poster-wrapper { + max-width: 350px; + margin: 0 auto 1rem; + } + + .item-details-content { + text-align: center; + } + + .item-details-meta, + .item-details-genres, + .item-details-crew, + .item-details-actions, + .item-details-external-links { + justify-content: center; + } +} + +@media (max-width: 768px) { + .item-details-title { + font-size: 2rem; + } + + .cast-grid { + grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); + gap: 1.5rem; + } + + .similar-items-grid { + grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); + gap: 1rem; + } + + .back-button { + top: 1rem; + left: 1rem; + width: 38px; + height: 38px; + } + + .item-details-poster-wrapper { + max-width: 280px; + } + + .season-info { + flex-direction: column; + align-items: flex-start; + gap: 1rem; + text-align: left; + } + + .season-poster { + width: 100%; + max-width: 150px; + height: auto; + margin: 0; + } + + .episode-card { + flex-direction: column; + align-items: flex-start; + gap: 0.5rem; + } + + .episode-number { + margin-bottom: 0.8rem; + width: 32px; + height: 32px; + font-size: 0.9rem; + } +} + +@media (max-width: 576px) { + .item-details-poster-wrapper { + max-width: 240px; + } + + .item-details-title { + font-size: 1.8rem; + } + + .cast-grid { + grid-template-columns: repeat(auto-fill, minmax(95px, 1fr)); + gap: 1rem; + } + + .cast-photo { + width: 80px; + height: 80px; + } + + .similar-items-grid { + grid-template-columns: repeat(2, 1fr); + gap: 1rem; + } +} + +@media (min-width: 992px) { + .back-button { + left: calc(var(--sidebar-width) + 2rem); + } +} + + + +/* --- stats.css --- */ +#stats-section { + padding-top: 2rem; +} + +.stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 2rem; + margin-top: 2rem; +} + +.stats-card { + background: var(--glass); + border: 1px solid var(--glass-border); + border-radius: var(--border-radius-lg); + padding: 2rem; + display: flex; + flex-direction: column; + justify-content: space-between; + transition: var(--transition); + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); +} + +.stats-card:hover { + transform: translateY(-8px); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); + border-color: var(--accent); +} + +.stat-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 1.5rem; +} + +.stat-icon { + font-size: 2.2rem; + color: var(--accent); + background: var(--gradient); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + opacity: 0.8; +} + +.stat-label { + font-size: 1.1rem; + font-weight: 500; + color: var(--text-secondary); + text-align: right; +} + +.stat-value { + font-family: 'Orbitron', sans-serif; + font-size: clamp(2.5rem, 5vw, 3.5rem); + font-weight: 700; + color: var(--text-primary); + line-height: 1; + text-align: right; +} + +.chart-container { + background: var(--card-bg); + border: 1px solid var(--glass-border); + border-radius: var(--border-radius-lg); + padding: 2rem; + box-shadow: var(--shadow); + grid-column: span 1; +} + +.chart-container.full-width { + grid-column: 1 / -1; +} + +.chart-title { + font-size: 1.3rem; + font-weight: 600; + margin-bottom: 1.5rem; + color: var(--text-primary); + text-align: center; +} + +.chart-container canvas { + max-height: 400px; + width: 100% !important; +} + +#stats-filters { + display: flex; + justify-content: flex-end; + margin-bottom: 2rem; +} + +.token-details-card { + grid-column: 1 / -1; + display: none; +} + +.token-details-card .card-body { + padding: 1.5rem; +} + +.token-details-card .server-list { + list-style: none; + padding: 0; + margin: 0; + max-height: 200px; + overflow-y: auto; +} + +.token-details-card .server-list li { + padding: 0.75rem 1rem; + border-bottom: 1px solid var(--glass-border); + font-size: 0.95rem; + color: var(--text-secondary); +} + +.token-details-card .server-list li:last-child { + border-bottom: none; +} + +.token-details-card .server-list strong { + color: var(--text-primary); + margin-right: 0.5rem; +} + + +@media (max-width: 768px) { + .stats-card { + padding: 1.5rem; + } +} + +@media (max-width: 576px) { + .stats-grid { + grid-template-columns: 1fr; + } +} + +/* --- history.css --- */ +#history-section .section-header { + align-items: center; +} + +#history-section .btn-danger.btn-sm { + padding: 0.4rem 1rem; + font-size: 0.8rem; + font-weight: 500; +} + +#history-list { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.history-item { + display: flex; + align-items: center; + gap: 1.5rem; + padding: 1rem; + background: var(--card-bg); + border: 1px solid var(--glass-border); + border-radius: var(--border-radius-md); + transition: var(--transition); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); +} + +.history-item:hover { + background: rgba(0, 224, 255, 0.08); + transform: translateX(8px); + border-color: var(--accent); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); +} + +.history-poster { + width: 60px; + height: 90px; + object-fit: cover; + border-radius: var(--border-radius-sm); + flex-shrink: 0; + cursor: pointer; +} + +.history-info { + flex: 1; + min-width: 0; + cursor: pointer; +} + +.history-title-wrapper { + display: flex; + align-items: center; + gap: 0.75rem; + margin-bottom: 0.3rem; +} + +.history-title { + font-size: 1.1rem; + font-weight: 600; + margin-bottom: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + color: var(--text-primary); + flex-grow: 1; +} + +.badge.local-badge-history { + background-color: var(--success); + color: var(--primary); + font-size: 0.7rem; + padding: 0.25rem 0.6rem; + border-radius: 20px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + flex-shrink: 0; +} + +.history-meta { + font-size: 0.85rem; + color: var(--text-secondary); +} + +.history-actions { + display: flex; + gap: 0.5rem; + align-items: center; +} + +.history-actions .action-btn { + width: 36px; + height: 36px; + font-size: 0.9rem; + background: var(--glass); + border: 1px solid transparent; +} + +.history-actions .action-btn:hover { + border-color: transparent; +} + +.history-actions .delete-btn:hover { + background: var(--danger); +} + +@media (max-width: 576px) { + .history-item { + flex-direction: column; + align-items: flex-start; + gap: 1rem; + } + + .history-main-content { + display: flex; + gap: 1rem; + width: 100%; + } + + .history-actions { + width: 100%; + justify-content: space-around; + padding-top: 1rem; + border-top: 1px solid var(--glass-border); + } +} + + +/* --- footer.css --- */ +.footer { + background: var(--secondary); + padding: 1.5rem 2rem; + border-top: 1px solid var(--glass-border); + margin-top: 0; +} + +.footer .container { + max-width: 1200px; + margin: 0 auto; + padding: 0; +} + +.footer-content { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 1.5rem; +} + +.footer-logo-link { + text-decoration: none; +} + +.footer-logo-text { + font-family: 'Orbitron', sans-serif; + font-size: 1.5rem; + font-weight: 700; + letter-spacing: 1px; + background: var(--gradient); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + transition: var(--transition); + display: inline-block; +} + +.footer-logo-link:hover .footer-logo-text { + transform: scale(1.03); + filter: brightness(1.1); +} + +.footer-links { + display: flex; + gap: 1.5rem; + flex-wrap: wrap; + justify-content: center; +} + +.footer-link { + color: var(--text-secondary); + text-decoration: none; + transition: var(--transition); + font-size: 0.9rem; + font-weight: 500; +} + +.footer-link:hover { + color: var(--accent); + transform: translateY(-2px); +} + +.footer-credit { + font-size: 0.9rem; + color: var(--text-secondary); + opacity: 0.8; + margin: 0; +} + +@media (max-width: 768px) { + .footer-content { + flex-direction: column; + gap: 1.2rem; + } + + .footer { + padding: 2rem 1rem; + } +} + + +/* --- overlays.css --- */ +.lightbox { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(10, 10, 15, 0.96); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + z-index: 2000; + justify-content: center; + align-items: center; + opacity: 0; + transition: opacity 0.4s ease; +} + +.lightbox.active { + display: flex; + opacity: 1; +} + +.lightbox-content { + position: relative; + width: 90%; + max-width: 960px; + background: var(--secondary); + padding: 1rem; + border-radius: var(--border-radius-lg); + box-shadow: 0 15px 50px rgba(0, 0, 0, 0.5); + border: 1px solid var(--glass-border); + transform: scale(0.95); + transition: transform 0.4s ease; +} + +.lightbox.active .lightbox-content { + transform: scale(1); +} + +.lightbox-close { + position: absolute; + top: -15px; + right: -15px; + width: 38px; + height: 38px; + display: flex; + justify-content: center; + align-items: center; + background: var(--accent); + color: var(--primary); + border: none; + border-radius: 50%; + font-size: 1.1rem; + cursor: pointer; + transition: var(--transition); + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2); +} + +.lightbox-close:hover { + transform: scale(1.1) rotate(90deg); + background: var(--accent-dark); + box-shadow: 0 6px 15px rgba(0, 224, 255, 0.4); +} + +.video-container { + position: relative; + padding-bottom: 56.25%; + height: 0; + overflow: hidden; + border-radius: var(--border-radius-md); +} + +.video-container iframe { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; +} + +.notification { + position: relative; + min-width: 280px; + max-width: 350px; + margin-bottom: 1rem; + padding: 1.1rem 1.5rem; + border-radius: var(--border-radius-md); + background: var(--secondary); + color: var(--text-primary); + box-shadow: 0 5px 20px rgba(0, 0, 0, 0.3); + border: 1px solid var(--glass-border); + border-left-width: 5px; + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + transform: translateX(120%); + opacity: 0; + transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.5s ease; + overflow: hidden; +} + +.notification.show { + transform: translateX(0); + opacity: 1; +} + +.notification-content { + display: flex; + align-items: center; + gap: 1rem; +} + +.notification i.fas { + font-size: 1.2rem; + line-height: 1; +} + +.notification span { + flex: 1; +} + +.notification.success { + border-left-color: var(--success); +} + +.notification.error { + border-left-color: var(--danger); +} + +.notification.info { + border-left-color: var(--info); +} + +.notification.warning { + border-left-color: var(--warning); +} + +.light-theme .notification { + color: var(--text-primary); +} + +.modal-backdrop { + background-color: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(3px); + -webkit-backdrop-filter: blur(3px); +} + +.modal-content { + background-color: var(--secondary); + color: var(--text-primary); + border: 1px solid var(--glass-border); + border-radius: var(--border-radius-lg); + box-shadow: var(--shadow); + overflow: hidden; +} + +.modal-header { + border-bottom: 1px solid var(--glass-border); + background: rgba(255, 255, 255, 0.03); +} + +.modal-title { + color: var(--accent); + font-family: 'Orbitron', sans-serif; + font-size: 1.3rem; +} + +.modal-footer { + border-top: 1px solid var(--glass-border); + background: rgba(10, 10, 15, 0.5); + padding: 1rem; +} + +.modal .btn-close { + filter: invert(1) grayscale(100%) brightness(150%) opacity(0.8); + transition: transform 0.3s ease; +} + +.modal .btn-close:hover { + filter: invert(1) grayscale(100%) brightness(200%) opacity(1); + transform: rotate(90deg); +} + +.light-theme .modal-content { + background-color: var(--secondary); +} +.light-theme .modal-header, .light-theme .modal-footer { + background-color: var(--primary); +} + +#settingsModal .nav-tabs { + border-bottom: 1px solid var(--glass-border); + padding: 0.5rem 1rem 0; + background-color: rgba(10,10,15,0.7); +} +.light-theme #settingsModal .nav-tabs { + background-color: var(--primary); +} + +#settingsModal .nav-tabs .nav-link { + border: none; + color: var(--text-secondary); + border-bottom: 3px solid transparent; + transition: var(--transition); + padding: 0.8rem 1.2rem; + font-weight: 500; +} + +#settingsModal .nav-tabs .nav-link:hover { + color: var(--text-primary); + border-bottom-color: var(--glass-border); +} + +#settingsModal .nav-tabs .nav-link.active { + color: var(--accent); + background-color: transparent; + border-bottom-color: var(--accent); + font-weight: 600; +} + +#settingsModal .tab-content label { + font-weight: 500; +} + +#settingsModal .tab-content p, +#settingsModal .tab-content .text-muted { + color: var(--text-secondary); +} + +#settingsModal .tab-content h5 { + color: var(--text-primary); +} + +#settingsModal .tab-content input[type="checkbox"] { + margin-right: 0.6rem; + transform: scale(1.1); + accent-color: var(--accent); +} + +#editor { + height: 300px; + width: 100%; + border-radius: var(--border-radius-md); + border: 1px solid var(--glass-border); + font-family: monospace; +} + +/* --- music-player.css --- */ +#musicPlayerContainer { + position: fixed; + top: 0; + left: 0; + width: 320px; + height: 100%; + background: var(--secondary); + box-shadow: 5px 0 35px rgba(0, 0, 0, 0.3); + display: flex; + flex-direction: column; + z-index: 1040; + border-right: 1px solid var(--glass-border); + transform: translateX(-100%); + transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1), height 0.3s ease; +} + +body.miniplayer-active #musicPlayerContainer { + height: calc(100% - 85px); +} + +.sidenav { + display: flex; + flex-direction: column; + height: 100%; + overflow: hidden; + background: transparent; + position: relative; +} + +.sidenav-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.7rem 1.2rem; + background: var(--primary); + color: var(--text-primary); + border-bottom: 1px solid var(--glass-border); + flex-shrink: 0; + position: relative; + z-index: 3; +} + +.sidenav-header h4 { + margin: 0; + font-family: 'Orbitron', sans-serif; + font-size: 1.4rem; + color: var(--accent); + line-height: 1; +} + +.sidenav-header button { + background: none; + border: none; + color: var(--text-secondary); + font-size: 1.4rem; + cursor: pointer; + padding: 0.5rem; + line-height: 1; + transition: color 0.3s ease, transform 0.3s ease; +} + +.sidenav-header button:hover { + color: var(--accent); + transform: rotate(90deg); +} + +.music-panel { + display: flex; + flex-direction: column; + flex-grow: 1; + overflow: hidden; + min-height: 0; + position: absolute; + top: 57px; + left: 0; + width: 100%; + height: calc(100% - 57px); + background-color: var(--secondary); +} + +#artistListContainer { + z-index: 2; + transform: translateX(0); +} +#songListContainer { + z-index: 3; + transform: translateX(100%); + opacity: 0; + visibility: hidden; +} + +.panel-controls { + padding: 1rem; + border-bottom: 1px solid var(--glass-border); + flex-shrink: 0; +} + +.search-wrapper { + position: relative; +} +.search-wrapper i { + position: absolute; + left: 1.1rem; + top: 50%; + transform: translateY(-50%); + color: var(--text-secondary); + font-size: 0.9em; +} +.search-wrapper input, +.search-wrapper-songs input { + width: 100%; + padding: 0.7rem 1.3rem 0.7rem 2.5rem; + font-size: 0.9rem; + background: var(--glass); + border: 1px solid var(--glass-border); + border-radius: 50px; + color: var(--text-primary); + transition: var(--transition); +} +.search-wrapper input:focus, +.search-wrapper-songs input:focus { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 3px rgba(0, 224, 255, 0.2); + background: rgba(0, 224, 255, 0.08); +} + +.artist-grid { + padding: 0.5rem; + display: grid; + grid-template-columns: 1fr; + gap: 0.25rem; + overflow-y: auto; + flex-grow: 1; +} + +.artist-card { + background: transparent; + border-radius: var(--border-radius-sm); + border: 1px solid transparent; + overflow: hidden; + cursor: pointer; + transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out; + display: flex; + flex-direction: row; + align-items: center; + padding: 0.6rem 0.75rem; + gap: 1rem; +} + +.artist-card:hover { + background: var(--glass); +} + +.artist-card.current-artist { + background: rgba(0, 224, 255, 0.1); + border-color: rgba(0, 224, 255, 0.2); +} + +.artist-thumb-wrapper { + background-color: var(--primary); + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + width: 40px; + height: 40px; + flex-shrink: 0; + border-radius: 50%; + border: 1px solid var(--glass-border); +} + +.artist-thumb { + width: 100%; + height: 100%; + object-fit: cover; +} + +.artist-thumb-placeholder { + font-size: 1.5rem; + color: var(--text-secondary); +} + +.artist-card-title { + padding: 0; + font-size: 0.9rem; + font-weight: 500; + color: var(--text-secondary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex-grow: 1; + transition: color 0.2s ease-in-out; +} + +.artist-card:hover .artist-card-title, +.artist-card.current-artist .artist-card-title { + color: var(--text-primary); +} + +.pagination-controls { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.75rem 1rem; + border-top: 1px solid var(--glass-border); + flex-shrink: 0; +} + +.pagination-controls #artistCounter { + font-size: 0.85rem; + color: var(--text-secondary); + font-weight: 500; +} + +.btn-icon-sm { + background: var(--glass); + border: 1px solid var(--glass-border); + color: var(--text-secondary); + width: 32px; + height: 32px; + border-radius: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + transition: var(--transition); +} + +.btn-icon-sm:hover { + background-color: var(--accent); + color: var(--primary); + border-color: var(--accent); +} + +.custom-select { + position: relative; + width: 100%; + margin-bottom: 1rem; +} + +.select-selected { + background-color: var(--glass); + color: var(--text-primary); + padding: 0.7rem 2.5rem 0.7rem 1.3rem; + border: 1px solid var(--glass-border); + border-radius: 50px; + cursor: pointer; + user-select: none; + display: flex; + justify-content: space-between; + align-items: center; + transition: var(--transition); +} +.select-selected:hover { + border-color: var(--accent); +} + +.select-items { + position: absolute; + background-color: var(--primary); + top: 105%; + left: 0; + right: 0; + z-index: 99; + border-radius: var(--border-radius-md); + border: 1px solid var(--glass-border); + overflow-y: auto; + box-shadow: 0 8px 20px rgba(0,0,0,0.3); + max-height: 250px; +} +.select-hide { display: none; } +.select-option { + color: var(--text-secondary); + padding: 0.8rem 1.3rem; + cursor: pointer; + user-select: none; +} +.select-option:hover { + background-color: var(--glass); + color: var(--text-primary); +} + +.artist-grid::-webkit-scrollbar, +.select-items::-webkit-scrollbar, +.song-list::-webkit-scrollbar { + width: 8px; +} +.artist-grid::-webkit-scrollbar-thumb, +.select-items::-webkit-scrollbar-thumb, +.song-list::-webkit-scrollbar-thumb { + background-color: var(--accent); + border-radius: 4px; +} +.artist-grid::-webkit-scrollbar-track, +.select-items::-webkit-scrollbar-track, +.song-list::-webkit-scrollbar-track { + background: transparent; +} + + +.song-list-controls { + display: flex; + align-items: center; + gap: 1rem; + padding: 0.75rem 1rem; +} +.back-btn-icon { + flex-shrink: 0; +} +#artist-header-info { + display: flex; + align-items: center; + gap: 1rem; + overflow: hidden; + flex-grow: 1; + justify-content: center; +} +#artist-header-thumb { + width: 45px; + height: 45px; + object-fit: cover; + border-radius: 50%; + border: 2px solid var(--glass-border); +} +#artist-header-title { + font-size: 1.2rem; + font-weight: 600; + color: var(--text-primary); + margin: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.search-wrapper-songs { + position: relative; + padding: 0 1rem 1rem; + border-bottom: 1px solid var(--glass-border); +} + +.song-list { + flex-grow: 1; + overflow-y: auto; + padding: 1rem; +} + +.album-group { + margin-bottom: 1.5rem; +} +.album-group-title { + font-size: 0.8rem; + font-weight: 600; + color: var(--accent); + text-transform: uppercase; + letter-spacing: 1px; + margin-bottom: 0.8rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid var(--glass-border); +} + +.song-item { + display: flex; + align-items: center; + padding: 0.6rem 0.5rem; + border-radius: var(--border-radius-sm); + cursor: pointer; + transition: background-color 0.2s ease; +} +.song-item:hover { + background: var(--glass); +} +.song-item.current-song { + background: var(--accent); +} +.song-item.current-song .song-number, +.song-item.current-song .item-title { + color: var(--primary) !important; +} + +.song-number { + font-size: 0.9rem; + color: var(--text-secondary); + width: 2rem; + text-align: center; + flex-shrink: 0; +} +.song-details { + flex-grow: 1; + overflow: hidden; +} +.song-details .item-title { + font-size: 0.9rem; + color: var(--text-primary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.song-item .play-icon { + color: var(--text-secondary); + opacity: 0; + transition: opacity 0.2s ease; +} + +@keyframes spin-song { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.song-item .loading-spinner { + width: 16px; + height: 16px; + border: 2px solid var(--text-secondary); + border-top-color: var(--accent); + border-radius: 50%; + animation: spin-song 0.8s linear infinite; +} +.song-item:hover .play-icon, +.song-item.current-song .play-icon { + opacity: 1; +} +.song-item.current-song .play-icon { + color: var(--primary); +} +.list-item-empty { + padding: 2rem 1rem; + text-align: center; + color: var(--text-secondary); + font-style: italic; + background-color: transparent; + border: 1px dashed var(--glass-border); + border-radius: var(--border-radius-md); +} + +#side-nav-now-playing { + display: flex; + align-items: center; + gap: 1rem; + padding: 1rem; + background: var(--primary); + border-top: 1px solid var(--glass-border); + flex-shrink: 0; + cursor: pointer; +} +#side-nav-now-playing .details { + overflow: hidden; +} +#side-nav-now-playing img { + width: 45px; + height: 45px; + border-radius: var(--border-radius-sm); + flex-shrink: 0; +} +#side-nav-now-playing p { + margin: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +#side-nav-track-title { + font-weight: 600; +} +#side-nav-track-artist { + font-size: 0.8rem; + color: var(--text-secondary); +} +#side-nav-play-pause { + margin-left: auto; + flex-shrink: 0; + font-size: 1.2rem; +} + +#miniplayer { + position: fixed; + bottom: 0; + left: 0; + width: 100%; + height: 85px; + padding: 0 1.5rem; + display: grid; + grid-template-columns: minmax(200px, 1fr) 2fr minmax(200px, 1fr); + gap: 1.5rem; + align-items: center; + z-index: 1045; + color: var(--text-primary); + transform: translateY(110%); +} + +.miniplayer-bg { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(16, 17, 22, 0.8); + backdrop-filter: blur(15px); + -webkit-backdrop-filter: blur(15px); + border-top: 1px solid var(--glass-border); + z-index: -1; +} + +.player-left-info { + display: flex; + align-items: center; + gap: 1rem; + overflow: hidden; + min-width: 0; +} +.album-cover { + width: 55px; + height: 55px; + border-radius: var(--border-radius-sm); + flex-shrink: 0; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25); + object-fit: cover; + cursor: pointer; + transition: transform 0.3s ease; +} +#trackInfo .details { + display: flex; + flex-direction: column; + justify-content: center; + overflow: hidden; + gap: 0.1rem; +} +#trackTitle, #trackArtist { + margin: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +#trackTitle { font-weight: 600; } +#trackArtist { font-size: 0.8rem; color: var(--text-secondary); } + +.player-center-controls { + display: flex; + flex-direction: column; + gap: 0.5rem; + width: 100%; + min-width: 250px; +} +#player-controls { + display: flex; + align-items: center; + justify-content: center; + gap: 0.8rem; +} +.control-btn { + background: transparent; + border: none; + color: var(--text-secondary); + font-size: 1rem; + cursor: pointer; + transition: all 0.2s ease; + width: 38px; + height: 38px; + border-radius: 50%; + display: inline-flex; + align-items: center; + justify-content: center; +} +.control-btn:hover { + color: var(--accent); + background: var(--glass); +} +.control-btn.active { color: var(--accent); } +.control-btn.play-pause-main { + font-size: 1.5rem; + width: 48px; + height: 48px; + background: var(--accent); + color: var(--primary); +} +.control-btn.play-pause-main:hover { + transform: scale(1.1); + box-shadow: 0 0 15px rgba(0, 224, 255, 0.4); +} + +#closeMiniplayerBtn { + color: var(--text-secondary); +} + +#closeMiniplayerBtn:hover { + color: var(--accent); +} + +.fab-btn { + position: fixed; + bottom: 2rem; + right: 2rem; + width: 60px; + height: 60px; + border-radius: 50%; + background-color: var(--accent); + color: var(--primary); + border: none; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + display: flex; + align-items: center; + justify-content: center; + font-size: 1.5rem; + cursor: pointer; + transition: transform 0.3s ease, box-shadow 0.3s ease; + z-index: 1030; +} + +.fab-btn:hover { + transform: scale(1.1); + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4); +} + +.time-and-progress { + display: flex; + align-items: center; + width: 100%; + gap: 1rem; +} +.time-label { font-size: 0.75rem; color: var(--text-secondary); } + +#progressBarContainer { + flex-grow: 1; + height: 5px; + background-color: rgba(255, 255, 255, 0.15); + border-radius: 2.5px; + cursor: pointer; + position: relative; + transition: height 0.2s ease; +} +#progressBarContainer:hover { + height: 8px; +} +#seek-hover-bar { + position: absolute; + height: 100%; + background-color: rgba(255, 255, 255, 0.25); + border-radius: inherit; + width: 0%; +} +#played-bar { + position: relative; + height: 100%; + background: var(--accent); + border-radius: inherit; + width: 0%; +} +#progress-handle { + position: absolute; + top: 50%; + transform: translate(-50%, -50%) scale(0); + width: 14px; + height: 14px; + background-color: var(--text-primary); + border-radius: 50%; + transition: transform 0.2s ease; +} +#progressBarContainer:hover #progress-handle { + transform: translate(-50%, -50%) scale(1); +} + +.player-right-actions { + display: flex; + justify-content: flex-end; + align-items: center; + gap: 0.5rem; +} +#volumeControl { position: relative; } +.volume-slider-wrapper { + position: absolute; + top: 50%; + left: calc(100% + 15px); + transform: translateY(-50%); + background: var(--secondary); + padding: 0.5rem 1rem; + border-radius: var(--border-radius-md); + border: 1px solid var(--glass-border); + box-shadow: 0 5px 15px rgba(0,0,0,0.3); + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; +} +.volume-slider-wrapper.active { + opacity: 1; + visibility: visible; + left: calc(100% + 5px); +} +#volumeSlider { + -webkit-appearance: none; + appearance: none; + /* Removed writing-mode: bt-lr; */ + width: 100px; /* Adjusted for horizontal orientation */ + height: 8px; /* Adjusted for horizontal orientation */ + background: var(--glass); + border-radius: 4px; +} +#volumeSlider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 16px; + height: 16px; + background: var(--accent); + border-radius: 50%; + cursor: pointer; +} + +#audioPlayer { display: none; } +body.miniplayer-active { padding-bottom: 85px; } + +@media (max-width: 768px) { + body.miniplayer-active { padding-bottom: 110px; } + + body.miniplayer-active #musicPlayerContainer { + height: calc(100% - 110px); + } + + #miniplayer { + grid-template-columns: 1fr auto; + grid-template-rows: auto auto; + grid-template-areas: + "info actions" + "center center"; + height: 110px; + padding: 0.5rem 1rem; + gap: 0.5rem; + } + .player-left-info { grid-area: info; } + .player-center-controls { grid-area: center; padding: 0 1rem; } + .player-right-actions { grid-area: actions; justify-content: flex-end; } + #downloadBtn, #downloadAlbumBtn, #eqBtn, #volumeControl { display: none; } +} + +@media (max-width: 576px) { + #player-controls { gap: 1.5rem; } +} + + +/* --- photos.css --- */ +#photos-section { + padding-top: 2rem; +} + +.photos-header { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + gap: 1rem; + margin-bottom: 2rem; + padding: 1.5rem; + background: var(--glass); + border: 1px solid var(--glass-border); + border-radius: var(--border-radius-lg); +} + +#photos-breadcrumb { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 0.5rem; + padding: 0; + margin: 0; + list-style: none; + font-size: 0.95rem; +} + +#photos-breadcrumb .breadcrumb-item a { + color: var(--text-secondary); + text-decoration: none; + transition: var(--transition); + padding: 0.3rem 0.6rem; + border-radius: var(--border-radius-sm); +} + +#photos-breadcrumb .breadcrumb-item a:hover { + color: var(--accent); + background-color: var(--glass); +} + +#photos-breadcrumb .breadcrumb-item.active { + color: var(--text-primary); + font-weight: 600; +} + +#photos-breadcrumb .breadcrumb-divider { + color: var(--text-secondary); + opacity: 0.5; + margin: 0 0.3rem; +} + +#photos-token-select { + min-width: 250px; +} + +#photos-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 1.8rem; +} + +.photo-card, .album-card { + background: var(--card-bg); + border-radius: var(--border-radius-lg); + overflow: hidden; + position: relative; + transition: var(--transition); + box-shadow: var(--shadow); + cursor: pointer; + border: 1px solid transparent; + aspect-ratio: 1 / 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + padding: 1rem; +} + +.photo-card:hover, .album-card:hover { + transform: translateY(-10px) scale(1.03); + box-shadow: 0 18px 45px rgba(0, 0, 0, 0.5); + border-color: rgba(0, 224, 255, 0.5); + z-index: 10; +} + +.album-card-icon { + font-size: 4rem; + color: var(--accent); + margin-bottom: 1rem; + transition: transform 0.4s ease; +} + +.album-card:hover .album-card-icon { + transform: scale(1.1); +} + +.album-card-title { + font-size: 1.1rem; + font-weight: 600; + color: var(--text-primary); +} + +.album-card-meta { + font-size: 0.85rem; + color: var(--text-secondary); + margin-top: 0.3rem; +} + +.photo-card { + padding: 0; + justify-content: flex-end; +} + +.photo-card-img { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 0.6s cubic-bezier(0.23, 1, 0.32, 1); +} + +.photo-card:hover .photo-card-img { + transform: scale(1.08); +} + +.photo-card-caption { + position: relative; + z-index: 1; + width: 100%; + padding: 0.8rem; + background: linear-gradient(to top, rgba(10, 10, 15, 0.95) 20%, transparent 100%); + color: var(--text-primary); + font-size: 0.9rem; + font-weight: 500; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + opacity: 0; + transform: translateY(10px); + transition: var(--transition); +} + +.photo-card:hover .photo-card-caption { + opacity: 1; + transform: translateY(0); +} + +#photo-lightbox { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(10, 10, 15, 0.96); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + z-index: 2050; + display: none; + justify-content: center; + align-items: center; + opacity: 0; +} + +#photo-lightbox.active { + display: flex; +} + +.photo-lightbox-container { + position: relative; + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; +} + +.photo-lightbox-img { + max-width: 90vw; + max-height: 85vh; + object-fit: contain; + box-shadow: 0 10px 40px rgba(0,0,0,0.5); + border-radius: var(--border-radius-sm); +} + +.photo-lightbox-btn { + position: absolute; + top: 50%; + transform: translateY(-50%); + background: rgba(255,255,255,0.1); + border: 1px solid var(--glass-border); + color: var(--text-primary); + width: 50px; + height: 70px; + border-radius: var(--border-radius-sm); + font-size: 1.5rem; + cursor: pointer; + transition: var(--transition); + backdrop-filter: blur(5px); + -webkit-backdrop-filter: blur(5px); +} +.photo-lightbox-btn:hover { + background: var(--accent); + color: var(--primary); + transform: translateY(-50%) scale(1.05); +} + +#photo-lightbox-prev { + left: 2vw; +} +#photo-lightbox-next { + right: 2vw; +} + +#photo-lightbox-close { + position: absolute; + top: 2rem; + right: 2rem; + width: 42px; + height: 42px; + top: 20px; + right: 20px; +} + +.photo-lightbox-caption { + position: absolute; + bottom: 2vh; + left: 50%; + transform: translateX(-50%); + background: rgba(10,10,15,0.8); + color: var(--text-primary); + padding: 0.8rem 1.5rem; + border-radius: var(--border-radius-md); + font-size: 1rem; + text-align: center; + max-width: 70vw; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + + +/* --- activity-viewer.css --- */ +#activityViewerModal .modal-body { + max-height: 70vh; + overflow-y: auto; +} + +.session-card { + display: flex; + gap: 1.5rem; + padding: 1.2rem; + background-color: var(--glass); + border: 1px solid var(--glass-border); + border-radius: var(--border-radius-md); + margin-bottom: 1rem; + transition: var(--transition); +} + +.session-card:hover { + background-color: rgba(255, 255, 255, 0.08); + border-color: var(--accent); +} + +.session-poster { + width: 80px; + height: 120px; + object-fit: cover; + border-radius: var(--border-radius-sm); + flex-shrink: 0; +} + +.session-info { + display: flex; + flex-direction: column; + flex-grow: 1; + gap: 1rem; +} + +.session-details p { + margin: 0 0 0.5rem 0; + font-size: 0.9rem; + color: var(--text-secondary); +} + +.session-details strong { + color: var(--text-primary); + font-weight: 600; +} + +.session-identifier { + margin-top: auto; +} + +.session-identifier label { + font-size: 0.8rem; + font-weight: 500; + color: var(--text-secondary); + margin-bottom: 0.25rem; +} + +.session-identifier .input-group .form-control { + background-color: var(--primary) !important; + font-family: monospace; + font-size: 0.85rem; +} + +#activity-results .empty-state { + padding: 2rem; + margin-top: 1rem; +} + + +/* --- m3u-generator.css --- */ +/* M3U Generator Section */ + +.m3u-animated-item { + opacity: 0; + transform: translateY(20px); +} + +#m3u-generator-section { + max-width: 1400px; + margin: 0 auto; +} + +.m3u-container { + display: grid; + grid-template-columns: 2fr 1fr; + gap: 2.5rem; + margin-top: 2rem; +} + +.m3u-config-panel { + background: var(--glass); + border: 1px solid var(--glass-border); + border-radius: var(--border-radius-lg); + padding: 2rem; + box-shadow: var(--shadow); +} + +.m3u-step { + margin-bottom: 2.5rem; +} + +.m3u-step:last-child { + margin-bottom: 0; +} + +.m3u-step-header { + display: flex; + align-items: center; + margin-bottom: 1.5rem; +} + +.m3u-step-number { + font-size: 1.2rem; + font-weight: 700; + color: var(--primary); + background: var(--gradient); + border-radius: 50%; + width: 36px; + height: 36px; + display: flex; + align-items: center; + justify-content: center; + margin-right: 1rem; +} + +.m3u-step-title { + font-family: 'Orbitron', sans-serif; + font-size: 1.4rem; + font-weight: 600; + color: var(--text-primary); +} + +#m3u-server-select { + width: 100%; +} + +#m3u-libraries-container { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); + gap: 1rem; + max-height: 400px; + overflow-y: auto; + padding-right: 1rem; +} + +#m3u-libraries-container::-webkit-scrollbar { + width: 8px; +} + +#m3u-libraries-container::-webkit-scrollbar-track { + background: transparent; +} + +#m3u-libraries-container::-webkit-scrollbar-thumb { + background-color: var(--glass-border); + border-radius: 8px; +} + +#m3u-libraries-container .form-check { + background: rgba(0, 0, 0, 0.15); + padding: 0.8rem 1rem; + border-radius: var(--border-radius-md); + transition: background-color 0.2s; + display: flex; + align-items: center; + cursor: pointer; +} + +#m3u-libraries-container .form-check:hover { + background: rgba(0, 0, 0, 0.25); +} + +#m3u-libraries-container .form-check-input { + width: 1.1em; + height: 1.1em; + margin-right: 0.8rem; + background-color: var(--secondary); + border: 1px solid var(--glass-border); + flex-shrink: 0; /* Prevent shrinking */ +} + +#m3u-libraries-container .form-check-label { + font-weight: 500; + cursor: pointer; + color: var(--text-secondary); + font-size: 0.9rem; + line-height: 1.2; /* Ensure consistent line height */ +} + +#m3u-libraries-container .form-check-input:checked { + background-color: var(--accent); + border-color: var(--accent); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%230a0a0f' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e"); +} + +#m3u-libraries-container .form-check-label i { + color: var(--text-secondary); + width: 20px; + text-align: center; + margin-right: 0.5rem; + transition: color 0.2s; +} + +#m3u-libraries-container .form-check-input:checked + .form-check-label i { + color: var(--accent); +} + +.m3u-info-panel { + background: var(--card-bg); + border-radius: var(--border-radius-lg); + padding: 2rem; + text-align: center; + display: flex; + flex-direction: column; + justify-content: space-between; +} + +.m3u-info-title { + font-family: 'Orbitron', sans-serif; + font-size: 1.4rem; + margin-bottom: 1.5rem; + color: white; /* Changed to white for better visibility */ +} + +.m3u-instructions { + text-align: left; + margin-left: 1.5rem; + color: var(--text-secondary); + font-size: 0.95rem; + margin-bottom: 2rem; +} + +.m3u-instructions li { + margin-bottom: 1rem; +} + +#download-m3u-btn { + width: 100%; + padding: 1rem; + font-size: 1rem; + border-radius: var(--border-radius-md); + transition: all 0.3s ease; /* Added for animation */ +} + +#download-m3u-btn:hover { + transform: translateY(-3px); /* Added for hover effect */ + box-shadow: var(--shadow-lg); /* Added for hover effect */ +} + +#download-m3u-btn span { + margin-left: 0.5rem; +} + +/* Loading state for libraries */ +#m3u-libraries-loader { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 150px; + color: var(--text-secondary); +} + +#m3u-libraries-loader .spinner-border { + width: 3rem; + height: 3rem; + margin-bottom: 1rem; +} + +/* Light Theme */ +.light-theme .m3u-config-panel { + background: var(--secondary); +} + +.light-theme #m3u-libraries-container .form-check { + background: rgba(0, 0, 0, 0.03); +} + +.light-theme #m3u-libraries-container .form-check:hover { + background: rgba(0, 0, 0, 0.06); +} + +.light-theme #m3u-libraries-container .form-check-input { + background-color: #e9ecef; + border-color: #ced4da; +} + +.light-theme #m3u-libraries-container .form-check-input:checked { + background-color: var(--accent-dark); + border-color: var(--accent-dark); +} + +.light-theme #m3u-libraries-container .form-check-label i { + color: var(--text-secondary); +} + +.light-theme #m3u-libraries-container .form-check-input:checked + .form-check-label i { + color: var(--accent-dark); +} + +/* Responsive */ +@media (max-width: 992px) { + .m3u-container { + grid-template-columns: 1fr; + } +} + + + +/* --- equalizer.css --- */ +#equalizer-panel { + position: absolute; + bottom: 100%; + left: 50%; + transform: translateX(-50%) translateY(100%); + width: 520px; + background-color: var(--secondary); + border-top: 1px solid var(--glass-border); + border-left: 1px solid var(--glass-border); + border-right: 1px solid var(--glass-border); + border-radius: 12px 12px 0 0; + box-shadow: 0 -5px 25px rgba(0,0,0,0.3); + z-index: 100; + overflow: hidden; + display: none; + font-family: 'Montserrat', sans-serif; +} + +.equalizer-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 15px; + background-color: var(--primary); + color: var(--text-primary); + border-bottom: 1px solid var(--glass-border); +} + +.equalizer-header h5 { + margin: 0; + font-weight: 600; + font-family: 'Orbitron', sans-serif; +} + +.close-btn { + background: none; + border: none; + color: var(--text-secondary); + font-size: 1.2rem; + cursor: pointer; + transition: color 0.2s; +} +.close-btn:hover { color: var(--text-primary); } + +.equalizer-top-bar { + display: flex; + justify-content: space-around; + align-items: center; + padding: 15px; + border-bottom: 1px solid var(--glass-border); + gap: 20px; +} + +.control-group { + display: flex; + align-items: center; + gap: 10px; +} + +.control-group label { + font-size: 0.8rem; + color: var(--text-secondary); + font-weight: 500; + white-space: nowrap; +} + +.control-group.preamp { + flex-grow: 1; +} + +.equalizer-bands-grid { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 20px 15px; + padding: 20px 15px; +} + +.band { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; +} + +.band label { + font-size: 0.75rem; + color: var(--text-secondary); + font-weight: 500; +} + +.eq-slider { + -webkit-appearance: none; + appearance: none; + width: 100%; + height: 6px; + background: var(--glass); + outline: none; + border-radius: 3px; + cursor: pointer; + transition: opacity 0.2s; +} + +.eq-slider::-webkit-slider-runnable-track { + width: 100%; + height: 6px; + cursor: pointer; + background: var(--glass); + border-radius: 3px; +} + +.eq-slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 18px; + height: 18px; + background: var(--accent); + border: 3px solid var(--secondary); + border-radius: 50%; + cursor: pointer; + margin-top: -6px; + transition: background 0.2s, box-shadow 0.2s; + box-shadow: 0 0 5px rgba(0, 224, 255, 0.4); +} + +.eq-slider:hover::-webkit-slider-thumb { + background: var(--accent); + box-shadow: 0 0 10px rgba(0, 224, 255, 0.6); +} + +.eq-slider::-moz-range-track { + width: 100%; + height: 6px; + cursor: pointer; + background: var(--glass); + border-radius: 3px; +} + +.eq-slider::-moz-range-thumb { + width: 18px; + height: 18px; + background: var(--accent); + border: 3px solid var(--secondary); + border-radius: 50%; + cursor: pointer; + box-shadow: 0 0 5px rgba(0, 224, 255, 0.4); +} + +.slider-value { + font-size: 0.7rem; + color: var(--text-secondary); + background: var(--primary); + padding: 2px 5px; + border-radius: 3px; + min-width: 35px; + text-align: center; +} + +.custom-select-sm { + background-color: var(--primary); + color: var(--text-primary); + border: 1px solid var(--glass-border); + border-radius: 4px; + padding: 5px 8px; + font-size: 0.8rem; +} + +.visualizer-container { + height: 80px; + background-color: var(--primary); + border-top: 1px solid var(--glass-border); + padding: 0; + margin: 0; + overflow: hidden; +} + +#visualizer-canvas { + width: 100%; + height: 100%; + display: block; +} + + +/* --- providers.css --- */ +.providers-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); + gap: 30px; + padding: 30px; + perspective: 1000px; +} + +.provider-card { + position: relative; + width: 140px; + height: 140px; + background-color: transparent; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: transform 0.4s ease, box-shadow 0.4s ease; + box-shadow: 0 0 0px rgba(0, 0, 0, 0.3); + overflow: hidden; + border: none; +} + +.provider-card:hover { + transform: scale(1.1) translateY(-5px); + box-shadow: 0 0 25px rgba(0, 224, 255, 0.7); +} + +.provider-logo { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 0; + transition: filter 0.3s ease; +} + +.provider-card:hover .provider-logo { + filter: brightness(1.1); +} + +.provider-name { + display: none; +} + +.provider-card.available { + box-shadow: 0 0 15px #00e0ff; +} + +.provider-card.available::after { + content: '00c'; + font-family: 'Font Awesome 5 Free'; + font-weight: 900; + position: absolute; + top: -5px; + right: -5px; + background-color: #00e0ff; + color: #121212; + border-radius: 50%; + width: 25px; + height: 25px; + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + box-shadow: 0 0 10px rgba(0, 224, 255, 0.5); + z-index: 11; +} + +.provider-tooltip { + position: absolute; + bottom: -35px; + background-color: #00e0ff; + color: #121212; + padding: 5px 12px; + border-radius: 15px; + font-size: 14px; + font-weight: 600; + white-space: nowrap; + opacity: 0; + transform: translateY(10px); + transition: opacity 0.3s ease, transform 0.3s ease; + pointer-events: none; + z-index: 10; +} + +.provider-card:hover .provider-tooltip { + opacity: 1; + transform: translateY(0); +} diff --git a/js/api.js b/js/api.js index 976391b..6e75844 100644 --- a/js/api.js +++ b/js/api.js @@ -95,7 +95,8 @@ export async function getMusicUrlsFromPlex(token, protocolo, ip, puerto, artista year: track.getAttribute("parentYear") || track.getAttribute("year"), genre: Array.from(track.querySelectorAll("Genre")).map(g => g.getAttribute('tag')).join(', ') || '', index: parseInt(track.getAttribute("index") || 0, 10), - albumIndex: parseInt(track.getAttribute("parentIndex") || 0, 10) + albumIndex: parseInt(track.getAttribute("parentIndex") || 0, 10), + trackIndex: parseInt(track.getAttribute("index") || 0, 10) }; }).filter(track => track !== null); @@ -114,6 +115,62 @@ export async function getMusicUrlsFromPlex(token, protocolo, ip, puerto, artista } } +export async function getMusicUrlsFromJellyfin(serverUrl, userId, token, artistId) { + try { + const albumsUrl = `${serverUrl}/Users/${userId}/Items?ParentId=${artistId}&IncludeItemTypes=MusicAlbum&Recursive=true&Fields=ImageTags`; + const albumsResponse = await fetch(albumsUrl, { headers: { 'X-Emby-Token': token } }); + if (!albumsResponse.ok) throw new Error(`Failed to fetch albums: ${albumsResponse.status}`); + const albumsData = await albumsResponse.json(); + + let allTracks = []; + + for (const album of albumsData.Items) { + const songsUrl = `${serverUrl}/Users/${userId}/Items?ParentId=${album.Id}&IncludeItemTypes=Audio&Recursive=true&Fields=MediaSources,ImageTags`; + const songsResponse = await fetch(songsUrl, { headers: { 'X-Emby-Token': token } }); + if (!songsResponse.ok) continue; + const songsData = await songsResponse.json(); + + const albumTracks = songsData.Items.map(track => { + const source = track.MediaSources?.[0]; + if (!source) return null; + + const streamUrl = `${serverUrl}/Audio/${track.Id}/stream.${source.Container || 'mp3'}?api_key=${token}&static=true`; + const coverUrl = album.ImageTags?.Primary ? `${serverUrl}/Items/${album.Id}/Images/Primary?tag=${album.ImageTags.Primary}` : 'img/no-poster.png'; + + return { + url: streamUrl, + titulo: track.Name || 'Pista desconocida', + album: album.Name || 'Álbum desconocido', + artista: track.AlbumArtist || 'Artista desconocido', + cover: coverUrl, + extension: source.Container || "mp3", + id: track.Id, + artistId: artistId, + year: album.ProductionYear, + genre: track.Genres?.join(', ') || '', + index: track.IndexNumber || 0, + albumIndex: album.IndexNumber || 0, + trackIndex: track.IndexNumber || 0 + }; + }).filter(track => track !== null); + allTracks.push(...albumTracks); + } + + allTracks.sort((a, b) => { + if (a.albumIndex !== b.albumIndex) { + return a.albumIndex - b.albumIndex; + } + return a.index - b.index; + }); + + return allTracks; + + } catch (error) { + console.error("Error in getMusicUrlsFromJellyfin:", error); + throw error; + } +} + export async function fetchAllStreamsFromPlex(busqueda, tipoContenido) { if (!busqueda || !tipoContenido) return { success: false, streams: [], message: _('invalidStreamInfo') }; if (!state.db) return { success: false, streams: [], message: _('dbUnavailableForStreams') }; diff --git a/js/jellyfin.js b/js/jellyfin.js index f5ad524..da91e17 100644 --- a/js/jellyfin.js +++ b/js/jellyfin.js @@ -1,5 +1,5 @@ import { state } from './state.js'; -import { addItemsToStore, clearStore } from './db.js'; +import { addItemsToStore, clearStore, getFromDB } from './db.js'; import { showNotification, _, emitirEventoActualizacion } from './utils.js'; async function authenticateJellyfin(url, username, password) { @@ -50,28 +50,34 @@ export async function fetchLibraryViews(url, userId, apiKey) { export async function fetchItemsFromLibrary(url, userId, apiKey, library) { - const itemsUrl = `${url}/Users/${userId}/Items?ParentId=${library.Id}&recursive=true&fields=ProductionYear,RunTimeTicks,SeriesName,ParentIndexNumber,ImageTags&includeItemTypes=Movie,Series`; - + const isMusic = library.CollectionType === 'music'; + const itemTypes = isMusic ? 'MusicArtist' : 'Movie,Series'; + const fields = isMusic ? 'ImageTags' : 'ProductionYear,RunTimeTicks,SeriesName,ParentIndexNumber,ImageTags'; + const itemsUrl = `${url}/Users/${userId}/Items?ParentId=${library.Id}&recursive=true&fields=${fields}&includeItemTypes=${itemTypes}`; + try { const response = await fetch(itemsUrl, { - headers: { - 'X-Emby-Token': apiKey - } + headers: { 'X-Emby-Token': apiKey } }); if (!response.ok) throw new Error(`Error ${response.status}`); const data = await response.json(); - const items = data.Items.map(item => ({ - id: item.Id, - title: item.Name, - year: item.ProductionYear, - type: item.Type, - duration: item.RunTimeTicks, - seriesTitle: item.SeriesName, - seasonNumber: item.ParentIndexNumber, - thumb: item.ImageTags?.Primary ? `${url}/Items/${item.Id}/Images/Primary?tag=${item.ImageTags.Primary}` : '' - })); + const items = data.Items.map(item => { + const baseItem = { + id: item.Id, + title: item.Name, + type: item.Type, + thumb: item.ImageTags?.Primary ? `${url}/Items/${item.Id}/Images/Primary?tag=${item.ImageTags.Primary}` : '' + }; + if (!isMusic) { + baseItem.year = item.ProductionYear; + baseItem.duration = item.RunTimeTicks; + baseItem.seriesTitle = item.SeriesName; + baseItem.seasonNumber = item.ParentIndexNumber; + } + return baseItem; + }); return { success: true, items, libraryName: library.Name, libraryId: library.Id }; @@ -135,7 +141,7 @@ export async function startJellyfinScan() { return; } - const mediaLibraries = viewsResult.views.filter(v => v.CollectionType === 'movies' || v.CollectionType === 'tvshows'); + const mediaLibraries = viewsResult.views.filter(v => v.CollectionType === 'movies' || v.CollectionType === 'tvshows' || v.CollectionType === 'music'); if (mediaLibraries.length === 0) { statusDiv.innerHTML += `