<script lang="ts">
	import { dndzone } from 'svelte-dnd-action';
	import { flip } from 'svelte/animate';
	import { onMount } from 'svelte';
	import { generatePosition } from '$lib/utils/fractional-index.js';
	import { getDueUrgency, getUrgencyClasses, formatDueDate } from '$lib/utils/due-date.js';
	import { joinBoard, leaveBoard, onBoardEvent, offBoardEvent, getSocketId } from '$lib/stores/socket.js';
	import { api } from '$lib/utils/api.js';
	import { toast } from '$lib/stores/toasts.svelte.js';
	import CardModal from '$lib/components/CardModal.svelte';
	import BoardSettingsModal from '$lib/components/BoardSettingsModal.svelte';
	import ActivityFeed from '$lib/components/ActivityFeed.svelte';
	import BoardFilters from '$lib/components/BoardFilters.svelte';
	import BulkToolbar from '$lib/components/BulkToolbar.svelte';
	import ColumnSortMenu from '$lib/components/ColumnSortMenu.svelte';
	import Avatar from '$lib/components/Avatar.svelte';
	import type { FilterState } from '$lib/components/BoardFilters.svelte';

	let { data } = $props();

	// Local mutable state derived from server data
	let columns = $state(
		data.board.columns.map((col: any) => ({
			...col,
			cards: col.cards.map((card: any) => ({ ...card, id: card.id }))
		}))
	);

	let addingColumnTitle = $state('');
	let showAddColumn = $state(false);
	let addingCardColumnId = $state<string | null>(null);
	let newCardTitle = $state('');
	let selectedCard = $state<any>(null);
	let editingColumnId = $state<string | null>(null);
	let editingColumnTitle = $state('');
	let viewingUsers = $state<any[]>([]);
	let showActivity = $state(false);
	let showSettings = $state(false);
	let showArchivedCards = $state(false);
	let archivedCards = $state<any[]>([]);
	let activeFilters = $state<FilterState>({ labelIds: [], assigneeIds: [], due: null });
	let selectedCardIds = $state(new Set<string>());
	let selectMode = $derived(selectedCardIds.size > 0);
	let columnSorts = $state<Record<string, string | null>>({});

	const flipDurationMs = 200;

	// ─── Filtering ──────────────────────────────────────
	let hasActiveFilters = $derived(
		activeFilters.labelIds.length > 0 || activeFilters.assigneeIds.length > 0 || activeFilters.due !== null
	);

	function filterCards(cards: any[]): any[] {
		if (!hasActiveFilters) return cards;
		return cards.filter((card: any) => {
			// Label filter
			if (activeFilters.labelIds.length > 0) {
				const cardTagIds = (card.labels || []).map((l: any) => l.tag.id);
				if (!activeFilters.labelIds.some((id) => cardTagIds.includes(id))) return false;
			}
			// Assignee filter
			if (activeFilters.assigneeIds.length > 0) {
				const cardUserIds = (card.assignees || []).map((a: any) => a.user.id);
				if (!activeFilters.assigneeIds.some((id) => cardUserIds.includes(id))) return false;
			}
			// Due date filter
			if (activeFilters.due) {
				const now = new Date();
				const soon = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);
				if (activeFilters.due === 'overdue') {
					if (!card.dueDate || new Date(card.dueDate) >= now) return false;
				} else if (activeFilters.due === 'due-soon') {
					if (!card.dueDate) return false;
					const d = new Date(card.dueDate);
					if (d < now || d > soon) return false;
				} else if (activeFilters.due === 'no-date') {
					if (card.dueDate) return false;
				}
			}
			return true;
		});
	}

	// ─── Card Sorting ────────────────────────────────────
	function sortCards(cards: any[], sort: string | null): any[] {
		if (!sort) return cards;
		const sorted = [...cards];
		switch (sort) {
			case 'due-date':
				sorted.sort((a, b) => {
					if (!a.dueDate && !b.dueDate) return 0;
					if (!a.dueDate) return 1;
					if (!b.dueDate) return -1;
					return new Date(a.dueDate).getTime() - new Date(b.dueDate).getTime();
				});
				break;
			case 'created':
				sorted.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
				break;
			case 'title':
				sorted.sort((a, b) => a.title.localeCompare(b.title));
				break;
			case 'assignee':
				sorted.sort((a, b) => {
					const aName = a.assignees?.[0]?.user?.name || '';
					const bName = b.assignees?.[0]?.user?.name || '';
					return aName.localeCompare(bName);
				});
				break;
		}
		return sorted;
	}

	// ─── Archived cards ─────────────────────────────────
	async function toggleArchivedCards() {
		showArchivedCards = !showArchivedCards;
		if (showArchivedCards && archivedCards.length === 0) {
			const { ok, data: result } = await api(`/api/boards/${data.board.id}/archived-cards`);
			if (ok) {
				archivedCards = result.cards;
			}
		}
	}

	function getArchivedCardsForColumn(columnId: string): any[] {
		return archivedCards.filter((c: any) => c.columnId === columnId);
	}

	// ─── Real-Time Event Handlers ────────────────────
	function isSelf(d: any) {
		return d?.socketId && d.socketId === getSocketId();
	}

	function onColumnCreated(d: any) {
		if (isSelf(d)) return;
		if (!columns.find((c: any) => c.id === d.column.id)) {
			columns = [...columns, { ...d.column, cards: d.column.cards ?? [] }];
		}
	}

	function onColumnUpdated(d: any) {
		if (isSelf(d)) return;
		const col = columns.find((c: any) => c.id === d.column.id);
		if (col) {
			col.title = d.column.title;
			col.position = d.column.position;
		}
	}

	function onColumnDeleted(d: any) {
		if (isSelf(d)) return;
		columns = columns.filter((c: any) => c.id !== d.columnId);
	}

	function onCardCreated(d: any) {
		if (isSelf(d)) return;
		const col = columns.find((c: any) => c.id === d.columnId);
		if (col && !col.cards.find((c: any) => c.id === d.card.id)) {
			col.cards = [...col.cards, d.card];
		}
	}

	async function onCardUpdated(d: any) {
		if (isSelf(d)) return;
		// Re-fetch card from API to get full updated data
		const colId = d.columnId || d.card?.columnId;
		const cardId = d.cardId || d.card?.id;
		if (!colId || !cardId) return;

		const { ok, data: result } = await api(`/api/boards/${data.board.id}/columns/${colId}/cards/${cardId}`);
		if (!ok) return;
		const freshCard = result.card;

		// The card might have moved columns — remove from old location first
		for (const col of columns) {
			const idx = col.cards.findIndex((c: any) => c.id === cardId);
			if (idx !== -1) {
				if (col.id === freshCard.columnId) {
					// Same column — update in-place
					col.cards[idx] = { ...col.cards[idx], ...freshCard };
				} else {
					// Moved to a different column — remove from old
					col.cards = col.cards.filter((c: any) => c.id !== cardId);
					// Add to new column
					const newCol = columns.find((c: any) => c.id === freshCard.columnId);
					if (newCol) newCol.cards = [...newCol.cards, freshCard];
				}
				break;
			}
		}

		// Update selected card if it's the one being viewed
		if (selectedCard?.id === cardId) {
			selectedCard = { ...selectedCard, ...freshCard };
		}
	}

	function onCardDeleted(d: any) {
		if (isSelf(d)) return;
		const col = columns.find((c: any) => c.id === d.columnId);
		if (col) col.cards = col.cards.filter((c: any) => c.id !== d.cardId);
		if (selectedCard?.id === d.cardId) selectedCard = null;
	}

	function onBoardUpdated(d: any) {
		if (isSelf(d)) return;
		if (d.board) {
			data.board.title = d.board.title;
			data.board.visibility = d.board.visibility;
			if (d.board.description !== undefined) data.board.description = d.board.description;
			if (d.board.background !== undefined) data.board.background = d.board.background;
			if (d.board.categoryId !== undefined) data.board.categoryId = d.board.categoryId;
		}
	}

	function onBoardDeleted(d: any) {
		if (isSelf(d)) return;
		window.location.href = '/boards';
	}

	function onPresenceUpdate(users: any[]) {
		viewingUsers = users;
	}

	onMount(() => {
		joinBoard(data.board.id);

		onBoardEvent('column:created', onColumnCreated);
		onBoardEvent('column:updated', onColumnUpdated);
		onBoardEvent('column:deleted', onColumnDeleted);
		onBoardEvent('card:created', onCardCreated);
		onBoardEvent('card:updated', onCardUpdated);
		onBoardEvent('card:deleted', onCardDeleted);
		onBoardEvent('board:updated', onBoardUpdated);
		onBoardEvent('board:deleted', onBoardDeleted);
		onBoardEvent('presence:update', onPresenceUpdate);

		// Check for ?card= param to auto-open a card
		const cardParam = new URL(window.location.href).searchParams.get('card');
		if (cardParam) {
			for (const col of columns) {
				const found = col.cards.find((c: any) => c.id === cardParam);
				if (found) {
					selectedCard = found;
					break;
				}
			}
		}

		return () => {
			leaveBoard(data.board.id);
			offBoardEvent('column:created', onColumnCreated);
			offBoardEvent('column:updated', onColumnUpdated);
			offBoardEvent('column:deleted', onColumnDeleted);
			offBoardEvent('card:created', onCardCreated);
			offBoardEvent('card:updated', onCardUpdated);
			offBoardEvent('card:deleted', onCardDeleted);
			offBoardEvent('board:updated', onBoardUpdated);
			offBoardEvent('board:deleted', onBoardDeleted);
			offBoardEvent('presence:update', onPresenceUpdate);
		};
	});

	function getChecklistProgress(card: any): { done: number; total: number } | null {
		if (!card.checklists || card.checklists.length === 0) return null;
		let done = 0, total = 0;
		for (const cl of card.checklists) {
			for (const item of cl.items) {
				total++;
				if (item.completed) done++;
			}
		}
		return total > 0 ? { done, total } : null;
	}

	// ─── Column DnD ──────────────────────────────────
	function handleColumnSort(e: CustomEvent<any>) {
		columns = e.detail.items;
		if (e.detail.info.trigger === 'droppedIntoZone') {
			columns.forEach((col: any, idx: number) => {
				const before = idx > 0 ? columns[idx - 1].position : null;
				const after = idx < columns.length - 1 ? columns[idx + 1].position : null;
				if (col.id === e.detail.info.id) {
					const newPos = generatePosition(before, after);
					col.position = newPos;
					api(`/api/boards/${data.board.id}/columns/${col.id}`, {
						method: 'PATCH',
						body: JSON.stringify({ position: newPos })
					});
				}
			});
		}
	}

	// ─── Card DnD ────────────────────────────────────
	function handleCardSort(columnId: string, e: CustomEvent<any>) {
		const col = columns.find((c: any) => c.id === columnId);
		if (!col) return;
		col.cards = e.detail.items;

		if (e.detail.info.trigger === 'droppedIntoZone') {
			const cardId = e.detail.info.id;
			const cardIdx = col.cards.findIndex((c: any) => c.id === cardId);
			if (cardIdx === -1) return;

			const before = cardIdx > 0 ? col.cards[cardIdx - 1].position : null;
			const after = cardIdx < col.cards.length - 1 ? col.cards[cardIdx + 1].position : null;
			const newPos = generatePosition(before, after);
			col.cards[cardIdx].position = newPos;

			api(`/api/boards/${data.board.id}/columns/${columnId}/cards/${cardId}`, {
				method: 'PATCH',
				body: JSON.stringify({ columnId, position: newPos })
			});
		}
	}

	// ─── Add Column ──────────────────────────────────
	async function addColumn() {
		if (!addingColumnTitle.trim()) return;
		const { ok, data: result } = await api(`/api/boards/${data.board.id}/columns`, {
			method: 'POST',
			body: JSON.stringify({ title: addingColumnTitle })
		});
		if (ok) {
			columns = [...columns, { ...result.column, cards: [] }];
			addingColumnTitle = '';
			showAddColumn = false;
			toast.success('Column created');
		}
	}

	// ─── Add Card ────────────────────────────────────
	async function addCard(columnId: string) {
		if (!newCardTitle.trim()) return;
		const { ok, data: result } = await api(`/api/boards/${data.board.id}/columns/${columnId}/cards`, {
			method: 'POST',
			body: JSON.stringify({ title: newCardTitle })
		});
		if (ok) {
			const col = columns.find((c: any) => c.id === columnId);
			if (col) col.cards = [...col.cards, result.card];
			newCardTitle = '';
			addingCardColumnId = null;
			toast.success('Card added');
		}
	}

	// ─── Delete Column ───────────────────────────────
	async function deleteColumn(columnId: string) {
		const { ok } = await api(`/api/boards/${data.board.id}/columns/${columnId}`, {
			method: 'DELETE'
		});
		if (ok) {
			columns = columns.filter((c: any) => c.id !== columnId);
			toast.success('Column deleted');
		}
	}

	// ─── Rename Column ───────────────────────────────
	async function renameColumn(columnId: string) {
		if (!editingColumnTitle.trim()) return;
		const { ok } = await api(`/api/boards/${data.board.id}/columns/${columnId}`, {
			method: 'PATCH',
			body: JSON.stringify({ title: editingColumnTitle })
		});
		if (ok) {
			const col = columns.find((c: any) => c.id === columnId);
			if (col) col.title = editingColumnTitle;
		}
		editingColumnId = null;
	}

	// ─── Delete Card ─────────────────────────────────
	async function deleteCard(columnId: string, cardId: string) {
		const { ok } = await api(`/api/boards/${data.board.id}/columns/${columnId}/cards/${cardId}`, {
			method: 'DELETE'
		});
		if (ok) {
			const col = columns.find((c: any) => c.id === columnId);
			if (col) col.cards = col.cards.filter((c: any) => c.id !== cardId);
			if (selectedCard?.id === cardId) selectedCard = null;
			toast.success('Card deleted');
		}
	}

	// ─── Card updated from modal ─────────────────────
	function handleCardUpdate(updatedCard: any) {
		// Handle unarchive: add card back to column's active cards
		if (updatedCard.archived === false) {
			const wasArchived = archivedCards.find((c: any) => c.id === updatedCard.id);
			if (wasArchived) {
				archivedCards = archivedCards.filter((c: any) => c.id !== updatedCard.id);
				const col = columns.find((c: any) => c.id === updatedCard.columnId);
				if (col) {
					col.cards = [...col.cards, updatedCard];
				}
				return;
			}
		}

		// Handle archive: remove card from column's active cards
		if (updatedCard.archived === true) {
			for (const col of columns) {
				const idx = col.cards.findIndex((c: any) => c.id === updatedCard.id);
				if (idx !== -1) {
					col.cards = col.cards.filter((c: any) => c.id !== updatedCard.id);
					archivedCards = [...archivedCards, updatedCard];
					break;
				}
			}
			return;
		}

		for (const col of columns) {
			const idx = col.cards.findIndex((c: any) => c.id === updatedCard.id);
			if (idx !== -1) {
				col.cards[idx] = { ...col.cards[idx], ...updatedCard };
				break;
			}
		}
	}
