<script lang="ts">
	import type { Node, Edge } from '$lib/realtime';
	import { edgeStore, nodeStore, nodeVisibility } from '$lib/realtime';
	import { createNode, createEdge, updateNode, deleteEdge, aiProcess } from '$lib/api';
	import PublishDialog from '$lib/components/PublishDialog.svelte';
	import { getDragPayload, setDragPayload, checkToolToNodeCompat, checkEditorCompat, type DragPayload } from '$lib/transfer';
	import type { BlockReceiver, PlacementIntent } from '$lib/components/blockshell/types';

	interface Props {
		collection?: Node;
		config: Record<string, unknown>;
		userId?: string;
		accessToken?: string;
		/** Full collection metadata (for reading traits) */
		collectionMetadata?: Record<string, unknown>;
		/** Called when a drop is received on this panel */
		onReceiveDrop?: (payload: DragPayload, intent: PlacementIntent) => void;
	}

	let { collection, config, userId, accessToken, collectionMetadata, onReceiveDrop }: Props = $props();

	/**
	 * BlockReceiver implementation for Editor (Artikkelverktøy).
	 * Accepts text nodes as source_material, media as embedded content.
	 * Also handles AI preset drops (tool_to_node) separately via inline handlers.
	 * Ref: docs/retninger/arbeidsflaten.md § Kompatibilitetsmatrise
	 */
	export const receiver: BlockReceiver = {
		canReceive(payload: DragPayload) {
			// AI preset drops are handled by the inline item-level handlers
			if (payload.nodeKind === 'ai_preset' && payload.presetId) {
				return { compatible: true };
			}
			return checkEditorCompat(payload);
		},
		receive(payload: DragPayload) {
			const intent: PlacementIntent = {
				mode: 'innholdstransfer',
				contextId: collection?.id ?? '',
				contextType: 'editor',
			};
			onReceiveDrop?.(payload, intent);
			return intent;
		}
	};

	const preset = $derived((config.preset as string) ?? 'longform');

	/** Check if this collection has a personal publishing trait */
	const pubConfig = $derived.by((): Record<string, unknown> | null => {
		if (!collectionMetadata) return null;
		const traits = collectionMetadata.traits as Record<string, Record<string, unknown>> | undefined;
		if (!traits?.publishing) return null;
		const pub = traits.publishing;
		if (pub.require_approval === true) return null;
		return pub;
	});

	const isPublishingCollection = $derived(pubConfig !== null);
	const pubSlug = $derived((pubConfig?.slug as string) ?? '');

	// =========================================================================
	// Content items: nodes belonging to this collection
	// =========================================================================

	interface ContentItem {
		node: Node;
		edge: Edge;
	}

	const contentItems = $derived.by((): ContentItem[] => {
		if (!collection) return [];
		const items: ContentItem[] = [];
		for (const edge of edgeStore.byTarget(collection.id)) {
			if (edge.edgeType !== 'belongs_to') continue;
			const node = nodeStore.get(edge.sourceId);
			if (node && node.nodeKind === 'content' && nodeVisibility(node, userId) !== 'hidden') {
				items.push({ node, edge });
			}
		}
		items.sort((a, b) => {
			const ta = a.node.createdAt ?? 0;
			const tb = b.node.createdAt ?? 0;
			return tb > ta ? 1 : tb < ta ? -1 : 0;
		});
		return items;
	});

	// =========================================================================
	// Source material: edges pointing FROM content items to their sources
	// =========================================================================

	interface SourceLink {
		edge: Edge;
		sourceNode: Node;
		context: string;
	}

	/** All source_material edges from content items in this collection */
	const sourceLinks = $derived.by((): SourceLink[] => {
		const links: SourceLink[] = [];
		const itemIds = new Set(contentItems.map(i => i.node.id));
		for (const item of contentItems) {
			for (const edge of edgeStore.bySource(item.node.id)) {
				if (edge.edgeType !== 'source_material') continue;
				const sourceNode = nodeStore.get(edge.targetId);
				if (!sourceNode) continue;
				let context = 'referenced';
				try {
					const meta = JSON.parse(edge.metadata ?? '{}');
					if (meta.context) context = meta.context;
				} catch { /* ignore */ }
				links.push({ edge, sourceNode, context });
			}
		}
		return links;
	});

	// =========================================================================
	// Publishable nodes (for publishing picker)
	// =========================================================================

	const publishableNodes = $derived.by((): Node[] => {
		if (!isPublishingCollection || !userId) return [];
		const inCollection = new Set(contentItems.map(i => i.node.id));
		const nodes: Node[] = [];
		for (const edge of edgeStore.bySource(userId)) {
			if (edge.edgeType !== 'owner') continue;
			const node = nodeStore.get(edge.targetId);
			if (node && node.nodeKind === 'content' && !inCollection.has(node.id)) {
				nodes.push(node);
			}
		}
		nodes.sort((a, b) => {
			const ta = a.createdAt ?? 0;
			const tb = b.createdAt ?? 0;
			return tb > ta ? 1 : tb < ta ? -1 : 0;
		});
		return nodes;
	});

	// =========================================================================
	// State
	// =========================================================================

	let showPublishPicker = $state(false);
	let publishTarget = $state<Node | null>(null);
	let unpublishing = $state<string | null>(null);
	let unpublishError = $state<string | null>(null);
	let confirmUnpublish = $state<ContentItem | null>(null);
	let selectedItem = $state<ContentItem | null>(null);
	let editContent = $state('');
	let editTitle = $state('');
	let saving = $state(false);
	let saveMessage = $state<string | null>(null);
	let showSources = $state(false);

	// --- AI tool_to_node drop state ---
	let dropTargetNodeId = $state<string | null>(null);
	let dropIncompat = $state<{ reason: string; suggestion?: string } | null>(null);
	let aiProcessing = $state<string | null>(null);
	let aiResult = $state<{ nodeId: string; success: boolean; message: string } | null>(null);

	// =========================================================================
	// Inline editing
	// =========================================================================

	function selectItem(item: ContentItem) {
		selectedItem = item;
		editTitle = item.node.title ?? '';
		editContent = item.node.content ?? '';
		saveMessage = null;
	}

	function closeEditor() {
		selectedItem = null;
		editTitle = '';
		editContent = '';
		saveMessage = null;
	}

	async function saveContent() {
		if (!selectedItem || !accessToken || saving) return;
		saving = true;
		saveMessage = null;
		try {
			await updateNode(accessToken, {
				node_id: selectedItem.node.id,
				title: editTitle.trim() || undefined,
				content: editContent,
			});
			saveMessage = 'Lagret';
			setTimeout(() => { if (saveMessage === 'Lagret') saveMessage = null; }, 2000);
		} catch (err) {
			saveMessage = `Feil: ${err instanceof Error ? err.message : String(err)}`;
		} finally {
			saving = false;
		}
	}

	/** Source links for the currently selected item */
	const selectedSourceLinks = $derived.by((): SourceLink[] => {
		if (!selectedItem) return [];
		return sourceLinks.filter(sl => {
			for (const edge of edgeStore.bySource(selectedItem!.node.id)) {
				if (edge.id === sl.edge.id) return true;
			}
			return false;
		});
	});

	// =========================================================================
	// New article creation
	// =========================================================================

	let newTitle = $state('');
	let isCreating = $state(false);
	let showCreateForm = $state(false);

	async function handleCreateArticle() {
		if (!accessToken || !collection || !newTitle.trim() || isCreating) return;
		isCreating = true;
		try {
			const { node_id } = await createNode(accessToken, {
				node_kind: 'content',
				title: newTitle.trim(),
				visibility: 'hidden',
				metadata: { document: { type: 'doc', content: [] } },
			});
			await createEdge(accessToken, {
				source_id: node_id,
				target_id: collection.id,
				edge_type: 'belongs_to',
				metadata: {},
			});
			newTitle = '';
			showCreateForm = false;
		} catch (err) {
			console.error('Feil ved oppretting:', err);
		} finally {
			isCreating = false;
		}
	}

	function handleCreateKeydown(e: KeyboardEvent) {
		if (e.key === 'Enter') {
			e.preventDefault();
			handleCreateArticle();
		} else if (e.key === 'Escape') {
			showCreateForm = false;
			newTitle = '';
		}
	}

	// =========================================================================
	// Drag-and-drop: items as drag source
	// =========================================================================

	function handleItemDragStart(e: DragEvent, item: ContentItem) {
		if (!e.dataTransfer) return;
		setDragPayload(e.dataTransfer, {
			nodeId: item.node.id,
			nodeKind: item.node.nodeKind,
			sourcePanel: 'editor'
		});
	}

	// --- AI preset drops (tool_to_node) ---
	function handleItemDragOver(e: DragEvent, item: ContentItem) {
		e.preventDefault();
		if (!e.dataTransfer) return;
		const payload = getDragPayload(e.dataTransfer);
		if (!payload || payload.nodeKind !== 'ai_preset' || !payload.presetId) return;

		const compat = checkToolToNodeCompat(item.node.nodeKind, !!item.node.content?.trim());
		if (!compat.compatible) {
			dropIncompat = { reason: compat.reason!, suggestion: compat.suggestion };
			e.dataTransfer.dropEffect = 'none';
		} else {
			dropIncompat = null;
			e.dataTransfer.dropEffect = 'copy';
		}
		dropTargetNodeId = item.node.id;
	}

	function handleItemDragLeave(e: DragEvent, item: ContentItem) {
		const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
		const x = e.clientX;
		const y = e.clientY;
		if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom) {
			if (dropTargetNodeId === item.node.id) {
				dropTargetNodeId = null;
				dropIncompat = null;
			}
		}
	}

	async function handleItemDrop(e: DragEvent, item: ContentItem) {
		e.preventDefault();
		dropTargetNodeId = null;
		dropIncompat = null;

		if (!e.dataTransfer || !accessToken) return;
		const payload = getDragPayload(e.dataTransfer);
		if (!payload || payload.nodeKind !== 'ai_preset' || !payload.presetId) return;

		const compat = checkToolToNodeCompat(item.node.nodeKind, !!item.node.content?.trim());
		if (!compat.compatible) {
			aiResult = {
				nodeId: item.node.id,
				success: false,
				message: [compat.reason, compat.suggestion].filter(Boolean).join(' ')
			};
			return;
		}

		aiProcessing = item.node.id;
		aiResult = null;

		try {
			const res = await aiProcess(accessToken, {
				source_node_id: item.node.id,
				ai_preset_id: payload.presetId,
				direction: 'tool_to_node'
			});
			const presetNode = nodeStore.get(payload.presetId);
			aiResult = {
				nodeId: item.node.id,
				success: true,
				message: `${presetNode?.title ?? 'AI'}: jobb opprettet (${res.job_id.slice(0, 8)}…). Noden oppdateres i sanntid.`
			};
		} catch (err) {
			aiResult = {
				nodeId: item.node.id,
				success: false,
				message: `Feil: ${err instanceof Error ? err.message : String(err)}`
			};
		} finally {
			aiProcessing = null;
		}
	}

	async function handleUnpublish(item: ContentItem) {
		if (!accessToken || unpublishing) return;
		unpublishing = item.node.id;
		unpublishError = null;

		try {
			await deleteEdge(accessToken, { edge_id: item.edge.id });
			confirmUnpublish = null;
		} catch (err) {
			console.error('Avpubliseringsfeil:', err);
			unpublishError = err instanceof Error ? err.message : 'Ukjent feil';
		} finally {
			unpublishing = null;
		}
	}

	// =========================================================================
	// Helpers
	// =========================================================================

	function formatTime(node: Node): string {
		if (!node.createdAt) return '';
		const ms = Math.floor(node.createdAt / 1000);
		const date = new Date(ms);
		return date.toLocaleDateString('nb-NO', { day: 'numeric', month: 'short' });
	}

	function kindIcon(kind: string): string {
		switch (kind) {
			case 'communication': return '💬';
			case 'content': return '📄';
			default: return '🔗';
		}
	}
