/* SoulStream — Spotify-style dark theme */ /* Bundled fonts (same set as the TriSign app) */ @font-face { font-family: "Nunito"; font-style: normal; font-weight: 400; font-display: swap; src: url("/assets/fonts/nunito-400.woff2") format("woff2"); } @font-face { font-family: "Nunito"; font-style: normal; font-weight: 600; font-display: swap; src: url("/assets/fonts/nunito-600.woff2") format("woff2"); } @font-face { font-family: "Nunito"; font-style: normal; font-weight: 700; font-display: swap; src: url("/assets/fonts/nunito-700.woff2") format("woff2"); } @font-face { font-family: "Inter"; font-style: normal; font-weight: 400; font-display: swap; src: url("/assets/fonts/inter-400.woff2") format("woff2"); } @font-face { font-family: "Inter"; font-style: normal; font-weight: 600; font-display: swap; src: url("/assets/fonts/inter-600.woff2") format("woff2"); } @font-face { font-family: "Inter"; font-style: normal; font-weight: 700; font-display: swap; src: url("/assets/fonts/inter-700.woff2") format("woff2"); } /* Typed accent registrations let scoped overrides (the artist page's browse accent) CSS-transition; the global rAF crossfade is unaffected. */ @property --accent { syntax: ""; inherits: true; initial-value: #1f9ad6; } @property --accent-hover { syntax: ""; inherits: true; initial-value: #41b7ee; } :root { --app-font: "Nunito", -apple-system, "Segoe UI", sans-serif; --bg-base: #121212; --bg-sidebar: #000000; --bg-elevated: #181818; --bg-highlight: #242424; --bg-press: #2a2a2a; --bg-player: #181818; --text-primary: #ffffff; --text-secondary: #b3b3b3; --text-muted: #7a7a7a; --accent: #1f9ad6; --accent-hover: #41b7ee; --accent-soft: color-mix(in srgb, var(--accent) 15%, transparent); --border: #2a2a2a; --radius: 8px; --sidebar-width: 232px; --player-height: 88px; --nowplaying-width: 340px; } /* Floating overlay scrollbar (see src/lib/scrollbar.js). The native bar is hidden so it never reserves width or reflows content; a thin thumb floats in the container's right padding, fading in on scroll/hover, out when idle. */ .float-scroll { scrollbar-width: none; -ms-overflow-style: none; } .float-scroll::-webkit-scrollbar { width: 0; height: 0; display: none; } /* Zero-height sticky rail: the compositor keeps it pinned to the scrollport top, so the thumb inside never needs JS counter-scrolling (which jitters on composited-scrolling panes — see scrollbar.js). In flex/grid scrollers its row-gap contribution is cancelled by an inline negative margin-bottom. */ .float-rail { position: sticky; top: 0; height: 0; z-index: 60; pointer-events: none; } .float-thumb { position: absolute; top: 0; right: 3px; /* refined inline: 3px inside the scroller's padding box */ width: 5px; border-radius: 2.5px; /* Song-name text tone (--text-secondary), nudged a little lighter. */ background: color-mix(in srgb, var(--text-secondary) 75%, #ffffff); opacity: 0; pointer-events: none; /* Slides along the track via translateY (composited — no layout/paint), so give it its own layer. Only opacity is animated. */ will-change: transform; transition: opacity 0.15s ease; } /* Order matters (equal specificity, last wins): a faint hint on hover, more present while scrolling, solid while dragging. hover < show < dragging. */ .float-thumb.hover { opacity: 0.35; pointer-events: auto; } .float-thumb.show { opacity: 0.8; pointer-events: auto; } .float-thumb.dragging { opacity: 1; pointer-events: auto; } /* The thumb's slide as a scroll-driven animation (scrollbar.js sets it up where animation-timeline is supported). Endpoints are written by measure(): --ft-y0 = topInset - padTop, --ft-y1 = --ft-y0 + (trackH - thumbH). The default animation range spans the scroller's whole scroll range, so linear progress IS scrollTop / maxScroll — the exact ratio the JS fallback computes — and the compositor tracks the scroll with zero main-thread work (and none of the fallback's one-frame lag under threaded scrolling). */ @keyframes float-thumb-ride { from { transform: translateY(var(--ft-y0, 0px)); } to { transform: translateY(var(--ft-y1, 0px)); } } * { margin: 0; padding: 0; box-sizing: border-box; } html, body { height: 100%; overflow: hidden; /* Kill WebView2's document-level elastic overscroll (it visibly shifts the whole view). Scoped to the root + the real scroll panes below — NOT `*`: applying it to every element turns each `overflow: hidden`/`auto` box (cards, panels, popovers, the album expander) into a scroll container that swallows the wheel instead of chaining it to the page, so hovering one froze scrolling. Inner regions now default to `auto` and chain. */ overscroll-behavior: none; background: var(--bg-base); color: var(--text-primary); font-family: var(--app-font); font-size: 14px; -webkit-font-smoothing: antialiased; user-select: none; } #app { height: 100%; } button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; } input { font: inherit; } ::-webkit-scrollbar { width: 12px; } ::-webkit-scrollbar-thumb { background: hsla(0, 0%, 100%, 0.3); border-radius: 6px; background-clip: padding-box; border: 3px solid transparent; } ::-webkit-scrollbar-thumb:hover { background: hsla(0, 0%, 100%, 0.5); background-clip: padding-box; border: 3px solid transparent; } ::-webkit-scrollbar-track { background: transparent; }