<script lang="ts">
	import { audioStore } from '$lib/audio.svelte';
	import { cn } from '$lib/utils';
	import { goto } from '$app/navigation';
	import type { Voice } from '$lib/types';

	interface Props {
		/** Called when the user closes the overlay. */
		onclose: () => void;
		/** When true, open the chapter picker immediately on mount. */
		openChapters?: boolean;
	}

	let { onclose, openChapters = false }: Props = $props();

	// Voices come from the store (populated by AudioPlayer on mount/play)
	const voices = $derived(audioStore.voices);
	const kokoroVoices = $derived(voices.filter((v) => v.engine === 'kokoro'));
	const pocketVoices = $derived(voices.filter((v) => v.engine === 'pocket-tts'));
	const cfaiVoices   = $derived(voices.filter((v) => v.engine === 'cfai'));

	let showVoiceModal = $state(false);
	// svelte-ignore state_referenced_locally
	let showChapterModal = $state(openChapters && audioStore.chapters.length > 0);
	let voiceSearch = $state('');
	let samplePlayingVoice = $state<string | null>(null);
	let sampleAudio: HTMLAudioElement | null = null;

	// ── Pull-down-to-dismiss gesture ─────────────────────────────────────────
	let dragY = $state(0);
	let isDragging = $state(false);
	let dragStartY = 0;
	let dragStartTime = 0;
	let overlayEl = $state<HTMLDivElement | null>(null);

	// Register ontouchmove with passive:false so e.preventDefault() works.
	// Svelte 5 does not support the |nonpassive modifier, so we use $effect.
	$effect(() => {
		if (!overlayEl) return;
		overlayEl.addEventListener('touchmove', onTouchMove, { passive: false });
		return () => overlayEl!.removeEventListener('touchmove', onTouchMove);
	});

	function onTouchStart(e: TouchEvent) {
		// Don't hijack touches that start inside a scrollable element
		const target = e.target as Element;
		if (target.closest('.overflow-y-auto')) return;
		// Don't activate if a modal is open (they handle their own scroll)
		if (showVoiceModal || showChapterModal) return;

		isDragging = true;
		dragStartY = e.touches[0].clientY;
		dragStartTime = Date.now();
		dragY = 0;
	}

	function onTouchMove(e: TouchEvent) {
		if (!isDragging) return;
		const delta = e.touches[0].clientY - dragStartY;
		// Only track downward movement
		if (delta > 0) {
			dragY = delta;
			// Prevent page scroll while dragging the overlay down
			e.preventDefault();
		} else {
			dragY = 0;
		}
	}

	function onTouchEnd() {
		if (!isDragging) return;
		isDragging = false;

		const elapsed = Date.now() - dragStartTime;
		const velocity = dragY / Math.max(elapsed, 1); // px/ms

		// Dismiss if dragged far enough (>130px) or flicked fast enough (>0.4px/ms)
		if (dragY > 130 || velocity > 0.4) {
			// Animate out: snap to bottom then close
			dragY = window.innerHeight;
			setTimeout(onclose, 220);
		} else {
			// Spring back to 0
			dragY = 0;
		}
	}

	// ── Voice search filtering ────────────────────────────────────────────────
	const voiceSearchLower = $derived(voiceSearch.toLowerCase());
	const filteredKokoro = $derived(kokoroVoices.filter((v) => voiceLabel(v).toLowerCase().includes(voiceSearchLower)));
	const filteredPocket = $derived(pocketVoices.filter((v) => voiceLabel(v).toLowerCase().includes(voiceSearchLower)));
	const filteredCfai   = $derived(cfaiVoices.filter((v) => voiceLabel(v).toLowerCase().includes(voiceSearchLower)));

	// ── Chapter search ────────────────────────────────────────────────────────
	let chapterSearch = $state('');

	// Scroll the current chapter into view instantly (no animation) when the
	// chapter modal opens. Applied to every chapter button; only scrolls when
	// the chapter number matches the currently playing one. Runs once on mount
	// before the browser paints so no scroll animation is ever visible.
	function scrollIfActive(node: HTMLElement, isActive: boolean) {
		if (isActive) node.scrollIntoView({ block: 'center', behavior: 'instant' });
	}
	const filteredChapters = $derived(
		chapterSearch.trim() === ''
			? audioStore.chapters
			: audioStore.chapters.filter((ch) =>
					(ch.title || `Chapter ${ch.number}`)
						.toLowerCase()
						.includes(chapterSearch.toLowerCase()) ||
					String(ch.number).includes(chapterSearch)
			  )
	);

	function voiceLabel(v: Voice | string): string {
		if (typeof v === 'string') {
			const found = voices.find((x) => x.id === v);
			if (found) return voiceLabel(found);
			const id = v as string;
			return id.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
		}
		const base = v.id.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
		return v.lang !== 'en-us' ? `${base} (${v.lang})` : base;
	}

	function stopSample() {
		if (sampleAudio) {
			sampleAudio.pause();
			sampleAudio.src = '';
			sampleAudio = null;
		}
		samplePlayingVoice = null;
	}

	async function playSample(voiceId: string) {
		if (samplePlayingVoice === voiceId) { stopSample(); return; }
		stopSample();
		samplePlayingVoice = voiceId;
		try {
			const res = await fetch(`/api/presign/voice-sample?voice=${encodeURIComponent(voiceId)}`);
			if (!res.ok) { samplePlayingVoice = null; return; }
			const { url } = (await res.json()) as { url: string };
			sampleAudio = new Audio(url);
			sampleAudio.onended = () => stopSample();
			sampleAudio.onerror = () => stopSample();
			sampleAudio.play().catch(() => stopSample());
		} catch {
			samplePlayingVoice = null;
		}
	}

	function selectVoice(voiceId: string) {
		stopSample();
		audioStore.voice = voiceId;
		showVoiceModal = false;
		voiceSearch = '';
	}

	// ── Chapter click-to-play ─────────────────────────────────────────────────
	function playChapter(chapterNumber: number) {
		audioStore.autoStartChapter = chapterNumber;
		onclose();
		goto(`/books/${audioStore.slug}/chapters/${chapterNumber}`);
	}

	// ── Speed ────────────────────────────────────────────────────────────────
	const SPEED_OPTIONS = [0.75, 1, 1.25, 1.5, 2] as const;

	// ── Sleep timer ──────────────────────────────────────────────────────────
	const SLEEP_OPTIONS = [15, 30, 45, 60]; // minutes

	let sleepRemainingSec = $derived.by(() => {
		void audioStore.currentTime; // re-run every second while playing
		if (!audioStore.sleepUntil) return 0;
		return Math.max(0, Math.floor((audioStore.sleepUntil - Date.now()) / 1000));
	});

	function cycleSleepTimer() {
		if (!audioStore.sleepUntil && !audioStore.sleepAfterChapter) {
			audioStore.sleepAfterChapter = true;
		} else if (audioStore.sleepAfterChapter) {
			audioStore.sleepAfterChapter = false;
			audioStore.sleepUntil = Date.now() + SLEEP_OPTIONS[0] * 60 * 1000;
		} else {
			const remaining = audioStore.sleepUntil - Date.now();
			const currentMin = Math.round(remaining / 60000);
			const idx = SLEEP_OPTIONS.findIndex((m) => m >= currentMin);
			if (idx === -1 || idx === SLEEP_OPTIONS.length - 1) {
				audioStore.sleepUntil = 0;
			} else {
				audioStore.sleepUntil = Date.now() + SLEEP_OPTIONS[idx + 1] * 60 * 1000;
			}
		}
	}

	function formatSleepRemaining(secs: number): string {
		if (secs <= 0) return 'Off';
		const m = Math.floor(secs / 60);
		const s = secs % 60;
		return m > 0 ? `${m}m${s > 0 ? ` ${s}s` : ''}` : `${s}s`;
	}

	const sleepLabel = $derived(
		audioStore.sleepAfterChapter
			? 'End Ch.'
			: audioStore.sleepUntil > Date.now()
				? formatSleepRemaining(sleepRemainingSec)
				: 'Sleep'
	);

	// ── Format time ──────────────────────────────────────────────────────────
	function formatTime(s: number): string {
		if (!isFinite(s) || s < 0) return '0:00';
		const m = Math.floor(s / 60);
		const sec = Math.floor(s % 60);
		return `${m}:${sec.toString().padStart(2, '0')}`;
	}

	// ── Playback controls ────────────────────────────────────────────────────
	function seek(e: Event) {
		audioStore.seekRequest = Number((e.currentTarget as HTMLInputElement).value);
	}
	function skipBack() {
		audioStore.seekRequest = Math.max(0, audioStore.currentTime - 15);
	}
	function skipForward() {
		audioStore.seekRequest = Math.min(audioStore.duration || 0, audioStore.currentTime + 30);
	}
	function togglePlay() {
		audioStore.toggleRequest++;
	}

	// Close on Escape
	$effect(() => {
		function onKey(e: KeyboardEvent) {
			if (e.key === 'Escape') {
				if (showChapterModal) { showChapterModal = false; }
				else if (showVoiceModal) { showVoiceModal = false; voiceSearch = ''; }
				else { onclose(); }
			}
		}
		window.addEventListener('keydown', onKey);
		return () => window.removeEventListener('keydown', onKey);
	});
