@import "tailwindcss"; /* ================================================================ § 1 THEME BRIDGE — Tailwind ↔ CSS Custom Properties ================================================================ Maps semantic CSS variables (set per-theme below) into Tailwind's @theme system so they work as utility classes. e.g. bg-background, text-foreground, border-border, etc. ================================================================ */ @theme inline { /* ── Surfaces ────────────────────────────────────────────── */ --color-background: var(--background); --color-foreground: var(--foreground); --color-card: var(--card); --color-card-foreground: var(--card-foreground); --color-popover: var(--popover); --color-popover-foreground: var(--popover-foreground); --color-surface: var(--surface); --color-surface-hover: var(--surface-hover); --color-surface-raised: var(--surface-raised); /* ── Interactive ─────────────────────────────────────────── */ --color-primary: var(--primary); --color-primary-foreground: var(--primary-foreground); --color-secondary: var(--secondary); --color-secondary-foreground: var(--secondary-foreground); --color-muted: var(--muted); --color-muted-foreground: var(--muted-foreground); --color-accent: var(--accent); --color-accent-foreground: var(--accent-foreground); --color-destructive: var(--destructive); --color-destructive-foreground: var(--destructive-foreground); /* ── Status ────────────────────────────────────────────────── */ --color-success: var(--success); --color-success-foreground: var(--success-foreground); --color-warning: var(--warning); --color-warning-foreground: var(--warning-foreground); --color-info: var(--info); --color-info-foreground: var(--info-foreground); /* ── Borders & Focus ─────────────────────────────────────── */ --color-border: var(--border); --color-input: var(--input); --color-ring: var(--ring); /* ── Charts ──────────────────────────────────────────────── */ --color-chart-1: var(--chart-1); --color-chart-2: var(--chart-2); --color-chart-3: var(--chart-3); --color-chart-4: var(--chart-4); --color-chart-5: var(--chart-5); /* ── Sidebar ─────────────────────────────────────────────── */ --color-sidebar: var(--sidebar); --color-sidebar-foreground: var(--sidebar-foreground); --color-sidebar-primary: var(--sidebar-primary); --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); --color-sidebar-accent: var(--sidebar-accent); --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); --color-sidebar-border: var(--sidebar-border); --color-sidebar-section-divider: var(--sidebar-section-divider); --color-sidebar-ring: var(--sidebar-ring); /* ── Border Radius ───────────────────────────────────────── */ --radius-sm: calc(var(--radius) - 4px); --radius-md: calc(var(--radius) - 2px); --radius-lg: var(--radius); --radius-xl: calc(var(--radius) + 4px); --radius-2xl: calc(var(--radius) + 8px); --radius-pill: 9999px; /* ── Shadows (dark-UI optimized) ─────────────────────────── */ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.25); --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.3), 0 2px 4px -2px rgb(0 0 0 / 0.3); --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.4), 0 4px 6px -4px rgb(0 0 0 / 0.4); --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.5), 0 8px 10px -6px rgb(0 0 0 / 0.5); /* ── Animations ──────────────────────────────────────────── */ --animate-accordion-down: accordion-down 0.2s ease-out; --animate-accordion-up: accordion-up 0.2s ease-out; --animate-fade-in: fade-in 0.3s ease-out; --animate-slide-up: slide-up 0.3s ease-out; --animate-scale-in: scale-in 0.2s cubic-bezier(0.34, 1.56, 0.64, 1); --animate-ken-burns-a: ken-burns-a 14s linear infinite; --animate-ken-burns-b: ken-burns-b 18s linear infinite; /* ── Motion: Duration ──────────────────────────────────────── */ --duration-instant: 0ms; --duration-fast: 150ms; --duration-normal: 250ms; --duration-slow: 400ms; --duration-glacial: 700ms; --duration-cinematic: 1200ms; --duration-drift: 2000ms; /* ── Motion: Easing ────────────────────────────────────────── */ --ease-default: cubic-bezier(0.4, 0, 0.2, 1); --ease-in: cubic-bezier(0.4, 0, 1, 1); --ease-out: cubic-bezier(0, 0, 0.2, 1); --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); --ease-gentle: cubic-bezier(0.25, 0, 0.35, 1); /* ── Font Family ───────────────────────────────────────────── */ --font-family-display: var(--font-display, var(--font-body)); /* ── Hero Backdrop Defaults ─────────────────────────────────── */ --hero-backdrop-brightness: 0.75; --hero-backdrop-saturate: 0.9; --hero-text-shadow: 0 18px 50px rgb(0 0 0 / 45%); } /* ================================================================ § 2 KEYFRAMES ================================================================ */ @keyframes accordion-down { from { height: 0; } to { height: var(--radix-accordion-content-height); } } @keyframes accordion-up { from { height: var(--radix-accordion-content-height); } to { height: 0; } } @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } } @keyframes slide-up { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } } @keyframes scale-in { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } } @keyframes pulse-opacity { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } @keyframes fade-out { from { opacity: 1; } to { opacity: 0; } } @keyframes ken-burns-a { 0% { transform: scale(1) translate(0, 0); } 50% { transform: scale(1.06) translate(-0.5%, -0.3%); } 100% { transform: scale(1) translate(0, 0); } } @keyframes ken-burns-b { 0% { transform: scale(1.04) translate(0.3%, 0); } 50% { transform: scale(1) translate(-0.3%, 0.2%); } 100% { transform: scale(1.04) translate(0.3%, 0); } } /* Soft radiating halo used behind the centered play button while paused — communicates "tap to resume" without distracting during playback. */ @keyframes player-breathe { 0%, 100% { box-shadow: 0 0 0 0 rgb(255 255 255 / 0.22), 0 20px 60px -18px rgb(0 0 0 / 0.7), inset 0 1px 0 rgb(255 255 255 / 0.7); } 60% { box-shadow: 0 0 0 18px rgb(255 255 255 / 0), 0 20px 60px -18px rgb(0 0 0 / 0.7), inset 0 1px 0 rgb(255 255 255 / 0.7); } } /* Cinematic reveal for the floating control cluster — fades in slightly below its resting position when controls become visible. Pairs with the opacity transition applied via Tailwind utilities. */ @keyframes player-rise { from { opacity: 0; transform: translateY(6px) scale(0.985); } to { opacity: 1; transform: translateY(0) scale(1); } } /* ================================================================ VIEW TRANSITIONS — Page navigation animations ================================================================ Uses the View Transitions API for smooth cross-fades between route changes. The browser captures old/new page snapshots and animates between them. ================================================================ */ ::view-transition-old(main-content) { animation: 200ms cubic-bezier(0, 0, 0.2, 1) both fade-out; } ::view-transition-new(main-content) { animation: 300ms cubic-bezier(0, 0, 0.2, 1) both slide-up; } @media (prefers-reduced-motion: reduce) { ::view-transition-old(main-content), ::view-transition-new(main-content) { animation: none; } :root { --duration-fast: 0ms; --duration-normal: 0ms; --duration-cinematic: 0ms; --duration-drift: 0ms; --duration-glacial: 0ms; --duration-slow: 150ms; --animate-ken-burns-a: none; --animate-ken-burns-b: none; } .ambient-glow { transition: none; } .sidebar-transition, .main-transition { transition: none; } .player-breathe, .player-rise { animation: none !important; } } @media (forced-colors: active) { * { forced-color-adjust: auto; } } /* ================================================================ § 3 THEME DEFINITIONS ================================================================ Each theme sets ALL semantic tokens. Themes are activated via the data-theme attribute on . Token naming conventions: --background Page background (darkest main surface) --foreground Primary text color --surface Default card/panel surface --surface-hover Surface on hover --surface-raised Elevated surface (modals, floating panels) --card Card background (usually = surface) --primary Primary accent/action color --secondary Secondary element backgrounds --muted Muted backgrounds --accent Active/highlighted element backgrounds --destructive Danger/error color --border Default border color --sidebar Sidebar background (often darkest surface) ================================================================ */ @layer base { /* ── Global Status Tokens ────────────────────────────────── Semantic status colors used across all themes. Individual themes may override these if needed. ───────────────────────────────────────────────────────── */ :root { --success: oklch(0.75 0.18 145); --success-foreground: oklch(0.98 0.01 145); --warning: oklch(0.8 0.15 85); --warning-foreground: oklch(0.98 0.01 85); --info: oklch(0.7 0.15 250); --info-foreground: oklch(0.98 0.01 250); } /* ── Midnight Cinema ────────────────────────────────────── */ /* Inspired by premium streaming UIs: monochromatic, cinematic, content-focused. White primary creates an elegant, theater-like atmosphere where media artwork is the star. */ [data-theme="midnight-cinema"] { --radius: 0.75rem; --font-body: "Outfit", sans-serif; --font-display: "Outfit", sans-serif; /* Surfaces: near-black with subtle cool undertone */ --background: #141417; --foreground: #e8e8ec; --card: #1c1c20; --card-foreground: #e8e8ec; --popover: #18181c; --popover-foreground: #e8e8ec; /* Primary: white — for play buttons, key CTAs, active indicators */ --primary: #e8e8ec; --primary-foreground: #141417; /* Secondary & Muted: subtle surface variations */ --secondary: #232328; --secondary-foreground: #d0d0d6; --muted: #1c1c20; /* Lightened from #6e6e78 (~3.4:1) to meet WCAG AA: ~6.3:1 on --background, ~5.3:1 even on the lightest chip surface (#232328). */ --muted-foreground: #9696a0; --accent: #232328; --accent-foreground: #e8e8ec; /* Destructive: warm red for danger actions */ --destructive: #ef4444; --destructive-foreground: #ffffff; /* Borders: barely visible, just enough structure */ --border: #28282e; --input: #1c1c20; --ring: #e8e8ec; /* Charts: desaturated pastels that fit the cinema aesthetic */ --chart-1: #8b9cf7; --chart-2: #7ec8e3; --chart-3: #81c995; --chart-4: #e8a87c; --chart-5: #c78dbd; /* Sidebar: darkest surface, creating depth */ --sidebar: #0f0f12; --sidebar-foreground: #e8e8ec; --sidebar-primary: #e8e8ec; --sidebar-primary-foreground: #0f0f12; --sidebar-accent: #1c1c20; --sidebar-accent-foreground: #e8e8ec; --sidebar-border: #232328; --sidebar-section-divider: #2e2e35; --sidebar-ring: #e8e8ec; /* Surfaces: layered depth */ --surface: #1c1c20; --surface-hover: #232328; --surface-raised: #28282e; --ambient: #e8e8ec; --ambient-glow-opacity: 0.08; } [data-theme="cinema-light"] { --radius: 0.75rem; --font-body: "Outfit", sans-serif; --font-display: "Outfit", sans-serif; /* Surfaces: warm off-white with cool undertone */ --background: #f4f4f6; --foreground: #1a1a1e; --card: #ffffff; --card-foreground: #1a1a1e; --popover: #ffffff; --popover-foreground: #1a1a1e; /* Primary: near-black — inverted from Cinema Dark */ --primary: #1a1a1e; --primary-foreground: #f4f4f6; /* Secondary & Muted: subtle surface variations */ --secondary: #e8e8ec; --secondary-foreground: #3a3a42; --muted: #ebebef; /* Darkened from #78787f (~4.0:1) to meet WCAG AA on light surfaces (~4.9:1 on the #e8e8ec chip, ~5.4:1 on --background). */ --muted-foreground: #63636b; --accent: #e8e8ec; --accent-foreground: #1a1a1e; /* Destructive: warm red for danger actions */ --destructive: #dc2626; --destructive-foreground: #ffffff; /* Borders: soft gray, enough structure without weight */ --border: #d8d8de; --input: #e8e8ec; --ring: #1a1a1e; /* Charts: richer tones for light backgrounds */ --chart-1: #6366f1; --chart-2: #0ea5e9; --chart-3: #22c55e; --chart-4: #f59e0b; --chart-5: #a855f7; /* Sidebar: slightly darker than background for depth */ --sidebar: #eaeaee; --sidebar-foreground: #1a1a1e; --sidebar-primary: #1a1a1e; --sidebar-primary-foreground: #f4f4f6; --sidebar-accent: #dfdfe5; --sidebar-accent-foreground: #1a1a1e; --sidebar-border: #d0d0d8; --sidebar-section-divider: #c2c2cc; --sidebar-ring: #1a1a1e; /* Surfaces: layered depth */ --surface: #ebebef; --surface-hover: #e0e0e6; --surface-raised: #ffffff; --ambient: #1a1a1e; --ambient-glow-opacity: 0.05; /* Hero: light-mode overrides */ --hero-backdrop-brightness: 1.15; --hero-backdrop-saturate: 0.6; --hero-text-shadow: 0 1px 3px rgb(255 255 255 / 60%); } [data-theme="cobalt-studio"] { --radius: 0.75rem; --font-body: "Outfit", sans-serif; --font-display: "Outfit", sans-serif; --background: #101722; --foreground: #f4f8ff; --card: #151e2b; --card-foreground: #f4f8ff; --popover: #121a25; --popover-foreground: #f4f8ff; --primary: #78aefc; --primary-foreground: #0f1722; --secondary: #1b2634; --secondary-foreground: #d7e2f1; --muted: #151e2b; --muted-foreground: #90a0b5; --accent: #203043; --accent-foreground: #f4f8ff; --destructive: #ef6b73; --destructive-foreground: #ffffff; --border: #28384d; --input: #182231; --ring: #78aefc; --chart-1: #78aefc; --chart-2: #87d2ff; --chart-3: #6ec7ba; --chart-4: #b9c7ff; --chart-5: #f18d8d; --sidebar: #0c131d; --sidebar-foreground: #f4f8ff; --sidebar-primary: #78aefc; --sidebar-primary-foreground: #0f1722; --sidebar-accent: #172231; --sidebar-accent-foreground: #f4f8ff; --sidebar-border: #1f2a39; --sidebar-section-divider: #283a4e; --sidebar-ring: #78aefc; --surface: #151e2b; --surface-hover: #1d2a3b; --surface-raised: #223245; --ambient: #78aefc; --ambient-glow-opacity: 0.11; } [data-theme="oxblood-noir"] { --radius: 0.75rem; --font-body: "Outfit", sans-serif; --font-display: "Outfit", sans-serif; --background: #171113; --foreground: #f8f2f3; --card: #20181b; --card-foreground: #f8f2f3; --popover: #1b1417; --popover-foreground: #f8f2f3; --primary: #d16a78; --primary-foreground: #180f12; --secondary: #281d21; --secondary-foreground: #decfd2; --muted: #20181b; --muted-foreground: #a28e95; --accent: #322228; --accent-foreground: #f8f2f3; --destructive: #f08080; --destructive-foreground: #ffffff; --border: #3b2830; --input: #251a1f; --ring: #d16a78; --chart-1: #d16a78; --chart-2: #f08e7a; --chart-3: #c8a0b8; --chart-4: #8aa0c6; --chart-5: #e1b86e; --sidebar: #120d0f; --sidebar-foreground: #f8f2f3; --sidebar-primary: #d16a78; --sidebar-primary-foreground: #180f12; --sidebar-accent: #21161a; --sidebar-accent-foreground: #f8f2f3; --sidebar-border: #2c1d23; --sidebar-section-divider: #3a252e; --sidebar-ring: #d16a78; --surface: #20181b; --surface-hover: #2a2025; --surface-raised: #34262c; --ambient: #d16a78; --ambient-glow-opacity: 0.11; } [data-theme="evergreen-studio"] { --radius: 0.75rem; --font-body: "Outfit", sans-serif; --font-display: "Outfit", sans-serif; --background: #101715; --foreground: #f2f8f5; --card: #16201d; --card-foreground: #f2f8f5; --popover: #131b19; --popover-foreground: #f2f8f5; --primary: #5bc39d; --primary-foreground: #0d1513; --secondary: #1b2824; --secondary-foreground: #d1e0d9; --muted: #16201d; --muted-foreground: #91a39c; --accent: #20322d; --accent-foreground: #f2f8f5; --destructive: #f07a7a; --destructive-foreground: #ffffff; --border: #284038; --input: #182420; --ring: #5bc39d; --chart-1: #5bc39d; --chart-2: #7dd8bd; --chart-3: #7eb7a4; --chart-4: #88a9cf; --chart-5: #e0b66b; --sidebar: #0c1210; --sidebar-foreground: #f2f8f5; --sidebar-primary: #5bc39d; --sidebar-primary-foreground: #0d1513; --sidebar-accent: #16221e; --sidebar-accent-foreground: #f2f8f5; --sidebar-border: #1d2f2a; --sidebar-section-divider: #253d35; --sidebar-ring: #5bc39d; --surface: #16201d; --surface-hover: #1e2b27; --surface-raised: #263732; --ambient: #5bc39d; --ambient-glow-opacity: 0.1; } } /* ================================================================ § 4 BASE STYLES ================================================================ */ @layer base { * { border-color: var(--border); } html { font-size: 16px; font-weight: 400; --ui-text-scale-factor: 1; } html[data-text-scale="large"] { font-size: 18px; --ui-text-scale-factor: 1.12; } html[data-text-scale="x-large"] { font-size: 20px; --ui-text-scale-factor: 1.24; } html[data-text-weight="strong"] { --font-weight-ui: 520; --font-weight-medium-ui: 600; --font-weight-semibold-ui: 700; --font-weight-bold-ui: 800; } html[data-text-weight="default"] { --font-weight-ui: 400; --font-weight-medium-ui: 500; --font-weight-semibold-ui: 600; --font-weight-bold-ui: 700; } html[data-high-contrast="true"] { --foreground: #ffffff; --ring: oklch(0.95 0 0); --muted-foreground: color-mix(in srgb, white 72%, var(--muted-foreground)); --border: color-mix(in srgb, white 34%, var(--border)); --input: color-mix(in srgb, white 16%, var(--input)); --surface: color-mix(in srgb, white 10%, var(--surface)); --surface-hover: color-mix(in srgb, white 16%, var(--surface-hover)); --surface-raised: color-mix(in srgb, white 20%, var(--surface-raised)); --accent: color-mix(in srgb, white 14%, var(--accent)); --ambient-glow-opacity: 0.04; } html[data-high-contrast="true"] .surface-panel-subtle, html[data-high-contrast="true"] .surface-panel, html[data-high-contrast="true"] .glass, html[data-high-contrast="true"] .glass-subtle { border-color: color-mix(in srgb, var(--border) 88%, white 12%); box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.08), 0 12px 36px -24px rgb(0 0 0 / 0.75); } html[data-high-contrast="true"] .text-muted-foreground { color: var(--muted-foreground) !important; } html[data-high-contrast="true"] .border-border, html[data-high-contrast="true"] .border-border\/50, html[data-high-contrast="true"] .border-border\/60, html[data-high-contrast="true"] .border-border\/70, html[data-high-contrast="true"] .border-border\/80 { border-color: var(--border) !important; } html[data-text-weight="strong"] .font-medium { font-weight: var(--font-weight-medium-ui, 600) !important; } html[data-text-weight="strong"] .font-semibold { font-weight: var(--font-weight-semibold-ui, 700) !important; } html[data-text-weight="strong"] .font-bold { font-weight: var(--font-weight-bold-ui, 800) !important; } html[data-text-scale="large"] .text-\[8px\], html[data-text-scale="x-large"] .text-\[8px\] { font-size: calc(8px * var(--ui-text-scale-factor)) !important; } html[data-text-scale="large"] .text-\[9px\], html[data-text-scale="x-large"] .text-\[9px\] { font-size: calc(9px * var(--ui-text-scale-factor)) !important; } html[data-text-scale="large"] .text-\[10px\], html[data-text-scale="x-large"] .text-\[10px\] { font-size: calc(10px * var(--ui-text-scale-factor)) !important; } html[data-text-scale="large"] .text-\[11px\], html[data-text-scale="x-large"] .text-\[11px\] { font-size: calc(11px * var(--ui-text-scale-factor)) !important; } html[data-text-scale="large"] .text-\[12px\], html[data-text-scale="x-large"] .text-\[12px\] { font-size: calc(12px * var(--ui-text-scale-factor)) !important; } html[data-text-scale="large"] .text-\[13px\], html[data-text-scale="x-large"] .text-\[13px\] { font-size: calc(13px * var(--ui-text-scale-factor)) !important; } html[data-text-scale="large"] .text-\[14px\], html[data-text-scale="x-large"] .text-\[14px\] { font-size: calc(14px * var(--ui-text-scale-factor)) !important; } html[data-text-scale="large"] .text-\[15px\], html[data-text-scale="x-large"] .text-\[15px\] { font-size: calc(15px * var(--ui-text-scale-factor)) !important; } html[data-text-scale="large"] .text-\[17px\], html[data-text-scale="x-large"] .text-\[17px\] { font-size: calc(17px * var(--ui-text-scale-factor)) !important; } html[data-text-scale="large"] .text-\[20px\], html[data-text-scale="x-large"] .text-\[20px\] { font-size: calc(20px * var(--ui-text-scale-factor)) !important; } html[data-text-scale="large"] .text-\[28px\], html[data-text-scale="x-large"] .text-\[28px\] { font-size: calc(28px * var(--ui-text-scale-factor)) !important; } html[data-text-scale="large"] .text-\[32px\], html[data-text-scale="x-large"] .text-\[32px\] { font-size: calc(32px * var(--ui-text-scale-factor)) !important; } html[data-text-scale="large"] .text-\[38px\], html[data-text-scale="x-large"] .text-\[38px\] { font-size: calc(38px * var(--ui-text-scale-factor)) !important; } body { background-color: var(--background); color: var(--foreground); font-family: var(--font-body); font-weight: var(--font-weight-ui, 400); background-image: radial-gradient( circle at top, color-mix(in srgb, var(--ambient) 10%, transparent), transparent 32% ), linear-gradient( to bottom, color-mix(in srgb, var(--background) 92%, transparent), var(--background) ); min-height: 100dvh; } } /* ================================================================ § 5 COMPONENT UTILITY CLASSES ================================================================ Reusable patterns extracted from the design system. See ../docs/design-system.md for pattern documentation. ================================================================ */ @layer components { /* ── Hero Gradient ───────────────────────────────────────── */ /* Place over a hero/backdrop image to create a cinematic bottom-to-top gradient fade into the page background. Usage:
// optional vignette
*/ .hero-gradient { position: absolute; inset: 0; background: linear-gradient( to top, var(--background) 0%, color-mix(in srgb, var(--background) 92%, transparent) 20%, color-mix(in srgb, var(--background) 55%, transparent) 50%, color-mix(in srgb, var(--background) 20%, transparent) 75%, transparent 100% ); pointer-events: none; } .hero-gradient-radial { position: absolute; inset: 0; background: radial-gradient( ellipse at center, transparent 0%, color-mix(in srgb, var(--background) 30%, transparent) 70%, var(--background) 100% ); pointer-events: none; } /* Side gradient — fades from left edge for sidebar overlap */ .hero-gradient-left { position: absolute; inset: 0; background: linear-gradient( to right, var(--background) 0%, color-mix(in srgb, var(--background) 80%, transparent) 20%, color-mix(in srgb, var(--background) 40%, transparent) 40%, transparent 60% ); pointer-events: none; } .hero-vignette { position: absolute; inset: 0; background: linear-gradient( 90deg, color-mix(in srgb, var(--background) 92%, transparent) 0%, color-mix(in srgb, var(--background) 64%, transparent) 28%, transparent 58% ), linear-gradient( to top, var(--background) 0%, color-mix(in srgb, var(--background) 75%, transparent) 28%, transparent 62% ); pointer-events: none; } /* Stronger bottom fade for edge-to-edge marquee heroes. The hero should melt into the page background rather than stop against a border — background reaches full opacity by ~35% up the frame. */ .hero-gradient-strong { position: absolute; inset: 0; background: linear-gradient( to top, var(--background) 0%, var(--background) 12%, color-mix(in srgb, var(--background) 88%, transparent) 30%, color-mix(in srgb, var(--background) 55%, transparent) 55%, color-mix(in srgb, var(--background) 18%, transparent) 80%, transparent 100% ); pointer-events: none; } /* Top scrim for the overlay marquee header — keeps eyebrow / tabs legible over bright backdrops without a hard bar. */ .hero-top-scrim { position: absolute; inset: 0 0 auto 0; height: 180px; background: linear-gradient( to bottom, color-mix(in srgb, var(--background) 70%, transparent) 0%, color-mix(in srgb, var(--background) 30%, transparent) 55%, transparent 100% ); pointer-events: none; } /* ── Ambient Glow ──────────────────────────────────────────── */ /* Radial gradient overlay tinted by --ambient color. Applied to hero sections for atmospheric color bleed from featured content. Usage:
(sibling to hero-gradient) */ .ambient-glow { position: absolute; inset: 0; background: radial-gradient( ellipse at 50% 80%, var(--ambient, var(--primary)) 0%, transparent 70% ); opacity: var(--ambient-glow-opacity, 0.12); pointer-events: none; transition: background var(--duration-cinematic) var(--ease-gentle), opacity var(--duration-cinematic) var(--ease-gentle); } /* ── Sidebar Immersion Transitions ─────────────────────────── */ .sidebar-transition { transition: width var(--duration-normal) var(--ease-default); overflow: hidden; } .main-transition { transition: margin-left var(--duration-normal) var(--ease-default); } /* ── Sidebar Scrollbar ────────────────────────────────────── */ /* Thin, auto-hiding scrollbar that blends with the sidebar. */ .sidebar-scroll { scrollbar-width: thin; scrollbar-color: transparent transparent; transition: scrollbar-color 0.2s ease; } .sidebar-scroll:hover { scrollbar-color: color-mix(in srgb, var(--sidebar-foreground) 20%, transparent) transparent; } /* WebKit (Chrome, Safari, Edge) */ .sidebar-scroll::-webkit-scrollbar { width: 6px; } .sidebar-scroll::-webkit-scrollbar-track { background: transparent; } .sidebar-scroll::-webkit-scrollbar-thumb { background: transparent; border-radius: 3px; transition: background 0.2s ease; } .sidebar-scroll:hover::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--sidebar-foreground) 20%, transparent); } .sidebar-scroll:hover::-webkit-scrollbar-thumb:hover { background: color-mix(in srgb, var(--sidebar-foreground) 35%, transparent); } /* ── Overlay Scrollbar ─────────────────────────────────── */ /* Thin, subtle scrollbar for dialogs and overlay panels. */ .overlay-scroll { scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--foreground) 15%, transparent) transparent; } .overlay-scroll::-webkit-scrollbar { width: 6px; } .overlay-scroll::-webkit-scrollbar-track { background: transparent; } .overlay-scroll::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--foreground) 15%, transparent); border-radius: 3px; } .overlay-scroll::-webkit-scrollbar-thumb:hover { background: color-mix(in srgb, var(--foreground) 30%, transparent); } /* ── Glass Surfaces ──────────────────────────────────────── */ /* Frosted glass effect for floating controls, overlay panels. Usage:
...
*/ .glass { background: color-mix(in srgb, var(--surface) 70%, transparent); backdrop-filter: blur(20px) saturate(1.2); -webkit-backdrop-filter: blur(20px) saturate(1.2); border: 1px solid color-mix(in srgb, var(--border) 50%, transparent); box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.06), 0 20px 50px -28px rgb(0 0 0 / 0.65); } .glass-subtle { background: color-mix(in srgb, var(--surface) 40%, transparent); backdrop-filter: blur(12px) saturate(1.1); -webkit-backdrop-filter: blur(12px) saturate(1.1); border: 1px solid color-mix(in srgb, var(--border) 30%, transparent); box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.05); } /* Less-transparent glass for small chips that sit over busy cover art (manga count/status pills) so the label stays legible. */ .glass-chip { background: color-mix(in srgb, var(--surface) 78%, transparent); backdrop-filter: blur(12px) saturate(1.1); -webkit-backdrop-filter: blur(12px) saturate(1.1); border: 1px solid color-mix(in srgb, var(--border) 45%, transparent); box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.06); } /* ── Terminal Surface ──────────────────────────────────────── */ /* Scoped terminal aesthetic for ffmpeg console and similar. Usage:
...
*/ .terminal-surface { --terminal-bg: oklch(0.15 0.01 260); --terminal-fg: oklch(0.85 0.15 155); --terminal-muted: oklch(0.65 0.1 155); --terminal-border: oklch(0.3 0.05 155); } .glass-dark { background: color-mix(in srgb, var(--background) 80%, transparent); backdrop-filter: blur(24px) saturate(1.3); -webkit-backdrop-filter: blur(24px) saturate(1.3); border: 1px solid color-mix(in srgb, var(--border) 40%, transparent); box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.04), 0 24px 60px -30px rgb(0 0 0 / 0.72); } /* ── Video Player — Cinema Glass ───────────────────────────── Bespoke surfaces for the floating player HUD. Lives on a black void (always on top of a video) so tokens here are hard-coded monochrome — independent of the app's theme. */ /* Primary play/pause disc — sits at the center of the frame. */ .player-disc-primary { background: linear-gradient(180deg, #ffffff 0%, #f3f3f5 100%); color: #0a0a0b; box-shadow: 0 20px 60px -18px rgb(0 0 0 / 0.7), inset 0 1px 0 rgb(255 255 255 / 0.7); transition: transform var(--duration-fast) var(--ease-spring), box-shadow var(--duration-normal) var(--ease-default), background var(--duration-fast) var(--ease-default); } .player-disc-primary:hover { transform: scale(1.04); } .player-disc-primary:active { transform: scale(0.97); } /* Secondary floating control (skip ±, prev/next episode). A barely-there glass disc that gains opacity on hover so the frame stays clear during playback. */ .player-disc-secondary { background: rgb(255 255 255 / 0.06); color: rgb(255 255 255 / 0.92); border: 1px solid rgb(255 255 255 / 0.1); backdrop-filter: blur(14px) saturate(1.1); -webkit-backdrop-filter: blur(14px) saturate(1.1); box-shadow: 0 10px 30px -14px rgb(0 0 0 / 0.6), inset 0 1px 0 rgb(255 255 255 / 0.08); transition: background var(--duration-fast) var(--ease-default), transform var(--duration-fast) var(--ease-spring), border-color var(--duration-fast) var(--ease-default); } .player-disc-secondary:hover { background: rgb(255 255 255 / 0.14); border-color: rgb(255 255 255 / 0.22); transform: scale(1.06); } .player-disc-secondary:active { transform: scale(0.95); } /* Breathing halo behind the primary disc when paused. */ .player-disc-primary[data-paused="true"] { animation: player-breathe 2.6s ease-in-out infinite; } /* Bottom HUD rail — title/time on the left, utilities on the right. */ .player-hud { background: linear-gradient( to top, rgb(0 0 0 / 0.82) 0%, rgb(0 0 0 / 0.56) 55%, rgb(0 0 0 / 0) 100% ); } /* Full-frame vignette for control legibility without washing out the picture. Two stacked gradients: a soft top scrim for the exit/title chrome and a stronger bottom scrim for the HUD. */ .player-scrim { background: linear-gradient(to bottom, rgb(0 0 0 / 0.55) 0%, rgb(0 0 0 / 0.18) 14%, rgb(0 0 0 / 0) 28%), radial-gradient(ellipse 70% 50% at 50% 50%, rgb(0 0 0 / 0.18), transparent 70%); } /* Floating cluster entrance animation — fades in with a gentle rise when the controls reveal themselves. */ .player-rise { animation: player-rise var(--duration-normal) var(--ease-gentle); } /* Subtle utility-icon button used in the bottom HUD rail. Relies on opacity to recede during playback; a thin active underline signals which secondary control is currently on. */ .player-utility-btn { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 2.5rem; height: 2.5rem; border-radius: 9999px; color: rgb(255 255 255 / 0.78); background: transparent; border: none; cursor: pointer; transition: color var(--duration-fast) var(--ease-default), background-color var(--duration-fast) var(--ease-default); } .player-utility-btn:hover { color: #fff; background: rgb(255 255 255 / 0.08); } .player-utility-btn:focus-visible { outline: 2px solid rgb(255 255 255 / 0.7); outline-offset: 2px; } .player-utility-btn[data-active="true"] { color: #fff; } .player-utility-btn[data-active="true"]::after { content: ""; position: absolute; bottom: 0.22rem; left: 50%; width: 0.28rem; height: 0.28rem; border-radius: 9999px; background: #e8b86e; /* warm projector-lamp amber */ transform: translateX(-50%); box-shadow: 0 0 8px rgb(232 184 110 / 0.7); } /* Vertical divider between title cluster and utility rail. */ .player-hud-divider { width: 1px; align-self: stretch; background: linear-gradient( to bottom, transparent, rgb(255 255 255 / 0.14) 20%, rgb(255 255 255 / 0.14) 80%, transparent ); margin-inline: 0.25rem; } /* Range-input thumb for the subtitle appearance panel's opacity slider. Mirrors the seek bar's hover thumb so form controls feel native to the player chrome rather than browser-default. */ .player-subtitle-opacity-slider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 14px; height: 14px; border-radius: 9999px; background: #ffffff; border: none; box-shadow: 0 2px 8px rgb(0 0 0 / 0.5), inset 0 0 0 1px rgb(0 0 0 / 0.12); cursor: pointer; } .player-subtitle-opacity-slider::-moz-range-thumb { width: 14px; height: 14px; border-radius: 9999px; background: #ffffff; border: none; box-shadow: 0 2px 8px rgb(0 0 0 / 0.5), inset 0 0 0 1px rgb(0 0 0 / 0.12); cursor: pointer; } /* ── Media Card ──────────────────────────────────────────── */ /* Hover effect for poster/backdrop cards. Scale is applied to the image inside the overflow-hidden .media-card-image container so it stays visually contained and doesn't overlap adjacent carousels. Usage:
Title
*/ .media-card { transition: transform var(--duration-normal) var(--ease-gentle), filter var(--duration-fast) var(--ease-default); } .media-card:hover { filter: brightness(1.1); transform: translateY(-4px); } /* Card image container with consistent rounded corners */ .media-card-image { overflow: hidden; border-radius: var(--radius-xl); background-color: var(--surface); border: 1px solid color-mix(in srgb, var(--border) 65%, transparent); box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.05), 0 24px 40px -26px rgb(0 0 0 / 0.8); } .media-card-image img { transition: transform var(--duration-normal) var(--ease-gentle), opacity var(--duration-normal) var(--ease-default); } .media-card:hover .media-card-image img { transform: scale(1.06); } /* ── Episode Row ─────────────────────────────────────────── */ /* Horizontal episode list item with hover highlight. Usage:
E1 Title ...
*/ .episode-row { display: flex; align-items: center; gap: 1rem; padding: 0.75rem 1rem; border-bottom: 1px solid var(--border); transition: background-color var(--duration-fast) var(--ease-default); cursor: pointer; } .episode-row:hover { background-color: var(--surface-hover); } .episode-row:last-child { border-bottom: none; } /* ── Metadata Badge ──────────────────────────────────────── */ /* Small inline badge for metadata (year, rating, duration). Usage: */ .metadata-badge { display: inline-flex; align-items: center; padding: 0.25rem 0.6rem; border-radius: 9999px; font-size: 0.7rem; font-weight: 600; line-height: 1rem; letter-spacing: 0.04em; text-transform: uppercase; background-color: color-mix(in srgb, var(--foreground) 8%, transparent); color: color-mix(in srgb, var(--foreground) 72%, var(--muted-foreground)); border: 1px solid color-mix(in srgb, var(--border) 55%, transparent); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); } /* ── Pill Button ─────────────────────────────────────────── */ /* Fully-rounded button shape for primary actions. Usage: */ .pill { display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem; padding: 0.78rem 1.4rem; border-radius: 9999px; font-size: 0.84rem; font-weight: 700; letter-spacing: 0.01em; transition: all var(--duration-fast) var(--ease-default); } .pill-primary { background-color: var(--primary); color: var(--primary-foreground); box-shadow: 0 16px 36px -20px color-mix(in srgb, var(--primary) 60%, transparent); } .pill-primary:hover { filter: brightness(0.96); transform: translateY(-1px); } .pill-secondary { background-color: var(--secondary); color: var(--secondary-foreground); } .pill-secondary:hover { background-color: var(--surface-hover); } .pill-glass { background: color-mix(in srgb, var(--foreground) 12%, transparent); color: var(--foreground); border: 1px solid color-mix(in srgb, var(--border) 50%, transparent); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); } .pill-glass:hover { background: color-mix(in srgb, var(--foreground) 18%, transparent); } /* ── Action Circle ───────────────────────────────────────── */ /* Circular icon button for secondary actions (shuffle, download, etc). Usage: */ .action-circle { display: inline-flex; align-items: center; justify-content: center; width: 2.5rem; height: 2.5rem; border-radius: 9999px; background-color: color-mix(in srgb, var(--foreground) 10%, transparent); color: var(--foreground); transition: background-color var(--duration-fast) var(--ease-default); cursor: pointer; border: none; } .action-circle:hover { background-color: color-mix(in srgb, var(--foreground) 18%, transparent); } /* ── Tab Bar ─────────────────────────────────────────────── */ /* Pill-style tab navigation (Recommended | Browse | etc). Usage:
*/ .tab-bar { display: flex; align-items: center; gap: 0.25rem; padding: 0.25rem; border-radius: var(--radius-lg); } .tab-item { padding: 0.375rem 1rem; border-radius: var(--radius-md); font-size: 0.875rem; font-weight: 500; color: var(--muted-foreground); transition: all var(--duration-fast) var(--ease-default); cursor: pointer; background: transparent; border: none; } .tab-item:hover { color: var(--foreground); background-color: var(--surface-hover); } .tab-active { color: var(--foreground); background-color: var(--surface); border: 1px solid var(--border); } /* ── Carousel Dots ───────────────────────────────────────── */ /* Indicator dots for hero carousels. Usage: */ .carousel-dots { display: flex; align-items: center; justify-content: center; gap: 0.45rem; } .dot { width: 0.42rem; height: 0.42rem; border-radius: 9999px; background-color: color-mix(in srgb, var(--foreground) 26%, transparent); border: none; padding: 0; cursor: pointer; transition: all var(--duration-normal) var(--ease-default); } .dot-active { width: 1.5rem; background-color: var(--primary); } /* ── Library Marquee Header ───────────────────────────────── */ /* Shared sticky header for the Library page. Sits transparently over the hero on the Recommended tab, and switches to a glass surface on scroll or when no hero backdrop is behind it. */ .library-marquee-header { /* Mobile: non-sticky so the tabs scroll with the hero instead of hovering over it — stacking a sticky tab bar under the sticky top nav eats too much vertical space on narrow viewports. At lg+ the mobile top nav is hidden, so we promote this header to sticky at top:0 there. */ position: relative; z-index: 20; display: flex; align-items: center; /* On mobile the eyebrow is hidden, so the tab pill bar is the only child — center it. sm+ overrides back to space-between once the eyebrow shows. */ justify-content: center; gap: 1.5rem; padding: 0.9rem 1rem; border-bottom: 1px solid transparent; background: color-mix(in srgb, var(--background) 82%, transparent); backdrop-filter: blur(18px) saturate(1.15); -webkit-backdrop-filter: blur(18px) saturate(1.15); border-bottom-color: color-mix(in srgb, var(--border) 55%, transparent); transition: background var(--duration-normal) var(--ease-default), backdrop-filter var(--duration-normal) var(--ease-default), border-color var(--duration-normal) var(--ease-default); } @media (min-width: 1024px) { .library-marquee-header { position: sticky; top: 0; z-index: 30; } } .library-marquee-header.is-overlay { background: transparent; backdrop-filter: none; -webkit-backdrop-filter: none; border-bottom-color: transparent; } .library-marquee-header.is-overlay[data-scrolled="true"] { background: color-mix(in srgb, var(--background) 72%, transparent); backdrop-filter: blur(18px) saturate(1.15); -webkit-backdrop-filter: blur(18px) saturate(1.15); border-bottom-color: color-mix(in srgb, var(--border) 50%, transparent); } @media (min-width: 640px) { .library-marquee-header { justify-content: space-between; padding: 1rem 1.5rem; } } @media (min-width: 1024px) { .library-marquee-header { padding: 1.1rem 2.5rem; } } @media (min-width: 1280px) { .library-marquee-header { padding: 1.1rem 3rem; } } /* Small-caps tracked eyebrow label used above hero content and in the marquee header as the page identifier. */ .hero-eyebrow { font-family: var(--font-body); font-size: 0.72rem; font-weight: 600; line-height: 1; letter-spacing: 0.22em; text-transform: uppercase; color: color-mix(in srgb, var(--foreground) 62%, transparent); white-space: nowrap; } .hero-eyebrow .hero-eyebrow-divider { display: inline-block; margin: 0 0.55em; opacity: 0.45; } .hero-eyebrow .hero-eyebrow-strong { color: color-mix(in srgb, var(--foreground) 92%, transparent); } .is-overlay .hero-eyebrow { color: color-mix(in srgb, #fff 70%, transparent); text-shadow: 0 2px 12px rgb(0 0 0 / 0.5); } .is-overlay .hero-eyebrow .hero-eyebrow-strong { color: #fff; } /* Middot-separated metadata track for hero content. Replaces chip-style badges with editorial cover-copy feel. */ .hero-meta-track { display: flex; flex-wrap: wrap; align-items: center; gap: 0 0.1rem; font-family: var(--font-body); font-size: 0.82rem; font-weight: 500; letter-spacing: 0.02em; color: color-mix(in srgb, var(--foreground) 78%, transparent); } .hero-meta-track > span { display: inline-block; } .hero-meta-track > span + span::before { content: "·"; display: inline-block; margin: 0 0.6rem; opacity: 0.55; color: color-mix(in srgb, var(--foreground) 55%, transparent); } /* Editorial slide indicator (counter + rail). */ .hero-slide-counter { display: inline-flex; align-items: center; gap: 0.75rem; font-family: var(--font-mono); font-size: 0.7rem; font-weight: 500; letter-spacing: 0.12em; color: color-mix(in srgb, #fff 68%, transparent); text-shadow: 0 2px 10px rgb(0 0 0 / 0.45); } .hero-slide-counter .num { color: #fff; } .hero-slide-counter .sep { width: 2.5rem; height: 1px; background: color-mix(in srgb, #fff 35%, transparent); } .hero-progress-rail { position: relative; width: 9rem; height: 2px; background: color-mix(in srgb, #fff 18%, transparent); overflow: hidden; border-radius: 9999px; } .hero-progress-rail > span { position: absolute; inset: 0; background: color-mix(in srgb, #fff 88%, transparent); transform-origin: left center; transform: scaleX(0); animation: hero-progress 8s linear forwards; } .hero-progress-rail[data-paused="true"] > span { animation-play-state: paused; } @keyframes hero-progress { from { transform: scaleX(0); } to { transform: scaleX(1); } } @media (prefers-reduced-motion: reduce) { .hero-progress-rail > span { animation: none; transform: scaleX(1); } } /* Pill-style tab bar used inside the marquee header. */ .marquee-tab-bar { display: inline-flex; align-items: center; gap: 0.15rem; padding: 0.25rem; border-radius: 9999px; background: color-mix(in srgb, #fff 8%, transparent); border: 1px solid color-mix(in srgb, #fff 10%, transparent); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); } .library-marquee-header:not(.is-overlay) .marquee-tab-bar { background: color-mix(in srgb, var(--foreground) 6%, transparent); border-color: color-mix(in srgb, var(--border) 60%, transparent); } .marquee-tab-trigger { display: inline-flex; align-items: center; padding: 0.5rem 1rem; border-radius: 9999px; font-size: 0.78rem; font-weight: 600; letter-spacing: 0.01em; color: color-mix(in srgb, var(--foreground) 62%, transparent); background: transparent; border: none; cursor: pointer; white-space: nowrap; transition: color var(--duration-fast) var(--ease-default); } .library-marquee-header.is-overlay .marquee-tab-trigger { color: color-mix(in srgb, #fff 68%, transparent); } .marquee-tab-trigger:hover { color: var(--foreground); } .library-marquee-header.is-overlay .marquee-tab-trigger:hover { color: #fff; } .marquee-tab-trigger[data-state="active"] { color: var(--foreground); background: color-mix(in srgb, var(--foreground) 12%, transparent); } .library-marquee-header.is-overlay .marquee-tab-trigger[data-state="active"] { color: #0a0a0b; background: color-mix(in srgb, #fff 92%, transparent); } .marquee-tab-trigger:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; } .panel-border { border: 1px solid color-mix(in srgb, var(--border) 72%, transparent); box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.04), 0 18px 45px -30px rgb(0 0 0 / 0.55); } .page-shell { width: 100%; max-width: 1400px; margin-inline: auto; padding-inline: 1rem; } .page-shell-wide { width: 100%; max-width: 1520px; margin-inline: auto; padding-inline: 1rem; } @media (min-width: 640px) { .page-shell, .page-shell-wide { padding-inline: 1.5rem; } } @media (min-width: 1024px) { .page-shell, .page-shell-wide { padding-inline: 2.5rem; } } @media (min-width: 1280px) { .page-shell-wide { padding-inline: 3rem; } } .page-header { display: flex; flex-wrap: wrap; align-items: end; justify-content: space-between; gap: 1rem; } .page-title { font-size: clamp(2rem, 4vw, 3.75rem); line-height: 0.95; font-weight: 800; letter-spacing: -0.05em; } .page-subtitle { max-width: 60ch; color: color-mix(in srgb, var(--foreground) 68%, var(--muted-foreground)); line-height: 1.7; } .surface-panel { background: color-mix(in srgb, var(--surface) 90%, transparent); border: 1px solid color-mix(in srgb, var(--border) 72%, transparent); border-radius: calc(var(--radius-2xl) + 0.35rem); box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.04), 0 24px 50px -34px rgb(0 0 0 / 0.65); } .surface-panel-subtle { background: color-mix(in srgb, var(--surface) 72%, transparent); border: 1px solid color-mix(in srgb, var(--border) 62%, transparent); border-radius: calc(var(--radius-xl) + 0.25rem); box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.04); } .caption-empty-state span { display: block; height: 0.35rem; border-radius: 999px; background: color-mix(in srgb, var(--foreground) 18%, transparent); } .caption-empty-state span:nth-child(1) { width: 72%; margin-inline: auto; } .caption-empty-state span:nth-child(2) { width: 54%; margin-inline: auto; } .caption-empty-state span:nth-child(3) { width: 38%; margin-inline: auto; } /* ── Scan Queue Accent ──────────────────────────────────── */ /* Animated gradient sweep across the top of the scan queue to signal live activity. Only visible when scans are active. */ .scan-queue-accent { background: linear-gradient( 90deg, transparent 0%, color-mix(in srgb, var(--primary) 50%, transparent) 45%, var(--primary) 50%, color-mix(in srgb, var(--primary) 50%, transparent) 55%, transparent 100% ); background-size: 200% 100%; animation: scan-accent-sweep 4s ease-in-out infinite; opacity: 0.6; } @keyframes scan-accent-sweep { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } .auth-shell { min-height: 100dvh; position: relative; overflow: hidden; display: flex; align-items: center; justify-content: center; padding: 1.5rem; } .auth-shell::before { content: ""; position: absolute; inset: 0; background: radial-gradient( circle at 18% 18%, color-mix(in srgb, var(--primary) 18%, transparent), transparent 28% ), radial-gradient( circle at 82% 12%, color-mix(in srgb, var(--ambient) 15%, transparent), transparent 26% ), linear-gradient( 135deg, color-mix(in srgb, var(--background) 96%, transparent), var(--background) ); pointer-events: none; } .auth-card { position: relative; z-index: 1; width: 100%; max-width: 28rem; border-radius: 2rem; padding: 1.75rem; } .admin-shell { width: 100%; max-width: 1560px; margin-inline: auto; } /* ── Section Header ──────────────────────────────────────── */ /* Consistent section heading with optional icon. Usage:
Continue Watching
*/ .section-header { display: flex; align-items: center; gap: 0.5rem; font-size: 1.25rem; font-weight: 600; color: var(--foreground); padding-bottom: 0.75rem; } /* ── Live Badge ──────────────────────────────────────────── */ .live-badge { margin-left: auto; padding: 2px 8px; border-radius: 6px; font-size: 10px; font-weight: 700; background: rgba(255, 60, 60, 0.12); color: #f87171; border: 1px solid rgba(255, 60, 60, 0.2); animation: pulse-opacity 2s ease-in-out infinite; } /* ── Progress Bar ────────────────────────────────────────── */ /* Thin progress indicator for watch progress on cards/heroes. Usage:
*/ .progress-bar { width: 100%; height: 3px; background-color: color-mix(in srgb, var(--foreground) 15%, transparent); border-radius: 9999px; overflow: hidden; } .progress-fill { height: 100%; background-color: var(--primary); border-radius: 9999px; transition: width var(--duration-normal) var(--ease-default); } /* ── Watched Badge ───────────────────────────────────────── */ /* Checkmark badge overlay on fully-watched media cards. Position: absolute top-right of card. Usage:
*/ .watched-badge { position: absolute; top: 0.5rem; right: 0.5rem; display: flex; align-items: center; justify-content: center; width: 1.5rem; height: 1.5rem; border-radius: 9999px; background-color: var(--primary); color: var(--primary-foreground); } /* ── Play Overlay ────────────────────────────────────────── */ /* Semi-transparent play button centered on thumbnails. Usage:
*/ .play-overlay { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: color-mix(in srgb, var(--background) 40%, transparent); border-radius: inherit; opacity: 0; transition: opacity var(--duration-normal) var(--ease-default); } .group:hover .play-overlay, .play-overlay:hover { opacity: 1; } /* ── Impersonation Banner ────────────────────────────────── */ /* Override strip shown while an admin is viewing the app as another user. Warm-gold accents on a glass-dark surface — unmistakable without being alarming. Pinned to the viewport top via Tailwind's `sticky top-0 z-50` utilities on the outer element. */ /* Global sidebar offset used by out-of-tree chrome that needs to align with the current sidebar width. Layout sets `data-sidebar-collapsed` on the root element; this media-query block resolves the correct px value per breakpoint/state. */ :root { --app-sidebar-offset: 0px; } @media (min-width: 1024px) { :root { --app-sidebar-offset: 260px; } :root[data-sidebar-collapsed="true"] { --app-sidebar-offset: 64px; } } .impersonation-banner { --impersonation-accent: oklch(0.82 0.14 78); --impersonation-accent-glow: oklch(0.82 0.14 78 / 0.55); --impersonation-accent-soft: oklch(0.82 0.14 78 / 0.12); --impersonation-accent-line: oklch(0.82 0.14 78 / 0.32); margin-left: var(--app-sidebar-offset); transition: margin-left var(--duration-normal) var(--ease-default); position: sticky; background: linear-gradient( 180deg, color-mix(in srgb, var(--background) 94%, transparent), color-mix(in srgb, var(--background) 88%, transparent) ); backdrop-filter: blur(20px) saturate(1.3); -webkit-backdrop-filter: blur(20px) saturate(1.3); border-bottom: 1px solid color-mix(in srgb, var(--impersonation-accent) 16%, var(--border)); box-shadow: inset 0 1px 0 color-mix(in srgb, var(--impersonation-accent) 22%, transparent), 0 10px 32px -20px rgb(0 0 0 / 0.65); } /* Luminous top accent line — fades in from both edges so the banner reads as a deliberate strip rather than a hard bar. */ .impersonation-banner::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 1px; background: linear-gradient( 90deg, transparent, var(--impersonation-accent-line) 18%, var(--impersonation-accent-line) 82%, transparent ); pointer-events: none; } /* Soft warm wash on the left edge so the eye lands on the status dot first. Fades out before the button column. */ .impersonation-banner::after { content: ""; position: absolute; inset: 0; background: linear-gradient( 90deg, color-mix(in srgb, var(--impersonation-accent) 10%, transparent), transparent 38% ); pointer-events: none; } .impersonation-banner-inner { position: relative; z-index: 1; } .impersonation-dot { background: var(--impersonation-accent); box-shadow: 0 0 10px 1px var(--impersonation-accent-glow); } .impersonation-pulse { background: var(--impersonation-accent); opacity: 0.55; } .impersonation-label { letter-spacing: 0.22em; color: color-mix(in srgb, var(--impersonation-accent) 85%, var(--foreground)); } .impersonation-chip { background: var(--impersonation-accent-soft); color: color-mix(in srgb, var(--impersonation-accent) 85%, var(--foreground)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--impersonation-accent) 32%, transparent); } .impersonation-end-btn { color: var(--foreground); background: color-mix(in srgb, var(--foreground) 5%, transparent); border: 1px solid color-mix(in srgb, var(--impersonation-accent) 24%, var(--border)); } .impersonation-end-btn:hover { background: color-mix(in srgb, var(--impersonation-accent) 14%, transparent); border-color: color-mix(in srgb, var(--impersonation-accent) 55%, var(--border)); box-shadow: 0 0 0 3px color-mix(in srgb, var(--impersonation-accent) 8%, transparent); } .impersonation-end-btn:focus-visible { border-color: var(--impersonation-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--impersonation-accent) 28%, transparent); } }