<script lang="ts">
	import { goto } from '$app/navigation';
	import { browser } from '$app/environment';
	import type { PageData } from './$types';
	import type { Book, VotedBook } from '$lib/server/pocketbase';

	let { data }: { data: PageData } = $props();

	// ── Preferences (localStorage) ──────────────────────────────────────────────
	interface Prefs {
		genres: string[];
		status: 'either' | 'ongoing' | 'completed';
		onboarded: boolean;
	}
	const PREFS_KEY = 'discover_prefs_v1';
	const GENRES = [
		'Martial Arts', 'Xianxia', 'Xuanhuan', 'Wuxia', 'Cultivation',
		'Romance', 'Action', 'Adventure', 'Fantasy', 'System',
		'Harem', 'Historical', 'Comedy', 'Drama', 'Mystery',
		'Sci-Fi', 'Horror', 'Slice of Life'
	];

	function loadPrefs(): Prefs {
		if (!browser) return { genres: [], status: 'either', onboarded: false };
		try {
			const raw = localStorage.getItem(PREFS_KEY);
			if (raw) return JSON.parse(raw) as Prefs;
		} catch { /* ignore */ }
		return { genres: [], status: 'either', onboarded: false };
	}
	function savePrefs(p: Prefs) {
		if (!browser) return;
		localStorage.setItem(PREFS_KEY, JSON.stringify(p));
	}

	let prefs = $state<Prefs>(loadPrefs());
	// svelte-ignore state_referenced_locally
	let showOnboarding = $state(!prefs.onboarded);

	// Onboarding temp state
	// svelte-ignore state_referenced_locally
	let tempGenres = $state<string[]>([...prefs.genres]);
	// svelte-ignore state_referenced_locally
	let tempStatus = $state<Prefs['status']>(prefs.status);

	function finishOnboarding(skip = false) {
		if (!skip) {
			prefs = { genres: tempGenres, status: tempStatus, onboarded: true };
		} else {
			prefs = { ...prefs, onboarded: true };
		}
		savePrefs(prefs);
		showOnboarding = false;
	}

	// ── Book deck (client-side filtered) ───────────────────────────────────────
	function parseBookGenres(genres: string[] | string): string[] {
		if (Array.isArray(genres)) return genres;
		if (!genres) return [];
		try { return JSON.parse(genres) as string[]; } catch { return []; }
	}

	let deck = $derived.by(() => {
		let books = data.books as Book[];
		if (prefs.onboarded && prefs.genres.length > 0) {
			const preferred = new Set(prefs.genres.map((g) => g.toLowerCase()));
			const filtered = books.filter((b) => {
				const g = parseBookGenres(b.genres);
				return g.some((genre) => preferred.has(genre.toLowerCase()));
			});
			if (filtered.length >= 5) books = filtered;
		}
		if (prefs.onboarded && prefs.status !== 'either') {
			const sf = books.filter((b) => b.status?.toLowerCase().includes(prefs.status));
			if (sf.length >= 3) books = sf;
		}
		return books;
	});

	// ── Card state ──────────────────────────────────────────────────────────────
	let idx = $state(0);
	let isDragging = $state(false);
	let animating = $state(false);
	let offsetX = $state(0);
	let offsetY = $state(0);
	let transitioning = $state(false);
	let showPreview = $state(false);
	let voted = $state<{ slug: string; action: string } | null>(null); // last voted, for undo
	let showHistory = $state(false);

	// svelte-ignore state_referenced_locally
	let votedBooks = $state<VotedBook[]>(data.votedBooks ?? []);

	// Keep in sync if server data refreshes
	$effect(() => {
		votedBooks = data.votedBooks ?? [];
	});

	async function undoVote(slug: string) {
		// Optimistic update
		votedBooks = votedBooks.filter((v) => v.slug !== slug);
		await fetch(`/api/discover/vote?slug=${encodeURIComponent(slug)}`, { method: 'DELETE' });
	}

	let startX = 0, startY = 0, hasMoved = false;

	let currentBook = $derived(deck[idx] as Book | undefined);
	let nextBook = $derived(deck[idx + 1] as Book | undefined);
	let nextNextBook = $derived(deck[idx + 2] as Book | undefined);
	let deckEmpty = $derived(!currentBook);
	let totalRemaining = $derived(Math.max(0, deck.length - idx));

	// Which direction/indicator to show
	let indicator = $derived.by((): 'like' | 'skip' | 'read_now' | 'nope' | null => {
		if (!isDragging) return null;
		const ax = Math.abs(offsetX), ay = Math.abs(offsetY);
		const threshold = 20;
		if (ax > ay) {
			if (offsetX > threshold) return 'like';
			if (offsetX < -threshold) return 'skip';
		} else {
			if (offsetY < -threshold) return 'read_now';
			if (offsetY > threshold) return 'nope';
		}
		return null;
	});

	const indicatorOpacity = $derived(
		isDragging
			? Math.min(1, Math.max(Math.abs(offsetX), Math.abs(offsetY)) / 60)
			: 0
	);
	const rotation = $derived(isDragging ? Math.max(-18, Math.min(18, offsetX / 12)) : 0);

	let cardEl = $state<HTMLDivElement | null>(null);

	// ── Card entry animation (prevents pop-to-full-size after swipe) ─────────────
	let cardEntering = $state(false);
	let entryTransition = $state(false);
	let entryCleanup: ReturnType<typeof setTimeout> | null = null;

	function startEntryAnimation() {
		if (entryCleanup) clearTimeout(entryCleanup);
		cardEntering = true;
		entryTransition = true;
		requestAnimationFrame(() => {
			cardEntering = false;
			entryCleanup = setTimeout(() => { entryTransition = false; }, 400);
		});
	}

	function cancelEntryAnimation() {
		if (entryCleanup) { clearTimeout(entryCleanup); entryCleanup = null; }
		cardEntering = false;
		entryTransition = false;
	}

	const activeTransform = $derived(
		cardEntering
			? 'scale(0.95) translateY(13px)'
			: `translateX(${offsetX}px) translateY(${offsetY}px) rotate(${rotation}deg)`
	);

	const activeTransition = $derived(
		isDragging
			? 'none'
			: transitioning
				? 'transform 0.35s cubic-bezier(0.175, 0.885, 0.32, 1.275)'
				: entryTransition
					? 'transform 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94)'
					: 'none'
	);

	function onPointerDown(e: PointerEvent) {
		cancelEntryAnimation();
		if (animating || !currentBook) return;
		(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);
		startX = e.clientX;
		startY = e.clientY;
		hasMoved = false;
		isDragging = true;
		transitioning = false;
		offsetX = 0;
		offsetY = 0;
	}

	function onPointerMove(e: PointerEvent) {
		if (!isDragging) return;
		offsetX = e.clientX - startX;
		offsetY = e.clientY - startY;
		if (Math.abs(offsetX) > 5 || Math.abs(offsetY) > 5) hasMoved = true;
	}

	async function onPointerUp(e: PointerEvent) {
		if (!isDragging) return;
		isDragging = false;

		const THRESHOLD_X = 90;
		const THRESHOLD_Y = 70;
		const ax = Math.abs(offsetX), ay = Math.abs(offsetY);

		if (ax > ay && ax > THRESHOLD_X) {
			await doAction(offsetX > 0 ? 'like' : 'skip');
		} else if (ay > ax && ay > THRESHOLD_Y) {
			await doAction(offsetY < 0 ? 'read_now' : 'nope');
		} else if (!hasMoved) {
			// Tap without drag → preview
			showPreview = true;
			offsetX = 0; offsetY = 0;
		} else {
			// Snap back
			transitioning = true;
			offsetX = 0; offsetY = 0;
			await delay(320);
			transitioning = false;
		}
	}

	function delay(ms: number) { return new Promise<void>((r) => setTimeout(r, ms)); }

	type VoteAction = 'like' | 'skip' | 'nope' | 'read_now';

	const flyTargets: Record<VoteAction, { x: number; y: number }> = {
		like:     { x: 1300, y: -80 },
		skip:     { x: -1300, y: -80 },
		read_now: { x: 30,   y: -1300 },
		nope:     { x: 0,    y: 1300 }
	};

	async function doAction(action: VoteAction) {
		if (animating || !currentBook) return;
		animating = true;
		const book = currentBook;

		// Record vote (fire and forget)
		fetch('/api/discover/vote', {
			method: 'POST',
			headers: { 'Content-Type': 'application/json' },
			body: JSON.stringify({ slug: book.slug, action })
		});

		// Optimistically add/update the history list so the drawer shows it immediately.
		// If this slug was already voted (e.g. swiped twice via undo+re-swipe), replace it.
		const existing = votedBooks.findIndex((v) => v.slug === book.slug);
		const entry: VotedBook = { slug: book.slug, action, votedAt: new Date().toISOString(), book };
		if (existing !== -1) {
			votedBooks = [entry, ...votedBooks.filter((_, i) => i !== existing)];
		} else {
			votedBooks = [entry, ...votedBooks];
		}

		// Fly out
		transitioning = true;
		const target = flyTargets[action];
		offsetX = target.x;
		offsetY = target.y;

		await delay(360);

		// Advance
		voted = { slug: book.slug, action };
		idx++;
		transitioning = false;
		offsetX = 0;
		offsetY = 0;
		animating = false;
		showPreview = false;

		if (action === 'read_now') {
			goto(`/books/${book.slug}`);
		} else {
			startEntryAnimation();
		}
	}

	async function resetDeck() {
		await fetch('/api/discover/vote', { method: 'DELETE' });
		votedBooks = [];
		idx = 0;
		window.location.reload();
	}
