<script lang="ts">
	import { onMount, onDestroy } from 'svelte';
	import DropZone from '$lib/components/DropZone.svelte';
	import {
		initSocket,
		onRoomStatus,
		onQueueUpdate,
		onPlaybackState,
		onQrVisibility,
		onVolume,
		onConnection,
		getRoomId,
		disconnect,
		queueAdd,
		queueRemove,
		queueRemovePlaylist,
		queuePlay,
		queueNext,
		queuePrev,
		queueClear,
		queueReorder,
		remotePlay,
		remotePause,
		remoteSeek,
		setVolume,
		setQrVisibility,
		requestQueueState
	} from '$lib/socket';
	import { roomStatus, filteredHistory, hasDisplay, queue, queueIndex, updateQueueState } from '$lib/stores/content';
	import type { ContentPayload, RoomStatus, QueueState, PlaybackState } from '$lib/socket';

	let roomId = $state<string | null>(null);
	let isPlaying = $state(false);
	let currentTime = $state(0);
	let duration = $state(0);
	let volume = $state(100);
	let qrVisible = $state(true);
	let draggedIndex = $state<number | null>(null);
	let dragOverIndex = $state<number | null>(null);
	let showCopied = $state(false);

	onMount(() => {
		initSocket('uploader');
		roomId = getRoomId();

		const unsubStatus = onRoomStatus((status: RoomStatus) => {
			roomStatus.set(status);
		});

		const unsubQueue = onQueueUpdate((state: QueueState) => {
			updateQueueState(state);
		});

		const unsubPlayback = onPlaybackState((state: PlaybackState) => {
			isPlaying = state.playing;
			currentTime = state.currentTime;
			duration = state.duration;
		});

		const unsubQr = onQrVisibility((visible: boolean) => {
			qrVisible = visible;
		});

		// Listen for volume changes from server (syncs across all dashboards)
		const unsubVolume = onVolume((vol: number) => {
			volume = Math.round(vol * 100);
			localStorage.setItem('droppy-volume', String(volume));
		});

		// Request queue state when connected (ensures sync even if initial message was missed)
		const unsubConnection = onConnection((connected) => {
			if (connected) {
				requestQueueState();
			}
		});

		return () => {
			unsubStatus();
			unsubQueue();
			unsubPlayback();
			unsubQr();
			unsubVolume();
			unsubConnection();
		};
	});

	onDestroy(() => {
		disconnect();
	});

	function handleClear() {
		queueClear();
	}

	function addToQueue(item: ContentPayload) {
		queueAdd(item);
	}

	function togglePlayPause() {
		if (isPlaying) {
			remotePause();
		} else {
			remotePlay();
		}
	}

	function handleSeek(event: Event) {
		const input = event.target as HTMLInputElement;
		const value = parseFloat(input.value);
		if (duration > 0) {
			const time = (value / 100) * duration;
			remoteSeek(time);
		}
	}

	function handleVolumeChange(event: Event) {
		const input = event.target as HTMLInputElement;
		volume = parseInt(input.value);
		setVolume(volume / 100);
		// localStorage is updated via onVolume callback when server broadcasts
	}

	function toggleQrVisibility() {
		qrVisible = !qrVisible;
		setQrVisibility(qrVisible);
	}

	// Drag and drop handlers
	function handleDragStart(index: number) {
		draggedIndex = index;
	}

	function handleDragOver(event: DragEvent, index: number) {
		event.preventDefault();
		dragOverIndex = index;
	}

	function handleDragLeave() {
		dragOverIndex = null;
	}

	function handleDrop(index: number) {
		if (draggedIndex !== null && draggedIndex !== index) {
			queueReorder(draggedIndex, index);
		}
		draggedIndex = null;
		dragOverIndex = null;
	}

	function handleDragEnd() {
		draggedIndex = null;
		dragOverIndex = null;
	}

	async function copyInviteLink() {
		const baseUrl = window.location.origin;
		const inviteUrl = `${baseUrl}/?room=${roomId}`;

		let success = false;

		// Try modern Clipboard API first (requires HTTPS or localhost)
		if (navigator.clipboard && window.isSecureContext) {
			try {
				await navigator.clipboard.writeText(inviteUrl);
				success = true;
			} catch (err) {
				console.warn('Clipboard API failed:', err);
			}
		}

		// Fallback for non-secure contexts (e.g., HTTP on LAN)
		if (!success) {
			try {
				const textArea = document.createElement('textarea');
				textArea.value = inviteUrl;
				textArea.style.position = 'fixed';
				textArea.style.left = '-9999px';
				textArea.style.top = '-9999px';
				document.body.appendChild(textArea);
				textArea.focus();
				textArea.select();
				success = document.execCommand('copy');
				document.body.removeChild(textArea);
			} catch (err) {
				console.error('Fallback copy failed:', err);
			}
		}

		if (success) {
			showCopied = true;
			setTimeout(() => {
				showCopied = false;
			}, 2000);
		} else {
			// Show the URL in a prompt as last resort
			window.prompt('Copy this invite link:', inviteUrl);
		}
	}

	let seekProgress = $derived(duration > 0 ? (currentTime / duration) * 100 : 0);

	// Queue time calculations
	function formatDuration(seconds: number): string {
		if (seconds < 0 || !isFinite(seconds)) return '';
		const hours = Math.floor(seconds / 3600);
		const mins = Math.floor((seconds % 3600) / 60);
		const secs = Math.floor(seconds % 60);
		if (hours > 0) return `${hours}h ${mins}m`;
		if (mins > 0) return `${mins}m`;
		return `${secs}s`;
	}

	function formatTime(date: Date): string {
		return date.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' });
	}

	let queueTimeInfo = $derived.by(() => {
		if ($queue.length === 0 || $queueIndex < 0) return null;

		let totalRemaining = 0;
		let hasAllDurations = true;

		// Add remaining time for current track
		if (duration > 0 && currentTime >= 0) {
			totalRemaining += Math.max(0, duration - currentTime);
		} else if ($queue[$queueIndex]?.duration) {
			totalRemaining += $queue[$queueIndex].duration;
		} else {
			hasAllDurations = false;
		}

		// Add durations for upcoming tracks
		for (let i = $queueIndex + 1; i < $queue.length; i++) {
			const item = $queue[i];
			if (item.duration && item.duration > 0) {
				totalRemaining += item.duration;
			} else {
				hasAllDurations = false;
			}
		}

		if (totalRemaining === 0) return null;

		const finishTime = new Date(Date.now() + totalRemaining * 1000);

		return {
			remaining: formatDuration(totalRemaining),
			finishTime: formatTime(finishTime),
			isEstimate: !hasAllDurations
		};
	});