</script>

<!--
	EditorTrait — fullverdig BlockShell-panel for artikkelverktøy.
	Viser innholdsnoder med inline editor, source_material-kilder,
	og støtter mottak av noder fra andre paneler.
	Forelder (collection page) wrapper dette i BlockShell.
-->

<div class="editor-trait">
	{#if selectedItem}
		<!-- ============================================================= -->
		<!-- EDITOR VIEW: inline editing for selected content node          -->
		<!-- ============================================================= -->
		<div class="editor-view">
			<div class="editor-toolbar">
				<button class="editor-back-btn" onclick={closeEditor} title="Tilbake til liste">
					← Tilbake
				</button>
				<div class="editor-toolbar-right">
					{#if selectedSourceLinks.length > 0}
						<button
							class="editor-sources-btn {showSources ? 'editor-sources-btn-active' : ''}"
							onclick={() => { showSources = !showSources; }}
							title="Vis kilder ({selectedSourceLinks.length})"
						>
							📎 {selectedSourceLinks.length}
						</button>
					{/if}
					<button
						class="editor-save-btn"
						onclick={saveContent}
						disabled={saving}
					>
						{saving ? 'Lagrer…' : 'Lagre'}
					</button>
					{#if saveMessage}
						<span class="editor-save-msg {saveMessage.startsWith('Feil') ? 'editor-save-error' : 'editor-save-ok'}">
							{saveMessage}
						</span>
					{/if}
				</div>
			</div>

			<div class="editor-body">
				<div class="editor-content-area">
					<input
						type="text"
						class="editor-title-input"
						bind:value={editTitle}
						placeholder="Tittel…"
					/>
					<textarea
						class="editor-textarea"
						bind:value={editContent}
						placeholder="Skriv innhold her…"
					></textarea>
				</div>

				<!-- Sources sidebar (toggled) -->
				{#if showSources && selectedSourceLinks.length > 0}
					<div class="editor-sources-panel">
						<h4 class="editor-sources-title">Kilder</h4>
						<ul class="editor-sources-list">
							{#each selectedSourceLinks as sl (sl.edge.id)}
								<li class="editor-source-item">
									<span class="editor-source-icon">{kindIcon(sl.sourceNode.nodeKind)}</span>
									<div class="editor-source-info">
										<span class="editor-source-name">{sl.sourceNode.title || 'Uten tittel'}</span>
										<span class="editor-source-context">{sl.context}</span>
									</div>
								</li>
							{/each}
						</ul>
					</div>
				{/if}
			</div>
		</div>
	{:else}
		<!-- ============================================================= -->
		<!-- LIST VIEW: content items belonging to collection               -->
		<!-- ============================================================= -->
		<div class="editor-list">
			{#if unpublishError}
				<div class="editor-error">
					{unpublishError}
					<button onclick={() => { unpublishError = null; }} class="editor-error-dismiss">Lukk</button>
				</div>
			{/if}

			<div class="editor-list-header">
				<span class="editor-list-meta">
					{preset}
					{#if config.allow_collaborators}
						· Samarbeid
					{/if}
					{#if isPublishingCollection}
						· Publisering
					{/if}
				</span>
				<span class="editor-list-count">{contentItems.length}</span>
			</div>

			{#if contentItems.length === 0}
				<div class="editor-empty">
					<span class="editor-empty-icon">📝</span>
					<p>Ingen artikler ennå.</p>
					<p class="editor-empty-hint">Opprett en ny, eller dra innhold fra andre paneler.</p>
				</div>
			{:else}
				<div class="editor-items">
					{#each contentItems as item (item.node.id)}
						{@const isDropTarget = dropTargetNodeId === item.node.id}
						{@const isIncompat = isDropTarget && dropIncompat}
						{@const isProcessing = aiProcessing === item.node.id}
						{@const itemResult = aiResult?.nodeId === item.node.id ? aiResult : null}
						{@const itemSourceCount = (() => {
							let c = 0;
							for (const e of edgeStore.bySource(item.node.id)) {
								if (e.edgeType === 'source_material') c++;
							}
							return c;
						})()}
						<!-- svelte-ignore a11y_no_static_element_interactions -->
						<div
							class="editor-item"
							class:editor-item-drop={isDropTarget && !isIncompat}
							class:editor-item-incompat={!!isIncompat}
							class:editor-item-processing={isProcessing}
							draggable={!isProcessing}
							ondragstart={(e) => handleItemDragStart(e, item)}
							ondragover={(e) => handleItemDragOver(e, item)}
							ondragleave={(e) => handleItemDragLeave(e, item)}
							ondrop={(e) => handleItemDrop(e, item)}
							onclick={() => selectItem(item)}
							onkeydown={(e) => { if (e.key === 'Enter') selectItem(item); }}
							role="button"
							tabindex="0"
						>
							{#if isIncompat}
								<div class="editor-item-feedback">
									<p class="editor-item-feedback-reason">{dropIncompat?.reason}</p>
									{#if dropIncompat?.suggestion}
										<p class="editor-item-feedback-suggestion">{dropIncompat.suggestion}</p>
									{/if}
								</div>
							{:else if isDropTarget}
								<div class="editor-item-feedback">
									<p class="editor-item-feedback-ok">Slipp for in-place revisjon</p>
								</div>
							{:else}
								<div class="editor-item-main">
									{#if itemSourceCount > 0}
										<span class="editor-item-source-indicator" title="{itemSourceCount} kilde(r)"></span>
									{/if}
									<div class="editor-item-text">
										<span class="editor-item-title">
											{item.node.title || 'Uten tittel'}
											{#if isProcessing}
												<span class="editor-item-processing-label">Prosesserer…</span>
											{/if}
										</span>
										{#if item.node.content}
											<span class="editor-item-preview">{item.node.content.slice(0, 120)}</span>
										{/if}
										{#if itemResult}
											<span class="editor-item-result {itemResult.success ? 'editor-item-result-ok' : 'editor-item-result-err'}">
												{itemResult.message}
											</span>
										{/if}
									</div>
									<div class="editor-item-meta">
										{#if itemSourceCount > 0}
											<span class="editor-item-source-count" title="Kilder">📎 {itemSourceCount}</span>
										{/if}
										{#if formatTime(item.node)}
											<span class="editor-item-time">{formatTime(item.node)}</span>
										{/if}
									</div>
								</div>

								{#if isPublishingCollection && accessToken}
									<div class="editor-item-actions">
										{#if pubSlug}
											<a
												href="/api/pub/{pubSlug}/{item.node.id.slice(0, 8)}"
												target="_blank"
												rel="noopener"
												class="editor-item-action"
												title="Se publisert"
												onclick={(e) => e.stopPropagation()}
											>Se</a>
										{/if}
										<button
											onclick={(e) => { e.stopPropagation(); confirmUnpublish = item; }}
											class="editor-item-action editor-item-action-danger"
											title="Fjern fra publisering"
											disabled={unpublishing === item.node.id}
										>
											{unpublishing === item.node.id ? '...' : 'Avpubliser'}
										</button>
									</div>
								{/if}
							{/if}
						</div>
					{/each}
				</div>
			{/if}

			<!-- Actions: create / publish -->
			{#if accessToken}
				<div class="editor-actions">
					{#if showCreateForm}
						<div class="editor-create-form">
							<input
								type="text"
								bind:value={newTitle}
								onkeydown={handleCreateKeydown}
								placeholder="Artikkeltittel…"
								class="editor-create-input"
								disabled={isCreating}
							/>
							<div class="editor-create-buttons">
								<button
									onclick={handleCreateArticle}
									disabled={isCreating || !newTitle.trim()}
									class="editor-create-btn"
								>
									{isCreating ? '…' : 'Opprett'}
								</button>
								<button
									onclick={() => { showCreateForm = false; newTitle = ''; }}
									class="editor-cancel-btn"
								>
									Avbryt
								</button>
							</div>
						</div>
					{:else}
						<button
							onclick={() => { showCreateForm = true; newTitle = ''; }}
							class="editor-new-btn"
						>
							+ Ny artikkel
						</button>
					{/if}

					{#if isPublishingCollection}
						<button
							onclick={() => { showPublishPicker = !showPublishPicker; }}
							class="editor-publish-btn"
						>
							Publiser artikkel
						</button>
					{/if}
				</div>
			{/if}

			<!-- Publishable nodes picker -->
			{#if showPublishPicker && isPublishingCollection}
				<div class="editor-publish-picker">
					<h4 class="editor-publish-picker-title">Velg artikkel å publisere</h4>
					{#if publishableNodes.length === 0}
						<p class="editor-publish-picker-empty">Ingen upubliserte artikler funnet.</p>
					{:else}
						<ul class="editor-publish-picker-list">
							{#each publishableNodes as pNode (pNode.id)}
								<li>
									<button
										onclick={() => { publishTarget = pNode; showPublishPicker = false; }}
										class="editor-publish-picker-item"
									>
										<span class="editor-publish-picker-name">{pNode.title || 'Uten tittel'}</span>
										{#if pNode.content}
											<span class="editor-publish-picker-preview">{pNode.content.slice(0, 60)}</span>
										{/if}
									</button>
								</li>
							{/each}
						</ul>
					{/if}
				</div>
			{/if}
		</div>
	{/if}
</div>

<!-- Publish dialog -->
{#if publishTarget && accessToken && pubConfig}
	<PublishDialog
		node={publishTarget}
		collection={collection!}
		pubConfig={pubConfig}
		{accessToken}
		onclose={() => { publishTarget = null; }}
	/>
{/if}

<!-- Unpublish confirmation -->
{#if confirmUnpublish}
	<!-- svelte-ignore a11y_no_static_element_interactions -->
	<div
		class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4"
		onkeydown={(e) => { if (e.key === 'Escape') confirmUnpublish = null; }}
	>
		<!-- svelte-ignore a11y_click_events_have_key_events -->
		<div
			class="w-full max-w-sm rounded-xl bg-white p-5 shadow-2xl"
			onclick={(e) => e.stopPropagation()}
			role="dialog"
			aria-label="Bekreft avpublisering"
		>
			<h3 class="text-base font-semibold text-gray-900">Avpubliser artikkel?</h3>
			<p class="mt-2 text-sm text-gray-600">
				<strong>{confirmUnpublish.node.title || 'Uten tittel'}</strong> vil bli fjernet fra
				publiseringen. Artikkelen slettes ikke, men er ikke lenger offentlig tilgjengelig.
			</p>
			<div class="mt-4 flex items-center justify-end gap-3">
				<button
					onclick={() => { confirmUnpublish = null; }}
					class="rounded-lg px-3 py-1.5 text-sm font-medium text-gray-600 hover:bg-gray-100"
				>
					Avbryt
				</button>
				<button
					onclick={() => { if (confirmUnpublish) handleUnpublish(confirmUnpublish); }}
					class="rounded-lg bg-red-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-red-700 disabled:opacity-50"
					disabled={unpublishing !== null}
				>
					{unpublishing ? 'Fjerner...' : 'Avpubliser'}
				</button>
			</div>
		</div>
	</div>
{/if}

<style>
	/* ================================================================= */
	/* Root — fills BlockShell content area                               */
	/* ================================================================= */
	.editor-trait {
		display: flex;
		flex-direction: column;
		height: 100%;
		min-height: 0;
		overflow: hidden;
	}

	/* ================================================================= */
	/* LIST VIEW                                                          */
	/* ================================================================= */
	.editor-list {
		display: flex;
		flex-direction: column;
		height: 100%;
		min-height: 0;
		overflow: hidden;
	}

	.editor-list-header {
		display: flex;
		align-items: center;
		justify-content: space-between;
		padding: 8px 12px;
		flex-shrink: 0;
		border-bottom: 1px solid var(--color-border);
	}

	.editor-list-meta {
		font-size: 11px;
		color: var(--color-text-dim);
		text-transform: uppercase;
		letter-spacing: 0.03em;
	}

	.editor-list-count {
		font-size: 11px;
		color: var(--color-text-dim);
		background: var(--color-surface-hover);
		border-radius: 9999px;
		padding: 0 6px;
		line-height: 18px;
	}

	.editor-error {
		margin: 8px 12px 0;
		padding: 8px 10px;
		border-radius: 6px;
		border: 1px solid var(--color-error);
		background: var(--color-surface);
		font-size: 12px;
		color: var(--color-error);
	}

	.editor-error-dismiss {
		margin-left: 4px;
		text-decoration: underline;
		cursor: pointer;
		background: none;
		border: none;
		color: inherit;
		font-size: inherit;
	}

	/* ================================================================= */
	/* Empty state                                                        */
	/* ================================================================= */
	.editor-empty {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		flex: 1;
		padding: 24px;
		color: var(--color-text-dim);
		font-size: 13px;
		text-align: center;
		gap: 4px;
	}

	.editor-empty-icon {
		font-size: 28px;
	}

	.editor-empty-hint {
		font-size: 11px;
		color: var(--color-text-muted);
		margin-top: 4px;
	}

	/* ================================================================= */
	/* Items list                                                         */
	/* ================================================================= */
	.editor-items {
		flex: 1;
		overflow-y: auto;
		padding: 6px;
		display: flex;
		flex-direction: column;
		gap: 4px;
		min-height: 0;
	}

	.editor-item {
		background: var(--color-surface);
		border: 1px solid var(--color-border);
		border-radius: 6px;
		padding: 8px 10px;
		cursor: pointer;
		transition: box-shadow 0.1s, border-color 0.15s, background 0.15s;
	}

	.editor-item:hover {
		box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
		border-color: var(--color-text-muted);
	}

	.editor-item-drop {
		border-color: var(--color-accent);
		background: var(--color-surface);
	}

	.editor-item-incompat {
		border-color: var(--color-error);
		background: var(--color-surface);
	}

	.editor-item-processing {
		border-color: var(--color-warning);
		background: var(--color-surface);
	}

	.editor-item-feedback {
		text-align: center;
		padding: 4px 0;
	}

	.editor-item-feedback-reason {
		font-size: 12px;
		color: var(--color-error);
		font-weight: 500;
	}

	.editor-item-feedback-suggestion {
		font-size: 11px;
		color: var(--color-error);
		margin-top: 2px;
	}

	.editor-item-feedback-ok {
		font-size: 12px;
		color: var(--color-accent);
		font-weight: 500;
	}

	.editor-item-main {
		display: flex;
		align-items: flex-start;
		gap: 8px;
	}

	.editor-item-source-indicator {
		flex-shrink: 0;
		width: 3px;
		min-height: 24px;
		border-radius: 2px;
		background: var(--color-accent);
		align-self: stretch;
	}

	.editor-item-text {
		flex: 1;
		min-width: 0;
		display: flex;
		flex-direction: column;
		gap: 2px;
	}

	.editor-item-title {
		font-size: 13px;
		font-weight: 500;
		color: var(--color-text);
		line-height: 1.3;
		word-break: break-word;
	}

	.editor-item-processing-label {
		margin-left: 4px;
		font-size: 11px;
		color: var(--color-warning);
		font-weight: 400;
	}

	.editor-item-preview {
		display: -webkit-box;
		-webkit-line-clamp: 2;
		-webkit-box-orient: vertical;
		overflow: hidden;
		font-size: 12px;
		color: var(--color-text-muted);
		line-height: 1.3;
	}

	.editor-item-result {
		font-size: 11px;
	}

	.editor-item-result-ok {
		color: var(--color-success);
	}

	.editor-item-result-err {
		color: var(--color-error);
	}

	.editor-item-meta {
		display: flex;
		flex-direction: column;
		align-items: flex-end;
		gap: 2px;
		flex-shrink: 0;
	}

	.editor-item-source-count {
		font-size: 10px;
		color: var(--color-accent);
	}

	.editor-item-time {
		font-size: 10px;
		color: var(--color-text-dim);
	}

	.editor-item-actions {
		display: flex;
		gap: 4px;
		margin-top: 4px;
		justify-content: flex-end;
		opacity: 0;
		transition: opacity 0.15s;
	}

	.editor-item:hover .editor-item-actions {
		opacity: 1;
	}

	.editor-item-action {
		font-size: 11px;
		padding: 2px 6px;
		border-radius: 4px;
		border: none;
		background: transparent;
		color: var(--color-text-muted);
		cursor: pointer;
		text-decoration: none;
	}

	.editor-item-action:hover {
		background: var(--color-surface-hover);
		color: var(--color-text-muted);
	}

	.editor-item-action-danger {
		color: var(--color-error);
	}

	.editor-item-action-danger:hover {
		background: var(--color-surface);
		color: var(--color-error);
	}

	/* ================================================================= */
	/* Actions bar (create, publish)                                      */
	/* ================================================================= */
	.editor-actions {
		display: flex;
		gap: 6px;
		padding: 8px;
		flex-shrink: 0;
		border-top: 1px solid var(--color-border);
		flex-wrap: wrap;
	}

	.editor-new-btn {
		padding: 6px 10px;
		border: none;
		background: transparent;
		font-size: 12px;
		color: var(--color-text-muted);
		cursor: pointer;
		border-radius: 6px;
		transition: background 0.1s, color 0.1s;
	}

	.editor-new-btn:hover {
		background: var(--color-surface-hover);
		color: var(--color-text-muted);
	}

	.editor-publish-btn {
		padding: 6px 10px;
		border: none;
		border-radius: 6px;
		font-size: 12px;
		font-weight: 500;
		background: var(--color-success);
		color: white;
		cursor: pointer;
		transition: background 0.1s;
	}

	.editor-publish-btn:hover {
		background: var(--color-success);
	}

	.editor-create-form {
		display: flex;
		flex-direction: column;
		gap: 4px;
		flex: 1;
	}

	.editor-create-input {
		width: 100%;
		padding: 6px 8px;
		border: 1px solid var(--color-border);
		border-radius: 4px;
		font-size: 12px;
		outline: none;
		box-sizing: border-box;
	}

	.editor-create-input:focus {
		border-color: var(--color-accent);
	}

	.editor-create-buttons {
		display: flex;
		gap: 4px;
	}

	.editor-create-btn {
		padding: 4px 10px;
		border: none;
		border-radius: 4px;
		font-size: 11px;
		font-weight: 500;
		background: var(--color-accent);
		color: white;
		cursor: pointer;
	}

	.editor-create-btn:hover {
		background: var(--color-accent-hover);
	}

	.editor-create-btn:disabled {
		opacity: 0.5;
		cursor: default;
	}

	.editor-cancel-btn {
		padding: 4px 10px;
		border: none;
		border-radius: 4px;
		font-size: 11px;
		background: transparent;
		color: var(--color-text-muted);
		cursor: pointer;
	}

	.editor-cancel-btn:hover {
		background: var(--color-surface-hover);
	}

	/* ================================================================= */
	/* Publish picker                                                     */
	/* ================================================================= */
	.editor-publish-picker {
		margin: 0 8px 8px;
		padding: 10px;
		border-radius: 6px;
		border: 1px solid var(--color-success);
		background: var(--color-surface);
	}

	.editor-publish-picker-title {
		font-size: 12px;
		font-weight: 600;
		color: var(--color-success);
		margin-bottom: 6px;
	}

	.editor-publish-picker-empty {
		font-size: 12px;
		color: var(--color-success);
	}

	.editor-publish-picker-list {
		max-height: 160px;
		overflow-y: auto;
		list-style: none;
		padding: 0;
		margin: 0;
		display: flex;
		flex-direction: column;
		gap: 2px;
	}

	.editor-publish-picker-item {
		display: flex;
		align-items: center;
		gap: 6px;
		width: 100%;
		padding: 6px 8px;
		border: none;
		border-radius: 4px;
		background: transparent;
		text-align: left;
		font-size: 12px;
		cursor: pointer;
	}

	.editor-publish-picker-item:hover {
		background: var(--color-surface-hover);
	}

	.editor-publish-picker-name {
		font-weight: 500;
		color: var(--color-text);
	}

	.editor-publish-picker-preview {
		font-size: 11px;
		color: var(--color-text-muted);
	}

	/* ================================================================= */
	/* EDITOR VIEW                                                        */
	/* ================================================================= */
	.editor-view {
		display: flex;
		flex-direction: column;
		height: 100%;
		min-height: 0;
		overflow: hidden;
	}

	.editor-toolbar {
		display: flex;
		align-items: center;
		justify-content: space-between;
		padding: 6px 10px;
		flex-shrink: 0;
		border-bottom: 1px solid var(--color-border);
		gap: 8px;
	}

	.editor-back-btn {
		border: none;
		background: transparent;
		font-size: 12px;
		color: var(--color-text-muted);
		cursor: pointer;
		padding: 4px 6px;
		border-radius: 4px;
		flex-shrink: 0;
	}

	.editor-back-btn:hover {
		background: var(--color-surface-hover);
		color: var(--color-text-muted);
	}

	.editor-toolbar-right {
		display: flex;
		align-items: center;
		gap: 6px;
	}

	.editor-sources-btn {
		border: 1px solid var(--color-border);
		background: transparent;
		font-size: 11px;
		color: var(--color-accent);
		cursor: pointer;
		padding: 3px 8px;
		border-radius: 4px;
		transition: background 0.1s;
	}

	.editor-sources-btn:hover {
		background: var(--color-surface);
	}

	.editor-sources-btn-active {
		background: var(--color-surface-hover);
		border-color: var(--color-accent);
	}

	.editor-save-btn {
		border: none;
		background: var(--color-accent);
		color: white;
		font-size: 11px;
		font-weight: 500;
		cursor: pointer;
		padding: 4px 10px;
		border-radius: 4px;
		transition: background 0.1s;
	}

	.editor-save-btn:hover {
		background: var(--color-accent-hover);
	}

	.editor-save-btn:disabled {
		opacity: 0.5;
		cursor: default;
	}

	.editor-save-msg {
		font-size: 11px;
	}

	.editor-save-ok {
		color: var(--color-success);
	}

	.editor-save-error {
		color: var(--color-error);
	}

	.editor-body {
		display: flex;
		flex: 1;
		min-height: 0;
		overflow: hidden;
	}

	.editor-content-area {
		display: flex;
		flex-direction: column;
		flex: 1;
		min-width: 0;
		padding: 10px;
		overflow: hidden;
	}

	.editor-title-input {
		width: 100%;
		padding: 6px 4px;
		border: none;
		border-bottom: 1px solid var(--color-border);
		font-size: 16px;
		font-weight: 600;
		color: var(--color-text);
		outline: none;
		box-sizing: border-box;
		flex-shrink: 0;
		background: transparent;
	}

	.editor-title-input:focus {
		border-bottom-color: var(--color-accent);
	}

	.editor-textarea {
		flex: 1;
		width: 100%;
		padding: 10px 4px;
		border: none;
		font-size: 14px;
		font-family: inherit;
		color: var(--color-text-muted);
		line-height: 1.6;
		outline: none;
		resize: none;
		background: transparent;
		box-sizing: border-box;
		min-height: 0;
	}

	/* ================================================================= */
	/* Sources panel (sidebar in editor view)                             */
	/* ================================================================= */
	.editor-sources-panel {
		width: 180px;
		flex-shrink: 0;
		border-left: 1px solid var(--color-border);
		padding: 10px;
		overflow-y: auto;
		background: var(--color-surface);
	}

	.editor-sources-title {
		font-size: 11px;
		font-weight: 600;
		color: var(--color-text-muted);
		text-transform: uppercase;
		letter-spacing: 0.03em;
		margin-bottom: 8px;
	}

	.editor-sources-list {
		list-style: none;
		padding: 0;
		margin: 0;
		display: flex;
		flex-direction: column;
		gap: 6px;
	}

	.editor-source-item {
		display: flex;
		align-items: flex-start;
		gap: 6px;
		padding: 4px;
		border-radius: 4px;
		transition: background 0.1s;
	}

	.editor-source-item:hover {
		background: var(--color-surface-hover);
	}

	.editor-source-icon {
		font-size: 14px;
		flex-shrink: 0;
		line-height: 1;
		margin-top: 1px;
	}

	.editor-source-info {
		display: flex;
		flex-direction: column;
		gap: 1px;
		min-width: 0;
	}

	.editor-source-name {
		font-size: 12px;
		font-weight: 500;
		color: var(--color-text-muted);
		word-break: break-word;
		line-height: 1.3;
	}

	.editor-source-context {
		font-size: 10px;
		color: var(--color-text-dim);
	}

	/* ================================================================= */
	/* Responsive within bounded container                                */
	/* ================================================================= */

	/* Small panels: hide sources sidebar, tighter spacing */
	@container (max-width: 400px) {
		.editor-sources-panel {
			display: none;
		}

		.editor-items {
			padding: 4px;
		}

		.editor-item {
			padding: 6px 8px;
		}

		.editor-item-title {
			font-size: 12px;
		}

		.editor-toolbar {
			padding: 4px 8px;
		}

		.editor-content-area {
			padding: 6px;
		}

		.editor-title-input {
			font-size: 14px;
		}

		.editor-textarea {
			font-size: 13px;
		}
	}

	/* Medium panels: narrower sources sidebar */
	@container (max-width: 600px) {
		.editor-sources-panel {
			width: 140px;
			padding: 8px;
		}

		.editor-list-header {
			padding: 6px 10px;
		}
	}

	/* Mobile viewport fallback */
	@media (max-width: 480px) {
		.editor-sources-panel {
			display: none;
		}

		.editor-items {
			padding: 4px;
		}

		.editor-item {
			padding: 6px 8px;
		}

		.editor-content-area {
			padding: 6px;
		}

		.editor-title-input {
			font-size: 14px;
		}

		.editor-textarea {
			font-size: 13px;
			line-height: 1.5;
		}

		.editor-actions {
			padding: 6px;
		}
	}
</style>