</script>

<svelte:head>
	<title>{data.board.title}</title>
</svelte:head>

{#if showSettings}
	<BoardSettingsModal
		board={data.board}
		categories={data.categories}
		canDelete={data.canDelete}
		onclose={() => (showSettings = false)}
		onsave={(updated) => {
			data.board.title = updated.title;
			data.board.visibility = updated.visibility;
			data.board.description = updated.description;
			data.board.background = updated.background;
			data.board.categoryId = updated.categoryId;
			showSettings = false;
		}}
	/>
{/if}

{#if selectedCard}
	<CardModal
		card={selectedCard}
		boardId={data.board.id}
		columnId={columns.find((c: any) => c.cards.some((card: any) => card.id === selectedCard.id))?.id ?? archivedCards.find((c: any) => c.id === selectedCard.id)?.columnId}
		canEdit={data.canEdit}
		boardMembers={data.board.members}
		currentUserId={data.user?.id || ''}
		cardPrefix={data.board.cardPrefix}
		onclose={() => (selectedCard = null)}
		ondelete={(cardId) => {
			const col = columns.find((c: any) => c.cards.some((card: any) => card.id === cardId));
			if (col) deleteCard(col.id, cardId);
		}}
		onupdate={handleCardUpdate}
		oncopied={(newCard) => {
			const col = columns.find((c: any) => c.id === newCard.columnId);
			if (col) col.cards = [...col.cards, newCard];
			toast.success('Card copied');
		}}
	/>
{/if}

<div class="sr-only" aria-live="polite" id="dnd-announcer"></div>

<div class="flex flex-col h-[calc(100vh-3.5rem)]">
	<!-- Board header -->
	<div class="flex items-center gap-3 px-4 py-3 bg-white/80 border-b border-gray-200 dark:bg-gray-900/80 dark:border-gray-700 flex-wrap">
		<a href="/boards" class="text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 transition" aria-label="Back to boards">
			<svg class="w-5 h-5" viewBox="0 0 20 20" fill="currentColor">
				<path fill-rule="evenodd" d="M17 10a.75.75 0 01-.75.75H5.612l4.158 3.96a.75.75 0 11-1.04 1.08l-5.5-5.25a.75.75 0 010-1.08l5.5-5.25a.75.75 0 111.04 1.08L5.612 9.25H16.25A.75.75 0 0117 10z" clip-rule="evenodd" />
			</svg>
		</a>
		<h1 class="text-lg font-bold text-gray-900 dark:text-gray-100">{data.board.title}</h1>
		<div class="flex gap-1 bg-gray-100 dark:bg-gray-800 rounded-lg p-0.5">
			<span class="px-3 py-1 text-xs rounded-md bg-white dark:bg-gray-700 shadow-sm font-medium text-gray-900 dark:text-gray-100">
				Board
			</span>
			<a
				href="/boards/{data.board.id}/calendar"
				class="px-3 py-1 text-xs rounded-md text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"
			>
				Calendar
			</a>
		</div>
		<span class="rounded bg-gray-100 px-2 py-0.5 text-xs text-gray-500 dark:bg-gray-800 dark:text-gray-400">
			{data.board.visibility === 'PUBLIC' ? 'Public' : 'Private'}
		</span>
		<a
			href="/boards/{data.board.id}/members"
			class="rounded bg-gray-100 px-2 py-0.5 text-xs text-gray-500 hover:bg-gray-200 hover:text-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-300 transition"
		>
			{data.board.members.length} {data.board.members.length === 1 ? 'member' : 'members'}
		</a>

		{#if viewingUsers.filter((u) => u.id !== data.user?.id).length > 0}
			<div class="ml-auto flex items-center gap-1.5">
				<span class="text-xs text-gray-400 dark:text-gray-500">Also here:</span>
				<div class="flex -space-x-1.5">
					{#each viewingUsers.filter((u) => u.id !== data.user?.id) as viewer}
						<Avatar name={viewer.name} avatarUrl={viewer.avatarUrl} size="sm" class="border-2 border-white dark:border-gray-900" title={viewer.name} />
					{/each}
				</div>
			</div>
		{/if}

		<button
			onclick={() => toggleArchivedCards()}
			class="ml-auto rounded px-2 py-1 text-sm text-gray-500 hover:bg-gray-200 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-300 transition flex items-center gap-1"
			class:bg-gray-200={showArchivedCards}
			title="Toggle archived cards"
			aria-label="Toggle archived cards"
		>
			<svg class="w-4 h-4" viewBox="0 0 20 20" fill="currentColor">
				<path d="M2 3a1 1 0 00-1 1v1a1 1 0 001 1h16a1 1 0 001-1V4a1 1 0 00-1-1H2z" />
				<path fill-rule="evenodd" d="M2 7.5h16l-.811 7.71a2 2 0 01-1.99 1.79H4.802a2 2 0 01-1.99-1.79L2 7.5zM7 11a1 1 0 011-1h4a1 1 0 110 2H8a1 1 0 01-1-1z" clip-rule="evenodd" />
			</svg>
			Archived
		</button>

		<a
			href="/api/boards/{data.board.id}/export"
			class="rounded px-2 py-1 text-sm text-gray-500 hover:bg-gray-200 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-300 transition flex items-center gap-1"
			title="Export board"
			aria-label="Export board"
			download
		>
			<svg class="w-4 h-4" viewBox="0 0 20 20" fill="currentColor">
				<path fill-rule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clip-rule="evenodd" />
			</svg>
			Export
		</a>

		<button
			onclick={() => (showActivity = !showActivity)}
			class="rounded px-2 py-1 text-sm text-gray-500 hover:bg-gray-200 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-300 transition flex items-center gap-1"
			class:bg-gray-200={showActivity}
			title="Toggle activity feed"
			aria-label="Toggle activity feed"
		>
			<svg class="w-4 h-4" viewBox="0 0 20 20" fill="currentColor">
				<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd" />
			</svg>
			Activity
		</button>

		{#if data.canEdit}
			<button
				onclick={() => (showSettings = true)}
				class="rounded px-2 py-1 text-sm text-gray-500 hover:bg-gray-200 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-300 transition flex items-center gap-1"
				title="Board settings"
				aria-label="Board settings"
			>
				<svg class="w-4 h-4" viewBox="0 0 20 20" fill="currentColor">
					<path fill-rule="evenodd" d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd" />
				</svg>
				Settings
			</button>
		{/if}
	</div>

	<!-- Filters bar -->
	<div class="px-4 py-2 bg-white/60 border-b border-gray-100 dark:bg-gray-900/60 dark:border-gray-800">
		<BoardFilters
			{columns}
			members={data.board.members}
			onfilter={(f) => (activeFilters = f)}
		/>
	</div>

	<!-- Main area: columns + optional activity sidebar -->
	<div class="flex-1 flex overflow-hidden">
	<!-- Columns container -->
	<div class="flex-1 overflow-x-auto p-4">
		<div
			class="flex gap-4 h-full items-start"
			use:dndzone={{
				items: columns,
				flipDurationMs,
				type: 'columns',
				dragDisabled: !data.canEdit
			}}
			onconsider={handleColumnSort}
			onfinalize={handleColumnSort}
		>
			{#each columns as column (column.id)}
				{@const colSort = columnSorts[column.id] ?? null}
				{@const visibleCards = sortCards(filterCards(column.cards), colSort)}
				<div
					class="flex-shrink-0 w-64 sm:w-72 rounded-xl flex flex-col max-h-full"
					style="background: var(--color-column-bg); border: 1px solid var(--color-column-border); color: var(--color-column-text); backdrop-filter: var(--column-backdrop-blur, none);"
					animate:flip={{ duration: flipDurationMs }}
				>
					<!-- Column header -->
					<div class="flex items-center justify-between px-3 py-2.5 cursor-grab active:cursor-grabbing">
						{#if data.canEdit && editingColumnId !== column.id}
							<div class="flex-shrink-0 text-gray-300 dark:text-gray-600 mr-1" aria-hidden="true">
								<svg class="w-4 h-4" viewBox="0 0 20 20" fill="currentColor">
									<path d="M7 2a2 2 0 10.001 4.001A2 2 0 007 2zm0 6a2 2 0 10.001 4.001A2 2 0 007 8zm0 6a2 2 0 10.001 4.001A2 2 0 007 14zm6-8a2 2 0 10-.001-4.001A2 2 0 0013 6zm0 2a2 2 0 10.001 4.001A2 2 0 0013 8zm0 6a2 2 0 10.001 4.001A2 2 0 0013 14z" />
								</svg>
							</div>
						{/if}
						{#if editingColumnId === column.id}
							<form
								onsubmit={(e) => { e.preventDefault(); renameColumn(column.id); }}
								class="flex-1 flex gap-1"
							>
								<input
									bind:value={editingColumnTitle}
									class="flex-1 rounded border border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100 px-2 py-1 text-sm font-semibold"
									autofocus
									onblur={() => renameColumn(column.id)}
								/>
							</form>
						{:else}
							<button
								class="text-sm font-semibold text-gray-700 hover:text-gray-900 dark:text-gray-300 dark:hover:text-gray-100 text-left flex-1"
								ondblclick={() => {
									if (data.canEdit) {
										editingColumnId = column.id;
										editingColumnTitle = column.title;
									}
								}}
							>
								{column.title}
								<span class="ml-1 text-xs text-gray-400 dark:text-gray-500 font-normal">{visibleCards.length}{#if hasActiveFilters}/{column.cards.length}{/if}</span>
							</button>
						{/if}
						<ColumnSortMenu
						currentSort={colSort}
						onsort={(sort) => (columnSorts[column.id] = sort)}
					/>
					{#if data.canEdit}
							<button
								onclick={() => deleteColumn(column.id)}
								class="text-gray-400 dark:text-gray-500 hover:text-[var(--color-danger)] transition p-1"
								title="Delete column"
								aria-label="Delete column"
							>
								<svg class="w-4 h-4" viewBox="0 0 20 20" fill="currentColor">
									<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
								</svg>
							</button>
						{/if}
					</div>

					<!-- Cards zone -->
					<div
						class="flex-1 overflow-y-auto px-2 pb-2 min-h-[60px]"
						use:dndzone={{
							items: visibleCards,
							flipDurationMs,
							type: 'cards',
							dragDisabled: !data.canEdit || hasActiveFilters || selectMode || !!colSort
						}}
						onconsider={(e) => handleCardSort(column.id, e)}
						onfinalize={(e) => handleCardSort(column.id, e)}
					>
						{#each visibleCards as card (card.id)}
							{@const progress = getChecklistProgress(card)}
							{@const dueUrgency = getDueUrgency(card.dueDate)}
							<div
								class="group/card relative w-full text-left mb-2 p-3 shadow-sm hover:shadow-md transition cursor-pointer {selectedCardIds.has(card.id) ? 'ring-2 ring-[var(--color-primary)]' : ''}"
								style="background: var(--color-card-bg); border: 1px solid var(--color-card-border); color: var(--color-card-text); border-radius: var(--color-card-radius); backdrop-filter: var(--card-backdrop-blur, none);"
								animate:flip={{ duration: flipDurationMs }}
								onclick={() => {
									if (selectMode) {
										const next = new Set(selectedCardIds);
										if (next.has(card.id)) next.delete(card.id);
										else next.add(card.id);
										selectedCardIds = next;
									} else {
										selectedCard = card;
									}
								}}
								role="button"
								tabindex="0"
								onkeydown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); selectedCard = card; } }}
							>
								{#if data.canEdit}
									<!-- svelte-ignore a11y_click_events_have_key_events -->
									<!-- svelte-ignore a11y_no_static_element_interactions -->
									<div
										class="absolute top-1.5 right-1.5 z-10 {selectedCardIds.has(card.id) ? 'opacity-100' : 'opacity-0 group-hover/card:opacity-100'} transition-opacity"
										onclick={(e) => {
											e.stopPropagation();
											const next = new Set(selectedCardIds);
											if (next.has(card.id)) next.delete(card.id);
											else next.add(card.id);
											selectedCardIds = next;
										}}
									>
										<div class="w-5 h-5 rounded border-2 flex items-center justify-center cursor-pointer {selectedCardIds.has(card.id) ? 'bg-[var(--color-primary)] border-[var(--color-primary)]' : 'border-gray-300 dark:border-gray-500 bg-white dark:bg-gray-700 hover:border-[var(--color-primary)]'} transition">
											{#if selectedCardIds.has(card.id)}
												<svg class="w-3 h-3 text-white" viewBox="0 0 20 20" fill="currentColor">
													<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
												</svg>
											{/if}
										</div>
									</div>
								{/if}
								{#if card.labels && card.labels.length > 0}
									<div class="flex flex-wrap gap-1 mb-1.5">
										{#each card.labels as label}
											<span
												class="rounded px-1.5 py-0.5 text-[10px] text-white leading-none"
												style="background-color: {label.tag.color}"
											>
												{label.tag.name}
											</span>
										{/each}
									</div>
								{/if}
								{#if data.board.cardPrefix && card.cardNumber}
									<span class="text-[10px] font-mono text-gray-400 dark:text-gray-500">{data.board.cardPrefix}-{card.cardNumber}</span>
								{/if}
								<span class="text-sm text-gray-800 dark:text-gray-200">{card.title}</span>
								{#if card.dueDate || card._count?.comments > 0 || card._count?.attachments > 0 || progress}
									<div class="flex items-center gap-2 mt-2 text-xs text-gray-400 dark:text-gray-500 flex-wrap">
										{#if card.dueDate}
											<span class="inline-flex items-center rounded px-1.5 py-0.5 text-[10px] font-medium {getUrgencyClasses(dueUrgency)}">
												{formatDueDate(card.dueDate)}
											</span>
										{/if}
										{#if progress}
											<span class="inline-flex items-center gap-1 {progress.done === progress.total ? 'text-[var(--color-success)]' : ''}">
												<svg class="w-3.5 h-3.5" viewBox="0 0 20 20" fill="currentColor">
													<path fill-rule="evenodd" d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z" clip-rule="evenodd" />
												</svg>
												{progress.done}/{progress.total}
											</span>
										{/if}
										{#if card._count?.comments > 0}
											<span class="inline-flex items-center gap-0.5">
												<svg class="w-3.5 h-3.5" viewBox="0 0 20 20" fill="currentColor">
													<path fill-rule="evenodd" d="M2 5a2 2 0 012-2h12a2 2 0 012 2v6a2 2 0 01-2 2H7.414l-3.207 3.207A1 1 0 012 15.5V5z" clip-rule="evenodd" />
												</svg>
												{card._count.comments}
											</span>
										{/if}
										{#if card._count?.attachments > 0}
											<span class="inline-flex items-center gap-0.5">
												<svg class="w-3.5 h-3.5" viewBox="0 0 20 20" fill="currentColor">
													<path fill-rule="evenodd" d="M15.621 4.379a3 3 0 00-4.242 0l-7 7a3 3 0 004.241 4.243h.001l.497-.5a.75.75 0 011.064 1.057l-.498.501a4.5 4.5 0 01-6.364-6.364l7-7a4.5 4.5 0 016.368 6.36l-3.455 3.553A2.625 2.625 0 119.52 9.52l3.45-3.451a.75.75 0 111.061 1.06l-3.45 3.451a1.125 1.125 0 001.587 1.595l3.454-3.553a3 3 0 000-4.242z" clip-rule="evenodd" />
												</svg>
												{card._count.attachments}
											</span>
										{/if}
									</div>
								{/if}
								{#if card.assignees && card.assignees.length > 0}
									<div class="flex -space-x-1 mt-2">
										{#each card.assignees.slice(0, 3) as assignee}
											<Avatar name={assignee.user.name} avatarUrl={assignee.user.avatarUrl} size="xs" class="border border-white" title={assignee.user.name} />
										{/each}
										{#if card.assignees.length > 3}
											<div
												class="w-5 h-5 rounded-full bg-gray-300 text-gray-600 text-[10px] flex items-center justify-center border border-white dark:bg-gray-600 dark:text-gray-300 dark:border-gray-700"
											>
												+{card.assignees.length - 3}
											</div>
										{/if}
									</div>
								{/if}
							</div>
						{/each}
					</div>

					<!-- Archived cards for this column -->
					{#if showArchivedCards}
						{@const archivedInCol = getArchivedCardsForColumn(column.id)}
						{#if archivedInCol.length > 0}
							<div class="px-2 pb-2">
								<div class="text-[10px] text-gray-400 dark:text-gray-500 uppercase tracking-wide mb-1 px-1">Archived</div>
								{#each archivedInCol as card}
									<button
										onclick={() => (selectedCard = card)}
										class="w-full text-left mb-1.5 rounded-lg bg-gray-100 dark:bg-gray-800 p-2.5 border border-gray-200 dark:border-gray-700 opacity-60 hover:opacity-80 transition cursor-pointer"
									>
										<span class="text-sm text-gray-500 dark:text-gray-400">{card.title}</span>
									</button>
								{/each}
							</div>
						{/if}
					{/if}

					<!-- Add card button -->
					{#if data.canEdit}
						{#if addingCardColumnId === column.id}
							<div class="px-2 pb-2">
								<form
									onsubmit={(e) => { e.preventDefault(); addCard(column.id); }}
								>
									<textarea
										bind:value={newCardTitle}
										placeholder="Enter a title for this card..."
										class="w-full rounded-lg border border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100 p-2 text-sm resize-none focus:border-[var(--color-primary)] focus:outline-none focus:ring-1 focus:ring-[var(--color-primary)]"
										rows="2"
										autofocus
										onkeydown={(e) => {
											if (e.key === 'Enter' && !e.shiftKey) {
												e.preventDefault();
												addCard(column.id);
											}
											if (e.key === 'Escape') {
												addingCardColumnId = null;
												newCardTitle = '';
											}
										}}
									></textarea>
									<div class="flex gap-2 mt-1">
										<button
											type="submit"
											class="rounded bg-[var(--color-primary)] px-3 py-1.5 text-sm text-white hover:bg-[var(--color-primary-hover)] transition"
										>
											Add card
										</button>
										<button
											type="button"
											onclick={() => { addingCardColumnId = null; newCardTitle = ''; }}
											class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300 transition text-sm"
										>
											Cancel
										</button>
									</div>
								</form>
							</div>
						{:else}
							<button
								onclick={() => { addingCardColumnId = column.id; newCardTitle = ''; }}
								class="mx-2 mb-2 rounded-lg px-3 py-2 text-sm text-gray-500 hover:bg-gray-200/50 dark:text-gray-400 dark:hover:bg-gray-600/50 transition text-left"
							>
								+ Add a card
							</button>
						{/if}
					{/if}
				</div>
			{/each}

			<!-- Add column -->
			{#if data.canEdit}
				<div class="flex-shrink-0 w-72">
					{#if showAddColumn}
						<div class="rounded-xl p-3" style="background: var(--color-column-bg);">
							<form onsubmit={(e) => { e.preventDefault(); addColumn(); }}>
								<input
									bind:value={addingColumnTitle}
									placeholder="Enter list title..."
									class="w-full rounded-lg border border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100 px-3 py-2 text-sm focus:border-[var(--color-primary)] focus:outline-none focus:ring-1 focus:ring-[var(--color-primary)]"
									autofocus
									onkeydown={(e) => { if (e.key === 'Escape') { showAddColumn = false; addingColumnTitle = ''; } }}
								/>
								<div class="flex gap-2 mt-2">
									<button
										type="submit"
										class="rounded bg-[var(--color-primary)] px-3 py-1.5 text-sm text-white hover:bg-[var(--color-primary-hover)] transition"
									>
										Add list
									</button>
									<button
										type="button"
										onclick={() => { showAddColumn = false; addingColumnTitle = ''; }}
										class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300 transition text-sm"
									>
										Cancel
									</button>
								</div>
							</form>
						</div>
					{:else}
						<button
							onclick={() => (showAddColumn = true)}
							class="w-full rounded-xl bg-white/60 hover:bg-white/80 dark:bg-gray-800/60 dark:hover:bg-gray-800/80 border border-dashed border-gray-300 dark:border-gray-600 px-4 py-3 text-sm text-gray-500 dark:text-gray-400 transition text-left"
						>
							+ Add another list
						</button>
					{/if}
				</div>
			{/if}
		</div>
	</div>

	<!-- Activity sidebar -->
	{#if showActivity}
		<div class="fixed inset-0 z-40 bg-white dark:bg-gray-900 overflow-y-auto p-4 sm:relative sm:inset-auto sm:z-auto sm:w-80 sm:flex-shrink-0 sm:border-l sm:border-gray-200 sm:dark:border-gray-700">
			<div class="flex items-center justify-between mb-4">
				<h2 class="text-sm font-semibold text-gray-700 dark:text-gray-300">Activity</h2>
				<button onclick={() => (showActivity = false)} class="text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 transition p-1" aria-label="Close activity">
					<svg class="w-4 h-4" viewBox="0 0 20 20" fill="currentColor">
						<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
					</svg>
				</button>
			</div>
			<ActivityFeed boardId={data.board.id} />
		</div>
	{/if}
	</div>
</div>

{#if selectMode && selectedCardIds.size > 0}
	<BulkToolbar
		boardId={data.board.id}
		{selectedCardIds}
		{columns}
		onaction={() => {
			selectedCardIds = new Set();
			window.location.reload();
		}}
		oncancel={() => {
			selectedCardIds = new Set();
		}}
	/>
{/if}