</script>

<!-- ── Onboarding modal ───────────────────────────────────────────────────────── -->
{#if showOnboarding}
<div class="fixed inset-0 z-50 flex items-end sm:items-center justify-center p-4 bg-black/60 backdrop-blur-sm">
	<div class="w-full max-w-md bg-(--color-surface-2) rounded-2xl border border-(--color-border) shadow-2xl overflow-hidden">
		<div class="p-6">
			<div class="mb-5">
				<h2 class="text-xl font-bold text-(--color-text) mb-1">What do you like to read?</h2>
				<p class="text-sm text-(--color-muted)">We'll show you books you'll actually enjoy. Skip to see everything.</p>
			</div>

			<!-- Genre pills -->
			<div class="mb-5">
				<p class="text-xs font-semibold text-(--color-muted) uppercase tracking-widest mb-3">Genres</p>
				<div class="flex flex-wrap gap-2">
					{#each GENRES as genre}
						<button
							type="button"
							onclick={() => {
								if (tempGenres.includes(genre)) {
									tempGenres = tempGenres.filter((g) => g !== genre);
								} else {
									tempGenres = [...tempGenres, genre];
								}
							}}
							class="px-3 py-1.5 rounded-full text-sm font-medium border transition-all
								{tempGenres.includes(genre)
									? 'bg-(--color-brand) text-(--color-surface) border-(--color-brand)'
									: 'bg-(--color-surface-3) text-(--color-muted) border-transparent hover:border-(--color-border) hover:text-(--color-text)'}"
						>
							{genre}
						</button>
					{/each}
				</div>
			</div>

			<!-- Status -->
			<div class="mb-6">
				<p class="text-xs font-semibold text-(--color-muted) uppercase tracking-widest mb-3">Status</p>
				<div class="flex gap-2">
					{#each (['either', 'ongoing', 'completed'] as const) as s}
						<button
							type="button"
							onclick={() => (tempStatus = s)}
							class="flex-1 py-2 rounded-xl text-sm font-medium border transition-all
								{tempStatus === s
									? 'bg-(--color-brand) text-(--color-surface) border-(--color-brand)'
									: 'bg-(--color-surface-3) text-(--color-muted) border-transparent hover:text-(--color-text)'}"
						>
							{s === 'either' ? 'Either' : s.charAt(0).toUpperCase() + s.slice(1)}
						</button>
					{/each}
				</div>
			</div>

			<div class="flex gap-3">
				<button
					type="button"
					onclick={() => finishOnboarding(true)}
					class="flex-1 py-2.5 rounded-xl text-sm font-medium text-(--color-muted) hover:text-(--color-text) transition-colors"
				>
					Skip
				</button>
				<button
					type="button"
					onclick={() => finishOnboarding(false)}
					class="flex-[2] py-2.5 rounded-xl text-sm font-bold bg-(--color-brand) text-(--color-surface) hover:bg-(--color-brand-dim) transition-colors"
				>
					Start Discovering
				</button>
			</div>
		</div>
	</div>
</div>
{/if}

<!-- ── Preview modal ───────────────────────────────────────────────────────────── -->
{#if showPreview && currentBook}
{@const previewBook = currentBook!}
<div
	class="fixed inset-0 z-40 flex items-end sm:items-center justify-center p-4"
	role="presentation"
	onclick={() => (showPreview = false)}
	onkeydown={(e) => { if (e.key === 'Escape') showPreview = false; }}
>
	<div class="absolute inset-0 bg-black/60 backdrop-blur-sm"></div>
	<div
		class="relative w-full max-w-md bg-(--color-surface-2) rounded-2xl border border-(--color-border) shadow-2xl overflow-hidden"
		role="dialog"
		aria-modal="true"
		tabindex="-1"
		onclick={(e) => e.stopPropagation()}
		onkeydown={(e) => e.stopPropagation()}
	>
		<!-- Cover strip -->
		<div class="relative h-40 overflow-hidden">
			{#if previewBook.cover}
				<img src={previewBook.cover} alt={previewBook.title} class="w-full h-full object-cover object-top" />
				<div class="absolute inset-0 bg-gradient-to-b from-transparent to-(--color-surface-2)"></div>
			{:else}
				<div class="w-full h-full bg-(--color-surface-3) flex items-center justify-center">
					<svg class="w-12 h-12 text-(--color-muted)" fill="none" stroke="currentColor" viewBox="0 0 24 24">
						<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"/>
					</svg>
				</div>
			{/if}
		</div>

		<div class="p-5">
			<h3 class="font-bold text-(--color-text) text-lg leading-snug mb-1">{previewBook.title}</h3>
			{#if previewBook.author}
				<p class="text-sm text-(--color-muted) mb-3">{previewBook.author}</p>
			{/if}
			{#if previewBook.summary}
				<p class="text-sm text-(--color-muted) leading-relaxed line-clamp-5 mb-4">{previewBook.summary}</p>
			{/if}
			<div class="flex flex-wrap gap-2 mb-5">
				{#each parseBookGenres(previewBook.genres).slice(0, 4) as genre}
					<span class="text-xs px-2 py-0.5 rounded-full bg-(--color-surface-3) text-(--color-muted)">{genre}</span>
				{/each}
				{#if previewBook.status}
					<span class="text-xs px-2 py-0.5 rounded-full bg-(--color-surface-3) text-(--color-text)">{previewBook.status}</span>
				{/if}
				{#if previewBook.total_chapters}
					<span class="text-xs px-2 py-0.5 rounded-full bg-(--color-surface-3) text-(--color-muted)">{previewBook.total_chapters} ch.</span>
				{/if}
			</div>

			<div class="flex gap-2">
				<button
					type="button"
					onclick={() => { showPreview = false; doAction('skip'); }}
					class="flex-1 py-2.5 rounded-xl text-sm font-medium bg-(--color-surface-3) text-(--color-muted) hover:text-(--color-danger) transition-colors"
				>
					Skip
				</button>
				<button
					type="button"
					onclick={() => { showPreview = false; doAction('read_now'); }}
					class="flex-1 py-2.5 rounded-xl text-sm font-bold bg-blue-500/20 text-blue-400 hover:bg-blue-500/30 transition-colors"
				>
					Read Now
				</button>
				<button
					type="button"
					onclick={() => { showPreview = false; doAction('like'); }}
					class="flex-1 py-2.5 rounded-xl text-sm font-bold bg-(--color-success)/20 text-(--color-success) hover:bg-(--color-success)/30 transition-colors"
				>
					Add ♥
				</button>
			</div>
		</div>
	</div>
</div>
{/if}

<!-- ── History drawer ─────────────────────────────────────────────────────────── -->
{#if showHistory}
<div
	class="fixed inset-0 z-40 flex items-end sm:items-center justify-center p-4"
	role="presentation"
	onclick={() => (showHistory = false)}
	onkeydown={(e) => { if (e.key === 'Escape') showHistory = false; }}
>
	<div class="absolute inset-0 bg-black/60 backdrop-blur-sm"></div>
	<div
		class="relative w-full max-w-md bg-(--color-surface-2) rounded-2xl border border-(--color-border) shadow-2xl overflow-hidden max-h-[80vh] flex flex-col"
		role="dialog"
		aria-modal="true"
		tabindex="-1"
		onclick={(e) => e.stopPropagation()}
		onkeydown={(e) => e.stopPropagation()}
	>
		<div class="flex items-center justify-between p-5 border-b border-(--color-border) flex-shrink-0">
			<h3 class="font-bold text-(--color-text)">History {#if votedBooks.length}<span class="text-(--color-muted) font-normal text-sm">({votedBooks.length})</span>{/if}</h3>
			<button
				type="button"
				onclick={() => (showHistory = false)}
				aria-label="Close history"
				class="w-8 h-8 flex items-center justify-center rounded-lg text-(--color-muted) hover:text-(--color-text) hover:bg-(--color-surface-3) transition-colors"
			>
				<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
					<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
				</svg>
			</button>
		</div>
		<div class="overflow-y-auto flex-1 p-4 space-y-2">
			{#if !votedBooks.length}
				<p class="text-center text-(--color-muted) text-sm py-12">No votes yet — start swiping!</p>
			{:else}
				{#each votedBooks as v (v.slug)}
					{@const actionColor = v.action === 'like' ? 'text-green-400' : v.action === 'read_now' ? 'text-blue-400' : 'text-(--color-muted)'}
					{@const actionLabel = v.action === 'like' ? 'Liked' : v.action === 'read_now' ? 'Read Now' : v.action === 'skip' ? 'Skipped' : 'Noped'}
					<div class="flex items-center gap-3 bg-(--color-surface-3) rounded-xl p-3">
						{#if v.book?.cover}
							<img src={v.book.cover} alt="" class="w-10 h-14 rounded-md object-cover flex-shrink-0" />
						{:else}
							<div class="w-10 h-14 rounded-md bg-(--color-surface-2) flex-shrink-0"></div>
						{/if}
						<div class="flex-1 min-w-0">
							<a href="/books/{v.slug}" class="text-sm font-semibold text-(--color-text) hover:text-(--color-brand) transition-colors line-clamp-1">
								{v.book?.title ?? v.slug}
							</a>
							{#if v.book?.author}
								<p class="text-xs text-(--color-muted) truncate">{v.book.author}</p>
							{/if}
							<span class="text-xs font-medium {actionColor}">{actionLabel}</span>
						</div>
						<button
							type="button"
							onclick={() => undoVote(v.slug)}
							title="Undo"
							class="w-8 h-8 flex items-center justify-center rounded-lg text-(--color-muted) hover:text-(--color-danger) hover:bg-(--color-danger)/10 transition-colors flex-shrink-0"
							aria-label="Undo vote for {v.book?.title ?? v.slug}"
						>
							<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
								<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6"/>
							</svg>
						</button>
					</div>
				{/each}
				<button
					type="button"
					onclick={resetDeck}
					class="w-full py-2 rounded-xl text-sm text-(--color-muted) hover:text-(--color-text) transition-colors mt-2"
				>
					Clear all history
				</button>
			{/if}
		</div>
	</div>
</div>
{/if}

<!-- ── Main layout ────────────────────────────────────────────────────────────── -->
<div class="min-h-screen bg-(--color-surface) flex flex-col items-center px-3 pt-6 pb-6 select-none">

	<!-- Header -->
	<div class="w-full max-w-sm flex items-center justify-between mb-4">
		<div>
			<h1 class="text-xl font-bold text-(--color-text)">Discover</h1>
			{#if !deckEmpty}
				<p class="text-xs text-(--color-muted)">{totalRemaining} books left</p>
			{/if}
		</div>
		<div class="flex items-center gap-1">
			<!-- History button -->
			<button
				type="button"
				onclick={() => (showHistory = true)}
				title="History"
				class="relative w-9 h-9 flex items-center justify-center rounded-lg text-(--color-muted) hover:text-(--color-text) hover:bg-(--color-surface-2) transition-colors"
			>
				<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="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
				</svg>
				{#if votedBooks.length}
					<span class="absolute -top-0.5 -right-0.5 w-4 h-4 rounded-full bg-(--color-brand) text-(--color-surface) text-[9px] font-bold flex items-center justify-center leading-none">
						{votedBooks.length > 9 ? '9+' : votedBooks.length}
					</span>
				{/if}
			</button>
			<!-- Preferences button -->
			<button
				type="button"
				onclick={() => { showOnboarding = true; tempGenres = [...prefs.genres]; tempStatus = prefs.status; }}
				title="Preferences"
				class="w-9 h-9 flex items-center justify-center rounded-lg text-(--color-muted) hover:text-(--color-text) hover:bg-(--color-surface-2) transition-colors"
			>
				<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="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"/>
				</svg>
			</button>
		</div>
	</div>

	{#if deckEmpty}
		<!-- Empty state -->
		<div class="flex-1 flex flex-col items-center justify-center gap-6 text-center max-w-xs">
			<div class="w-20 h-20 rounded-full bg-(--color-surface-2) flex items-center justify-center text-4xl">
				📚
			</div>
			<div>
				<h2 class="text-lg font-bold text-(--color-text) mb-2">All caught up!</h2>
				<p class="text-sm text-(--color-muted)">
					You've seen all available books.
					{#if prefs.genres.length > 0}
						Try adjusting your preferences to see more.
					{:else}
						Check your library for books you liked.
					{/if}
				</p>
			</div>
			<div class="flex flex-col gap-2 w-full">
				<a href="/books" class="py-2.5 rounded-xl text-sm font-bold bg-(--color-brand) text-(--color-surface) text-center hover:bg-(--color-brand-dim) transition-colors">
					My Library
				</a>
				<button
					type="button"
					onclick={resetDeck}
					class="py-2.5 rounded-xl text-sm font-medium bg-(--color-surface-2) text-(--color-muted) hover:text-(--color-text) transition-colors"
				>
					Start over
				</button>
			</div>
		</div>
	{:else}
		{@const book = currentBook!}

		<!-- Card stack — fills available width, taller ratio -->
		<div class="w-full max-w-sm relative" style="aspect-ratio: 3/4.6;">

			<!-- Back card 2 -->
			{#if nextNextBook}
				<div
					class="absolute inset-0 rounded-2xl overflow-hidden shadow-lg"
					style="transform: scale(0.90) translateY(26px); z-index: 1; transition: transform 0.35s ease;"
				>
					{#if nextNextBook.cover}
						<img src={nextNextBook.cover} alt="" class="w-full h-full object-cover" />
					{:else}
						<div class="w-full h-full bg-(--color-surface-3)"></div>
					{/if}
				</div>
			{/if}

			<!-- Back card 1 -->
			{#if nextBook}
				<div
					class="absolute inset-0 rounded-2xl overflow-hidden shadow-xl"
					style="transform: scale(0.95) translateY(13px); z-index: 2; transition: transform 0.35s ease;"
				>
					{#if nextBook.cover}
						<img src={nextBook.cover} alt="" class="w-full h-full object-cover" />
					{:else}
						<div class="w-full h-full bg-(--color-surface-3)"></div>
					{/if}
				</div>
			{/if}

			<!-- Active card -->
			{#if currentBook}
			<!-- svelte-ignore a11y_no_static_element_interactions -->
			<div
				bind:this={cardEl}
				role="none"
				class="absolute inset-0 rounded-2xl overflow-hidden shadow-2xl cursor-grab active:cursor-grabbing z-10"
				style="
					transform: {activeTransform};
					transition: {activeTransition};
					touch-action: none;
				"
				onpointerdown={onPointerDown}
				onpointermove={onPointerMove}
				onpointerup={onPointerUp}
				onpointercancel={onPointerUp}
			>
				<!-- Cover image -->
				{#if book.cover}
					<img src={book.cover} alt={book.title} class="w-full h-full object-cover pointer-events-none" draggable="false" />
				{:else}
					<div class="w-full h-full bg-(--color-surface-3) flex items-center justify-center pointer-events-none">
						<svg class="w-16 h-16 text-(--color-border)" fill="none" stroke="currentColor" viewBox="0 0 24 24">
							<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"/>
						</svg>
					</div>
				{/if}

				<!-- Bottom gradient + info -->
				<div class="absolute inset-0 bg-gradient-to-t from-black/90 via-black/20 to-transparent pointer-events-none"></div>
				<div class="absolute bottom-0 left-0 right-0 p-5 pointer-events-none">
					<h2 class="text-white font-bold text-2xl leading-snug line-clamp-2 mb-1">{book.title}</h2>
					{#if book.author}
						<p class="text-white/70 text-sm mb-2">{book.author}</p>
					{/if}
					<div class="flex flex-wrap gap-1.5 items-center">
						{#each parseBookGenres(book.genres).slice(0, 2) as genre}
							<span class="text-xs bg-white/15 text-white/90 px-2 py-0.5 rounded-full backdrop-blur-sm">{genre}</span>
						{/each}
						{#if book.status}
							<span class="text-xs bg-white/10 text-white/60 px-2 py-0.5 rounded-full">{book.status}</span>
						{/if}
						{#if book.total_chapters}
							<span class="text-xs text-white/50 ml-auto">{book.total_chapters} ch.</span>
						{/if}
					</div>
				</div>

				<!-- LIKE indicator (right swipe) -->
				<div
					class="absolute top-8 right-6 px-4 py-2 rounded-xl border-[3px] border-green-400 rotate-[-15deg] pointer-events-none bg-green-400/10"
					style="opacity: {indicator === 'like' ? indicatorOpacity : 0}; transition: opacity 0.1s;"
				>
					<span class="text-green-400 font-black text-2xl tracking-widest">LIKE</span>
				</div>

				<!-- SKIP indicator (left swipe) -->
				<div
					class="absolute top-8 left-6 px-4 py-2 rounded-xl border-[3px] border-red-400 rotate-[15deg] pointer-events-none bg-red-400/10"
					style="opacity: {indicator === 'skip' ? indicatorOpacity : 0}; transition: opacity 0.1s;"
				>
					<span class="text-red-400 font-black text-2xl tracking-widest">SKIP</span>
				</div>

				<!-- READ NOW indicator (swipe up) -->
				<div
					class="absolute top-8 left-1/2 -translate-x-1/2 px-4 py-2 rounded-xl border-[3px] border-blue-400 pointer-events-none bg-blue-400/10"
					style="opacity: {indicator === 'read_now' ? indicatorOpacity : 0}; transition: opacity 0.1s;"
				>
					<span class="text-blue-400 font-black text-2xl tracking-widest">READ NOW</span>
				</div>

				<!-- NOPE indicator (swipe down) -->
				<div
					class="absolute bottom-32 left-1/2 -translate-x-1/2 px-4 py-2 rounded-xl border-[3px] border-zinc-400 pointer-events-none bg-black/20"
					style="opacity: {indicator === 'nope' ? indicatorOpacity : 0}; transition: opacity 0.1s;"
				>
					<span class="text-zinc-300 font-black text-2xl tracking-widest">NOPE</span>
				</div>
			</div>
			{/if}
		</div>

		<!-- Action buttons — 3 prominent labeled buttons -->
		<div class="w-full max-w-sm flex items-stretch gap-3 mt-5">
			<!-- Skip -->
			<button
				type="button"
				onclick={() => doAction('skip')}
				disabled={animating}
				title="Skip"
				class="flex-1 flex flex-col items-center justify-center gap-1.5 py-3.5 rounded-2xl
					bg-red-500/15 border border-red-500/30 text-red-400
					hover:bg-red-500/25 hover:border-red-500/50
					active:scale-95 transition-all disabled:opacity-40"
			>
				<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
					<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M6 18L18 6M6 6l12 12"/>
				</svg>
				<span class="text-xs font-bold tracking-wide">Skip</span>
			</button>

			<!-- Read Now — center, most prominent -->
			<button
				type="button"
				onclick={() => doAction('read_now')}
				disabled={animating}
				title="Read Now"
				class="flex-[1.4] flex flex-col items-center justify-center gap-1.5 py-3.5 rounded-2xl
					bg-blue-500 text-white
					hover:bg-blue-400
					active:scale-95 transition-all disabled:opacity-40 shadow-lg shadow-blue-500/25"
			>
				<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
					<path d="M8 5v14l11-7z"/>
				</svg>
				<span class="text-xs font-bold tracking-wide">Read Now</span>
			</button>

			<!-- Like -->
			<button
				type="button"
				onclick={() => doAction('like')}
				disabled={animating}
				title="Add to Library"
				class="flex-1 flex flex-col items-center justify-center gap-1.5 py-3.5 rounded-2xl
					bg-green-500/15 border border-green-500/30 text-green-400
					hover:bg-green-500/25 hover:border-green-500/50
					active:scale-95 transition-all disabled:opacity-40"
			>
				<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
					<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
				</svg>
				<span class="text-xs font-bold tracking-wide">Like</span>
			</button>
		</div>
	{/if}
</div>
