* { margin: 0; padding: 0; box-sizing: border-box; } :root { /* Warm "classic" dark: deep charcoal with a hint of brown, never pure black. */ --bg-primary: #14110d; --bg-secondary: rgba(28, 24, 18, 0.86); --bg-tertiary: rgba(244, 232, 212, 0.06); --text-primary: #f4ece0; --text-secondary: rgba(244, 236, 224, 0.62); /* Modern brass accent — the single chromatic note against the neutrals. */ --accent: #c9a567; --accent-hover: #d9b87f; --border: rgba(244, 232, 212, 0.12); --border-hover: rgba(201, 165, 103, 0.45); --shadow: rgba(8, 6, 3, 0.5); --font-body: 'Sora', 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif; --font-display: 'Fraunces', 'Iowan Old Style', 'Palatino Linotype', Georgia, serif; --font-logo: 'Fraunces', 'Iowan Old Style', 'Palatino Linotype', Georgia, serif; --radius-lg: 14px; --radius-md: 10px; --radius-sm: 7px; --grid-max: 1480px; --focus-ring: rgba(201, 165, 103, 0.32); color-scheme: dark; } html, body { height: 100%; } html { scroll-behavior: smooth; } body { margin: 0; background: radial-gradient(1100px 560px at 14% -12%, rgba(201, 165, 103, 0.10), transparent 60%), radial-gradient(900px 520px at 108% 4%, rgba(201, 165, 103, 0.06), transparent 58%), var(--bg-primary); background-attachment: fixed; color: var(--text-primary); font-family: var(--font-body); overflow-x: hidden; font-size: 14px; line-height: 1.5; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; } body.drawer-open { overflow: hidden; } /* Subtle film grain so large flat surfaces don't read as sterile vector flats. */ body::before { content: ''; position: fixed; inset: 0; z-index: 50; pointer-events: none; opacity: 0.035; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); } body.theme-light::before { opacity: 0.05; mix-blend-mode: multiply; } body:not(.theme-light) { color-scheme: dark; } body.theme-light { /* Warm ivory "paper" with a warm-ink text — editorial, not stark white/black. */ --bg-primary: #f6f1e7; --bg-secondary: rgba(255, 252, 245, 0.86); --bg-tertiary: rgba(43, 33, 18, 0.05); --text-primary: #241d12; --text-secondary: rgba(36, 29, 18, 0.62); --accent: #9a7338; --accent-hover: #835f2c; --border: rgba(43, 33, 18, 0.12); --border-hover: rgba(154, 115, 56, 0.5); --shadow: rgba(74, 56, 28, 0.14); --focus-ring: rgba(154, 115, 56, 0.28); color-scheme: light; background: radial-gradient(1100px 560px at 14% -12%, rgba(154, 115, 56, 0.08), transparent 60%), radial-gradient(900px 520px at 108% 4%, rgba(154, 115, 56, 0.05), transparent 58%), var(--bg-primary); } /* Top Bar */ .top-bar { height: 64px; display: flex; justify-content: space-between; align-items: center; padding: 0 clamp(16px, 3vw, 36px); background: linear-gradient(180deg, rgba(20, 17, 13, 0.86), rgba(20, 17, 13, 0.62)); position: sticky; top: 0; z-index: 100; border-bottom: 1px solid var(--border); gap: 24px; backdrop-filter: blur(12px); box-shadow: 0 8px 24px var(--shadow); } body.theme-light .top-bar { background: linear-gradient(180deg, rgba(246, 241, 231, 0.94), rgba(246, 241, 231, 0.78)); } .logo { font-size: 28px; font-weight: 500; letter-spacing: -0.01em; color: var(--text-primary); font-family: var(--font-logo); font-optical-sizing: auto; flex-shrink: 0; } .search-container { flex: 1; max-width: 500px; position: relative; } .search-container input { width: 100%; padding: 10px 44px 10px 16px; background: var(--bg-tertiary); border: 1px solid var(--border); border-radius: 999px; color: var(--text-primary); font-size: 14px; transition: all 0.2s ease; } .search-container input:focus { outline: none; border-color: var(--accent); background: var(--bg-tertiary); box-shadow: 0 0 0 4px var(--focus-ring); } .search-container input::placeholder { color: var(--text-secondary); } .search-clear-btn { position: absolute; top: 50%; right: 12px; transform: translateY(-50%); width: 26px; height: 26px; border: none; border-radius: 50%; background: var(--bg-secondary); color: var(--text-secondary); cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 16px; line-height: 1; opacity: 0; pointer-events: none; transition: opacity 0.2s ease, background 0.2s ease, color 0.2s ease; } .search-clear-btn.is-visible { opacity: 1; pointer-events: auto; } .search-clear-btn:hover { background: var(--bg-tertiary); color: var(--text-primary); } .actions { display: flex; gap: 12px; flex-shrink: 0; } .icon-btn { width: 48px; height: 48px; border: none; background: transparent; border-radius: 12px; cursor: pointer; display: flex; align-items: center; justify-content: center; color: var(--text-primary); transition: all 0.2s ease; font-size: 20px; } .icon-btn:hover { background: var(--bg-tertiary); color: var(--accent); } .icon-btn:active { transform: scale(0.92); } :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } /* CDN-served icon images (Heroicons). Black SVG sources, tinted via filter: neutral to match text by default, brass on hover for the accent moment. */ .icon-svg { width: 24px; height: 24px; display: block; filter: invert(92%) sepia(6%) saturate(220%) hue-rotate(2deg) brightness(101%); } /* Brass tint approximated from a pure-black source. */ .icon-btn:hover .icon-svg { filter: invert(68%) sepia(42%) saturate(560%) hue-rotate(1deg) brightness(94%) contrast(86%); } body.theme-light .icon-svg { filter: invert(12%) sepia(20%) saturate(620%) hue-rotate(2deg) brightness(95%); } body.theme-light .icon-btn:hover .icon-svg { filter: invert(46%) sepia(36%) saturate(720%) hue-rotate(1deg) brightness(82%) contrast(88%); } /* Hamburger Menu */ .hamburger { display: flex; flex-direction: column; gap: 4px; } .hamburger span { width: 24px; height: 2px; background: currentColor; border-radius: 1px; transition: all 0.3s ease; } .menu-toggle.active .hamburger span:nth-child(1) { transform: translateY(6px) rotate(45deg); } .menu-toggle.active .hamburger span:nth-child(2) { opacity: 0; } .menu-toggle.active .hamburger span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); } /* Sidebar */ .sidebar { position: fixed; top: 0; right: -100%; width: 320px; height: 100vh; background: var(--bg-secondary); z-index: 1001; transition: right 0.3s cubic-bezier(0.4, 0, 0.2, 1); border-left: 1px solid var(--border); display: flex; flex-direction: column; backdrop-filter: blur(15px); box-shadow: -20px 0 40px var(--shadow); } body.theme-light .sidebar { background: rgba(255, 255, 255, 0.9); backdrop-filter: blur(10px); } .sidebar.open { right: 0; } .sidebar-header { display: flex; justify-content: space-between; align-items: center; padding: 20px 24px; border-bottom: 1px solid var(--border); } .sidebar-header h3 { font-size: 18px; font-weight: 600; font-family: var(--font-display); } .close-btn { background: none; border: none; color: var(--text-primary); cursor: pointer; font-size: 24px; padding: 4px; display: flex; align-items: center; justify-content: center; transition: color 0.2s ease; } .close-btn:hover { color: var(--accent); } .sidebar-content { flex: 1; padding: 12px 0; overflow-y: auto; } .sidebar-section { padding: 8px 0 4px 0; border-bottom: 1px solid var(--border); } .sidebar-section:last-child { border-bottom: none; } .sidebar-subtitle { font-size: 12px; font-weight: 600; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.6px; padding: 8px 24px 4px 24px; } .sidebar-item { display: block; padding: 12px 24px; color: var(--text-primary); text-decoration: none; transition: all 0.2s ease; border-left: 3px solid transparent; } .sidebar-item:hover { background: var(--bg-tertiary); border-left-color: var(--accent); color: var(--accent); } .setting-item { padding: 16px 24px; border-bottom: 1px solid var(--border); } .filters-container .setting-item { padding-top: 12px; padding-bottom: 12px; } .filters-empty { padding: 12px 24px 20px 24px; color: var(--text-secondary); font-size: 13px; } .setting-item label { display: block; font-size: 13px; color: var(--text-secondary); margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.5px; } .setting-label-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 8px; } .setting-label-row label { margin-bottom: 0; } .setting-toggle .setting-label-row { margin-bottom: 10px; } .toggle { position: relative; display: inline-flex; align-items: center; width: 46px; height: 26px; } .toggle input { opacity: 0; width: 0; height: 0; } .toggle-track { position: absolute; inset: 0; border-radius: 999px; background: var(--bg-tertiary); border: 1px solid var(--border); transition: all 0.2s ease; } .toggle-track::after { content: ''; position: absolute; top: 2px; left: 2px; width: 20px; height: 20px; border-radius: 50%; background: var(--text-primary); transition: transform 0.2s ease, background 0.2s ease; box-shadow: 0 4px 10px var(--shadow); } .toggle input:checked + .toggle-track { background: var(--accent); border-color: var(--accent); } .toggle input:checked + .toggle-track::after { transform: translateX(20px); background: var(--bg-primary); } .toggle input:focus-visible + .toggle-track { box-shadow: 0 0 0 4px var(--focus-ring); } .btn-link { background: transparent; border: none; color: var(--text-secondary); font-size: 12px; cursor: pointer; text-decoration: underline; padding: 0; } .btn-link:hover { color: var(--text-primary); } .btn-link:disabled { color: var(--border); cursor: not-allowed; text-decoration: none; } .input-row { display: flex; gap: 8px; align-items: center; } .input-row input { flex: 1; padding: 9px 12px; background: var(--bg-tertiary); border: 1px solid var(--border); border-radius: 10px; color: var(--text-primary); font-size: 14px; } .input-row input::placeholder { color: var(--text-secondary); } .input-row input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 4px var(--focus-ring); } body.theme-light .input-row input:focus { box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.08); } .btn-secondary { padding: 9px 14px; border-radius: 999px; border: 1px solid var(--border); background: transparent; color: var(--text-primary); cursor: pointer; font-size: 13px; transition: all 0.2s ease; width: 100%; text-align: center; } .btn-secondary:hover { background: var(--bg-tertiary); border-color: var(--border-hover); } .btn-secondary:active { transform: translateY(1px); } .sources-list { padding: 8px 24px 16px 24px; display: flex; flex-direction: column; gap: 8px; } .source-item { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 9px 12px; border: 1px solid var(--border); border-radius: 10px; background: var(--bg-tertiary); } .source-item span { font-size: 12px; color: var(--text-primary); word-break: break-all; } .source-item button { padding: 6px 10px; border-radius: 999px; border: 1px solid var(--border); background: transparent; color: var(--text-primary); cursor: pointer; font-size: 12px; } .source-item button:hover { background: var(--bg-tertiary); } .setting-item input[type="range"] { width: 100%; appearance: none; -webkit-appearance: none; height: 6px; border-radius: 999px; background: var(--bg-tertiary); border: 1px solid var(--border); cursor: pointer; margin: 6px 0; } .setting-item input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 18px; height: 18px; border-radius: 50%; background: var(--text-primary); border: none; cursor: pointer; } .setting-item input[type="range"]::-moz-range-thumb { width: 18px; height: 18px; border-radius: 50%; background: var(--text-primary); border: none; cursor: pointer; } .setting-item select { width: 100%; padding: 9px 12px; background: var(--bg-tertiary); border: 1px solid var(--border); border-radius: 10px; color: var(--text-primary); cursor: pointer; font-size: 14px; color-scheme: dark; appearance: none; background-image: linear-gradient(45deg, transparent 50%, var(--text-secondary) 50%), linear-gradient(135deg, var(--text-secondary) 50%, transparent 50%), linear-gradient(to right, transparent, transparent); background-position: calc(100% - 16px) calc(1em + 1px), calc(100% - 11px) calc(1em + 1px), 100% 0; background-size: 5px 5px, 5px 5px, 2.5em 2.5em; background-repeat: no-repeat; } .multi-select { display: flex; flex-direction: column; gap: 8px; } .multi-select-item { display: flex; align-items: center; gap: 12px; font-size: 13px; color: var(--text-primary); padding: 6px 10px; border-radius: 8px; border: 1px solid transparent; background: transparent; transition: all 0.2s ease; } .multi-select-item input[type="checkbox"] { width: 18px; height: 18px; accent-color: var(--accent); flex-shrink: 0; } .multi-select-item span { padding-left: 6px; } .multi-select-item:hover { border-color: var(--border); background: var(--bg-tertiary); } .multi-select-item input[type="checkbox"]:checked + span { color: var(--accent); } .setting-item select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 4px var(--focus-ring); } body.theme-light .setting-item select:focus { box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.08); } .setting-item select, .setting-item select option, .setting-item select optgroup { background-color: #0a0a0a; color: #ffffff; } body.theme-light .setting-item select { color-scheme: light; } body.theme-light .setting-item select, body.theme-light .setting-item select option, body.theme-light .setting-item select optgroup { background-color: #ffffff; color: #000000; } .setting-item select option { background: #0a0a0a; color: #ffffff; } body.theme-light .setting-item select option { background: #ffffff; color: #000000; } /* Overlay */ .sidebar-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.6); display: none; z-index: 1000; transition: opacity 0.3s ease; backdrop-filter: blur(2px); } .sidebar-overlay.open { display: block; } .load-more-btn { position: sticky; left: 50%; transform: translateX(-50%); margin: 16px auto 32px auto; width: 52px; height: 52px; border-radius: 999px; border: 1px solid var(--border); background: var(--bg-secondary); color: var(--text-primary); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.2s ease; z-index: 10; box-shadow: 0 10px 20px var(--shadow); } .load-more-btn:hover { background: var(--bg-tertiary); } .load-more-btn:disabled { opacity: 0.5; cursor: not-allowed; } /* Favorites Bar */ .favorites-bar { padding: 16px clamp(16px, 3vw, 36px) 4px; max-width: var(--grid-max); margin: 0 auto; } .favorites-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; } .favorites-header h3 { font-size: 12px; letter-spacing: 1px; text-transform: uppercase; color: var(--text-secondary); font-family: var(--font-display); } .favorites-list { display: flex; gap: 12px; overflow-x: auto; padding-bottom: 12px; scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; } .favorites-list::-webkit-scrollbar { height: 8px; } .favorites-list::-webkit-scrollbar-thumb { background: var(--bg-tertiary); border-radius: 999px; } .favorites-list::-webkit-scrollbar-track { background: transparent; } .favorite-card { position: relative; flex: 0 0 auto; width: 220px; border-radius: var(--radius-md); border: 1px solid var(--border); background: var(--bg-secondary); overflow: hidden; cursor: pointer; box-shadow: 0 8px 18px var(--shadow); transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease; scroll-snap-align: start; } .favorite-card:hover { transform: translateY(-4px); box-shadow: 0 14px 24px var(--shadow); border-color: var(--border-hover); } .favorite-card img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; background: var(--bg-tertiary); } .favorite-info { padding: 10px 12px 12px 12px; } .favorite-info h4 { font-size: 13px; font-weight: 600; margin: 0; color: var(--text-primary); font-family: var(--font-display); line-height: 1.3; } .favorites-empty { font-size: 13px; color: var(--text-secondary); padding: 4px 0 12px 0; } /* Grid Container */ .grid-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); grid-auto-rows: 10px; gap: 16px; align-items: start; padding: 24px; max-width: var(--grid-max); margin: 0 auto; } @media (max-width: 768px) { .grid-container { /* Exactly two larger cards per row on phones, rather than packing in several small ones. */ grid-template-columns: repeat(2, 1fr); grid-auto-rows: 10px; gap: 12px; padding: 16px; } .top-bar { flex-wrap: wrap; height: auto; padding: 12px 16px; row-gap: 10px; } .search-container { order: 3; width: 100%; max-width: 100%; flex-basis: 100%; } .actions { order: 2; width: auto; margin-left: auto; justify-content: flex-end; } } @media (max-width: 480px) { .grid-container { /* One full-width card per row on phones. */ grid-template-columns: 1fr; gap: 16px; } .logo { font-size: 18px; } .icon-btn { width: 44px; height: 44px; } } @media (min-width: 1600px) { body { font-size: 16px; } .top-bar { height: 72px; padding: 0 36px; } .grid-container { gap: 24px; padding: 32px 48px; } .video-card h4 { font-size: calc(16px * var(--card-font-scale, 1)); } .video-card p { font-size: calc(13px * var(--card-font-scale, 1)); } } @media (min-width: 1920px) { .grid-container { column-width: 280px; } } @media (pointer: coarse) { .icon-btn { width: 52px; height: 52px; } .btn-secondary { padding: 10px 14px; } .setting-item select, .input-row input { padding: 10px 14px; } } @media (prefers-reduced-motion: reduce) { * { transition: none !important; animation: none !important; } } /* Video Card */ .video-card { cursor: pointer; border-radius: var(--radius-lg); overflow: hidden; transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease; background: var(--bg-secondary); display: inline-flex; flex-direction: column; width: 100%; border: 1px solid var(--border); box-shadow: 0 6px 16px var(--shadow); position: relative; margin-bottom: 0; /* The grid is virtualized in JS (see App.virtualGrid): only cards near the viewport are in the DOM at all, and each is absolutely positioned at a precomputed (top,left). */ } .video-card:hover { transform: translateY(-6px); box-shadow: 0 16px 28px var(--shadow); border-color: var(--border-hover); } .video-thumb { position: relative; display: block; } .video-card img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; display: block; background: var(--bg-tertiary); } /* Dark-transparent pills overlaid on the bottom of the thumbnail. Scoped under .video-thumb so they outrank the later .uploader-link rule. */ .video-thumb .video-uploader, .video-thumb .video-duration { position: absolute; bottom: 8px; z-index: 3; padding: 3px 8px; border-radius: 6px; background: rgba(0, 0, 0, 0.6); color: #fff; font-size: 12px; font-weight: 500; line-height: 1.2; backdrop-filter: blur(2px); pointer-events: none; } .video-thumb .video-duration { right: 8px; } .video-thumb .video-uploader { left: 8px; max-width: 60%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; border: none; text-align: left; font-family: inherit; cursor: pointer; pointer-events: auto; } .video-thumb .video-uploader:hover { background: rgba(0, 0, 0, 0.78); color: #fff; text-decoration: underline; } .video-card h4 { font-size: calc(15px * var(--card-font-scale, 1)); font-weight: 500; padding: 12px 12px 10px; line-height: 1.3; color: var(--text-primary); margin: 0; flex: 1; font-family: var(--font-display); font-optical-sizing: auto; letter-spacing: -0.01em; white-space: nowrap; overflow: hidden; position: relative; } .video-title-text { display: inline-block; padding-right: 24px; transform: translateX(0); } .video-card.is-title-active .video-title-text { animation: video-title-marquee var(--marquee-duration, 10s) linear infinite; will-change: transform; } @keyframes video-title-marquee { to { transform: translateX(calc(-1 * var(--marquee-distance, 0px))); } } .video-card p { font-size: calc(12px * var(--card-font-scale, 1)); color: var(--text-secondary); padding: 0 12px 12px 12px; margin: 0; } .video-tags { display: flex; flex-wrap: nowrap; gap: 6px; padding: 0 12px 8px 12px; overflow-x: auto; overflow-y: hidden; scrollbar-width: thin; -webkit-overflow-scrolling: touch; } .video-tag { font-size: calc(11px * var(--card-font-scale, 1)); color: var(--text-secondary); background: var(--bg-tertiary); border: 1px solid var(--border); border-radius: 999px; padding: 3px 8px; line-height: 1.2; letter-spacing: 0.2px; white-space: nowrap; font-family: inherit; cursor: pointer; } .video-tag:focus-visible { outline: 2px solid var(--text-primary); outline-offset: 1px; } .uploader-link { background: transparent; border: none; color: inherit; font: inherit; cursor: pointer; padding: 0; text-align: left; } .video-loading { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: rgba(0, 0, 0, 0.45); opacity: 0; transition: opacity 0.2s ease; pointer-events: none; z-index: 4; } .video-card.is-loading .video-loading, .favorite-card.is-loading .video-loading { opacity: 1; } .video-loading-spinner { width: 34px; height: 34px; border-radius: 50%; border: 3px solid rgba(255, 255, 255, 0.35); border-top-color: var(--text-primary); animation: video-card-spinner 0.8s linear infinite; } @keyframes video-card-spinner { to { transform: rotate(360deg); } } .uploader-link:hover { color: var(--text-primary); text-decoration: underline; } .video-menu-btn { position: absolute; top: 10px; right: 10px; width: 32px; height: 32px; border-radius: 50%; border: 1px solid var(--border); background: rgba(0, 0, 0, 0.55); color: var(--text-primary); display: flex; align-items: center; justify-content: center; font-size: 20px; line-height: 1; cursor: pointer; z-index: 2; transition: transform 0.2s ease, background 0.2s ease, border-color 0.2s ease; } body.theme-light .video-menu-btn { background: rgba(255, 255, 255, 0.8); } .video-menu-btn:hover { transform: scale(1.05); background: var(--bg-secondary); } .video-menu { position: absolute; top: 48px; right: 10px; min-width: 140px; border-radius: 10px; border: 1px solid var(--border); background: var(--bg-secondary); box-shadow: 0 12px 24px var(--shadow); opacity: 0; transform: translateY(-6px); pointer-events: none; transition: opacity 0.18s ease, transform 0.18s ease; z-index: 3; } .video-menu.open { opacity: 1; transform: translateY(0); pointer-events: auto; } .video-menu-item { width: 100%; padding: 10px 12px; text-align: left; border: none; background: transparent; color: var(--text-primary); cursor: pointer; font-size: 13px; } .video-menu-item:hover { background: var(--bg-tertiary); } .live-badge { position: absolute; top: 10px; left: 50px; z-index: 2; display: inline-flex; align-items: center; gap: 4px; padding: 3px 8px; border-radius: 999px; background: #e0245e; color: #fff; font-size: 11px; font-weight: 600; letter-spacing: 0.04em; line-height: 1; text-transform: uppercase; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.35); pointer-events: none; } .feed-live-badge { top: 16px; left: 16px; } /* Live streams can't be seeked, so hide the feed scrubber. */ .feed-slide.is-live .feed-timeline { display: none; } .favorite-btn { position: absolute; top: 10px; left: 10px; width: 32px; height: 32px; border-radius: 50%; border: 1px solid var(--border); background: rgba(0, 0, 0, 0.55); color: var(--text-primary); display: flex; align-items: center; justify-content: center; font-size: 18px; line-height: 1; cursor: pointer; z-index: 1; transition: transform 0.2s ease, background 0.2s ease, border-color 0.2s ease; } body.theme-light .favorite-btn { background: rgba(255, 255, 255, 0.8); } .favorite-btn:hover { transform: scale(1.05); background: var(--bg-secondary); } .favorite-btn.is-favorite { color: #ff3b30; border-color: rgba(255, 59, 48, 0.6); background: rgba(255, 59, 48, 0.12); } /* Info Modal */ .info-modal { position: fixed; inset: 0; display: none; align-items: center; justify-content: center; background: rgba(0, 0, 0, 0.6); z-index: 2500; padding: 20px; } .info-modal.open { display: flex; } .info-card { position: relative; width: min(520px, 92vw); background: var(--bg-secondary); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 20px 22px; box-shadow: 0 18px 36px var(--shadow); } .info-card h3 { font-family: var(--font-display); font-size: 18px; margin: 0 0 16px 0; } .info-close { position: absolute; right: 16px; top: 16px; border: none; background: var(--bg-tertiary); color: var(--text-primary); border-radius: 50%; width: 32px; height: 32px; cursor: pointer; } .info-rows, .info-list { display: flex; flex-direction: column; gap: 10px; } .info-row { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; font-size: 13px; color: var(--text-secondary); border-bottom: 1px solid var(--border); padding-bottom: 8px; } .info-row:last-child { border-bottom: none; padding-bottom: 0; } .info-label { text-transform: uppercase; letter-spacing: 0.6px; font-size: 11px; color: var(--text-secondary); } .info-value { text-align: right; color: var(--text-primary); word-break: break-all; } .info-json { margin: 0; white-space: pre-wrap; text-align: left; color: var(--text-primary); background: var(--bg-tertiary); border-radius: 8px; padding: 8px 10px; font-size: 12px; line-height: 1.4; max-height: 180px; overflow: auto; flex: 1; } .info-empty { font-size: 13px; color: var(--text-secondary); padding-top: 6px; } /* Modal */ .modal { display: none; position: fixed; inset: 0; background: #000; z-index: 2000; } body.theme-light .modal { background: #0b0b0b; } .modal.open { display: flex; } .modal-content { width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 20px; box-sizing: border-box; position: relative; } .close { position: absolute; top: 20px; right: 20px; color: #fff; font-size: 32px; cursor: pointer; background: rgba(0, 0, 0, 0.5); border: none; border-radius: 50%; width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; z-index: 2001; transition: all 0.2s ease; } .close:hover { background: rgba(255, 255, 255, 0.2); } video { width: 100%; height: 100%; max-width: 100%; max-height: 100vh; object-fit: contain; } #mobile-video-host { position: fixed; left: -9999px; top: 0; width: 1px; height: 1px; overflow: hidden; } .error-toast { position: fixed; right: 20px; bottom: 20px; max-width: min(360px, 90vw); background: rgba(0, 0, 0, 0.85); color: #ffffff; border: 1px solid rgba(255, 255, 255, 0.15); border-radius: 10px; padding: 12px 14px; display: flex; align-items: center; gap: 12px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35); z-index: 3000; opacity: 0; pointer-events: none; transform: translateY(8px); transition: opacity 0.2s ease, transform 0.2s ease; } .error-toast.show { opacity: 1; pointer-events: auto; transform: translateY(0); } .error-toast button { background: transparent; border: none; color: inherit; cursor: pointer; font-size: 16px; } body.theme-light .error-toast { background: rgba(255, 255, 255, 0.95); color: #000000; border: 1px solid rgba(0, 0, 0, 0.12); } .update-banner { position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%) translateY(8px); max-width: min(420px, 92vw); background: rgba(0, 0, 0, 0.88); color: #ffffff; border: 1px solid rgba(255, 255, 255, 0.15); border-radius: 10px; padding: 12px 14px; display: flex; align-items: center; gap: 14px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35); z-index: 3100; opacity: 0; pointer-events: none; transition: opacity 0.2s ease, transform 0.2s ease; } .update-banner.show { opacity: 1; pointer-events: auto; transform: translateX(-50%) translateY(0); } .update-banner button { background: #ffffff; color: #000000; border: none; border-radius: 6px; padding: 6px 14px; font: inherit; font-weight: 600; cursor: pointer; white-space: nowrap; } body.theme-light .update-banner { background: rgba(255, 255, 255, 0.96); color: #000000; border: 1px solid rgba(0, 0, 0, 0.12); } body.theme-light .update-banner button { background: #111111; color: #ffffff; } /* Scrollbar styling */ ::-webkit-scrollbar { width: 8px; } ::-webkit-scrollbar-track { background: var(--bg-primary); } ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; } ::-webkit-scrollbar-thumb:hover { background: var(--text-secondary); } /* Mode toggle FAB (grid <-> reels) */ .mode-toggle-btn { position: fixed; right: 24px; bottom: 90px; width: 52px; height: 52px; border-radius: 50%; border: 1px solid var(--border); background: var(--bg-secondary); backdrop-filter: blur(12px); display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: 0 10px 24px var(--shadow); z-index: 2600; transition: transform 0.2s ease, background 0.2s ease, opacity 0.4s ease; } .mode-toggle-btn:hover { background: var(--bg-tertiary); transform: scale(1.06); } .mode-toggle-btn:active { transform: scale(0.94); } body.feed-mode-open .mode-toggle-btn { background: rgba(0, 0, 0, 0.55); border-color: rgba(255, 255, 255, 0.18); } body.feed-mode-open .mode-toggle-btn:hover { background: rgba(255, 255, 255, 0.2); } body.feed-mode-open .mode-toggle-btn .icon-svg { filter: invert(100%) saturate(0%) !important; } /* Reels-style scrollable feed */ .feed-view { display: none; position: fixed; inset: 0; background: #000; z-index: 2500; } .feed-view.open { display: block; } .feed-scroll { width: 100%; height: 100%; overflow-y: scroll; scroll-snap-type: y mandatory; scrollbar-width: none; } .feed-scroll::-webkit-scrollbar { display: none; } .feed-sentinel { height: 1px; } /* Stands in for the slides above the rendered window so scroll position and snap points stay stable while slides are virtualized in and out. */ .feed-top-spacer { width: 100%; height: 0; flex: none; } .feed-slide { position: relative; width: 100%; height: 100vh; height: 100dvh; scroll-snap-align: start; scroll-snap-stop: always; display: flex; align-items: center; justify-content: center; overflow: hidden; background: #000; } .feed-poster, .feed-video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; } .feed-poster { transition: opacity 0.2s ease; } .feed-slide.is-loaded .feed-poster { opacity: 0; } .feed-info { position: absolute; left: 0; right: 0; bottom: 0; padding: 24px 20px 44px; background: linear-gradient(to top, rgba(0, 0, 0, 0.75), transparent); color: #fff; pointer-events: none; transition: opacity 0.4s ease; } .feed-title { font-size: 16px; font-weight: 600; margin: 0 0 4px; white-space: nowrap; overflow: hidden; } .feed-title-text { display: inline-block; padding-right: 28px; transform: translateX(0); } .feed-title.is-marquee .feed-title-text { animation: video-title-marquee var(--marquee-duration, 10s) linear infinite; will-change: transform; } .feed-uploader { font-size: 13px; opacity: 0.8; margin: 0; } .feed-timeline { position: absolute; left: 0; right: 0; bottom: 4px; height: 28px; display: flex; align-items: center; padding: 0 16px; cursor: pointer; touch-action: none; z-index: 5; } .feed-timeline-track { position: relative; width: 100%; height: 3px; background: rgba(255, 255, 255, 0.3); border-radius: 999px; overflow: visible; transition: height 0.15s ease; } .feed-timeline:hover .feed-timeline-track, .feed-timeline.is-scrubbing .feed-timeline-track { height: 5px; } .feed-timeline-fill { position: absolute; left: 0; top: 0; bottom: 0; width: 0%; background: #fff; border-radius: 999px; } .feed-timeline-handle { position: absolute; top: 50%; left: 0%; width: 11px; height: 11px; margin-left: -5.5px; background: #fff; border-radius: 50%; transform: translateY(-50%) scale(0); transition: transform 0.15s ease; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); } .feed-timeline:hover .feed-timeline-handle, .feed-timeline.is-scrubbing .feed-timeline-handle { transform: translateY(-50%) scale(1); } .feed-mute-btn { position: fixed; right: 24px; bottom: 154px; width: 44px; height: 44px; border-radius: 50%; border: 1px solid rgba(255, 255, 255, 0.18); background: rgba(0, 0, 0, 0.5); display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 2600; transition: background 0.2s ease, opacity 0.4s ease; } .feed-mute-btn:hover { background: rgba(255, 255, 255, 0.2); } .feed-mute-btn .icon-svg { filter: invert(100%) saturate(0%) !important; } /* Per-slide favorite (heart) button on the feed HUD right rail. */ .feed-fav-btn { position: absolute; top: auto; left: auto; right: 24px; bottom: 210px; width: 44px; height: 44px; border-radius: 50%; border: 1px solid rgba(255, 255, 255, 0.18); background: rgba(0, 0, 0, 0.5); color: #fff; display: flex; align-items: center; justify-content: center; font-size: 22px; line-height: 1; cursor: pointer; z-index: 6; transition: background 0.2s ease, transform 0.2s ease, opacity 0.4s ease; } .feed-fav-btn:hover { background: rgba(255, 255, 255, 0.2); transform: scale(1.05); } .feed-fav-btn.is-favorite { color: #ff3b30; border-color: rgba(255, 59, 48, 0.6); background: rgba(255, 59, 48, 0.18); } /* Reels/TikTok mode: HUD auto-hides after a short idle. Elements stay interactive (pointer-events untouched) so the buttons keep working while invisible; any pointer/scroll activity reveals them again (see App.feed). */ body.feed-hud-idle .feed-info, body.feed-hud-idle .feed-timeline, body.feed-hud-idle .feed-mute-btn, body.feed-hud-idle .feed-fav-btn, body.feed-hud-idle .mode-toggle-btn { opacity: 0; } /* ===================================================================== Modern enhancements (see enhance.js) ===================================================================== */ /* --- Card entrance: cards rise/fade in the first time they mount -------- */ @keyframes card-rise { from { opacity: 0; transform: translateY(18px) scale(0.985); } to { opacity: 1; transform: none; } } .video-card.is-revealing { animation: card-rise 0.5s cubic-bezier(0.2, 0.7, 0.2, 1) both; } /* --- Cursor-tracking brass spotlight border on hover ------------------- */ .video-card::after { content: ''; position: absolute; inset: 0; border-radius: inherit; padding: 1px; background: radial-gradient(180px circle at var(--mx, 50%) var(--my, 50%), rgba(201, 165, 103, 0.65), rgba(201, 165, 103, 0) 60%); -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); mask-composite: exclude; opacity: 0; transition: opacity 0.25s ease; pointer-events: none; z-index: 3; } .video-card:hover::after { opacity: 1; } /* --- Thumbnail skeleton shimmer until the image paints ----------------- */ @keyframes thumb-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } } .video-card img:not(.is-loaded) { background-image: linear-gradient(100deg, transparent 30%, rgba(255, 255, 255, 0.08) 50%, transparent 70%); background-size: 200% 100%; background-repeat: no-repeat; animation: thumb-shimmer 1.3s ease-in-out infinite; } body.theme-light .video-card img:not(.is-loaded) { background-image: linear-gradient(100deg, transparent 30%, rgba(43, 33, 18, 0.06) 50%, transparent 70%); } /* --- Hover video preview (sits over the poster, under the buttons) ----- */ .card-preview { position: absolute; top: 0; left: 0; width: 100%; object-fit: cover; background: #000; z-index: 0; opacity: 0; transition: opacity 0.3s ease; } .video-card.is-previewing .card-preview { opacity: 1; } /* --- Favorite heart: pop + expanding brass ring on add ----------------- */ @keyframes heart-pop { 0% { transform: scale(1); } 35% { transform: scale(1.35); } 100% { transform: scale(1); } } @keyframes heart-ring { from { opacity: 0.8; transform: scale(0.6); } to { opacity: 0; transform: scale(1.7); } } .favorite-btn.just-favorited { animation: heart-pop 0.45s ease; } .favorite-btn.just-favorited::after { content: ''; position: absolute; inset: -4px; border-radius: 50%; border: 2px solid var(--accent); animation: heart-ring 0.5s ease forwards; pointer-events: none; } /* --- Scroll progress bar ---------------------------------------------- */ .scroll-progress { position: fixed; top: 0; left: 0; height: 2px; width: 0; background: linear-gradient(90deg, var(--accent), var(--accent-hover)); z-index: 200; transition: width 0.1s linear; pointer-events: none; } /* --- Back-to-top FAB --------------------------------------------------- */ .back-to-top { position: fixed; right: 24px; bottom: 154px; width: 46px; height: 46px; border-radius: 50%; border: 1px solid var(--border); background: var(--bg-secondary); color: var(--text-primary); font-size: 20px; line-height: 1; display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: 0 10px 24px var(--shadow); backdrop-filter: blur(12px); opacity: 0; transform: translateY(12px) scale(0.9); pointer-events: none; transition: opacity 0.3s ease, transform 0.3s ease, background 0.2s ease; z-index: 2400; } .back-to-top.is-visible { opacity: 1; transform: none; pointer-events: auto; } .back-to-top:hover { background: var(--bg-tertiary); border-color: var(--border-hover); } .back-to-top:active { transform: scale(0.92); } /* --- Command palette (⌘K) --------------------------------------------- */ .command-palette { position: fixed; inset: 0; z-index: 4000; display: none; align-items: flex-start; justify-content: center; padding-top: 14vh; background: rgba(10, 8, 4, 0.55); backdrop-filter: blur(6px); } .command-palette.open { display: flex; } .cmdk-box { width: min(560px, 92vw); background: var(--bg-secondary); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: 0 30px 70px var(--shadow); overflow: hidden; animation: card-rise 0.2s ease both; } .cmdk-input { width: 100%; padding: 18px 20px; border: none; border-bottom: 1px solid var(--border); background: transparent; color: var(--text-primary); font-size: 16px; font-family: var(--font-body); } .cmdk-input::placeholder { color: var(--text-secondary); } .cmdk-input:focus { outline: none; } .cmdk-list { max-height: 50vh; overflow-y: auto; padding: 8px; } .cmdk-item { display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; text-align: left; padding: 11px 14px; border: none; background: transparent; color: var(--text-primary); border-radius: 10px; cursor: pointer; font: inherit; } .cmdk-item.is-active { background: var(--bg-tertiary); } .cmdk-label { font-size: 14px; } .cmdk-hint { font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-secondary); flex-shrink: 0; } /* --- Player ambient backdrop: blurred poster behind the video --------- */ .modal-content::before { content: ''; position: absolute; inset: 0; background-image: var(--poster); background-size: cover; background-position: center; filter: blur(60px) saturate(1.3) brightness(0.5); transform: scale(1.25); opacity: 0.55; z-index: 0; pointer-events: none; } .modal-content > * { position: relative; z-index: 1; } /* --- Reels HUD polish: serif title + brass scrubber + mute pulse ------- */ .feed-title { font-family: var(--font-display); } .feed-timeline-fill, .feed-timeline-handle { background: #c9a567; } @keyframes mute-pulse { 0% { transform: scale(0.8); opacity: 0.75; } 100% { transform: scale(1.5); opacity: 0; } } .feed-mute-btn.is-muted::after { content: ''; position: absolute; inset: -4px; border-radius: 50%; border: 2px solid rgba(201, 165, 103, 0.7); animation: mute-pulse 2s ease-out infinite; pointer-events: none; } /* --- View Transition timing (player open/close crossfade) -------------- */ @media (prefers-reduced-motion: no-preference) { ::view-transition-old(root), ::view-transition-new(root) { animation-duration: 0.32s; } }