<script lang="ts">
	import logo from '$lib/assets/logo.png';
	import {
		APP_NAME,
		APP_TAGLINE,
		APP_CANONICAL_URL
	} from '$lib/branding';
	import Player from '$lib/components/Player.svelte';
	import Toast from '$lib/components/Toast.svelte';
	import Queue from '$lib/components/Queue.svelte';
	import SearchModal from '$lib/components/SearchModal.svelte';
	import LogoutModal from '$lib/components/LogoutModal.svelte';
	import FeedbackModal from '$lib/components/FeedbackModal.svelte';
	import TermsOverlay from '$lib/components/TermsOverlay.svelte';
	import LikersSheet from '$lib/components/LikersSheet.svelte';
	import { onMount, onDestroy, untrack } from 'svelte';
	import { page } from '$app/stores';
	import { afterNavigate } from '$app/navigation';
	import { auth } from '$lib/auth.svelte';
	import { preferences } from '$lib/preferences.svelte';
	import { ambient } from '$lib/ambient.svelte';
	import { moderation } from '$lib/moderation.svelte';
	import { player } from '$lib/player.svelte';
	import { queue } from '$lib/queue.svelte';
	import { jam } from '$lib/jam.svelte';
	import { search } from '$lib/search.svelte';
	import { browser } from '$app/environment';
	import { initObservability } from '$lib/observability';
	import { getServerConfig } from '$lib/config';
	let { children } = $props<{ children: any }>();
	let showQueue = $state(false);

	// pages that define their own <title> in svelte:head
	let hasPageMetadata = $derived(
		$page.url.pathname === '/' || // homepage
		$page.url.pathname === '/activity' || // activity feed
		$page.url.pathname === '/record' || // in-browser recording
		$page.url.pathname.startsWith('/track/') || // track detail
		$page.url.pathname.startsWith('/playlist/') || // playlist detail
		$page.url.pathname.startsWith('/tag/') || // tag detail
		$page.url.pathname === '/liked' || // liked tracks
		$page.url.pathname.startsWith('/jam/') || // jam invite
		$page.url.pathname.match(/^\/u\/[^/]+$/) || // artist detail
		$page.url.pathname.match(/^\/u\/[^/]+\/album\/[^/]+/) // album detail
	);

	let isEmbed = $derived($page.url.pathname.startsWith('/embed/'));

	// show terms overlay if authenticated and either never accepted or accepted before latest update
	// exclude legal pages so users can read full terms/privacy/cookies
	// uses preferences.needsTermsAcceptance which compares accepted_at vs terms_last_updated
	let showTermsOverlay = $derived(
		auth.isAuthenticated &&
		preferences.needsTermsAcceptance &&
		!$page.url.pathname.startsWith('/terms') &&
		!$page.url.pathname.startsWith('/privacy') &&
		!$page.url.pathname.startsWith('/cookies')
	);

	// initialize auth and preferences once on mount (not on every navigation)
	// this prevents repeated /auth/me calls for unauthenticated users
	onMount(async () => {
		// set up browser observability if enabled by backend config
		getServerConfig()
			.then((config) => {
				if (config.browser_observability) initObservability();
			})
			.catch(() => {});

		// fetch sensitive images client-side (small payload, fast)
		moderation.initialize();

		// check auth status once
		await auth.initialize();

		// if authenticated, fetch preferences and reconnect to active jam or personal queue
		if (auth.isAuthenticated) {
			await preferences.initialize();

			// check for active jam first — if rejoining, jam owns the queue state
			let joinedJam = false;
			if (!jam.active) {
				const activeJam = await jam.fetchActive();
				if (activeJam) {
					await jam.join(activeJam.code);
					joinedJam = true;
				}
			}

			if (!joinedJam && queue.revision === null) {
				void queue.fetchQueue();
			}
		}
	});

	// auto-open queue when joining a jam (only on jam.active transition, not on queue toggle)
	$effect(() => {
		if (!browser) return;
		if (jam.active) {
			untrack(() => {
				if (!showQueue) {
					showQueue = true;
					localStorage.setItem('showQueue', 'true');
				}
			});
		}
	});

	// document title: show playing track, or fall back to page title
	let pageTitle = $state(`${APP_NAME} - ${APP_TAGLINE}`);

	function updateTitle() {
		const track = player.currentTrack;
		const playing = track && !player.paused;
		document.title = playing
			? `${track.title} - ${track.artist} • ${APP_NAME}`
			: pageTitle;
	}

	afterNavigate(() => {
		// capture page title after svelte:head renders, then apply correct title
		window.requestAnimationFrame(() => {
			const currentTitle = document.title;
			if (!currentTitle.includes(` • ${APP_NAME}`)) {
				pageTitle = currentTitle;
			}
			updateTitle();
		});
	});

	// react to play/pause changes
	$effect(() => {
		if (!browser) return;
		player.currentTrack;
		player.paused;
		updateTitle();
	});

	// set CSS custom property for queue width adjustment
	$effect(() => {
		if (!browser) return;
		const queueWidth = showQueue && !isEmbed ? '360px' : '0px';
		document.documentElement.style.setProperty('--queue-width', queueWidth);
	});

	// apply background image from ui_settings or playing track artwork
	// only apply when preferences are actually loaded (not null) to avoid clearing on initial load
	$effect(() => {
		if (!browser) return;
		// don't clear bg image if preferences haven't loaded yet
		if (!preferences.loaded) return;

		const uiSettings = preferences.uiSettings;
		const root = document.documentElement;

		// determine background image URL
		// priority: playing artwork (if enabled and available) > custom URL
		let bgImageUrl: string | undefined;
		let isUsingPlayingArtwork = false;
		if (uiSettings.use_playing_artwork_as_background && player.currentTrack?.image_url) {
			bgImageUrl = player.currentTrack.image_url;
			isUsingPlayingArtwork = true;
		} else if (uiSettings.background_image_url) {
			// fall back to custom URL (whether playing artwork is enabled or not)
			bgImageUrl = uiSettings.background_image_url;
		}

		if (bgImageUrl) {
			root.style.setProperty('--bg-image', `url(${bgImageUrl})`);
			// playing artwork tiles in a 4x4 grid with blur, custom image respects tile setting
			const shouldTile = isUsingPlayingArtwork || uiSettings.background_tile;
			root.style.setProperty('--bg-image-mode', shouldTile ? 'repeat' : 'no-repeat');
			// playing artwork: 25% size (4x4 grid), custom: auto if tiled, cover if not
			root.style.setProperty('--bg-image-size', isUsingPlayingArtwork ? '25%' : (uiSettings.background_tile ? 'auto' : 'cover'));
			// blur playing artwork for smoother look
			root.style.setProperty('--bg-blur', isUsingPlayingArtwork ? '40px' : '0px');
			// glass button styling for visibility against background images
			const isLight = root.classList.contains('theme-light');
			root.style.setProperty('--glass-btn-bg', isLight ? 'rgba(255, 255, 255, 0.8)' : 'rgba(18, 18, 18, 0.8)');
			root.style.setProperty('--glass-btn-bg-hover', isLight ? 'rgba(255, 255, 255, 0.9)' : 'rgba(30, 30, 30, 0.9)');
			root.style.setProperty('--glass-btn-border', isLight ? 'rgba(0, 0, 0, 0.12)' : 'rgba(255, 255, 255, 0.12)');
			// very subtle text outline for readability against background images
			root.style.setProperty('--text-shadow', isLight ? '0 0 8px rgba(255, 255, 255, 0.6)' : '0 0 8px rgba(0, 0, 0, 0.6)');
		} else {
			root.style.removeProperty('--bg-image');
			root.style.removeProperty('--bg-image-mode');
			root.style.removeProperty('--bg-image-size');
			root.style.removeProperty('--bg-blur');
			root.style.removeProperty('--glass-btn-bg');
			root.style.removeProperty('--glass-btn-bg-hover');
			root.style.removeProperty('--glass-btn-border');
			root.style.removeProperty('--text-shadow');
		}
	});

	// apply or clear ambient gradient
	$effect(() => {
		if (!browser) return;
		if (ambient.enabled && ambient.gradient) {
			ambient.applyToDOM();
		} else {
			ambient.clearFromDOM();
		}
	});

	// re-snapshot base values when theme changes while ambient is active
	$effect(() => {
		if (!browser) return;
		const _theme = preferences.theme;
		if (ambient.enabled) ambient.refreshBaseValues();
	});

	const SEEK_AMOUNT = 10; // seconds
	let previousVolume = 0.7; // for mute toggle

	function handleKeyboardShortcuts(event: KeyboardEvent) {
		// Cmd/Ctrl+K: toggle search
		if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'k') {
			event.preventDefault();
			search.toggle();
			return;
		}

		// ignore other modifier keys for remaining shortcuts
		if (event.metaKey || event.ctrlKey || event.altKey) {
			return;
		}

		// ignore if inside input/textarea/contenteditable
		const target = event.target as HTMLElement;
		if (
			target.tagName === 'INPUT' ||
			target.tagName === 'TEXTAREA' ||
			target.isContentEditable
		) {
			return;
		}

		// ignore playback shortcuts when search modal is open
		if (search.isOpen) {
			return;
		}

		const key = event.key.toLowerCase();

		// toggle queue on 'q' key
		if (key === 'q') {
			event.preventDefault();
			toggleQueue();
			return;
		}

		// playback shortcuts - only when a track is loaded
		if (!player.currentTrack) {
			return;
		}

		switch (event.key) {
			case ' ': // space - play/pause
				event.preventDefault();
				queue.togglePlayPause();
				break;

			case 'ArrowLeft': // seek backward
				event.preventDefault();
				seekBy(-SEEK_AMOUNT);
				break;

			case 'ArrowRight': // seek forward
				event.preventDefault();
				seekBy(SEEK_AMOUNT);
				break;

			case 'j': // previous track (youtube-style)
			case 'J':
				event.preventDefault();
				handlePreviousTrack();
				break;

			case 'l': // next track (youtube-style)
			case 'L':
				event.preventDefault();
				if (queue.hasNext) {
					queue.next();
				}
				break;

			case 'm': // mute/unmute
			case 'M':
				event.preventDefault();
				toggleMute();
				break;
		}
	}

	function seekBy(seconds: number) {
		if (!player.audioElement || !player.duration) return;

		const newTime = Math.max(0, Math.min(player.duration, player.currentTime + seconds));
		queue.seek(newTime * 1000);
	}

	function handlePreviousTrack() {
		const RESTART_THRESHOLD = 3; // restart if more than 3 seconds in

		if (player.currentTime > RESTART_THRESHOLD) {
			queue.seek(0);
		} else if (queue.hasPrevious) {
			queue.previous();
		} else {
			queue.seek(0);
		}
	}

	function toggleMute() {
		if (player.volume > 0) {
			previousVolume = player.volume;
			player.volume = 0;
		} else {
			player.volume = previousVolume || 0.7;
		}
	}

	onMount(() => {
		// apply saved accent color from localStorage
		const savedAccent = localStorage.getItem('accentColor');
		if (savedAccent) {
			document.documentElement.style.setProperty('--accent', savedAccent);
			document.documentElement.style.setProperty('--accent-hover', getHoverColor(savedAccent));
		}

		// apply saved theme from localStorage
		const savedTheme = localStorage.getItem('theme') as 'dark' | 'light' | 'system' | 'live' | null;
		if (savedTheme) {
			preferences.applyTheme(savedTheme);
		} else {
			// default to dark
			document.documentElement.classList.add('theme-dark');
		}

		// restore queue visibility preference
		const savedQueueVisibility = localStorage.getItem('showQueue');
		if (savedQueueVisibility !== null) {
			showQueue = savedQueueVisibility === 'true';
		}

		// add keyboard listener for shortcuts
		window.addEventListener('keydown', handleKeyboardShortcuts);

		// listen for system theme changes
		const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
		const handleSystemThemeChange = () => {
			const currentTheme = localStorage.getItem('theme');
			if (currentTheme === 'system') {
				preferences.applyTheme('system');
			}
		};
		mediaQuery.addEventListener('change', handleSystemThemeChange);

		return () => {
			mediaQuery.removeEventListener('change', handleSystemThemeChange);
		};
	});

	onDestroy(() => {
		// cleanup keyboard listener
		if (browser) {
			window.removeEventListener('keydown', handleKeyboardShortcuts);
		}
	});

	function getHoverColor(hex: string): string {
		// lighten the accent color by mixing with white
		const r = parseInt(hex.slice(1, 3), 16);
		const g = parseInt(hex.slice(3, 5), 16);
		const b = parseInt(hex.slice(5, 7), 16);
		return `rgb(${Math.min(255, r + 30)}, ${Math.min(255, g + 30)}, ${Math.min(255, b + 30)})`;
	}

	function toggleQueue() {
		showQueue = !showQueue;
		localStorage.setItem('showQueue', showQueue.toString());
	}
