<script lang="ts">
	import { onMount, getContext } from 'svelte';
	import { browser } from '$app/environment';
	import { goto } from '$app/navigation';
	import { page } from '$app/state';
	import AudioPlayer from '$lib/components/AudioPlayer.svelte';
	import CommentsSection from '$lib/components/CommentsSection.svelte';
	import type { PageData } from './$types';
	import * as m from '$lib/paraglide/messages.js';
	import { audioStore } from '$lib/audio.svelte';

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

	let scrapedHtml = $state('');           // only set by the live-preview fallback
	let html = $derived(scrapedHtml || data.html || '');
	let fetchingContent = $state(false);
	let fetchError = $state('');
	let audioProRequired = $state(false);

	// ── Reader settings panel ────────────────────────────────────────────────
	const settingsCtx = getContext<{ current: string; fontFamily: string; fontSize: number } | undefined>('theme');
	let settingsPanelOpen = $state(false);
	let settingsTab = $state<'reading' | 'listening'>('reading');

	const READER_FONTS = [
		{ id: 'system', label: 'System' },
		{ id: 'serif',  label: 'Serif'  },
		{ id: 'mono',   label: 'Mono'   },
	] as const;
	const READER_SIZES = [
		{ value: 0.9,  label: 'S' },
		{ value: 1.0,  label: 'M' },
		{ value: 1.15, label: 'L' },
		{ value: 1.3,  label: 'XL'},
	] as const;

	// Mirror context values into local reactive state so the panel shows current values
	let panelFont  = $state(settingsCtx?.fontFamily ?? 'system');
	let panelSize  = $state(settingsCtx?.fontSize ?? 1.0);

	function applyFont(id: string) {
		panelFont = id;
		if (settingsCtx) settingsCtx.fontFamily = id;
	}
	function applySize(v: number) {
		panelSize = v;
		if (settingsCtx) settingsCtx.fontSize = v;
	}

	// ── Layout / reading-view prefs (localStorage) ──────────────────────────────
	type ReadMode    = 'scroll' | 'paginated';
	type LineSpacing = 'compact' | 'normal' | 'relaxed';
	type ReadWidth   = 'narrow' | 'normal' | 'wide';
	type ParaStyle   = 'spaced' | 'indented';
	type PlayerStyle = 'standard' | 'float';
	/** Controls how many lines fit on a page by adjusting the container height offset. */
	type PageLines   = 'less' | 'normal' | 'more';

	interface LayoutPrefs {
		readMode:    ReadMode;
		lineSpacing: LineSpacing;
		readWidth:   ReadWidth;
		paraStyle:   ParaStyle;
		focusMode:   boolean;
		playerStyle: PlayerStyle;
		pageLines:   PageLines;
	}

	const LAYOUT_KEY = 'reader_layout_v2';
	const LINE_HEIGHTS:    Record<LineSpacing, number> = { compact: 1.55, normal: 1.85, relaxed: 2.2 };
	const READ_WIDTHS:     Record<ReadWidth,   string> = { narrow: '58ch', normal: '72ch', wide: 'min(90ch, 100%)' };
	/**
	 * Extra rem subtracted from (or added to) the paginated container height.
	 * Normal (0rem) keeps the existing calc(); Less (-4rem) makes the container
	 * shorter so fewer lines fit per page; More (+4rem) grows it for more lines.
	 */
	const PAGE_LINES_OFFSET: Record<PageLines, string> = { less: '4rem', normal: '0rem', more: '-4rem' };
	const DEFAULT_LAYOUT: LayoutPrefs = { readMode: 'scroll', lineSpacing: 'normal', readWidth: 'normal', paraStyle: 'spaced', focusMode: false, playerStyle: 'standard', pageLines: 'normal' };

	function loadLayout(): LayoutPrefs {
		if (!browser) return DEFAULT_LAYOUT;
		try {
			const raw = localStorage.getItem(LAYOUT_KEY);
			if (raw) return { ...DEFAULT_LAYOUT, ...JSON.parse(raw) as Partial<LayoutPrefs> };
		} catch { /* ignore */ }
		return DEFAULT_LAYOUT;
	}

	let layout = $state<LayoutPrefs>(loadLayout());

	function setLayout<K extends keyof LayoutPrefs>(key: K, value: LayoutPrefs[K]) {
		layout = { ...layout, [key]: value };
		if (browser) localStorage.setItem(LAYOUT_KEY, JSON.stringify(layout));
	}

	// Apply reading CSS vars whenever layout changes
	$effect(() => {
		if (!browser) return;
		document.documentElement.style.setProperty('--reading-line-height', String(LINE_HEIGHTS[layout.lineSpacing]));
		document.documentElement.style.setProperty('--reading-max-width', READ_WIDTHS[layout.readWidth]);
	});

	// ── Scroll progress bar ──────────────────────────────────────────────────────
	let scrollProgress = $state(0);

	$effect(() => {
		if (!browser || layout.readMode === 'paginated') { scrollProgress = 0; return; }
		function onScroll() {
			const el = document.documentElement;
			const max = el.scrollHeight - el.clientHeight;
			scrollProgress = max > 0 ? el.scrollTop / max : 0;
		}
		window.addEventListener('scroll', onScroll, { passive: true });
		return () => window.removeEventListener('scroll', onScroll);
	});

	// ── Paginated mode ───────────────────────────────────────────────────────────
	let pageIndex = $state(0);
	let totalPages = $state(1);
	let paginatedContainerEl = $state<HTMLDivElement | null>(null);
	let paginatedContentEl   = $state<HTMLDivElement | null>(null);
	let containerH = $state(0);

	$effect(() => {
		if (layout.readMode !== 'paginated') { pageIndex = 0; totalPages = 1; return; }
		// Re-run when html, container refs, or mini-player visibility changes
		// (mini-player adds pb-24 which reduces the available viewport height)
		void html; void paginatedContainerEl; void paginatedContentEl;
		void audioStore.active; void audioExpanded;
		requestAnimationFrame(() => {
			if (!paginatedContainerEl || !paginatedContentEl) return;
			const h = paginatedContainerEl.clientHeight;
			if (h > 0) {
				containerH = h;
				totalPages = Math.max(1, Math.ceil(paginatedContentEl.scrollHeight / h));
				pageIndex = Math.min(pageIndex, totalPages - 1);
			}
		});
	});

	// Reset page index when chapter changes
	$effect(() => { void data.chapter.number; pageIndex = 0; });

	function handlePaginatedClick(e: MouseEvent) {
		const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
		if (e.clientX - rect.left > rect.width / 2) {
			if (pageIndex < totalPages - 1) pageIndex++;
		} else {
			if (pageIndex > 0) pageIndex--;
		}
	}

	// Keyboard nav for paginated mode + Escape to close settings overlay
	$effect(() => {
		if (!browser) return;
		function onKey(e: KeyboardEvent) {
			if (e.key === 'Escape' && settingsPanelOpen) {
				settingsPanelOpen = false;
				return;
			}
			if (layout.readMode !== 'paginated') return;
			if (e.key === 'ArrowRight' || e.key === 'PageDown' || e.key === ' ') {
				e.preventDefault();
				if (pageIndex < totalPages - 1) pageIndex++;
			} else if (e.key === 'ArrowLeft' || e.key === 'PageUp') {
				e.preventDefault();
				if (pageIndex > 0) pageIndex--;
			}
		}
		window.addEventListener('keydown', onKey);
		return () => window.removeEventListener('keydown', onKey);
	});

	// ── Translation state
	const SUPPORTED_LANGS = [
		{ code: 'ru', label: 'RU' },
		{ code: 'id', label: 'ID' },
		{ code: 'pt', label: 'PT' },
		{ code: 'fr', label: 'FR' }
	];
	// svelte-ignore state_referenced_locally
	let translationStatus = $state(data.translationStatus ?? 'idle');
	// svelte-ignore state_referenced_locally
	let translatingLang = $state(data.lang ?? '');
	let pollingTimer: ReturnType<typeof setTimeout> | null = null;

	function currentLang() {
		return page.url.searchParams.get('lang') ?? '';
	}

	function langUrl(lang: string) {
		const u = new URL(page.url);
		if (lang) u.searchParams.set('lang', lang);
		else u.searchParams.delete('lang');
		return u.pathname + u.search;
	}

	async function requestTranslation(lang: string) {
		if (!data.isPro) {
			// Don't even attempt — show upgrade inline
			return;
		}
		translatingLang = lang;
		translationStatus = 'pending';
		try {
			const res = await fetch(
				`/api/translation/${encodeURIComponent(data.book.slug)}/${data.chapter.number}?lang=${lang}`,
				{ method: 'POST' }
			);
			if (res.status === 402) {
				translationStatus = 'idle';
				translatingLang = '';
				return;
			}
			const d = (await res.json()) as { status: string };
			translationStatus = d.status ?? 'pending';
			if (d.status === 'done') {
				goto(langUrl(lang));
			} else {
				startPolling(lang);
			}
		} catch {
			translationStatus = 'idle';
		}
	}

	function startPolling(lang: string) {
		if (pollingTimer) clearTimeout(pollingTimer);
		pollingTimer = setTimeout(async () => {
			try {
				const res = await fetch(
					`/api/translation/status/${encodeURIComponent(data.book.slug)}/${data.chapter.number}?lang=${lang}`
				);
				const d = (await res.json()) as { status: string };
				translationStatus = d.status ?? 'idle';
				if (d.status === 'done') {
					goto(langUrl(lang));
				} else if (d.status === 'pending' || d.status === 'running') {
					startPolling(lang);
				}
			} catch {
				startPolling(lang); // retry on network error
			}
		}, 3000);
	}

	onMount(() => {
		// Umami analytics: track chapter reads
		window.umami?.track('chapter_read', {
			slug: data.book.slug,
			chapter: data.chapter.number
		});

		// Record reading progress (skip for preview chapters)
		if (!data.isPreview) {
			try {
				fetch('/api/progress', {
					method: 'POST',
					headers: { 'Content-Type': 'application/json' },
					body: JSON.stringify({ slug: data.book.slug, chapter: data.chapter.number })
				});
			} catch {
				// Non-critical — silently ignore
			}
		}

		// Resume polling if translation was in-progress at load time
		if (translatingLang && (translationStatus === 'pending' || translationStatus === 'running')) {
			startPolling(translatingLang);
		}

		// If the normal path returned no content, fall back to live preview scrape
		if (!data.isPreview && !data.html) {
			fetchingContent = true;
			(async () => {
				try {
					const res = await fetch(
						`/api/chapter-text-preview/${encodeURIComponent(data.book.slug)}/${data.chapter.number}`
					);
					if (!res.ok) throw new Error(`status ${res.status}`);
					const d = (await res.json()) as { text?: string };
					if (d.text) {
						const { marked } = await import('marked');
						scrapedHtml = await marked(d.text, { async: true });
					} else {
						fetchError = m.reader_audio_error();
					}
				} catch {
					fetchError = m.reader_fetching_chapter();
				} finally {
					fetchingContent = false;
				}
			})();
		}

		return () => {
			if (pollingTimer) clearTimeout(pollingTimer);
		};
	});

	const wordCount = $derived(
		html ? (html.replace(/<[^>]*>/g, '').match(/\S+/g)?.length ?? 0) : 0
	);

	// Strip scraper artifacts from chapter titles:
	//  - Leading digit(s) prefixed before "Chapter" (e.g. "6Chapter 6 : ...")
	//  - Everything after the first newline (often includes a scraped date)
	const cleanTitle = $derived.by(() => {
		let t = (data.chapter.title || '').split('\n')[0].trim();
		t = t.replace(/^\d+(?=Chapter)/i, '').trim();
		return t || `Chapter ${data.chapter.number}`;
	});

	// Audio panel: auto-open if this chapter is already loaded/playing in the store,
	// OR if auto-next is about to start it (autoStartChapter is set before navigation).
	// svelte-ignore state_referenced_locally
	let audioExpanded = $state(
		(audioStore.slug === data.book.slug && audioStore.chapter === data.chapter.number) ||
		audioStore.autoStartChapter === data.chapter.number
	);
	$effect(() => {
		// Expand automatically when the store starts playing this chapter,
		// or when auto-next targets this chapter (before startPlayback has run).
		if (
			(audioStore.slug === data.book.slug && audioStore.chapter === data.chapter.number && audioStore.isPlaying) ||
			audioStore.autoStartChapter === data.chapter.number
		) {
			audioExpanded = true;
		}
	});