</script>

<!-- Full-screen listening mode overlay -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
	bind:this={overlayEl}
	class="fixed inset-0 z-60 flex flex-col overflow-hidden"
	style="
		background: var(--color-surface);
		transform: translateY({dragY}px);
		opacity: {Math.max(0, 1 - dragY / 500)};
		transition: {isDragging ? 'none' : 'transform 0.32s cubic-bezier(0.32,0.72,0,1), opacity 0.32s ease'};
		will-change: transform;
		touch-action: pan-x;
		pointer-events: auto;
	"
	ontouchstart={onTouchStart}
	ontouchend={onTouchEnd}
>

	<!-- ── Blurred background (full-screen atmospheric layer) ───────────── -->
	{#if audioStore.cover}
		<img
			src={audioStore.cover}
			alt=""
			aria-hidden="true"
			class="absolute inset-0 w-full h-full object-cover pointer-events-none select-none"
			style="filter: blur(40px) brightness(0.25) saturate(1.4); transform: scale(1.15); z-index: 0;"
		/>
	{:else}
		<div class="absolute inset-0 pointer-events-none" style="background: var(--color-surface-2); z-index: 0;"></div>
	{/if}
	<!-- Subtle vignette overlay for depth -->
	<div
		class="absolute inset-0 pointer-events-none"
		style="background: radial-gradient(ellipse at center, transparent 40%, rgba(0,0,0,0.55) 100%); z-index: 1;"
		aria-hidden="true"
	></div>

	<!-- ── Header bar ─────────────────────────────────────────────────────── -->
	<div class="relative flex items-center justify-between px-4 pt-3 pb-2 shrink-0" style="z-index: 2;">
		<button
			type="button"
			onclick={onclose}
			class="p-2 rounded-full text-(--color-text)/70 hover:text-(--color-text) hover:bg-white/10 transition-colors"
			aria-label="Close listening mode"
		>
			<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
				<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
			</svg>
		</button>
		<span class="text-xs font-semibold text-(--color-text)/60 uppercase tracking-wider">Now Playing</span>
		<div class="flex items-center gap-2">
			<!-- Chapters button -->
			{#if audioStore.chapters.length > 0}
				<button
					type="button"
					onclick={() => { showChapterModal = !showChapterModal; showVoiceModal = false; voiceSearch = ''; }}
					class={cn(
						'flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-medium border transition-colors',
						showChapterModal
							? 'border-(--color-brand) bg-(--color-brand)/15 text-(--color-brand)'
							: 'border-white/20 bg-black/25 text-(--color-text)/70 hover:text-(--color-text) backdrop-blur-sm'
					)}
					aria-label="Browse chapters"
				>
					<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
						<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h10"/>
					</svg>
					Chapters
				</button>
			{/if}
			<!-- Voice selector button -->
			<button
				type="button"
				onclick={() => { showVoiceModal = !showVoiceModal; showChapterModal = false; }}
				class={cn(
					'flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-medium border transition-colors',
					showVoiceModal
						? 'border-(--color-brand) bg-(--color-brand)/15 text-(--color-brand)'
						: 'border-white/20 bg-black/25 text-(--color-text)/70 hover:text-(--color-text) backdrop-blur-sm'
				)}
			>
				<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
					<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z"/>
				</svg>
				<span class="max-w-[80px] truncate">{voiceLabel(audioStore.voice)}</span>
			</button>
		</div>
	</div>

	<!-- ── Portrait cover card + track info ───────────────────────────────── -->
	<div class="relative flex flex-col items-center gap-4 px-8 pt-2 pb-4 shrink-0" style="z-index: 2;">
		<!-- Cover card -->
		<div
			class="rounded-2xl overflow-hidden shadow-2xl"
			style="height: 38svh; min-height: 180px; max-height: 320px; aspect-ratio: 2/3;"
		>
			{#if audioStore.cover}
				<img
					src={audioStore.cover}
					alt=""
					class="w-full h-full object-cover"
				/>
			{:else}
				<div class="w-full h-full bg-(--color-surface-2) flex items-center justify-center">
					<svg class="w-16 h-16 text-(--color-muted)/30" fill="currentColor" viewBox="0 0 24 24">
						<path d="M18 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 14H8v-2h8v2zm0-4H8v-2h8v2zm0-4H8V6h8v2z"/>
					</svg>
				</div>
			{/if}
		</div>

		<!-- Track info -->
		<div class="text-center w-full">
			{#if audioStore.chapter > 0}
				<p class="text-[10px] font-bold uppercase tracking-widest text-(--color-brand) mb-0.5">
					Chapter {audioStore.chapter}
				</p>
			{/if}
			<p class="text-lg font-bold text-(--color-text) leading-snug line-clamp-2">
				{audioStore.chapterTitle || (audioStore.chapter > 0 ? `Chapter ${audioStore.chapter}` : '')}
			</p>
			<p class="text-sm text-(--color-text)/50 mt-0.5 truncate">{audioStore.bookTitle}</p>
		</div>
	</div>

	<!-- Voice modal (full-screen overlay) -->
	{#if showVoiceModal && voices.length > 0}
		<!-- svelte-ignore a11y_no_static_element_interactions -->
		<div
			class="fixed inset-0 z-[80] flex flex-col"
			style="background: var(--color-surface);"
		>
			<!-- Modal header -->
			<div class="flex items-center gap-3 px-4 py-3 border-b border-(--color-border) shrink-0" style="padding-top: max(0.75rem, env(safe-area-inset-top));">
				<button
					type="button"
					onclick={() => { stopSample(); showVoiceModal = false; voiceSearch = ''; }}
					class="p-2 rounded-full text-(--color-muted) hover:text-(--color-text) hover:bg-(--color-surface-2) transition-colors"
					aria-label="Close voice picker"
				>
					<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
						<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
					</svg>
				</button>
				<span class="text-xs font-semibold text-(--color-muted) uppercase tracking-wider flex-1">Select Voice</span>
			</div>
			<!-- Search input -->
			<div class="px-4 py-3 shrink-0 border-b border-(--color-border)">
				<div class="relative">
					<svg class="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-(--color-muted)" fill="none" stroke="currentColor" viewBox="0 0 24 24">
						<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
					</svg>
					<input
						type="search"
						placeholder="Search voices…"
						bind:value={voiceSearch}
						class="w-full pl-9 pr-4 py-2 text-sm bg-(--color-surface-2) border border-(--color-border) rounded-lg text-(--color-text) placeholder:text-(--color-muted) focus:outline-none focus:border-(--color-brand) transition-colors"
					/>
				</div>
			</div>
			<!-- Voice list -->
			<div class="flex-1 overflow-y-auto overscroll-contain" style="padding-bottom: env(safe-area-inset-bottom);">
				{#each ([['Kokoro', filteredKokoro], ['Pocket TTS', filteredPocket], ['CF AI', filteredCfai]] as [string, Voice[]][]) as [label, group]}
					{#if group.length > 0}
						<p class="text-[10px] font-semibold text-(--color-muted) uppercase tracking-wider px-4 py-2 sticky top-0 bg-(--color-surface) border-b border-(--color-border)/50">{label}</p>
						{#each group as v (v.id)}
							<div
								class={cn(
									'flex items-center gap-3 px-4 py-3 border-b border-(--color-border)/40 transition-colors',
									audioStore.voice === v.id ? 'bg-(--color-brand)/8' : 'hover:bg-(--color-surface-2)'
								)}
							>
								<button type="button" onclick={() => selectVoice(v.id)} class="flex-1 flex items-center gap-3 text-left">
									<span class={cn(
										'w-4 h-4 shrink-0 rounded-full border-2 flex items-center justify-center transition-colors',
										audioStore.voice === v.id ? 'border-(--color-brand) bg-(--color-brand)' : 'border-(--color-border)'
									)}>
										{#if audioStore.voice === v.id}
											<svg class="w-2 h-2 text-(--color-surface)" fill="currentColor" viewBox="0 0 24 24"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>
										{/if}
									</span>
									<span class={cn('text-sm', audioStore.voice === v.id ? 'font-semibold text-(--color-brand)' : 'text-(--color-text)')}>{voiceLabel(v)}</span>
								</button>
								<button
									type="button"
									onclick={() => playSample(v.id)}
									class={cn('shrink-0 p-2 rounded-full transition-colors', samplePlayingVoice === v.id ? 'text-(--color-brand) bg-(--color-brand)/10' : 'text-(--color-muted) hover:text-(--color-text) hover:bg-(--color-surface-2)')}
									title={samplePlayingVoice === v.id ? 'Stop sample' : 'Play sample'}
									aria-label={samplePlayingVoice === v.id ? 'Stop sample' : 'Play sample'}
								>
									{#if samplePlayingVoice === v.id}
										<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z"/></svg>
									{:else}
										<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>
									{/if}
								</button>
							</div>
						{/each}
					{/if}
				{/each}
				{#if filteredKokoro.length === 0 && filteredPocket.length === 0 && filteredCfai.length === 0}
					<p class="px-4 py-8 text-sm text-(--color-muted) text-center">No voices match "{voiceSearch}"</p>
				{/if}
			</div>
		</div>
	{/if}

	<!-- Chapter modal (full-screen overlay) -->
	{#if showChapterModal && audioStore.chapters.length > 0}
		<!-- svelte-ignore a11y_no_static_element_interactions -->
		<div
			class="fixed inset-0 z-[80] flex flex-col"
			style="background: var(--color-surface);"
		>
			<div class="flex items-center gap-3 px-4 py-3 border-b border-(--color-border) shrink-0" style="padding-top: max(0.75rem, env(safe-area-inset-top));">
				<button
					type="button"
					onclick={() => { showChapterModal = false; }}
					class="p-2 rounded-full text-(--color-muted) hover:text-(--color-text) hover:bg-(--color-surface-2) transition-colors"
					aria-label="Close chapter picker"
				>
					<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
						<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
					</svg>
				</button>
				<span class="text-xs font-semibold text-(--color-muted) uppercase tracking-wider flex-1">Chapters</span>
			</div>
			<div class="px-4 py-3 shrink-0 border-b border-(--color-border)">
				<div class="relative">
					<svg class="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-(--color-muted)" fill="none" stroke="currentColor" viewBox="0 0 24 24">
						<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
					</svg>
					<input
						type="search"
						placeholder="Search chapters…"
						bind:value={chapterSearch}
						class="w-full pl-9 pr-4 py-2 text-sm bg-(--color-surface-2) border border-(--color-border) rounded-lg text-(--color-text) placeholder:text-(--color-muted) focus:outline-none focus:border-(--color-brand) transition-colors"
					/>
				</div>
			</div>
			<div class="flex-1 overflow-y-auto overscroll-contain" style="padding-bottom: env(safe-area-inset-bottom);">
				{#each filteredChapters as ch (ch.number)}
					<button
						type="button"
						onclick={() => playChapter(ch.number)}
						use:scrollIfActive={ch.number === audioStore.chapter}
						class={cn(
							'w-full flex items-center gap-3 px-4 py-3 border-b border-(--color-border)/40 transition-colors text-left',
							ch.number === audioStore.chapter ? 'bg-(--color-brand)/8' : 'hover:bg-(--color-surface-2)'
						)}
					>
						<span class={cn(
							'w-8 h-8 shrink-0 rounded-full border-2 flex items-center justify-center tabular-nums text-xs font-semibold transition-colors',
							ch.number === audioStore.chapter ? 'border-(--color-brand) bg-(--color-brand) text-(--color-surface)' : 'border-(--color-border) text-(--color-muted)'
						)}>{ch.number}</span>
						<span class={cn('flex-1 text-sm truncate', ch.number === audioStore.chapter ? 'font-semibold text-(--color-brand)' : 'text-(--color-text)')}>{ch.title || `Chapter ${ch.number}`}</span>
						{#if ch.number === audioStore.chapter}
							<svg class="w-4 h-4 shrink-0 text-(--color-brand)" fill="currentColor" viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>
						{/if}
					</button>
				{/each}
				{#if filteredChapters.length === 0}
					<p class="px-4 py-8 text-sm text-(--color-muted) text-center">No chapters match "{chapterSearch}"</p>
				{/if}
			</div>
		</div>
	{/if}

	<!-- ── Controls area (bottom half) ───────────────────────────────────── -->
	<div class="flex-1 flex flex-col justify-end px-6 pb-6 gap-0 shrink-0 overflow-hidden" style="z-index: 2; position: relative;">

		<!-- Seek bar -->
		<div class="shrink-0 mb-1">
			<input
				type="range"
				aria-label="Seek"
				min="0"
				max={audioStore.duration || 0}
				value={audioStore.currentTime}
				oninput={seek}
				class="w-full h-1.5 cursor-pointer block"
				style="accent-color: var(--color-brand);"
			/>
			<div class="flex justify-between text-xs text-(--color-muted) tabular-nums mt-1">
				<span>{formatTime(audioStore.currentTime)}</span>
				<!-- Remaining time in centre -->
				{#if audioStore.duration > 0}
					<span class="text-(--color-muted)/60">−{formatTime(Math.max(0, audioStore.duration - audioStore.currentTime))}</span>
				{/if}
				<span>{formatTime(audioStore.duration)}</span>
			</div>
		</div>

		<!-- Transport controls -->
		<div class="flex items-center justify-between pt-3 pb-4 shrink-0">
			<!-- Prev chapter — smaller, clearly secondary -->
			{#if audioStore.chapter > 1 && audioStore.slug}
				<button
					type="button"
					onclick={() => playChapter(audioStore.chapter - 1)}
					class="p-2 rounded-full text-(--color-muted)/60 hover:text-(--color-text) hover:bg-(--color-surface-2) transition-colors"
					title="Previous chapter"
					aria-label="Previous chapter"
				>
					<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
						<path d="M6 6h2v12H6zm2 6 8.5 6V6z"/>
					</svg>
				</button>
			{:else}
				<div class="w-9 h-9"></div>
			{/if}

			<!-- Skip back 15s — medium -->
			<button
				type="button"
				onclick={skipBack}
				class="p-2 rounded-full text-(--color-muted) hover:text-(--color-text) hover:bg-(--color-surface-2) transition-colors"
				aria-label="Skip back 15 seconds"
				title="Back 15s"
			>
				<svg class="w-7 h-7" fill="currentColor" viewBox="0 0 24 24">
					<path d="M11.99 5V1l-5 5 5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6h-2c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/>
					<text x="8.5" y="14.5" font-size="5" font-family="sans-serif" font-weight="bold" fill="currentColor">15</text>
				</svg>
			</button>

			<!-- Play / Pause — largest, centred -->
			<button
				type="button"
				onclick={togglePlay}
				class="w-18 h-18 rounded-full bg-(--color-brand) text-(--color-surface) flex items-center justify-center hover:bg-(--color-brand-dim) transition-colors shadow-xl"
				style="width: 4.5rem; height: 4.5rem;"
				aria-label={audioStore.isPlaying ? 'Pause' : 'Play'}
			>
				{#if audioStore.isPlaying}
					<svg class="w-8 h-8" fill="currentColor" viewBox="0 0 24 24">
						<path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z"/>
					</svg>
				{:else}
					<svg class="w-8 h-8 ml-1" fill="currentColor" viewBox="0 0 24 24">
						<path d="M8 5v14l11-7z"/>
					</svg>
				{/if}
			</button>

			<!-- Skip forward 30s — medium -->
			<button
				type="button"
				onclick={skipForward}
				class="p-2 rounded-full text-(--color-muted) hover:text-(--color-text) hover:bg-(--color-surface-2) transition-colors"
				aria-label="Skip forward 30 seconds"
				title="Forward 30s"
			>
				<svg class="w-7 h-7" fill="currentColor" viewBox="0 0 24 24">
					<path d="M12 5V1l5 5-5 5V7c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6h2c0 4.42-3.58 8-8 8s-8-3.58-8-8 3.58-8 8-8z"/>
					<text x="8.5" y="14.5" font-size="5" font-family="sans-serif" font-weight="bold" fill="currentColor">30</text>
				</svg>
			</button>

			<!-- Next chapter — smaller, clearly secondary -->
			{#if audioStore.nextChapter !== null && audioStore.slug}
				<button
					type="button"
					onclick={() => playChapter(audioStore.nextChapter!)}
					class="p-2 rounded-full text-(--color-muted)/60 hover:text-(--color-text) hover:bg-(--color-surface-2) transition-colors"
					title="Next chapter"
					aria-label="Next chapter"
				>
					<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
						<path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"/>
					</svg>
				</button>
			{:else}
				<div class="w-9 h-9"></div>
			{/if}
		</div>

		<!-- Secondary controls: unified single row — Speed · Auto · Announce · Sleep -->
		<div class="flex items-center justify-center gap-2 shrink-0 flex-wrap">
			<!-- Speed — segmented pill -->
			<div class="flex items-center gap-0.5 bg-(--color-surface-2) rounded-full px-1.5 py-1 border border-(--color-border)">
				{#each SPEED_OPTIONS as s}
					<button
						type="button"
						onclick={() => (audioStore.speed = s)}
						class={cn(
							'px-2 py-0.5 rounded-full text-xs font-semibold transition-colors',
							audioStore.speed === s
								? 'bg-(--color-brand) text-(--color-surface)'
								: 'text-(--color-muted) hover:text-(--color-text)'
						)}
						aria-pressed={audioStore.speed === s}
					>{s}×</button>
				{/each}
			</div>

			<!-- Auto-next pill -->
			<button
				type="button"
				onclick={() => (audioStore.autoNext = !audioStore.autoNext)}
				class={cn(
					'flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-semibold border transition-colors',
					audioStore.autoNext
						? 'border-(--color-brand) bg-(--color-brand)/15 text-(--color-brand)'
						: 'border-(--color-border) bg-(--color-surface-2) text-(--color-muted) hover:text-(--color-text)'
				)}
				aria-pressed={audioStore.autoNext}
				title={audioStore.autoNext ? 'Auto-next on' : 'Auto-next off'}
			>
				<svg class="w-3.5 h-3.5" fill="currentColor" viewBox="0 0 24 24">
					<path d="M6 18l8.5-6L6 6v12zm8.5-6L23 6v12l-8.5-6z"/>
				</svg>
				Auto
				{#if audioStore.autoNext && audioStore.nextStatus === 'prefetching'}
					<span class="w-1.5 h-1.5 rounded-full bg-(--color-brand) animate-pulse"></span>
				{:else if audioStore.autoNext && audioStore.nextStatus === 'prefetched'}
					<span class="w-1.5 h-1.5 rounded-full bg-green-400"></span>
				{/if}
			</button>

			<!-- Announce chapter pill (only meaningful when auto-next is on) -->
			<button
				type="button"
				onclick={() => (audioStore.announceChapter = !audioStore.announceChapter)}
				class={cn(
					'flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-semibold border transition-colors',
					audioStore.announceChapter
						? 'border-(--color-brand) bg-(--color-brand)/15 text-(--color-brand)'
						: 'border-(--color-border) bg-(--color-surface-2) text-(--color-muted) hover:text-(--color-text)'
				)}
				aria-pressed={audioStore.announceChapter}
				title={audioStore.announceChapter ? 'Chapter announcing on' : 'Chapter announcing off'}
			>
				<svg class="w-3.5 h-3.5" fill="currentColor" viewBox="0 0 24 24">
					<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z"/>
				</svg>
				Announce
			</button>

			<!-- Sleep timer pill -->
			<button
				type="button"
				onclick={cycleSleepTimer}
				class={cn(
					'flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-semibold border transition-colors',
					audioStore.sleepUntil || audioStore.sleepAfterChapter
						? 'border-(--color-brand) bg-(--color-brand)/15 text-(--color-brand)'
						: 'border-(--color-border) bg-(--color-surface-2) text-(--color-muted) hover:text-(--color-text)'
				)}
				title="Sleep timer"
			>
				<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
					<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/>
				</svg>
				{sleepLabel}
			</button>
		</div>

	</div>
</div>