</script>

<svelte:head>
	<link rel="icon" href={logo} />
	<link rel="manifest" href="/manifest.webmanifest" />
	<meta name="theme-color" content="#0a0a0a" />
	<link rel="preconnect" href="https://fonts.googleapis.com" />
	<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous" />
	<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300..900&family=Inter:wght@300..900&display=swap" rel="stylesheet" />

	{#if !hasPageMetadata}
		<!-- default meta tags for pages without specific metadata -->
		<title>{APP_NAME} - {APP_TAGLINE}</title>
		<meta
			name="description"
			content={`discover and stream audio on the AT Protocol with ${APP_NAME}`}
		/>

		<!-- Open Graph / Facebook -->
		<meta property="og:type" content="website" />
		<meta property="og:title" content="{APP_NAME} - {APP_TAGLINE}" />
		<meta
			property="og:description"
			content={`discover and stream audio on the AT Protocol with ${APP_NAME}`}
		/>
		<meta property="og:site_name" content={APP_NAME} />
		<meta property="og:url" content={APP_CANONICAL_URL} />
		<meta property="og:image" content={logo} />

		<!-- Twitter -->
		<meta name="twitter:card" content="summary" />
		<meta name="twitter:title" content="{APP_NAME} - {APP_TAGLINE}" />
		<meta
			name="twitter:description"
			content={`discover and stream audio on the AT Protocol with ${APP_NAME}`}
		/>
		<meta name="twitter:image" content={logo} />
	{/if}

	<script>
		// prevent flash by applying saved settings immediately
		if (typeof window !== 'undefined') {
			(function() {
				const root = document.documentElement;

				// apply accent color
				const savedAccent = localStorage.getItem('accentColor');
				if (savedAccent) {
					root.style.setProperty('--accent', savedAccent);
					// simple lightening for hover state
					const r = parseInt(savedAccent.slice(1, 3), 16);
					const g = parseInt(savedAccent.slice(3, 5), 16);
					const b = parseInt(savedAccent.slice(5, 7), 16);
					const hover = `rgb(${Math.min(255, r + 30)}, ${Math.min(255, g + 30)}, ${Math.min(255, b + 30)})`;
					root.style.setProperty('--accent-hover', hover);
				}

				// apply font
				const savedFont = localStorage.getItem('fontFamily');
				if (savedFont) {
					const fonts = {
						'mono': "'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', 'Consolas', monospace",
						'geist': "'Geist', 'Inter', system-ui, sans-serif",
						'inter': "'Inter', system-ui, sans-serif",
						'system-ui': "system-ui, -apple-system, 'Segoe UI', sans-serif",
						'georgia': "'Georgia', 'Times New Roman', serif",
						'comic-sans': "'Comic Sans MS', 'Comic Sans', cursive",
					};
					if (fonts[savedFont]) root.style.setProperty('--font-family', fonts[savedFont]);
				}

				// apply theme
				const savedTheme = localStorage.getItem('theme') || 'dark';
				let effectiveTheme = savedTheme;
				if (savedTheme === 'live') {
					effectiveTheme = 'dark';
				} else if (savedTheme === 'system') {
					effectiveTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
				}
				root.classList.add('theme-' + effectiveTheme);

			})();
		}
	</script>
</svelte:head>

<div class="app-layout">
	<main class="main-content" class:with-queue={showQueue && !isEmbed}>
		{@render children?.()}
	</main>

	{#if showQueue && !isEmbed}
		<aside class="queue-sidebar">
			<Queue />
		</aside>
	{/if}
</div>

{#if !isEmbed}
	<button
		class="queue-toggle"
		onclick={toggleQueue}
		aria-pressed={showQueue}
		aria-label="toggle queue (Q)"
		title={showQueue ? 'hide queue (Q)' : 'show queue (Q)'}
	>
		<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
			<line x1="3" y1="6" x2="21" y2="6"></line>
			<line x1="3" y1="12" x2="21" y2="12"></line>
			<line x1="3" y1="18" x2="21" y2="18"></line>
		</svg>
	</button>

	<Player />
{/if}
<Toast />
<SearchModal />
<LogoutModal />
<FeedbackModal />
<LikersSheet />
{#if showTermsOverlay}
	<TermsOverlay />
{/if}

<style>
	:global(*),
	:global(*::before),
	:global(*::after) {
		box-sizing: border-box;
	}

	:global(:root) {
		/* layout */
		--queue-width: 0px;

		/* accent colors - configurable */
		--accent: #6a9fff;
		--accent-hover: #8ab3ff;
		--accent-muted: #4a7ddd;
		--accent-rgb: 106, 159, 255;

		/* backgrounds */
		--bg-primary: #0a0a0a;
		--bg-secondary: #141414;
		--bg-tertiary: #1a1a1a;
		--bg-hover: #1f1f1f;

		/* borders */
		--border-subtle: #282828;
		--border-default: #333333;
		--border-emphasis: #444444;

		/* text */
		--text-primary: #e8e8e8;
		--text-secondary: #b0b0b0;
		--text-tertiary: #808080;
		--text-muted: #666666;

		/* typography scale */
		--text-xs: 0.75rem;
		--text-sm: 0.85rem;
		--text-base: 0.9rem;
		--text-lg: 1rem;
		--text-xl: 1.1rem;
		--text-2xl: 1.25rem;
		--text-3xl: 1.5rem;

		/* semantic typography (aliases) */
		--text-page-heading: var(--text-3xl);
		--text-section-heading: 1.2rem;
		--text-body: var(--text-lg);
		--text-small: var(--text-base);

		/* border radius scale */
		--radius-sm: 4px;
		--radius-base: 6px;
		--radius-md: 8px;
		--radius-lg: 12px;
		--radius-xl: 16px;
		--radius-2xl: 24px;
		--radius-full: 9999px;

		/* semantic */
		--success: #4ade80;
		--warning: #fbbf24;
		--error: #ef4444;

		/* glass effects (dark theme) */
		--glass-bg: rgba(20, 20, 20, 0.75);
		--glass-blur: blur(12px);
		--glass-border: rgba(255, 255, 255, 0.06);

		/* track item glass (no blur, just translucent) */
		--track-bg: rgba(18, 18, 18, 0.88);
		--track-bg-hover: rgba(24, 24, 24, 0.92);
		--track-bg-playing: rgba(18, 18, 18, 0.88);
		--track-border: rgba(255, 255, 255, 0.06);
		--track-border-hover: rgba(255, 255, 255, 0.1);
	}

	/* light theme overrides */
	:global(:root.theme-light) {
		--bg-primary: #fafafa;
		--bg-secondary: #ffffff;
		--bg-tertiary: #f5f5f5;
		--bg-hover: #ebebeb;

		--border-subtle: #e5e5e5;
		--border-default: #d4d4d4;
		--border-emphasis: #a3a3a3;

		--text-primary: #171717;
		--text-secondary: #525252;
		--text-tertiary: #737373;
		--text-muted: #a3a3a3;

		/* accent colors preserved from user preference */
		/* accent-muted darkened for light bg readability */
		--accent-muted: color-mix(in srgb, var(--accent) 70%, black);

		/* semantic colors adjusted for light bg */
		--success: #16a34a;
		--warning: #d97706;
		--error: #dc2626;

		/* glass effects (light theme) */
		--glass-bg: rgba(250, 250, 250, 0.75);
		--glass-border: rgba(0, 0, 0, 0.06);

		/* track item glass (light theme) */
		--track-bg: rgba(255, 255, 255, 0.94);
		--track-bg-hover: rgba(250, 250, 250, 0.96);
		--track-bg-playing: rgba(255, 255, 255, 0.94);
		--track-border: rgba(0, 0, 0, 0.08);
		--track-border-hover: rgba(0, 0, 0, 0.12);
	}

	/* light theme specific overrides for components */
	:global(:root.theme-light) :global(.tag-badge) {
		background: color-mix(in srgb, var(--accent) 12%, white);
		color: var(--accent-muted);
	}

	/* shared animation for active play buttons */
	@keyframes -global-ethereal-glow {
		0%, 100% {
			box-shadow: 0 0 8px 1px color-mix(in srgb, var(--accent) 25%, transparent);
		}
		50% {
			box-shadow: 0 0 14px 3px color-mix(in srgb, var(--accent) 45%, transparent);
		}
	}

	:global(body) {
		margin: 0;
		padding: 0;
		font-family: var(--font-family, 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', 'Consolas', monospace);
		background-color: var(--bg-primary);
		color: var(--text-primary);
		-webkit-font-smoothing: antialiased;
	}

	/* ambient weather gradient layer */
	:global(body::after) {
		content: '';
		position: fixed;
		inset: 0;
		background: var(--ambient-gradient, none);
		opacity: 0;
		z-index: -2;
		pointer-events: none;
		transition: opacity 1.5s ease-in-out, background 3s ease-in-out;
	}

	:global(body.ambient-active::after) {
		opacity: 0.4;
		animation: ambient-drift 12s ease-in-out infinite;
	}

	@keyframes -global-ambient-drift {
		0%, 100% { opacity: 0.35; filter: brightness(1); }
		50% { opacity: 0.5; filter: brightness(1.08); }
	}

	/* background image with blur effect */
	:global(body::before) {
		content: '';
		position: fixed;
		inset: 0;
		background-image: var(--bg-image, none);
		background-repeat: var(--bg-image-mode, no-repeat);
		background-size: var(--bg-image-size, cover);
		background-position: center;
		filter: blur(var(--bg-blur, 0px));
		transform: scale(1.1); /* prevent blur edge artifacts */
		z-index: -1;
		pointer-events: none;
	}

	.app-layout {
		display: flex;
		min-height: 100vh; /* fallback for browsers without dvh support */
		width: 100%;
		overflow-x: clip; /* clip instead of hidden to preserve position: sticky on descendants */
	}

	@supports (min-height: 100dvh) {
		.app-layout {
			min-height: 100dvh; /* dynamic viewport height (accounts for mobile browser UI) */
		}
	}

	.main-content {
		flex: 1;
		min-width: 0;
		width: 100%;
		transition: margin-right 0.3s ease;
	}

	.main-content.with-queue {
		margin-right: 360px;
	}

	.queue-sidebar {
		position: fixed;
		top: 0;
		right: 0;
		width: min(360px, 100%);
		height: 100vh; /* fallback for browsers without dvh support */
		background: var(--glass-bg, var(--bg-primary));
		backdrop-filter: var(--glass-blur, none);
		-webkit-backdrop-filter: var(--glass-blur, none);
		border-left: 1px solid var(--glass-border, var(--border-subtle));
		z-index: 50;
	}

	@supports (height: 100dvh) {
		.queue-sidebar {
			height: 100dvh; /* dynamic viewport height (accounts for mobile browser UI) */
		}
	}

	.queue-toggle {
		position: fixed;
		bottom: calc(var(--player-height, 0px) + 20px + env(safe-area-inset-bottom, 0px));
		right: 20px;
		width: 48px;
		height: 48px;
		border-radius: var(--radius-full);
		background: var(--bg-secondary);
		border: 1px solid var(--border-default);
		color: var(--text-secondary);
		cursor: pointer;
		display: flex;
		align-items: center;
		justify-content: center;
		transition: all 0.2s;
		z-index: 60;
		box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
		transform: translate3d(0, var(--visual-viewport-offset, 0px), 0);
		will-change: transform;
	}

	.queue-toggle:hover {
		background: var(--bg-hover);
		color: var(--accent);
		border-color: var(--accent);
		transform: translate3d(0, var(--visual-viewport-offset, 0px), 0) scale(1.05);
	}

	@media (max-width: 768px) {
		.main-content.with-queue {
			margin-right: 0;
		}

		.queue-sidebar {
			width: 100%;
		}

		.queue-toggle {
			bottom: calc(var(--player-height, 0px) + 20px + env(safe-area-inset-bottom, 0px));
		}
	}
</style>