</script>

<svelte:head>
	<title>{cleanTitle} — {data.book.title} — libnovel</title>
</svelte:head>

<!-- Reading progress bar (scroll mode, fixed at top of viewport) -->
{#if layout.readMode === 'scroll'}
<div class="reading-progress" style="width: {scrollProgress * 100}%"></div>
{/if}

<!-- ── Top navigation (hidden in focus mode) ─────────────────────────────── -->
{#if !layout.focusMode}
<div class="flex items-center justify-between mb-8 gap-2">
	<!-- Left: back to chapters list -->
	<a
		href="/books/{data.book.slug}/chapters"
		class="flex items-center gap-1 text-(--color-muted) hover:text-(--color-text) text-sm transition-colors shrink-0"
	>
		<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="M15 19l-7-7 7-7" />
		</svg>
		{m.reader_back_to_chapters()}
	</a>

	<!-- Right: prev/next chapter arrows + settings -->
	<div class="flex items-center gap-1">
		{#if data.prev}
			<a
				href="/books/{data.book.slug}/chapters/{data.prev}"
				title="{m.reader_chapter_n({ n: String(data.prev) })}"
				class="p-2 rounded-lg text-(--color-muted) hover:text-(--color-text) hover:bg-(--color-surface-2) 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="M15 19l-7-7 7-7" />
				</svg>
			</a>
		{/if}
		{#if data.next}
			<a
				href="/books/{data.book.slug}/chapters/{data.next}"
				title="{m.reader_chapter_n({ n: String(data.next) })}"
				class="p-2 rounded-lg text-(--color-muted) hover:text-(--color-text) hover:bg-(--color-surface-2) 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="M9 5l7 7-7 7" />
				</svg>
			</a>
		{/if}
		{#if settingsCtx}
			<button
				type="button"
				onclick={() => { settingsPanelOpen = !settingsPanelOpen; settingsTab = 'reading'; }}
				aria-label="Reader settings"
				class="p-2 rounded-lg transition-colors hover:bg-(--color-surface-2) {settingsPanelOpen ? 'text-(--color-brand)' : 'text-(--color-muted) hover:text-(--color-text)'}"
			>
				<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="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>
		{/if}
	</div>
</div>

<!-- Chapter heading + meta + language switcher -->
<div class="mb-6">
	<p class="text-xs font-semibold text-(--color-brand) uppercase tracking-widest mb-1.5">
		{m.reader_chapter_n({ n: String(data.chapter.number) })}
	</p>
	<h1 class="text-xl font-bold text-(--color-text) leading-snug">
		{cleanTitle}
	</h1>
	<div class="flex items-center flex-wrap gap-x-3 gap-y-1.5 mt-2">
		{#if wordCount > 0}
			<p class="text-(--color-muted) text-xs">
				{m.reader_words({ n: wordCount.toLocaleString() })}
				<span class="opacity-40 mx-0.5">·</span>
				~{Math.max(1, Math.round(wordCount / 200))} min
			</p>
		{/if}
		{#if data.chapter.date_label}
			<span class="text-(--color-muted) text-xs opacity-60">{data.chapter.date_label}</span>
		{/if}

		<!-- Language switcher (inline, compact) -->
		{#if !data.isPreview}
		<div class="flex items-center gap-1">
			<a
				href={langUrl('')}
				class="px-2 py-0.5 rounded text-xs font-medium transition-colors
					{currentLang() === '' ? 'bg-(--color-brand) text-(--color-surface)' : 'bg-(--color-surface-2) text-(--color-muted) hover:text-(--color-text)'}"
			>EN</a>
			{#each SUPPORTED_LANGS as { code, label }}
				{#if !data.isPro}
					<a
						href="/profile"
						title="Upgrade to Pro to read in {label}"
						class="flex items-center gap-0.5 px-2 py-0.5 rounded text-xs font-medium bg-(--color-surface-2) text-(--color-muted)/50 cursor-pointer"
					>
						<svg class="w-2.5 h-2.5 shrink-0" fill="currentColor" viewBox="0 0 20 20">
							<path fill-rule="evenodd" d="M10 1a4.5 4.5 0 00-4.5 4.5V9H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-.5V5.5A4.5 4.5 0 0010 1zm3 8V5.5a3 3 0 10-6 0V9h6z" clip-rule="evenodd" />
						</svg>
						{label}
					</a>
				{:else if currentLang() === code && (translationStatus === 'pending' || translationStatus === 'running')}
					<span class="flex items-center gap-0.5 px-2 py-0.5 rounded text-xs font-medium bg-(--color-surface-2) text-(--color-muted)">
						<svg class="w-2.5 h-2.5 animate-spin" fill="none" viewBox="0 0 24 24">
							<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
							<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
						</svg>
						{label}
					</span>
				{:else if currentLang() === code}
					<a href={langUrl(code)} class="px-2 py-0.5 rounded text-xs font-medium bg-(--color-brand) text-(--color-surface)">{label}</a>
				{:else}
					<button
						type="button"
						onclick={() => requestTranslation(code)}
						disabled={translatingLang !== '' && translatingLang !== code && (translationStatus === 'pending' || translationStatus === 'running')}
						class="px-2 py-0.5 rounded text-xs font-medium bg-(--color-surface-2) text-(--color-muted) hover:text-(--color-text) transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
					>{label}</button>
				{/if}
			{/each}
			{#if !data.isPro}
				<a href="/profile" class="text-xs text-(--color-brand) hover:underline ml-0.5">Pro</a>
			{/if}
		</div>
		{/if}
	</div>
</div>
{/if}

<!-- ── Audio section (collapsible, hidden in focus/preview mode) ──────────── -->
{#if !data.isPreview && !layout.focusMode}
	{#if !page.data.user}
		<!-- Unauthenticated -->
		<div class="mb-6 px-4 py-3 rounded-lg bg-(--color-surface-2) border border-(--color-border) flex items-center justify-between gap-4">
			<div>
				<p class="text-(--color-text) text-sm font-medium">{m.reader_signin_for_audio()}</p>
				<p class="text-(--color-muted) text-xs mt-0.5">{m.reader_signin_audio_desc()}</p>
			</div>
			<a href="/login" class="shrink-0 px-4 py-2 rounded bg-(--color-brand) text-(--color-surface) text-sm font-semibold hover:bg-(--color-brand-dim) transition-colors">
				{m.nav_sign_in()}
			</a>
		</div>
	{:else if audioProRequired}
		<div class="mb-6 px-4 py-2.5 rounded-lg bg-(--color-surface-2) border border-(--color-brand)/30 flex items-center justify-between gap-4">
			<div>
				<p class="text-(--color-text) text-sm font-medium">Daily audio limit reached</p>
				<p class="text-(--color-muted) text-xs mt-0.5">Upgrade to Pro for unlimited audio.</p>
			</div>
			<a href="/profile" class="shrink-0 px-3 py-1.5 rounded bg-(--color-brand) text-(--color-surface) text-sm font-semibold hover:bg-(--color-brand-dim) transition-colors">
				Upgrade
			</a>
		</div>
	{:else}
		<!-- Collapsible audio panel -->
		<div class="mb-6">
			<button
				type="button"
				onclick={() => { audioExpanded = !audioExpanded; }}
				class="w-full flex items-center justify-between px-4 py-2.5 bg-(--color-surface-2) border border-(--color-border) text-sm text-(--color-text) transition-colors hover:border-(--color-brand)/40
					{audioExpanded ? 'rounded-t-lg' : 'rounded-lg'}"
			>
				<span class="flex items-center gap-2">
					<svg class="w-4 h-4 text-(--color-brand)" fill="none" stroke="currentColor" viewBox="0 0 24 24">
						<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.536 8.464a5 5 0 010 7.072M12 18.364a8 8 0 010-12.728M8.464 15.536a5 5 0 010-7.072" />
					</svg>
					<span class="font-medium">Listen to this chapter</span>
					{#if audioStore.slug === data.book.slug && audioStore.chapter === data.chapter.number && audioStore.isPlaying}
						<span class="text-xs text-(--color-brand)">● Playing</span>
					{/if}
				</span>
				<svg class="w-4 h-4 text-(--color-muted) transition-transform duration-200 {audioExpanded ? 'rotate-180' : ''}" 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>
			{#if audioExpanded}
				<div class="border border-t-0 border-(--color-border) rounded-b-lg overflow-hidden">
					{#if audioStore.slug === data.book.slug && audioStore.chapter === data.chapter.number && audioStore.active}
						<!-- Mini-player is already playing this chapter — don't duplicate controls -->
						<div class="px-4 py-3 flex items-center gap-2 text-sm text-(--color-muted)">
							<svg class="w-4 h-4 text-(--color-brand) shrink-0" fill="currentColor" viewBox="0 0 24 24">
								<path d="M12 3v10.55A4 4 0 1014 17V7h4V3h-6z"/>
							</svg>
							<span>Controls are in the player bar below.</span>
						</div>
					{:else}
						<AudioPlayer
							slug={data.book.slug}
							chapter={data.chapter.number}
							chapterTitle={cleanTitle}
							bookTitle={data.book.title}
							cover={data.book.cover}
							nextChapter={data.next}
							chapters={data.chapters}
							voices={data.voices}
							playerStyle={layout.playerStyle}
							wordCount={wordCount}
							onProRequired={() => { audioProRequired = true; }}
						/>
					{/if}
				</div>
			{/if}
		</div>
	{/if}
{:else if data.isPreview}
	<div class="mb-6 px-4 py-2 rounded bg-(--color-surface-2)/60 border border-(--color-border) text-(--color-muted) text-sm">
		{m.reader_preview_audio_notice()}
	</div>
{/if}

<!-- ── Chapter content ───────────────────────────────────────────────────── -->
{#if fetchingContent}
	<div class="flex flex-col items-center gap-3 py-16 text-(--color-muted) text-sm">
		<svg class="w-6 h-6 animate-spin" fill="none" viewBox="0 0 24 24">
			<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
			<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
		</svg>
		{m.reader_fetching_chapter()}
	</div>
{:else if !html}
	<div class="text-(--color-muted) text-center py-16">
		<p>{fetchError || m.reader_audio_error()}</p>
	</div>
{:else}
	<!-- Chapter illustration hero (if generated, hidden in focus mode) -->
	{#if data.chapterImageUrl && !layout.focusMode}
		<div class="mt-4 mb-6 -mx-4 sm:mx-0 sm:rounded-xl overflow-hidden">
			<img
				src={data.chapterImageUrl}
				alt="Chapter {data.chapter.number} illustration"
				class="w-full object-cover max-h-72 sm:max-h-96"
			/>
		</div>
	{/if}

	{#if layout.readMode === 'paginated'}
	<!-- ── Paginated reader ───────────────────────────────────────────── -->
	<!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
	<div
		role="none"
		bind:this={paginatedContainerEl}
		class="paginated-container mt-8"
		style="height: {layout.focusMode
			? 'calc(100svh - 8rem)'
			: `calc(100svh - 26rem - ${PAGE_LINES_OFFSET[layout.pageLines]})`};"
		onclick={handlePaginatedClick}
	>
		<div
			bind:this={paginatedContentEl}
			class="prose-chapter {layout.paraStyle === 'indented' ? 'para-indented' : ''}"
			style="transform: translateY({containerH > 0 ? -(pageIndex * containerH) : 0}px);"
		>
			{@html html}
		</div>
	</div>

	<!-- Page indicator + nav (hidden in focus mode — shown in floating pill instead) -->
	{#if !layout.focusMode}
	<div class="flex items-center justify-between mt-4 select-none">
		<button
			type="button"
			onclick={() => { if (pageIndex > 0) pageIndex--; }}
			disabled={pageIndex === 0}
			class="flex items-center gap-1.5 px-3 py-1.5 rounded-lg bg-(--color-surface-2) border border-(--color-border) text-sm text-(--color-muted) hover:text-(--color-text) disabled:opacity-30 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="M15 19l-7-7 7-7"/>
			</svg>
			Prev
		</button>
		<span class="text-sm text-(--color-muted) tabular-nums">
			{pageIndex + 1} <span class="opacity-40">/</span> {totalPages}
		</span>
		<button
			type="button"
			onclick={() => { if (pageIndex < totalPages - 1) pageIndex++; }}
			disabled={pageIndex === totalPages - 1}
			class="flex items-center gap-1.5 px-3 py-1.5 rounded-lg bg-(--color-surface-2) border border-(--color-border) text-sm text-(--color-muted) hover:text-(--color-text) disabled:opacity-30 transition-colors"
		>
			Next
			<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="M9 5l7 7-7 7"/>
			</svg>
		</button>
	</div>
	<p class="text-center text-xs text-(--color-muted)/40 mt-2">Tap left/right · Arrow keys · Space</p>
	{/if}
{:else}
	<!-- ── Scroll reader ──────────────────────────────────────────────── -->
	<div class="prose-chapter mt-8 {layout.paraStyle === 'indented' ? 'para-indented' : ''}">
		{@html html}
	</div>
{/if}
{/if}

<!-- ── Bottom navigation + comments (hidden in focus mode) ───────────────── -->
{#if !layout.focusMode}
	<div class="mt-14 pt-6 border-t border-(--color-border)">
		<!-- Next chapter: prominent full-width CTA -->
		{#if data.next}
			<a
				href="/books/{data.book.slug}/chapters/{data.next}"
				class="group flex items-center justify-between px-5 py-4 rounded-xl bg-(--color-surface-2) border border-(--color-border) hover:border-(--color-brand)/50 transition-colors"
			>
				<div>
					<p class="text-xs text-(--color-muted) mb-0.5">{m.reader_next_chapter()}</p>
					<p class="text-(--color-text) font-semibold group-hover:text-(--color-brand) transition-colors">
						{m.reader_chapter_n({ n: String(data.next) })}
					</p>
				</div>
				<svg class="w-5 h-5 text-(--color-muted) group-hover:text-(--color-brand) group-hover:translate-x-0.5 transition-all" fill="none" stroke="currentColor" viewBox="0 0 24 24">
					<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
				</svg>
			</a>
		{/if}
		<!-- Previous chapter: small secondary link -->
		{#if data.prev}
			<a
				href="/books/{data.book.slug}/chapters/{data.prev}"
				class="mt-3 inline-flex items-center gap-1 text-sm text-(--color-muted) hover:text-(--color-text) transition-colors"
			>
				<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="M15 19l-7-7 7-7" />
				</svg>
				{m.reader_prev_chapter()}
			</a>
		{/if}
	</div>

	<div class="mt-12">
		<CommentsSection
			slug={data.book.slug}
			chapter={data.chapter.number}
			isLoggedIn={!!page.data.user}
			currentUserId={page.data.user?.id ?? ''}
		/>
	</div>
{/if}

<!-- ── Focus mode floating nav ───────────────────────────────────────────── -->
{#if layout.focusMode}
<div class="fixed {audioStore.active ? 'bottom-[4.5rem]' : 'bottom-6'} left-1/2 -translate-x-1/2 z-50 max-w-[calc(100vw-2rem)]">
	<div class="flex items-center divide-x divide-(--color-border) rounded-full bg-(--color-surface-2)/95 backdrop-blur border border-(--color-border) shadow-lg text-xs text-(--color-muted) overflow-hidden">
		<!-- Prev chapter -->
		{#if data.prev}
			<a
				href="/books/{data.book.slug}/chapters/{data.prev}"
				class="flex items-center gap-1 px-3 py-2 hover:text-(--color-text) hover:bg-(--color-surface-3) transition-colors shrink-0"
				aria-label="Previous chapter"
			>
				<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="M15 19l-7-7 7-7"/>
				</svg>
				{m.reader_chapter_n({ n: String(data.prev) })}
			</a>
		{/if}

		<!-- Page prev / counter / next (paginated mode only) -->
		{#if layout.readMode === 'paginated'}
			<button
				type="button"
				onclick={() => { if (pageIndex > 0) pageIndex--; }}
				disabled={pageIndex === 0}
				class="flex items-center justify-center px-2.5 py-2 hover:text-(--color-text) hover:bg-(--color-surface-3) disabled:opacity-30 transition-colors shrink-0"
				aria-label="Previous page"
			>
				<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="M15 19l-7-7 7-7"/>
				</svg>
			</button>
			<span class="px-2.5 py-2 tabular-nums text-(--color-muted) shrink-0 select-none">
				{pageIndex + 1}<span class="opacity-40">/</span>{totalPages}
			</span>
			<button
				type="button"
				onclick={() => { if (pageIndex < totalPages - 1) pageIndex++; }}
				disabled={pageIndex === totalPages - 1}
				class="flex items-center justify-center px-2.5 py-2 hover:text-(--color-text) hover:bg-(--color-surface-3) disabled:opacity-30 transition-colors shrink-0"
				aria-label="Next page"
			>
				<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="M9 5l7 7-7 7"/>
				</svg>
			</button>
		{/if}

		<!-- Exit focus -->
		<button
			type="button"
			onclick={() => setLayout('focusMode', false)}
			class="flex items-center gap-1 px-3 py-2 hover:text-(--color-brand) hover:bg-(--color-surface-3) transition-colors shrink-0"
			aria-label="Exit focus mode"
		>
			<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="M6 18L18 6M6 6l12 12"/>
			</svg>
			Exit focus
		</button>

		<!-- Next chapter -->
		{#if data.next}
			<a
				href="/books/{data.book.slug}/chapters/{data.next}"
				class="flex items-center gap-1 px-3 py-2 hover:text-(--color-text) hover:bg-(--color-surface-3) transition-colors shrink-0"
				aria-label="Next chapter"
			>
				{m.reader_chapter_n({ n: String(data.next) })}
				<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="M9 5l7 7-7 7"/>
				</svg>
			</a>
		{/if}
	</div>
</div>
{/if}

<!-- ── Reader settings overlay ───────────────────────────────────────────── -->
{#if settingsCtx}
{#if settingsPanelOpen}
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
	class="fixed inset-0 z-50 flex flex-col"
	style="background: var(--color-surface);"
>
	<!-- Header -->
	<div class="flex items-center gap-3 px-4 py-3 border-b border-(--color-border) shrink-0">
		<button
			type="button"
			onclick={() => (settingsPanelOpen = false)}
			class="p-2 rounded-full text-(--color-muted) hover:text-(--color-text) hover:bg-(--color-surface-2) transition-colors"
			aria-label="Close settings"
		>
			<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">Reader Settings</span>
	</div>

	<!-- Tab bar -->
	<div class="flex gap-1 mx-4 mt-3 mb-1 p-1 rounded-xl bg-(--color-surface-2) shrink-0 border border-(--color-border)">
		<button
			type="button"
			onclick={() => (settingsTab = 'reading')}
			class="flex-1 py-1.5 rounded-lg text-xs font-semibold transition-colors
				{settingsTab === 'reading'
					? 'bg-(--color-brand) text-(--color-surface)'
					: 'text-(--color-muted) hover:text-(--color-text)'}"
		>Reading</button>
		<button
			type="button"
			onclick={() => (settingsTab = 'listening')}
			class="flex-1 py-1.5 rounded-lg text-xs font-semibold transition-colors
				{settingsTab === 'listening'
					? 'bg-(--color-brand) text-(--color-surface)'
					: 'text-(--color-muted) hover:text-(--color-text)'}"
		>Listening</button>
	</div>

	<!-- Scrollable content -->
	<div class="flex-1 overflow-y-auto px-4 pb-6 flex flex-col gap-0 mt-2">

		{#if settingsTab === 'reading'}

		<!-- ── Typography ──────────────────────────────────────────────── -->
		<div class="mb-1">
			<p class="text-[10px] font-semibold text-(--color-muted) uppercase tracking-wider mb-2">Typography</p>
			<div class="bg-(--color-surface-3) rounded-xl overflow-hidden divide-y divide-(--color-border)">

				<div class="flex items-center gap-3 px-3 py-2.5">
					<span class="text-xs text-(--color-muted) w-10 shrink-0">Font</span>
					<div class="flex gap-1.5 flex-1">
						{#each READER_FONTS as f}
							<button
								onclick={() => applyFont(f.id)}
								class="flex-1 py-1.5 rounded-lg border text-xs font-medium transition-colors
									{panelFont === f.id
										? 'border-(--color-brand) bg-(--color-brand)/10 text-(--color-brand)'
										: 'border-(--color-border) bg-(--color-surface-2) text-(--color-muted) hover:border-(--color-brand)/50 hover:text-(--color-text)'}"
								aria-pressed={panelFont === f.id}
							>{f.label}</button>
						{/each}
					</div>
				</div>

				<div class="flex items-center gap-3 px-3 py-2.5">
					<span class="text-xs text-(--color-muted) w-10 shrink-0">Size</span>
					<div class="flex gap-1.5 flex-1">
						{#each READER_SIZES as s}
							<button
								onclick={() => applySize(s.value)}
								class="flex-1 py-1.5 rounded-lg border text-xs font-medium transition-colors
									{panelSize === s.value
										? 'border-(--color-brand) bg-(--color-brand)/10 text-(--color-brand)'
										: 'border-(--color-border) bg-(--color-surface-2) text-(--color-muted) hover:border-(--color-brand)/50 hover:text-(--color-text)'}"
								aria-pressed={panelSize === s.value}
							>{s.label}</button>
						{/each}
					</div>
				</div>

			</div>
		</div>

		<!-- ── Layout ──────────────────────────────────────────────────── -->
		<div class="mt-4 mb-1">
			<p class="text-[10px] font-semibold text-(--color-muted) uppercase tracking-wider mb-2">Layout</p>
			<div class="bg-(--color-surface-3) rounded-xl overflow-hidden divide-y divide-(--color-border)">

				<div class="flex items-center gap-3 px-3 py-2.5">
					<span class="text-xs text-(--color-muted) w-16 shrink-0">Mode</span>
					<div class="flex gap-1.5 flex-1">
						{#each ([['scroll', 'Scroll'], ['paginated', 'Pages']] as const) as [mode, lbl]}
							<button
								type="button"
								onclick={() => setLayout('readMode', mode)}
								class="flex-1 py-1.5 rounded-lg border text-xs font-medium transition-colors
									{layout.readMode === mode
										? 'border-(--color-brand) bg-(--color-brand)/10 text-(--color-brand)'
										: 'border-(--color-border) bg-(--color-surface-2) text-(--color-muted) hover:border-(--color-brand)/50 hover:text-(--color-text)'}"
								aria-pressed={layout.readMode === mode}
							>{lbl}</button>
						{/each}
					</div>
				</div>

				{#if layout.readMode === 'paginated'}
				<div class="flex items-center gap-3 px-3 py-2.5">
					<span class="text-xs text-(--color-muted) w-16 shrink-0">Lines</span>
					<div class="flex gap-1.5 flex-1">
						{#each ([['less', 'Few'], ['normal', 'Normal'], ['more', 'Many']] as const) as [v, lbl]}
							<button
								type="button"
								onclick={() => setLayout('pageLines', v)}
								class="flex-1 py-1.5 rounded-lg border text-xs font-medium transition-colors
									{layout.pageLines === v
										? 'border-(--color-brand) bg-(--color-brand)/10 text-(--color-brand)'
										: 'border-(--color-border) bg-(--color-surface-2) text-(--color-muted) hover:border-(--color-brand)/50 hover:text-(--color-text)'}"
								aria-pressed={layout.pageLines === v}
							>{lbl}</button>
						{/each}
					</div>
				</div>
				{/if}

				<div class="flex items-center gap-3 px-3 py-2.5">
					<span class="text-xs text-(--color-muted) w-16 shrink-0">Spacing</span>
					<div class="flex gap-1.5 flex-1">
						{#each ([['compact', 'Tight'], ['normal', 'Normal'], ['relaxed', 'Loose']] as const) as [s, lbl]}
							<button
								type="button"
								onclick={() => setLayout('lineSpacing', s)}
								class="flex-1 py-1.5 rounded-lg border text-xs font-medium transition-colors
									{layout.lineSpacing === s
										? 'border-(--color-brand) bg-(--color-brand)/10 text-(--color-brand)'
										: 'border-(--color-border) bg-(--color-surface-2) text-(--color-muted) hover:border-(--color-brand)/50 hover:text-(--color-text)'}"
								aria-pressed={layout.lineSpacing === s}
							>{lbl}</button>
						{/each}
					</div>
				</div>

				<div class="flex items-center gap-3 px-3 py-2.5">
					<span class="text-xs text-(--color-muted) w-16 shrink-0">Width</span>
					<div class="flex gap-1.5 flex-1">
						{#each ([['narrow', 'Narrow'], ['normal', 'Normal'], ['wide', 'Wide']] as const) as [w, lbl]}
							<button
								type="button"
								onclick={() => setLayout('readWidth', w)}
								class="flex-1 py-1.5 rounded-lg border text-xs font-medium transition-colors
									{layout.readWidth === w
										? 'border-(--color-brand) bg-(--color-brand)/10 text-(--color-brand)'
										: 'border-(--color-border) bg-(--color-surface-2) text-(--color-muted) hover:border-(--color-brand)/50 hover:text-(--color-text)'}"
								aria-pressed={layout.readWidth === w}
							>{lbl}</button>
						{/each}
					</div>
				</div>

				<div class="flex items-center gap-3 px-3 py-2.5">
					<span class="text-xs text-(--color-muted) w-16 shrink-0">Paragraphs</span>
					<div class="flex gap-1.5 flex-1">
						{#each ([['spaced', 'Spaced'], ['indented', 'Indented']] as const) as [s, lbl]}
							<button
								type="button"
								onclick={() => setLayout('paraStyle', s)}
								class="flex-1 py-1.5 rounded-lg border text-xs font-medium transition-colors
									{layout.paraStyle === s
										? 'border-(--color-brand) bg-(--color-brand)/10 text-(--color-brand)'
										: 'border-(--color-border) bg-(--color-surface-2) text-(--color-muted) hover:border-(--color-brand)/50 hover:text-(--color-text)'}"
								aria-pressed={layout.paraStyle === s}
							>{lbl}</button>
						{/each}
					</div>
				</div>

				<button
					type="button"
					onclick={() => setLayout('focusMode', !layout.focusMode)}
					class="w-full flex items-center justify-between px-3 py-2.5 text-xs font-medium transition-colors
						{layout.focusMode ? 'text-(--color-brand)' : 'text-(--color-text) hover:text-(--color-brand)'}"
					aria-pressed={layout.focusMode}
				>
					<span>Focus mode</span>
					<span class="text-(--color-muted) text-[11px]">{layout.focusMode ? 'On — audio & nav hidden' : 'Off'}</span>
				</button>

			</div>
		</div>

		{:else}

		<!-- ── Listening tab ───────────────────────────────────────────── -->
		<div class="mb-1">
			<p class="text-[10px] font-semibold text-(--color-muted) uppercase tracking-wider mb-2">Player</p>
			<div class="bg-(--color-surface-3) rounded-xl overflow-hidden divide-y divide-(--color-border)">

				<div class="flex items-center gap-3 px-3 py-2.5">
					<span class="text-xs text-(--color-muted) w-14 shrink-0">Style</span>
					<div class="flex gap-1.5 flex-1">
						{#each ([['standard', 'Standard'], ['float', 'Float']] as const) as [s, lbl]}
							<button
								type="button"
								onclick={() => setLayout('playerStyle', s)}
								class="flex-1 py-1.5 rounded-lg border text-xs font-medium transition-colors
									{layout.playerStyle === s
										? 'border-(--color-brand) bg-(--color-brand)/10 text-(--color-brand)'
										: 'border-(--color-border) bg-(--color-surface-2) text-(--color-muted) hover:border-(--color-brand)/50 hover:text-(--color-text)'}"
								aria-pressed={layout.playerStyle === s}
							>{lbl}</button>
						{/each}
					</div>
				</div>

				<!-- Speed -->
				<div class="flex items-center gap-3 px-3 py-2.5">
					<span class="text-xs text-(--color-muted) w-14 shrink-0">Speed</span>
					<div class="flex gap-1.5 flex-1">
						{#each ([[0.75, '0.75×'], [1, '1×'], [1.25, '1.25×'], [1.5, '1.5×'], [2, '2×']] as const) as [s, lbl]}
							<button
								type="button"
								onclick={() => { audioStore.speed = s; }}
								class="flex-1 py-1.5 rounded-lg border text-xs font-medium transition-colors
									{audioStore.speed === s
										? 'border-(--color-brand) bg-(--color-brand)/10 text-(--color-brand)'
										: 'border-(--color-border) bg-(--color-surface-2) text-(--color-muted) hover:border-(--color-brand)/50 hover:text-(--color-text)'}"
								aria-pressed={audioStore.speed === s}
							>{lbl}</button>
						{/each}
					</div>
				</div>

				<!-- Auto-next -->
				<div class="flex items-center gap-3 px-3 py-2.5">
					<span class="text-xs text-(--color-muted) w-14 shrink-0">Auto-next</span>
					<div class="flex gap-1.5 flex-1">
						{#each ([[true, 'On'], [false, 'Off']] as const) as [v, lbl]}
							<button
								type="button"
								onclick={() => { audioStore.autoNext = v; }}
								class="flex-1 py-1.5 rounded-lg border text-xs font-medium transition-colors
									{audioStore.autoNext === v
										? 'border-(--color-brand) bg-(--color-brand)/10 text-(--color-brand)'
										: 'border-(--color-border) bg-(--color-surface-2) text-(--color-muted) hover:border-(--color-brand)/50 hover:text-(--color-text)'}"
								aria-pressed={audioStore.autoNext === v}
							>{lbl}</button>
						{/each}
					</div>
				</div>

				<!-- Announce chapter -->
				<div class="flex items-center gap-3 px-3 py-2.5 {!audioStore.autoNext ? 'opacity-40 pointer-events-none' : ''}">
					<span class="text-xs text-(--color-muted) w-14 shrink-0">Announce</span>
					<div class="flex gap-1.5 flex-1">
						{#each ([[true, 'On'], [false, 'Off']] as const) as [v, lbl]}
							<button
								type="button"
								onclick={() => { audioStore.announceChapter = v; }}
								disabled={!audioStore.autoNext}
								class="flex-1 py-1.5 rounded-lg border text-xs font-medium transition-colors
									{audioStore.announceChapter === v
										? 'border-(--color-brand) bg-(--color-brand)/10 text-(--color-brand)'
										: 'border-(--color-border) bg-(--color-surface-2) text-(--color-muted) hover:border-(--color-brand)/50 hover:text-(--color-text)'}"
								aria-pressed={audioStore.announceChapter === v}
							>{lbl}</button>
						{/each}
					</div>
				</div>

			</div>
		</div>

		{/if}

		<p class="text-[11px] text-(--color-muted)/50 text-center mt-3">Changes save automatically</p>

	</div>
</div>
{/if}
{/if}