</script>

<svelte:head>
	<title>Droppy</title>
	<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=Instrument+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
</svelte:head>

<div class="app">
	<header class="header">
		<div class="brand">
			<div class="brand-mark">
				<svg viewBox="0 0 24 24">
					<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/>
				</svg>
			</div>
			<span class="brand-name">Droppy</span>
		</div>
		<div class="status-pill">
			{#if $hasDisplay}
				<span class="status-indicator connected"></span>
				<span>Display ready</span>
			{:else}
				<span class="status-indicator"></span>
				<span>No display</span>
			{/if}
		</div>
	</header>

	<main class="main">
		<DropZone />

		<div class="media-sections">
			<!-- Display controls - always visible -->
			<div class="display-controls">
				<div class="volume-controls standalone">
					<svg viewBox="0 0 24 24" class="volume-icon">
						{#if volume === 0}
							<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
							<line x1="23" y1="9" x2="17" y2="15"/>
							<line x1="17" y1="9" x2="23" y2="15"/>
						{:else if volume < 50}
							<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
							<path d="M15.54 8.46a5 5 0 0 1 0 7.07"/>
						{:else}
							<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
							<path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07"/>
						{/if}
					</svg>
					<input
						type="range"
						min="0"
						max="100"
						value={volume}
						class="volume-slider"
						oninput={handleVolumeChange}
						title="Volume"
					/>
					<span class="volume-value">{volume}%</span>
				</div>
				<button
					class="qr-toggle"
					class:active={qrVisible}
					onclick={toggleQrVisibility}
					title={qrVisible ? 'Hide QR code on display' : 'Show QR code on display'}
				>
					<svg viewBox="0 0 24 24" class="qr-icon">
						<rect x="3" y="3" width="7" height="7" rx="1"/>
						<rect x="14" y="3" width="7" height="7" rx="1"/>
						<rect x="3" y="14" width="7" height="7" rx="1"/>
						<rect x="14" y="14" width="3" height="3"/>
						<rect x="18" y="14" width="3" height="3"/>
						<rect x="14" y="18" width="3" height="3"/>
						<rect x="18" y="18" width="3" height="3"/>
					</svg>
					{#if !qrVisible}
						<div class="qr-slash"></div>
					{/if}
				</button>
			</div>

			{#if $queue.length > 0}
				<section class="queue-section">
					<div class="section-header">
						<span class="section-title">Queue</span>
						<span class="section-count">{$queue.length} items</span>
						{#if queueTimeInfo}
							<span class="queue-time">
								{queueTimeInfo.isEstimate ? '~' : ''}{queueTimeInfo.remaining}
								<span class="queue-finish">ends {queueTimeInfo.finishTime}</span>
							</span>
						{/if}
						<button class="clear-queue-btn" onclick={handleClear}>Clear</button>
					</div>
					<div class="queue-controls">
						<button class="control-btn" onclick={queuePrev} disabled={$queueIndex <= 0} title="Previous">
							<svg viewBox="0 0 24 24"><polygon points="19 20 9 12 19 4 19 20"/><line x1="5" y1="19" x2="5" y2="5"/></svg>
						</button>
						<button class="control-btn play-btn" class:playing={isPlaying} onclick={togglePlayPause} title={isPlaying ? 'Pause' : 'Play'}>
							{#if isPlaying}
								<svg viewBox="0 0 24 24"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>
							{:else}
								<svg viewBox="0 0 24 24"><polygon points="5 3 19 12 5 21 5 3"/></svg>
							{/if}
						</button>
						<button class="control-btn" onclick={queueNext} disabled={$queueIndex >= $queue.length - 1} title="Next">
							<svg viewBox="0 0 24 24"><polygon points="5 4 15 12 5 20 5 4"/><line x1="19" y1="5" x2="19" y2="19"/></svg>
						</button>
					</div>
					<div class="seek-controls">
						<input
							type="range"
							min="0"
							max="100"
							value={seekProgress}
							class="seek-slider"
							oninput={handleSeek}
							title="Seek"
						/>
						<div class="time-display">
							<span>{Math.floor(currentTime / 60)}:{String(Math.floor(currentTime % 60)).padStart(2, '0')}</span>
							<span>{Math.floor(duration / 60)}:{String(Math.floor(duration % 60)).padStart(2, '0')}</span>
						</div>
					</div>
					<div class="queue-list">
						{#each $queue as item, i}
							<div
								class="queue-item"
								class:active={i === $queueIndex}
								class:dragging={draggedIndex === i}
								class:drag-over={dragOverIndex === i && draggedIndex !== i}
								draggable="true"
								ondragstart={() => handleDragStart(i)}
								ondragover={(e) => handleDragOver(e, i)}
								ondragleave={handleDragLeave}
								ondrop={() => handleDrop(i)}
								ondragend={handleDragEnd}
								role="listitem"
							>
								<div class="drag-handle" title="Drag to reorder">
									<svg viewBox="0 0 24 24"><circle cx="9" cy="6" r="1.5"/><circle cx="15" cy="6" r="1.5"/><circle cx="9" cy="12" r="1.5"/><circle cx="15" cy="12" r="1.5"/><circle cx="9" cy="18" r="1.5"/><circle cx="15" cy="18" r="1.5"/></svg>
								</div>
								<button class="queue-item-play" onclick={() => queuePlay(item.id)}>
									{#if item.thumbnail}
										<img src={item.thumbnail} alt="" />
									{:else}
										<div class="queue-item-icon">
											<svg viewBox="0 0 24 24"><polygon points="5 3 19 12 5 21 5 3"/></svg>
										</div>
									{/if}
									{#if i === $queueIndex}
										<div class="now-playing-indicator">
											<span></span><span></span><span></span>
										</div>
									{/if}
								</button>
								<div class="queue-item-info">
									<span class="queue-item-title">{item.title || 'Untitled'}</span>
									<span class="queue-item-meta">
										<span class="queue-item-type">{item.type}</span>
										{#if item.duration}
											<span class="queue-item-duration">{formatDuration(item.duration)}</span>
										{/if}
										{#if item.playlistTitle}
											<span class="queue-item-playlist" title={item.playlistTitle}>
												<svg viewBox="0 0 24 24"><path d="M19 9l-7 7-7-7"/></svg>
												{item.playlistIndex !== undefined ? `${item.playlistIndex + 1}/${item.playlistLength}` : ''}
											</span>
										{/if}
									</span>
								</div>
								{#if item.playlistId}
									<button
										class="queue-item-remove-playlist"
										onclick={() => queueRemovePlaylist(item.playlistId!)}
										title="Remove entire playlist"
									>
										<svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><line x1="14" y1="18" x2="21" y2="18"/></svg>
									</button>
								{/if}
								<button class="queue-item-remove" onclick={() => queueRemove(item.id)}>
									<svg viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
								</button>
							</div>
						{/each}
					</div>
				</section>
			{/if}

			{#if $filteredHistory.length > 0}
				<section class="recent-section">
					<div class="section-header">
						<span class="section-title">Recent</span>
						<span class="section-count">{$filteredHistory.length} items</span>
					</div>
					<div class="recent-grid">
						{#each $filteredHistory.slice(0, 8) as item}
							<div class="recent-item-wrapper">
								<button
									class="recent-item"
									data-label={item.title || 'Media'}
									onclick={() => addToQueue(item)}
								>
									{#if item.thumbnail}
										<img src={item.thumbnail} alt="" />
									{:else}
										<div class="icon-placeholder">
											<svg viewBox="0 0 24 24">
												{#if item.type === 'video'}
													<polygon points="5 3 19 12 5 21 5 3"/>
												{:else if item.type === 'audio'}
													<path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/>
												{:else}
													<rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/>
												{/if}
											</svg>
										</div>
									{/if}
									<div class="recast-overlay">
										<svg viewBox="0 0 24 24"><polygon points="5 3 19 12 5 21 5 3"/></svg>
									</div>
								</button>
								<button class="add-to-queue-btn" onclick={() => addToQueue(item)} title="Add to queue">
									<svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
								</button>
							</div>
						{/each}
					</div>
				</section>
			{/if}
		</div>
	</main>

	<footer class="footer">
		<div class="footer-info">
			<span>room://{roomId?.slice(0, 6) || '...'}</span>
		</div>
		<div class="footer-actions">
			<button class="footer-btn" class:copied={showCopied} onclick={copyInviteLink}>
				<svg viewBox="0 0 24 24">
					{#if showCopied}
						<polyline points="20 6 9 17 4 12"/>
					{:else}
						<path d="M4 12v8a2 2 0 002 2h12a2 2 0 002-2v-8"/>
						<polyline points="16 6 12 2 8 6"/>
						<line x1="12" y1="2" x2="12" y2="15"/>
					{/if}
				</svg>
				{showCopied ? 'Copied!' : 'Invite'}
			</button>
			<button class="footer-btn" onclick={handleClear} disabled={!$hasDisplay}>
				<svg viewBox="0 0 24 24">
					<path d="M3 6h18M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6"/>
					<path d="M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2"/>
				</svg>
				Clear
			</button>
			<a href="/display?room={roomId}" target="_blank" class="footer-btn primary">
				<svg viewBox="0 0 24 24">
					<rect x="2" y="3" width="20" height="14" rx="2" ry="2"/>
					<line x1="8" y1="21" x2="16" y2="21"/>
					<line x1="12" y1="17" x2="12" y2="21"/>
				</svg>
				Open Display
			</a>
		</div>
	</footer>
</div>

<style>
	:global(*) {
		margin: 0;
		padding: 0;
		box-sizing: border-box;
	}

	:global(html, body) {
		height: 100%;
	}

	:global(body) {
		font-family: 'Instrument Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
		background: #0C0C0C;
		color: #FAFAFA;
		line-height: 1.5;
		-webkit-font-smoothing: antialiased;
	}

	.app {
		min-height: 100vh;
		display: flex;
		flex-direction: column;
	}

	.header {
		display: flex;
		align-items: center;
		justify-content: space-between;
		padding: 1.25rem 2rem;
		border-bottom: 1px solid #2A2A2A;
	}

	.brand {
		display: flex;
		align-items: center;
		gap: 0.75rem;
	}

	.brand-mark {
		width: 36px;
		height: 36px;
		background: #FF6B35;
		border-radius: 10px;
		display: flex;
		align-items: center;
		justify-content: center;
	}

	.brand-mark svg {
		width: 20px;
		height: 20px;
		fill: white;
		stroke: none;
	}

	.brand-name {
		font-size: 1.25rem;
		font-weight: 700;
		letter-spacing: -0.02em;
	}

	.status-pill {
		display: flex;
		align-items: center;
		gap: 0.5rem;
		padding: 0.5rem 0.875rem;
		background: #161616;
		border: 1px solid #2A2A2A;
		border-radius: 100px;
		font-size: 0.8125rem;
		font-weight: 500;
		color: #999999;
	}

	.status-indicator {
		width: 8px;
		height: 8px;
		background: #666;
		border-radius: 50%;
	}

	.status-indicator.connected {
		background: #4ADE80;
		animation: pulse 2s ease-in-out infinite;
	}

	@keyframes pulse {
		0%, 100% { opacity: 1; transform: scale(1); }
		50% { opacity: 0.6; transform: scale(0.9); }
	}

	.main {
		flex: 1;
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		padding: 2rem;
		gap: 1.5rem;
	}

	.media-sections {
		width: 100%;
		max-width: 560px;
		margin-top: 1rem;
	}

	.section-header {
		display: flex;
		align-items: center;
		justify-content: space-between;
		margin-bottom: 0.75rem;
	}

	.section-title {
		font-size: 0.6875rem;
		font-weight: 600;
		color: #666666;
		text-transform: uppercase;
		letter-spacing: 0.1em;
	}

	.section-count {
		font-size: 0.6875rem;
		font-family: 'JetBrains Mono', 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Menlo, Consolas, monospace;
		color: #666666;
	}

	.queue-time {
		font-size: 0.75rem;
		font-family: 'JetBrains Mono', 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Menlo, Consolas, monospace;
		color: #FF6B35;
		font-weight: 500;
		margin-left: auto;
		display: flex;
		align-items: center;
		gap: 0.5rem;
	}

	.queue-finish {
		color: #666;
		font-weight: 400;
	}

	/* Queue section */
	.queue-section {
		margin-bottom: 1.5rem;
	}

	.clear-queue-btn {
		background: none;
		border: none;
		color: #666;
		font-size: 0.6875rem;
		cursor: pointer;
		padding: 0.25rem 0.5rem;
		border-radius: 4px;
		transition: all 0.15s;
	}

	.clear-queue-btn:hover {
		color: #ef4444;
		background: rgba(239, 68, 68, 0.1);
	}

	.queue-controls {
		display: flex;
		justify-content: center;
		gap: 0.5rem;
		margin-bottom: 0.75rem;
	}

	.control-btn {
		width: 40px;
		height: 40px;
		background: #161616;
		border: 1px solid #2A2A2A;
		border-radius: 50%;
		cursor: pointer;
		display: flex;
		align-items: center;
		justify-content: center;
		transition: all 0.15s;
	}

	.control-btn:hover:not(:disabled) {
		border-color: #FF6B35;
		background: rgba(255, 107, 53, 0.1);
	}

	.control-btn:disabled {
		opacity: 0.3;
		cursor: not-allowed;
	}

	.control-btn svg {
		width: 18px;
		height: 18px;
		stroke: #999;
		fill: none;
		stroke-width: 2;
	}

	.control-btn.play-btn {
		width: 48px;
		height: 48px;
		background: #FF6B35;
		border-color: #FF6B35;
	}

	.control-btn.play-btn svg {
		stroke: #fff;
		fill: #fff;
	}

	.control-btn.play-btn:hover {
		background: #ff8555;
		border-color: #ff8555;
	}

	.seek-controls {
		margin-bottom: 0.75rem;
		padding: 0 0.5rem;
	}

	.seek-slider {
		width: 100%;
		height: 4px;
		-webkit-appearance: none;
		appearance: none;
		background: #2A2A2A;
		border-radius: 2px;
		cursor: pointer;
	}

	.seek-slider::-webkit-slider-thumb {
		-webkit-appearance: none;
		width: 14px;
		height: 14px;
		background: #FF6B35;
		border-radius: 50%;
		cursor: pointer;
	}

	.seek-slider::-moz-range-thumb {
		width: 14px;
		height: 14px;
		background: #FF6B35;
		border-radius: 50%;
		border: none;
		cursor: pointer;
	}

	.time-display {
		display: flex;
		justify-content: space-between;
		font-size: 0.6875rem;
		font-family: 'JetBrains Mono', 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Menlo, Consolas, monospace;
		color: #666;
		margin-top: 0.25rem;
	}

	/* Display controls container */
	.display-controls {
		display: flex;
		align-items: center;
		justify-content: center;
		gap: 0.5rem;
		margin-bottom: 1rem;
	}

	/* Volume controls */
	.volume-controls {
		display: flex;
		align-items: center;
		gap: 0.5rem;
		margin-bottom: 0.75rem;
		padding: 0 0.5rem;
	}

	.volume-controls.standalone {
		background: #161616;
		border: 1px solid #2A2A2A;
		border-radius: 20px;
		padding: 0.5rem 0.875rem;
		margin-bottom: 0;
		width: fit-content;
		gap: 0.75rem;
	}

	/* QR toggle button */
	.qr-toggle {
		width: 40px;
		height: 40px;
		background: #161616;
		border: 1px solid #2A2A2A;
		border-radius: 50%;
		cursor: pointer;
		display: flex;
		align-items: center;
		justify-content: center;
		transition: all 0.15s;
		position: relative;
	}

	.qr-toggle:hover {
		border-color: #666;
		background: #1a1a1a;
	}

	.qr-toggle.active {
		border-color: #4ADE80;
		background: rgba(74, 222, 128, 0.1);
	}

	.qr-toggle.active .qr-icon {
		stroke: #4ADE80;
		fill: #4ADE80;
	}

	.qr-icon {
		width: 18px;
		height: 18px;
		stroke: #666;
		fill: #666;
		stroke-width: 0;
	}

	.qr-slash {
		position: absolute;
		width: 28px;
		height: 2px;
		background: #ef4444;
		transform: rotate(-45deg);
		border-radius: 1px;
	}

	.volume-controls.standalone .volume-slider {
		width: 100px;
	}

	.volume-icon {
		width: 18px;
		height: 18px;
		stroke: #666;
		fill: none;
		stroke-width: 2;
		flex-shrink: 0;
	}

	.volume-slider {
		flex: 1;
		height: 4px;
		-webkit-appearance: none;
		appearance: none;
		background: #2A2A2A;
		border-radius: 2px;
		cursor: pointer;
	}

	.volume-slider::-webkit-slider-thumb {
		-webkit-appearance: none;
		width: 12px;
		height: 12px;
		background: #999;
		border-radius: 50%;
		cursor: pointer;
		transition: background 0.15s;
	}

	.volume-slider::-webkit-slider-thumb:hover {
		background: #FF6B35;
	}

	.volume-slider::-moz-range-thumb {
		width: 12px;
		height: 12px;
		background: #999;
		border-radius: 50%;
		border: none;
		cursor: pointer;
	}

	.volume-value {
		font-size: 0.6875rem;
		font-family: 'JetBrains Mono', 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Menlo, Consolas, monospace;
		color: #666;
		min-width: 36px;
		text-align: right;
	}

	.control-btn.play-btn.playing {
		background: #FF6B35;
		border-color: #FF6B35;
	}

	.control-btn.play-btn.playing:hover {
		background: #ff8555;
		border-color: #ff8555;
	}

	.queue-list {
		display: flex;
		flex-direction: column;
		gap: 0.5rem;
	}

	.queue-item {
		display: flex;
		align-items: center;
		gap: 0.5rem;
		padding: 0.5rem;
		background: #161616;
		border: 1px solid #2A2A2A;
		border-radius: 10px;
		transition: all 0.15s;
		cursor: grab;
	}

	.queue-item:active {
		cursor: grabbing;
	}

	.queue-item.active {
		border-color: #FF6B35;
		background: rgba(255, 107, 53, 0.05);
	}

	.queue-item.dragging {
		opacity: 0.5;
		border-color: #FF6B35;
	}

	.queue-item.drag-over {
		border-color: #4ADE80;
		background: rgba(74, 222, 128, 0.1);
	}

	.drag-handle {
		display: flex;
		align-items: center;
		justify-content: center;
		width: 24px;
		height: 40px;
		opacity: 0.6;
		transition: opacity 0.15s;
		flex-shrink: 0;
		cursor: grab;
	}

	.drag-handle:active {
		cursor: grabbing;
	}

	.queue-item:hover .drag-handle {
		opacity: 1;
	}

	.drag-handle svg {
		width: 16px;
		height: 16px;
		fill: #888;
	}

	.queue-item-play {
		width: 48px;
		height: 48px;
		border-radius: 8px;
		overflow: hidden;
		background: #0C0C0C;
		border: none;
		cursor: pointer;
		position: relative;
		flex-shrink: 0;
	}

	.queue-item-play img {
		width: 100%;
		height: 100%;
		object-fit: cover;
	}

	.queue-item-icon {
		width: 100%;
		height: 100%;
		display: flex;
		align-items: center;
		justify-content: center;
	}

	.queue-item-icon svg {
		width: 20px;
		height: 20px;
		stroke: #666;
		fill: none;
		stroke-width: 1.5;
	}

	.now-playing-indicator {
		position: absolute;
		inset: 0;
		background: rgba(0, 0, 0, 0.6);
		display: flex;
		align-items: center;
		justify-content: center;
		gap: 3px;
	}

	.now-playing-indicator span {
		width: 3px;
		height: 12px;
		background: #FF6B35;
		border-radius: 2px;
		animation: equalizer 0.8s ease-in-out infinite;
	}

	.now-playing-indicator span:nth-child(2) {
		animation-delay: 0.2s;
	}

	.now-playing-indicator span:nth-child(3) {
		animation-delay: 0.4s;
	}

	@keyframes equalizer {
		0%, 100% { height: 4px; }
		50% { height: 16px; }
	}

	.queue-item-info {
		flex: 1;
		min-width: 0;
	}

	.queue-item-title {
		display: block;
		font-size: 0.875rem;
		font-weight: 500;
		color: #FAFAFA;
		white-space: nowrap;
		overflow: hidden;
		text-overflow: ellipsis;
	}

	.queue-item-meta {
		display: flex;
		align-items: center;
		gap: 0.5rem;
	}

	.queue-item-type {
		font-size: 0.6875rem;
		color: #666;
		text-transform: uppercase;
		letter-spacing: 0.05em;
	}

	.queue-item-duration {
		font-size: 0.6875rem;
		font-family: 'JetBrains Mono', 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Menlo, Consolas, monospace;
		color: #888;
	}

	.queue-item-playlist {
		display: inline-flex;
		align-items: center;
		gap: 0.2rem;
		font-size: 0.6875rem;
		font-family: 'JetBrains Mono', 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Menlo, Consolas, monospace;
		color: #FF6B35;
		background: rgba(255, 107, 53, 0.1);
		padding: 0.125rem 0.375rem;
		border-radius: 4px;
	}

	.queue-item-playlist svg {
		width: 10px;
		height: 10px;
		stroke: currentColor;
		fill: none;
		stroke-width: 2;
	}

	.queue-item-remove-playlist {
		width: 28px;
		height: 28px;
		background: none;
		border: none;
		border-radius: 50%;
		cursor: pointer;
		display: flex;
		align-items: center;
		justify-content: center;
		opacity: 0.5;
		transition: all 0.15s;
		flex-shrink: 0;
	}

	.queue-item-remove-playlist:hover {
		opacity: 1;
		background: rgba(255, 107, 53, 0.1);
	}

	.queue-item-remove-playlist svg {
		width: 16px;
		height: 16px;
		stroke: #FF6B35;
		fill: none;
		stroke-width: 1.5;
	}

	.queue-item-remove {
		width: 28px;
		height: 28px;
		background: none;
		border: none;
		border-radius: 50%;
		cursor: pointer;
		display: flex;
		align-items: center;
		justify-content: center;
		opacity: 0.5;
		transition: all 0.15s;
	}

	.queue-item-remove:hover {
		opacity: 1;
		background: rgba(239, 68, 68, 0.1);
	}

	.queue-item-remove svg {
		width: 16px;
		height: 16px;
		stroke: #ef4444;
		stroke-width: 2;
	}

	/* Recent grid with add-to-queue */
	.recent-item-wrapper {
		position: relative;
	}

	.add-to-queue-btn {
		position: absolute;
		top: 4px;
		right: 4px;
		width: 24px;
		height: 24px;
		background: rgba(0, 0, 0, 0.7);
		border: none;
		border-radius: 50%;
		cursor: pointer;
		display: flex;
		align-items: center;
		justify-content: center;
		opacity: 0;
		transition: all 0.15s;
		z-index: 2;
	}

	.recent-item-wrapper:hover .add-to-queue-btn {
		opacity: 1;
	}

	.add-to-queue-btn:hover {
		background: #FF6B35;
	}

	.add-to-queue-btn svg {
		width: 14px;
		height: 14px;
		stroke: white;
		stroke-width: 2;
	}

	.recent-grid {
		display: grid;
		grid-template-columns: repeat(4, 1fr);
		gap: 0.75rem;
	}

	.recent-item {
		aspect-ratio: 1;
		background: #161616;
		border: 1px solid #2A2A2A;
		border-radius: 12px;
		overflow: hidden;
		cursor: pointer;
		transition: all 0.15s ease;
		position: relative;
		padding: 0;
	}

	.recent-item:hover {
		border-color: #666666;
		transform: translateY(-2px);
	}

	.recent-item img {
		width: 100%;
		height: 100%;
		object-fit: cover;
	}

	.recent-item .icon-placeholder {
		width: 100%;
		height: 100%;
		display: flex;
		align-items: center;
		justify-content: center;
	}

	.recent-item .icon-placeholder svg {
		width: 24px;
		height: 24px;
		stroke: #666666;
		fill: none;
		stroke-width: 1.5;
	}

	.recent-item::after {
		content: attr(data-label);
		position: absolute;
		bottom: 0;
		left: 0;
		right: 0;
		padding: 0.5rem;
		background: linear-gradient(transparent, rgba(0,0,0,0.85));
		font-size: 0.6875rem;
		font-weight: 500;
		color: #999999;
		white-space: nowrap;
		overflow: hidden;
		text-overflow: ellipsis;
	}

	.recent-item .recast-overlay {
		position: absolute;
		inset: 0;
		background: rgba(0,0,0,0.7);
		display: flex;
		align-items: center;
		justify-content: center;
		opacity: 0;
		transition: opacity 0.15s ease;
	}

	.recent-item:hover .recast-overlay {
		opacity: 1;
	}

	.recast-overlay svg {
		width: 24px;
		height: 24px;
		stroke: white;
		fill: none;
		stroke-width: 2;
	}

	.footer {
		padding: 1.25rem 2rem;
		border-top: 1px solid #2A2A2A;
		display: flex;
		align-items: center;
		justify-content: space-between;
	}

	.footer-info {
		font-size: 0.75rem;
		color: #666666;
		font-family: 'JetBrains Mono', 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Menlo, Consolas, monospace;
	}

	.footer-actions {
		display: flex;
		gap: 0.5rem;
	}

	.footer-btn {
		display: flex;
		align-items: center;
		gap: 0.375rem;
		padding: 0.5rem 0.875rem;
		background: transparent;
		border: 1px solid #2A2A2A;
		border-radius: 8px;
		color: #999999;
		font-family: inherit;
		font-size: 0.8125rem;
		font-weight: 500;
		cursor: pointer;
		text-decoration: none;
		transition: all 0.15s ease;
	}

	.footer-btn:hover:not(:disabled) {
		background: #161616;
		border-color: #666666;
		color: #FAFAFA;
	}

	.footer-btn:disabled {
		opacity: 0.3;
		cursor: not-allowed;
	}

	.footer-btn svg {
		width: 16px;
		height: 16px;
		stroke: currentColor;
		fill: none;
		stroke-width: 1.5;
	}

	.footer-btn.primary {
		background: #161616;
		border-color: #FF6B35;
		color: #FF6B35;
	}

	.footer-btn.primary:hover {
		background: #FF6B35;
		color: white;
	}

	.footer-btn.copied {
		background: #4ADE80;
		border-color: #4ADE80;
		color: white;
	}

	@media (max-width: 640px) {
		.header {
			padding: 1rem 1.25rem;
		}

		.main {
			padding: 1.5rem 1.25rem;
		}

		.recent-grid {
			grid-template-columns: repeat(2, 1fr);
		}

		.footer {
			flex-direction: column;
			gap: 1rem;
			padding: 1rem 1.25rem;
		}
	}
</style>
