/**
 * <folk-swag-designer> — Full-featured merch design tool.
 *
 * 4-tab layout:
 * - Browse: Product catalog with mockups, filtering, add-to-cart
 * - Create: AI generate, upload, manage designs
 * - HitherDither: Dithering tools + screen-print separations
 * - Orders: rCart order status + fulfillment tracking
 *
 * Demo mode: 4-step interactive flow (preserved from original).
 */

// --- Demo data (self-contained, zero API calls in demo mode) ---

interface DemoProduct {
	id: string;
	name: string;
	printArea: string;
	baseCost: string;
	printful: boolean;
	sizes?: string[];
	colors?: { id: string; name: string; hex: string }[];
}

interface DemoProvider {
	name: string;
	type: "cosmolocal" | "global";
	city: string;
	lat: number;
	lng: number;
	capabilities: string[];
	unitCost: number;
	turnaround: string;
}

const DEMO_PRODUCTS: DemoProduct[] = [
	{
		id: "tee", name: "T-Shirt", printArea: "305x406mm", baseCost: "$9.25-$13.25",
		printful: true,
		sizes: ["S", "M", "L", "XL", "2XL", "3XL"],
		colors: [
			{ id: "black", name: "Black", hex: "#0a0a0a" },
			{ id: "white", name: "White", hex: "#ffffff" },
			{ id: "forest_green", name: "Forest Green", hex: "#2d4a3e" },
			{ id: "heather_charcoal", name: "Heather Charcoal", hex: "#4a4a4a" },
			{ id: "maroon", name: "Maroon", hex: "#5a2d2d" },
		],
	},
	{
		id: "sticker", name: "Sticker Sheet", printArea: "210x297mm", baseCost: "$1.20-$1.50",
		printful: true,
	},
	{
		id: "poster", name: "Poster (A3)", printArea: "297x420mm", baseCost: "$4.50-$7.00",
		printful: false,
	},
	{
		id: "hoodie", name: "Hoodie", printArea: "356x406mm", baseCost: "$23.95-$27.95",
		printful: true,
		sizes: ["S", "M", "L", "XL", "2XL"],
		colors: [
			{ id: "black", name: "Black", hex: "#0a0a0a" },
			{ id: "dark_grey_heather", name: "Dark Grey Heather", hex: "#3a3a3a" },
		],
	},
];

const DEMO_PROVIDERS: DemoProvider[] = [
	{ name: "De Drukker Collective", type: "cosmolocal", city: "Amsterdam", lat: 52.37, lng: 4.90, capabilities: ["dtg-print", "vinyl-cut", "screen-print"], unitCost: 8.50, turnaround: "3-4 days" },
	{ name: "Kuona Print Collective", type: "cosmolocal", city: "Nairobi", lat: -1.29, lng: 36.82, capabilities: ["dtg-print", "screen-print", "vinyl-cut"], unitCost: 5.50, turnaround: "5-7 days" },
	{ name: "Grafica Popular", type: "cosmolocal", city: "Sao Paulo", lat: -23.55, lng: -46.63, capabilities: ["screen-print", "risograph", "inkjet-print"], unitCost: 6.00, turnaround: "4-6 days" },
	{ name: "Printful", type: "global", city: "Global", lat: 0, lng: 0, capabilities: ["dtg-print", "vinyl-cut", "inkjet-print"], unitCost: 9.25, turnaround: "5-7 days" },
];

const DEMO_BUYER = { lat: 52.52, lng: 13.41 }; // Berlin

function haversineKm(lat1: number, lng1: number, lat2: number, lng2: number): number {
	const R = 6371;
	const dLat = (lat2 - lat1) * Math.PI / 180;
	const dLng = (lng2 - lng1) * Math.PI / 180;
	const a = Math.sin(dLat / 2) ** 2 + Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLng / 2) ** 2;
	return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}

// --- SVG generators ---

function cosmoDesignSvg(): string {
	return `<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
		<rect width="200" height="200" fill="#0f172a"/>
		<circle cx="100" cy="100" r="70" fill="none" stroke="#6366f1" stroke-width="1.5" opacity="0.4"/>
		<circle cx="100" cy="100" r="50" fill="none" stroke="#818cf8" stroke-width="1.5" opacity="0.6"/>
		<circle cx="100" cy="100" r="30" fill="none" stroke="#a5b4fc" stroke-width="1.5" opacity="0.8"/>
		<circle cx="100" cy="100" r="10" fill="#6366f1"/>
		${Array.from({length: 12}, (_, i) => {
			const a = i * 30 * Math.PI / 180;
			return `<line x1="${100 + 15 * Math.cos(a)}" y1="${100 + 15 * Math.sin(a)}" x2="${100 + 75 * Math.cos(a)}" y2="${100 + 75 * Math.sin(a)}" stroke="#818cf8" stroke-width="1" opacity="0.3"/>`;
		}).join("")}
		<circle cx="100" cy="100" r="5" fill="#e2e8f0"/>
		<text x="100" y="160" text-anchor="middle" fill="#94a3b8" font-size="8" font-family="system-ui">COSMOLOCAL</text>
		<text x="100" y="170" text-anchor="middle" fill="#64748b" font-size="6" font-family="system-ui">NETWORK</text>
	</svg>`;
}

function teeMockupSvg(color: string): string {
	return `<svg viewBox="0 0 240 300" xmlns="http://www.w3.org/2000/svg">
		<path d="M60,40 L30,60 L10,120 L40,130 L50,90 L50,280 L190,280 L190,90 L200,130 L230,120 L210,60 L180,40 L160,55 Q140,70 120,70 Q100,70 80,55 Z" fill="${color}" stroke="#475569" stroke-width="1.5"/>
		<defs><clipPath id="tee-clip"><rect x="80" y="90" width="80" height="107" rx="4"/></clipPath></defs>
		<g clip-path="url(#tee-clip)" transform="translate(80,90)">
			<svg viewBox="0 0 200 200" width="80" height="80" y="13">${cosmoDesignSvg().replace(/<svg[^>]*>/, "").replace("</svg>", "")}</svg>
		</g>
		<rect x="80" y="90" width="80" height="107" rx="4" fill="none" stroke="#6366f1" stroke-width="0.75" stroke-dasharray="3,2" opacity="0.5"/>
	</svg>`;
}

function hoodieMockupSvg(color: string): string {
	return `<svg viewBox="0 0 260 310" xmlns="http://www.w3.org/2000/svg">
		<path d="M65,50 L30,70 L5,140 L40,150 L55,100 L55,290 L205,290 L205,100 L220,150 L255,140 L230,70 L195,50 L175,60 Q155,80 130,85 Q105,80 85,60 Z" fill="${color}" stroke="#475569" stroke-width="1.5"/>
		<path d="M85,60 Q105,30 130,25 Q155,30 175,60 Q155,80 130,85 Q105,80 85,60 Z" fill="${color}" stroke="#475569" stroke-width="1"/>
		<ellipse cx="130" cy="55" rx="20" ry="15" fill="#1e293b" stroke="#475569" stroke-width="1"/>
		<defs><clipPath id="hoodie-clip"><rect x="80" y="100" width="100" height="115" rx="4"/></clipPath></defs>
		<g clip-path="url(#hoodie-clip)" transform="translate(80,100)">
			<svg viewBox="0 0 200 200" width="100" height="100" y="8">${cosmoDesignSvg().replace(/<svg[^>]*>/, "").replace("</svg>", "")}</svg>
		</g>
		<rect x="80" y="100" width="100" height="115" rx="4" fill="none" stroke="#6366f1" stroke-width="0.75" stroke-dasharray="3,2" opacity="0.5"/>
	</svg>`;
}

function stickerMockupSvg(): string {
	return `<svg viewBox="0 0 240 240" xmlns="http://www.w3.org/2000/svg">
		<rect x="20" y="20" width="200" height="200" rx="16" fill="#1e293b" stroke="#475569" stroke-width="1.5"/>
		<rect x="30" y="30" width="180" height="180" rx="12" fill="none" stroke="#6366f1" stroke-width="1" stroke-dasharray="4,3" opacity="0.5"/>
		<g transform="translate(40,40)">
			<svg viewBox="0 0 200 200" width="160" height="160">${cosmoDesignSvg().replace(/<svg[^>]*>/, "").replace("</svg>", "")}</svg>
		</g>
		<text x="120" y="232" text-anchor="middle" fill="#64748b" font-size="7" font-family="system-ui">kiss-cut border</text>
	</svg>`;
}

function posterMockupSvg(): string {
	return `<svg viewBox="0 0 220 300" xmlns="http://www.w3.org/2000/svg">
		<rect x="15" y="15" width="190" height="270" fill="#1e293b" stroke="#475569" stroke-width="2"/>
		<rect x="25" y="25" width="170" height="250" fill="none" stroke="#334155" stroke-width="1"/>
		<g transform="translate(40,50)">
			<svg viewBox="0 0 200 200" width="140" height="140">${cosmoDesignSvg().replace(/<svg[^>]*>/, "").replace("</svg>", "")}</svg>
		</g>
		<text x="110" y="230" text-anchor="middle" fill="#94a3b8" font-size="10" font-family="system-ui" font-weight="600">COSMOLOCAL NETWORK</text>
		<text x="110" y="245" text-anchor="middle" fill="#64748b" font-size="7" font-family="system-ui">A3 - 297x420mm - 300 DPI</text>
	</svg>`;
}

// --- Component ---

import { TourEngine } from "../../../shared/tour-engine";
import { SwagLocalFirstClient } from "../local-first-client";
import type { SwagDoc, SwagDesign } from "../schemas";
import { swagSchema, swagDocId } from "../schemas";
import { startPresenceHeartbeat } from '../../../shared/collab-presence';
import type { DocumentId } from "../../../shared/local-first/document";
import { ViewHistory } from "../../../shared/view-history.js";

// Auth helpers
function getSession(): { accessToken: string; claims: { sub: string; did?: string; username?: string } } | null {
	try {
		const raw = localStorage.getItem("encryptid_session");
		if (!raw) return null;
		const s = JSON.parse(raw);
		return s?.accessToken ? s : null;
	} catch { return null; }
}
function getMyDid(): string | null {
	const s = getSession();
	if (!s) return null;
	return (s.claims as any).did || s.claims.sub;
}

// Dithering algorithms for the HitherDither tab
const DITHER_ALGORITHMS = [
	{ id: "floyd-steinberg", name: "Floyd-Steinberg", group: "Error Diffusion" },
	{ id: "atkinson", name: "Atkinson", group: "Error Diffusion" },
	{ id: "stucki", name: "Stucki", group: "Error Diffusion" },
	{ id: "burkes", name: "Burkes", group: "Error Diffusion" },
	{ id: "sierra", name: "Sierra", group: "Error Diffusion" },
	{ id: "sierra-two-row", name: "Sierra Two-Row", group: "Error Diffusion" },
	{ id: "sierra-lite", name: "Sierra Lite", group: "Error Diffusion" },
	{ id: "jarvis-judice-ninke", name: "Jarvis-Judice-Ninke", group: "Error Diffusion" },
	{ id: "bayer", name: "Bayer (Ordered)", group: "Ordered" },
	{ id: "ordered", name: "Ordered", group: "Ordered" },
	{ id: "cluster-dot", name: "Cluster Dot", group: "Ordered" },
];

type TabId = "browse" | "create" | "dither" | "orders";

class FolkSwagDesigner extends HTMLElement {
	private shadow: ShadowRoot;
	private space = "";

	// Tab state
	private activeTab: TabId = "browse";
	private _history = new ViewHistory<TabId>("browse", "rswag");

	// Demo state
	private selectedProduct = "tee";
	private selectedSize = "M";
	private selectedColor = "black";
	private imageFile: File | null = null;
	private imagePreview = "";
	private designTitle = "";
	private generating = false;
	private artifact: any = null;
	private error = "";
	private demoStep: 1 | 2 | 3 | 4 = 1;
	private progressStep = 0;
	private usedSampleDesign = false;

	// Browse tab state
	private catalogProducts: any[] = [];
	private catalogLoading = false;
	private catalogSearch = "";
	private catalogCategory = "";

	// Create tab state
	private createMode: "ai" | "upload" | "designs" = "designs";
	private aiConcept = "";
	private aiName = "";
	private aiTags = "";
	private aiGenerating = false;
	private uploadFile: File | null = null;
	private uploadPreview = "";
	private uploadName = "";
	private uploadDescription = "";
	private myDesigns: any[] = [];

	// Dither tab state
	private ditherDesignSlug = "";
	private ditherAlgorithm = "floyd-steinberg";
	private ditherNumColors = 8;
	private ditherPreviewUrl = "";
	private ditherLoading = false;
	private ditherColors: string[] = [];
	private separationData: any = null;

	// Orders tab state
	private orders: any[] = [];

	// Multiplayer state
	private lfClient: SwagLocalFirstClient | null = null;
	private _lfcUnsub: (() => void) | null = null;
	private _stopPresence: (() => void) | null = null;
	private _subscribedDocIds: string[] = [];
	private sharedDesigns: SwagDesign[] = [];

	private _tour!: TourEngine;
	private static readonly TOUR_STEPS = [
		{ target: '.product', title: "Choose Product", message: "Select a product type - tee, sticker, poster, or hoodie.", advanceOnClick: true },
		{ target: '.steps-bar', title: "Design Flow", message: "Follow the 4-step flow: Product, Design, Generate, Pipeline.", advanceOnClick: false },
		{ target: '.sample-btn', title: "Sample Design", message: "Try the demo with a pre-made sample design to see the full pipeline.", advanceOnClick: true },
		{ target: '.generate-btn', title: "Generate", message: "Generate print-ready files and see provider matching + revenue splits.", advanceOnClick: false },
	];

	constructor() {
		super();
		this.shadow = this.attachShadow({ mode: "open" });
		this._tour = new TourEngine(
			this.shadow,
			FolkSwagDesigner.TOUR_STEPS,
			"rswag_tour_done",
			() => this.shadow.host as HTMLElement,
		);
	}

	connectedCallback() {
		this.space = this.getAttribute("space") || "";
		if (this.space === "demo") {
			this.selectedProduct = "tee";
			this.selectedSize = "M";
			this.selectedColor = "black";
			this.designTitle = "Cosmolocal Network Tee";
			this.demoStep = 1;
			this.render();
		} else {
			this.initMultiplayer();
			this.loadCatalog();
			this.loadMyDesigns();
			this.render();
		}
		if (!localStorage.getItem("rswag_tour_done")) {
			setTimeout(() => this._tour.start(), 1200);
		}
		this._stopPresence = startPresenceHeartbeat(() => ({ module: 'rswag', context: 'Swag Designer' }));
		window.addEventListener('rspace-view-restored', this._onViewRestored as EventListener);
	}

	disconnectedCallback() {
		this._history.destroy();
		window.removeEventListener('rspace-view-restored', this._onViewRestored as EventListener);
		this._stopPresence?.();
		this._lfcUnsub?.();
		this._lfcUnsub = null;
		this.lfClient?.disconnect();
		const runtime = (window as any).__rspaceOfflineRuntime;
		if (runtime) {
			for (const id of this._subscribedDocIds) runtime.unsubscribe(id);
		}
		this._subscribedDocIds = [];
	}

	private _onViewRestored = (e: CustomEvent) => {
		if (e.detail?.moduleId !== 'rswag') return;
		this.activeTab = e.detail.view;
		this.render();
	};

	private async initMultiplayer() {
		try {
			this.lfClient = new SwagLocalFirstClient(this.space);
			await this.lfClient.init();
			await this.lfClient.subscribe();

			this._lfcUnsub = this.lfClient.onChange((doc) => {
				this.extractDesigns(doc);
				this.render();
			});

			const doc = this.lfClient.getDoc();
			if (doc) this.extractDesigns(doc);
			this.render();
		} catch (err) {
			console.warn('[rSwag] Local-first init failed:', err);
		}
		this.subscribeCollabOverlay();
	}

	private async subscribeCollabOverlay() {
		const runtime = (window as any).__rspaceOfflineRuntime;
		if (!runtime?.isInitialized) return;
		try {
			const docId = swagDocId(this.space) as DocumentId;
			await runtime.subscribe(docId, swagSchema);
			this._subscribedDocIds.push(docId);
		} catch { /* runtime unavailable */ }
	}

	private extractDesigns(doc: SwagDoc) {
		this.sharedDesigns = doc.designs
			? Object.values(doc.designs).sort((a, b) => b.updatedAt - a.updatedAt)
			: [];
	}

	private saveDesignToSync(artifactId: string) {
		if (!this.lfClient) return;
		const design: SwagDesign = {
			id: crypto.randomUUID(),
			title: this.designTitle || 'Untitled Design',
			productType: this.selectedProduct as SwagDesign['productType'],
			artifactId,
			source: 'artifact',
			status: 'active',
			imageUrl: null,
			products: [],
			slug: null,
			description: null,
			tags: [],
			createdBy: getMyDid(),
			createdAt: Date.now(),
			updatedAt: Date.now(),
		};
		this.lfClient.saveDesign(design);
	}

	private deleteSharedDesign(designId: string) {
		if (!this.lfClient) return;
		if (confirm('Delete this design?')) {
			this.lfClient.deleteDesign(designId);
		}
	}

	private getApiBase(): string {
		const path = window.location.pathname;
		const match = path.match(/^(\/[^/]+)?\/rswag/);
		return match ? match[0] : "/rswag";
	}

	// ── Data loading ──

	private async loadCatalog() {
		this.catalogLoading = true;
		try {
			const params = new URLSearchParams();
			if (this.catalogSearch) params.set("q", this.catalogSearch);
			if (this.catalogCategory) params.set("category", this.catalogCategory);
			const resp = await fetch(`${this.getApiBase()}/api/storefront?${params}`);
			if (resp.ok) {
				const data = await resp.json();
				this.catalogProducts = data.products || [];
			}
		} catch (e) {
			console.warn("[rSwag] Failed to load catalog:", e);
		}
		this.catalogLoading = false;
		this.render();
	}

	private async loadMyDesigns() {
		try {
			const resp = await fetch(`${this.getApiBase()}/api/designs`);
			if (resp.ok) {
				const data = await resp.json();
				this.myDesigns = data.designs || [];
			}
		} catch (e) {
			console.warn("[rSwag] Failed to load designs:", e);
		}
	}

	// ── Demo mode methods (preserved) ──

	private getDemoProduct(): DemoProduct {
		return DEMO_PRODUCTS.find(p => p.id === this.selectedProduct) || DEMO_PRODUCTS[0];
	}

	private getAutoTitle(): string {
		const p = this.getDemoProduct();
		return `Cosmolocal Network ${p.name}`;
	}

	private demoSelectProduct(id: string) {
		this.selectedProduct = id;
		const p = this.getDemoProduct();
		this.selectedSize = p.sizes?.[1] || "";
		this.selectedColor = p.colors?.[0]?.id || "";
		this.designTitle = this.getAutoTitle();
		this.demoStep = 1;
		this.usedSampleDesign = false;
		this.artifact = null;
		this.render();
	}

	private demoUseSample() {
		this.usedSampleDesign = true;
		this.demoStep = 2;
		this.render();
	}

	private demoGenerate() {
		this.demoStep = 3;
		this.progressStep = 0;
		this.render();
		const steps = [1, 2, 3, 4];
		const delays = [400, 400, 400, 300];
		let elapsed = 0;
		for (const s of steps) {
			elapsed += delays[s - 1];
			setTimeout(() => {
				this.progressStep = s;
				if (s === 4) {
					this.buildDemoArtifact();
					this.demoStep = 4;
				}
				this.render();
			}, elapsed);
		}
	}

	private buildDemoArtifact() {
		const p = this.getDemoProduct();
		const dims: Record<string, { w: number; h: number }> = {
			tee: { w: 305, h: 406 }, sticker: { w: 210, h: 297 },
			poster: { w: 297, h: 420 }, hoodie: { w: 356, h: 406 },
		};
		const d = dims[p.id] || dims.tee;
		const wpx = Math.round((d.w / 25.4) * 300);
		const hpx = Math.round((d.h / 25.4) * 300);
		const capabilities: Record<string, string[]> = {
			tee: ["dtg-print"], sticker: ["vinyl-cut"], poster: ["inkjet-print"], hoodie: ["dtg-print"],
		};
		const substrates: Record<string, string[]> = {
			tee: ["cotton-standard", "cotton-organic"], sticker: ["vinyl-matte", "vinyl-gloss"],
			poster: ["paper-160gsm-cover", "paper-100gsm"], hoodie: ["cotton-polyester-blend"],
		};
		this.artifact = {
			title: this.designTitle || this.getAutoTitle(),
			product: p.id,
			spec: {
				product_type: p.id === "sticker" ? "sticker-sheet" : p.id,
				dimensions: { width_mm: d.w, height_mm: d.h },
				dpi: 300, color_space: "sRGB",
				required_capabilities: capabilities[p.id],
				substrates: substrates[p.id],
			},
			render_targets: {
				print: { format: "png", label: "Print-Ready PNG", dimensions: `${wpx}x${hpx} px`, url: "#demo-print" },
				preview: { format: "png", label: "Preview PNG", dimensions: `${Math.round(wpx / 4)}x${Math.round(hpx / 4)} px`, url: "#demo-preview" },
			},
			pricing: { creator_share: "35%", community_share: "15%", provider_share: "50%" },
			next_actions: [
				{ action: "ingest_to_rcart", label: "Send to rCart", url: "/rcart?tab=catalog" },
				{ action: "edit_design", label: "Edit Design" },
				{ action: "save_to_rfiles", label: "Save to rFiles" },
			],
			...(this.selectedSize ? { size: this.selectedSize } : {}),
			...(this.selectedColor ? { color: this.selectedColor } : {}),
		};
	}

	private getMatchedProviders(): (DemoProvider & { distance: number })[] {
		const p = this.getDemoProduct();
		const cap = p.id === "sticker" ? "vinyl-cut" : p.id === "poster" ? "inkjet-print" : "dtg-print";
		return DEMO_PROVIDERS
			.filter(prov => prov.capabilities.includes(cap))
			.map(prov => ({
				...prov,
				distance: prov.type === "global" ? Infinity : Math.round(haversineKm(DEMO_BUYER.lat, DEMO_BUYER.lng, prov.lat, prov.lng)),
			}))
			.sort((a, b) => a.distance - b.distance);
	}

	// ── Generate (full mode) ──

	private async generate() {
		if (this.space === "demo") { this.demoGenerate(); return; }
		if (!this.imageFile || this.generating) return;
		this.generating = true;
		this.error = "";
		this.artifact = null;
		this.render();
		try {
			const formData = new FormData();
			formData.append("image", this.imageFile);
			formData.append("product", this.selectedProduct);
			formData.append("title", this.designTitle || "Untitled Design");
			const res = await fetch(`${this.getApiBase()}/api/artifact`, { method: "POST", body: formData });
			if (!res.ok) {
				const err = await res.json();
				throw new Error(err.error || `Failed: ${res.status}`);
			}
			this.artifact = await res.json();
			if (this.artifact?.id) this.saveDesignToSync(this.artifact.id);
		} catch (e) {
			this.error = e instanceof Error ? e.message : "Generation failed";
		} finally {
			this.generating = false;
			this.render();
		}
	}

	// ── AI Generate ──

	private async aiGenerate() {
		if (this.aiGenerating || !this.aiConcept || !this.aiName) return;
		this.aiGenerating = true;
		this.error = "";
		this.render();
		try {
			const resp = await fetch(`${this.getApiBase()}/api/design/generate`, {
				method: "POST",
				headers: { "Content-Type": "application/json" },
				body: JSON.stringify({
					concept: this.aiConcept,
					name: this.aiName,
					tags: this.aiTags ? this.aiTags.split(",").map(t => t.trim()) : [],
					product_type: "sticker",
				}),
			});
			if (!resp.ok) {
				const err = await resp.json();
				throw new Error(err.error || `Failed: ${resp.status}`);
			}
			const result = await resp.json();
			this.error = "";
			this.aiConcept = "";
			this.aiName = "";
			this.aiTags = "";
			this.createMode = "designs";
			await this.loadMyDesigns();
		} catch (e) {
			this.error = e instanceof Error ? e.message : "AI generation failed";
		} finally {
			this.aiGenerating = false;
			this.render();
		}
	}

	// ── Upload ──

	private async uploadDesign() {
		if (!this.uploadFile) return;
		this.generating = true;
		this.error = "";
		this.render();
		try {
			const formData = new FormData();
			formData.append("image", this.uploadFile);
			formData.append("name", this.uploadName || "Untitled Upload");
			formData.append("description", this.uploadDescription || "");
			formData.append("product_type", "sticker");
			const resp = await fetch(`${this.getApiBase()}/api/design/upload`, { method: "POST", body: formData });
			if (!resp.ok) {
				const err = await resp.json();
				throw new Error(err.error || `Failed: ${resp.status}`);
			}
			this.uploadFile = null;
			this.uploadPreview = "";
			this.uploadName = "";
			this.uploadDescription = "";
			this.createMode = "designs";
			await this.loadMyDesigns();
		} catch (e) {
			this.error = e instanceof Error ? e.message : "Upload failed";
		} finally {
			this.generating = false;
			this.render();
		}
	}

	// ── Dither ──

	private async applyDither() {
		if (!this.ditherDesignSlug) return;
		this.ditherLoading = true;
		this.render();
		try {
			const params = new URLSearchParams({
				algorithm: this.ditherAlgorithm,
				num_colors: String(this.ditherNumColors),
				format: "json",
			});
			const resp = await fetch(`${this.getApiBase()}/api/designs/${this.ditherDesignSlug}/dither?${params}`);
			if (resp.ok) {
				const data = await resp.json();
				this.ditherPreviewUrl = data.image_url;
				this.ditherColors = data.colors_used || [];
			}
		} catch (e) {
			console.warn("[rSwag] Dither error:", e);
		}
		this.ditherLoading = false;
		this.render();
	}

	private async generateSeparations() {
		if (!this.ditherDesignSlug) return;
		this.ditherLoading = true;
		this.render();
		try {
			const resp = await fetch(`${this.getApiBase()}/api/designs/${this.ditherDesignSlug}/screen-print`, {
				method: "POST",
				headers: { "Content-Type": "application/json" },
				body: JSON.stringify({
					num_colors: this.ditherNumColors,
					algorithm: this.ditherAlgorithm,
				}),
			});
			if (resp.ok) {
				this.separationData = await resp.json();
			}
		} catch (e) {
			console.warn("[rSwag] Separation error:", e);
		}
		this.ditherLoading = false;
		this.render();
	}

	// ── Render ──

	private render() {
		if (this.space === "demo") {
			this.renderDemo();
		} else {
			this.renderFull();
		}
		this._tour.renderOverlay();
	}

	startTour() { this._tour.start(); }

	// ──── Full mode (4-tab layout) ────

	private renderFull() {
		const isLive = this.lfClient?.isConnected ?? false;

		this.shadow.innerHTML = `
		<style>${this.getFullStyles()}</style>

		${isLive ? `<div class="live-header"><span class="live-badge"><span class="live-dot"></span>LIVE</span></div>` : ''}

		<!-- Tab bar -->
		<div class="tab-bar">
			${(["browse", "create", "dither", "orders"] as TabId[]).map(tab => `
				<button class="tab-btn ${this.activeTab === tab ? 'active' : ''}" data-tab="${tab}">
					${tab === "browse" ? "Browse" : tab === "create" ? "Create" : tab === "dither" ? "HitherDither" : "Orders"}
				</button>
			`).join("")}
		</div>

		<!-- Tab content -->
		<div class="tab-content">
			${this.activeTab === "browse" ? this.renderBrowseTab() : ""}
			${this.activeTab === "create" ? this.renderCreateTab() : ""}
			${this.activeTab === "dither" ? this.renderDitherTab() : ""}
			${this.activeTab === "orders" ? this.renderOrdersTab() : ""}
		</div>
		`;

		this.bindFullEvents();
	}

	// ── Browse Tab ──

	private renderBrowseTab(): string {
		return `
		<div class="browse-controls">
			<input class="search-input" type="text" placeholder="Search products..." value="${this.esc(this.catalogSearch)}">
			<select class="category-select">
				<option value="">All Categories</option>
				<option value="stickers" ${this.catalogCategory === 'stickers' ? 'selected' : ''}>Stickers</option>
				<option value="apparel" ${this.catalogCategory === 'apparel' ? 'selected' : ''}>Apparel</option>
				<option value="prints" ${this.catalogCategory === 'prints' ? 'selected' : ''}>Prints</option>
			</select>
		</div>

		${this.catalogLoading ? '<div class="loading">Loading catalog...</div>' : ''}

		${this.catalogProducts.length === 0 && !this.catalogLoading ? `
		<div class="empty-state">
			<p>No products yet. Switch to the <strong>Create</strong> tab to add designs!</p>
		</div>` : ''}

		<div class="product-grid">
			${this.catalogProducts.map(p => `
			<div class="catalog-card">
				<div class="catalog-img" style="background-image:url('${this.getApiBase()}${p.imageUrl}')"></div>
				<div class="catalog-body">
					<div class="catalog-name">${this.esc(p.name)}</div>
					<div class="catalog-meta">
						<span class="badge badge-${p.category}">${p.category}</span>
						<span class="catalog-price">$${p.basePrice.toFixed(2)}</span>
					</div>
					${p.variants?.length ? `<div class="catalog-variants">${p.variants.map((v: string) => `<span class="variant-chip">${v}</span>`).join("")}</div>` : ""}
					<button class="btn btn-primary btn-sm add-to-cart-btn" data-slug="${p.slug}">Add to Cart</button>
				</div>
			</div>`).join("")}
		</div>`;
	}

	// ── Create Tab ──

	private renderCreateTab(): string {
		return `
		<div class="create-modes">
			<button class="mode-btn ${this.createMode === 'ai' ? 'active' : ''}" data-mode="ai">AI Generate</button>
			<button class="mode-btn ${this.createMode === 'upload' ? 'active' : ''}" data-mode="upload">Upload</button>
			<button class="mode-btn ${this.createMode === 'designs' ? 'active' : ''}" data-mode="designs">My Designs</button>
		</div>

		${this.error ? `<div class="error">${this.esc(this.error)}</div>` : ""}

		${this.createMode === "ai" ? this.renderAiCreate() : ""}
		${this.createMode === "upload" ? this.renderUploadCreate() : ""}
		${this.createMode === "designs" ? this.renderMyDesigns() : ""}
		`;
	}

	private renderAiCreate(): string {
		return `
		<div class="create-section">
			<h3 class="section-title">AI Design Generator</h3>
			<p class="section-desc">Describe your design concept and let Gemini create it.</p>
			<input class="input-field ai-name-input" type="text" placeholder="Design name" value="${this.esc(this.aiName)}">
			<textarea class="input-field ai-concept-input" placeholder="Describe your design concept..." rows="3">${this.esc(this.aiConcept)}</textarea>
			<input class="input-field ai-tags-input" type="text" placeholder="Tags (comma-separated)" value="${this.esc(this.aiTags)}">
			<button class="btn btn-primary ai-generate-btn" ${this.aiGenerating || !this.aiConcept || !this.aiName ? 'disabled' : ''}>
				${this.aiGenerating ? 'Generating...' : 'Generate Design'}
			</button>
		</div>`;
	}

	private renderUploadCreate(): string {
		return `
		<div class="create-section">
			<h3 class="section-title">Upload Artwork</h3>
			<p class="section-desc">Upload PNG, JPEG, or WebP (min 500x500, max 10MB).</p>
			<div class="upload-area ${this.uploadPreview ? 'has-image' : ''}">
				${this.uploadPreview
					? `<img class="preview-img" src="${this.uploadPreview}" alt="Preview">`
					: `<div class="upload-label">Click or drag to upload artwork</div>`}
				<input type="file" class="upload-file-input" accept="image/png,image/jpeg,image/webp">
			</div>
			<input class="input-field upload-name-input" type="text" placeholder="Design name" value="${this.esc(this.uploadName)}">
			<input class="input-field upload-desc-input" type="text" placeholder="Description (optional)" value="${this.esc(this.uploadDescription)}">
			<button class="btn btn-primary upload-submit-btn" ${!this.uploadFile || this.generating ? 'disabled' : ''}>
				${this.generating ? 'Uploading...' : 'Upload Design'}
			</button>
		</div>`;
	}

	private renderMyDesigns(): string {
		return `
		<div class="create-section">
			<h3 class="section-title">My Designs</h3>
			${this.myDesigns.length === 0 ? `<p class="section-desc">No designs yet. Use AI Generate or Upload to create one.</p>` : ""}
			<div class="designs-grid">
				${this.myDesigns.map(d => `
				<div class="design-card">
					<div class="design-img" style="background-image:url('${this.getApiBase()}${d.image_url}')"></div>
					<div class="design-body">
						<div class="design-name">${this.esc(d.name)}</div>
						<div class="design-meta">
							<span class="badge badge-${d.status}">${d.status}</span>
							<span class="badge badge-source">${d.source}</span>
						</div>
						<div class="design-actions">
							${d.status === "draft" ? `<button class="btn btn-sm btn-primary activate-btn" data-slug="${d.slug}">Activate</button>` : ""}
							${d.status === "draft" ? `<button class="btn btn-sm btn-danger delete-design-btn" data-slug="${d.slug}">Delete</button>` : ""}
							<button class="btn btn-sm btn-secondary dither-btn" data-slug="${d.slug}">Dither</button>
						</div>
					</div>
				</div>`).join("")}
			</div>
		</div>

		${this.sharedDesigns.length > 0 ? `
		<div class="create-section" style="margin-top:1.5rem">
			<h3 class="section-title">Space Designs (Multiplayer)</h3>
			<div class="designs-grid">
				${this.sharedDesigns.map(d => `
				<div class="design-card">
					<div class="design-card-icon">${this.productIcon(d.productType)}</div>
					<div class="design-body">
						<div class="design-name">${this.esc(d.title)}</div>
						<div class="design-meta">${d.productType}${d.artifactId ? ' - ready' : ''}</div>
						<div class="design-actions">
							${d.artifactId ? `<a class="btn btn-sm btn-primary" href="${this.getApiBase()}/api/artifact/${d.artifactId}/file" target="_blank">Download</a>` : ''}
							${d.createdBy === getMyDid() ? `<button class="btn btn-sm btn-danger shared-delete-btn" data-id="${d.id}">Delete</button>` : ''}
						</div>
					</div>
				</div>`).join("")}
			</div>
		</div>` : ""}`;
	}

	// ── HitherDither Tab ──

	private renderDitherTab(): string {
		const designOptions = this.myDesigns.map(d => `<option value="${d.slug}" ${this.ditherDesignSlug === d.slug ? 'selected' : ''}>${d.name}</option>`).join("");

		return `
		<div class="dither-section">
			<h3 class="section-title">HitherDither</h3>
			<p class="section-desc">Apply dithering algorithms for screen printing and artistic effects.</p>

			<div class="dither-controls">
				<div class="control-group">
					<label>Design</label>
					<select class="dither-design-select">
						<option value="">Select a design...</option>
						${designOptions}
					</select>
				</div>
				<div class="control-group">
					<label>Algorithm</label>
					<select class="dither-algo-select">
						${DITHER_ALGORITHMS.map(a => `<option value="${a.id}" ${this.ditherAlgorithm === a.id ? 'selected' : ''}>${a.name} (${a.group})</option>`).join("")}
					</select>
				</div>
				<div class="control-group">
					<label>Colors (2-32)</label>
					<input type="range" class="dither-colors-range" min="2" max="32" value="${this.ditherNumColors}">
					<span class="range-value">${this.ditherNumColors}</span>
				</div>
			</div>

			<div class="dither-actions">
				<button class="btn btn-primary apply-dither-btn" ${!this.ditherDesignSlug || this.ditherLoading ? 'disabled' : ''}>
					${this.ditherLoading ? 'Processing...' : 'Apply Dither'}
				</button>
				<button class="btn btn-secondary generate-separations-btn" ${!this.ditherDesignSlug || this.ditherLoading ? 'disabled' : ''}>
					Screen-Print Separations
				</button>
			</div>

			${this.ditherPreviewUrl ? `
			<div class="dither-preview">
				<h4>Dithered Preview</h4>
				<div class="preview-row">
					<div class="preview-original">
						<img src="${this.getApiBase()}/api/designs/${this.ditherDesignSlug}/image" alt="Original">
						<span>Original</span>
					</div>
					<div class="preview-arrow">&rarr;</div>
					<div class="preview-dithered">
						<img src="${this.getApiBase()}${this.ditherPreviewUrl}" alt="Dithered">
						<span>${this.ditherAlgorithm}</span>
					</div>
				</div>
				${this.ditherColors.length ? `
				<div class="palette-row">
					<span class="palette-label">Palette:</span>
					${this.ditherColors.map(c => `<span class="palette-swatch" style="background:#${c}" title="#${c}"></span>`).join("")}
				</div>` : ""}
				<a class="btn btn-sm btn-secondary" href="${this.getApiBase()}${this.ditherPreviewUrl}" download="${this.ditherDesignSlug}-${this.ditherAlgorithm}.png">Download Dithered PNG</a>
			</div>` : ""}

			${this.separationData ? `
			<div class="separations-preview">
				<h4>Screen-Print Separations</h4>
				<div class="sep-grid">
					<div class="sep-card">
						<img src="${this.getApiBase()}${this.separationData.composite_url}" alt="Composite">
						<span>Composite</span>
					</div>
					${(this.separationData.colors || []).map((color: string) => `
					<div class="sep-card">
						<img src="${this.getApiBase()}${this.separationData.separation_urls[color]}" alt="${color}">
						<span class="sep-color"><span class="palette-swatch" style="background:#${color}"></span> #${color}</span>
					</div>`).join("")}
				</div>
			</div>` : ""}
		</div>`;
	}

	// ── Orders Tab ──

	private renderOrdersTab(): string {
		return `
		<div class="orders-section">
			<h3 class="section-title">Orders</h3>
			<p class="section-desc">Orders containing rSwag products are shown here via rCart.</p>
			${this.orders.length === 0 ? `
			<div class="empty-state">
				<p>No orders yet. Browse products and add them to your cart to get started.</p>
				<button class="btn btn-primary" data-tab="browse">Browse Products</button>
			</div>` : `
			<div class="orders-list">
				${this.orders.map(o => `
				<div class="order-card">
					<div class="order-id">${o.id}</div>
					<div class="order-status"><span class="badge badge-${o.status}">${o.status}</span></div>
					${o.tracking ? `<a href="${o.tracking.url}" target="_blank" class="order-tracking">Track shipment</a>` : ""}
				</div>`).join("")}
			</div>`}
		</div>`;
	}

	// ── Event binding (full mode) ──

	private bindFullEvents() {
		// Tab switching
		this.shadow.querySelectorAll<HTMLElement>(".tab-btn").forEach(btn => {
			btn.addEventListener("click", () => {
				const tab = btn.dataset.tab as TabId;
				this._history.push(tab);
				this.activeTab = tab;
				this.render();
			});
		});

		// Orders tab button in empty state
		this.shadow.querySelectorAll<HTMLElement>("[data-tab]").forEach(btn => {
			if (!btn.classList.contains("tab-btn")) {
				btn.addEventListener("click", () => {
					const tab = btn.dataset.tab as TabId;
					this._history.push(tab);
					this.activeTab = tab;
					this.render();
				});
			}
		});

		// Browse tab
		this.shadow.querySelector(".search-input")?.addEventListener("input", (e) => {
			this.catalogSearch = (e.target as HTMLInputElement).value;
			clearTimeout((this as any)._searchTimeout);
			(this as any)._searchTimeout = setTimeout(() => this.loadCatalog(), 300);
		});
		this.shadow.querySelector(".category-select")?.addEventListener("change", (e) => {
			this.catalogCategory = (e.target as HTMLSelectElement).value;
			this.loadCatalog();
		});
		this.shadow.querySelectorAll<HTMLElement>(".add-to-cart-btn").forEach(btn => {
			btn.addEventListener("click", () => {
				const slug = btn.dataset.slug;
				// TODO: integrate with rCart catalog ingest
				alert(`Added ${slug} to cart (rCart integration pending)`);
			});
		});

		// Create tab
		this.shadow.querySelectorAll<HTMLElement>(".mode-btn").forEach(btn => {
			btn.addEventListener("click", () => {
				this.createMode = btn.dataset.mode as "ai" | "upload" | "designs";
				this.error = "";
				this.render();
			});
		});

		// AI Generate
		this.shadow.querySelector(".ai-name-input")?.addEventListener("input", (e) => { this.aiName = (e.target as HTMLInputElement).value; });
		this.shadow.querySelector(".ai-concept-input")?.addEventListener("input", (e) => { this.aiConcept = (e.target as HTMLTextAreaElement).value; });
		this.shadow.querySelector(".ai-tags-input")?.addEventListener("input", (e) => { this.aiTags = (e.target as HTMLInputElement).value; });
		this.shadow.querySelector(".ai-generate-btn")?.addEventListener("click", () => this.aiGenerate());

		// Upload
		const uploadArea = this.shadow.querySelector(".upload-area");
		const uploadInput = this.shadow.querySelector(".upload-file-input") as HTMLInputElement;
		uploadArea?.addEventListener("click", () => uploadInput?.click());
		uploadInput?.addEventListener("change", () => {
			const file = uploadInput.files?.[0];
			if (file) {
				this.uploadFile = file;
				this.uploadPreview = URL.createObjectURL(file);
				this.render();
			}
		});
		this.shadow.querySelector(".upload-name-input")?.addEventListener("input", (e) => { this.uploadName = (e.target as HTMLInputElement).value; });
		this.shadow.querySelector(".upload-desc-input")?.addEventListener("input", (e) => { this.uploadDescription = (e.target as HTMLInputElement).value; });
		this.shadow.querySelector(".upload-submit-btn")?.addEventListener("click", () => this.uploadDesign());

		// My designs actions
		this.shadow.querySelectorAll<HTMLElement>(".activate-btn").forEach(btn => {
			btn.addEventListener("click", async () => {
				const slug = btn.dataset.slug;
				await fetch(`${this.getApiBase()}/api/design/${slug}/activate`, { method: "POST" });
				await this.loadMyDesigns();
				this.render();
			});
		});
		this.shadow.querySelectorAll<HTMLElement>(".delete-design-btn").forEach(btn => {
			btn.addEventListener("click", async () => {
				const slug = btn.dataset.slug;
				if (!confirm(`Delete design "${slug}"?`)) return;
				await fetch(`${this.getApiBase()}/api/design/${slug}`, { method: "DELETE" });
				await this.loadMyDesigns();
				this.render();
			});
		});
		this.shadow.querySelectorAll<HTMLElement>(".dither-btn").forEach(btn => {
			btn.addEventListener("click", () => {
				this.ditherDesignSlug = btn.dataset.slug || "";
				this._history.push("dither");
				this.activeTab = "dither";
				this.render();
			});
		});
		this.shadow.querySelectorAll<HTMLElement>(".shared-delete-btn").forEach(btn => {
			btn.addEventListener("click", () => {
				const id = btn.dataset.id;
				if (id) this.deleteSharedDesign(id);
			});
		});

		// Dither tab
		this.shadow.querySelector(".dither-design-select")?.addEventListener("change", (e) => {
			this.ditherDesignSlug = (e.target as HTMLSelectElement).value;
			this.ditherPreviewUrl = "";
			this.separationData = null;
			this.render();
		});
		this.shadow.querySelector(".dither-algo-select")?.addEventListener("change", (e) => {
			this.ditherAlgorithm = (e.target as HTMLSelectElement).value;
		});
		this.shadow.querySelector(".dither-colors-range")?.addEventListener("input", (e) => {
			this.ditherNumColors = parseInt((e.target as HTMLInputElement).value, 10);
			const span = this.shadow.querySelector(".range-value");
			if (span) span.textContent = String(this.ditherNumColors);
		});
		this.shadow.querySelector(".apply-dither-btn")?.addEventListener("click", () => this.applyDither());
		this.shadow.querySelector(".generate-separations-btn")?.addEventListener("click", () => this.generateSeparations());
	}

	// ──── Demo mode rendering (preserved from original) ────

	private renderDemo() {
		const p = this.getDemoProduct();
		const isApparel = p.id === "tee" || p.id === "hoodie";

		this.shadow.innerHTML = `
		<style>${this.getDemoStyles()}</style>

		<div style="display:flex;justify-content:flex-end;margin-bottom:4px"><button id="btn-tour" style="background:transparent;border:1px solid rgba(255,255,255,0.15);color:var(--rs-text-secondary,#94a3b8);border-radius:6px;padding:2px 10px;font-size:0.78rem;cursor:pointer">Tour</button></div>

		<!-- Step indicators -->
		<div class="steps-bar">
			${[
				{ n: 1, label: "Product" },
				{ n: 2, label: "Design" },
				{ n: 3, label: "Generate" },
				{ n: 4, label: "Pipeline" },
			].map(s => `<div class="step-dot ${this.demoStep >= s.n ? 'active' : ''} ${this.demoStep === s.n ? 'current' : ''}">
				<span class="step-num">${this.demoStep > s.n ? '&#10003;' : s.n}</span>
				<span class="step-label">${s.label}</span>
			</div>`).join('<div class="step-line"></div>')}
		</div>

		<!-- Step 1: Product Selection -->
		<section class="step-section ${this.demoStep >= 1 ? 'visible' : ''}">
			<div class="products">
				${DEMO_PRODUCTS.map(dp => `
					<div class="product ${this.selectedProduct === dp.id ? 'active' : ''}" data-product="${dp.id}" data-collab-id="product:${dp.id}">
						<div class="product-icon">${this.productIcon(dp.id)}</div>
						<div class="product-name">${dp.name}</div>
						<div class="product-specs">${dp.printArea}</div>
						<div class="product-cost">${dp.baseCost}</div>
						${dp.printful ? '<span class="badge badge-printful">Printful</span>' : '<span class="badge badge-local">cosmolocal only</span>'}
					</div>
				`).join("")}
			</div>

			${isApparel && p.sizes ? `
			<div class="option-row">
				<span class="option-label">Size</span>
				<div class="pills">
					${p.sizes.map(s => `<button class="pill ${this.selectedSize === s ? 'active' : ''}" data-size="${s}">${s}</button>`).join("")}
				</div>
			</div>` : ""}

			${isApparel && p.colors ? `
			<div class="option-row">
				<span class="option-label">Color</span>
				<div class="color-swatches">
					${p.colors.map(c => `<button class="swatch ${this.selectedColor === c.id ? 'active' : ''}" data-color="${c.id}" style="background:${c.hex}" title="${c.name}"></button>`).join("")}
				</div>
			</div>` : ""}
		</section>

		<!-- Step 2: Design Mockup -->
		<section class="step-section visible">
			<div class="mockup-area">
				<div class="mockup-svg">${this.getMockupSvg()}</div>
				<div class="mockup-info">
					<h3 class="mockup-title">${this.esc(this.designTitle || this.getAutoTitle())}</h3>
					<div class="mockup-meta">${p.name} ${this.selectedSize ? `/ ${this.selectedSize}` : ""} ${this.selectedColor && p.colors ? `/ ${p.colors.find(c => c.id === this.selectedColor)?.name || ""}` : ""}</div>
					${!this.usedSampleDesign ? `<button class="btn btn-secondary sample-btn">Use Sample Design</button>` : '<span class="badge badge-ok">Sample design loaded</span>'}
				</div>
			</div>
		</section>

		${this.usedSampleDesign && this.demoStep < 3 ? `
		<div class="generate-row">
			<button class="btn btn-primary generate-btn">Generate Print-Ready Files</button>
		</div>` : ""}

		${this.demoStep >= 3 ? this.renderStep3() : ""}
		${this.demoStep >= 4 ? this.renderStep4() : ""}
		`;

		this.bindDemoEvents();
	}

	private renderStep3(): string {
		const progressLabels = ["Processing image...", "Generating artifact...", "Matching providers...", "Done!"];
		let html = `<section class="step-section visible">`;
		html += `<div class="progress-bar"><div class="progress-fill" style="width:${this.progressStep * 25}%"></div></div>
		<div class="progress-steps">${progressLabels.map((label, i) => `<span class="prog-label ${this.progressStep > i ? 'done' : this.progressStep === i + 1 ? 'active' : ''}">${this.progressStep > i + 1 ? '&#10003; ' : ''}${label}</span>`).join("")}</div>`;

		if (this.demoStep >= 4 && this.artifact) {
			const providers = this.getMatchedProviders();
			const selectedProvider = providers[0];
			const unitCost = selectedProvider?.unitCost || 9.25;
			const provAmt = (unitCost * 0.5).toFixed(2);
			const creatorAmt = (unitCost * 0.35).toFixed(2);
			const communityAmt = (unitCost * 0.15).toFixed(2);

			html += `
			<div class="artifact-card">
				<h3 class="artifact-heading">Artifact Envelope</h3>
				<div class="artifact-grid">
					<div class="artifact-field"><span class="af-label">Product</span><span class="af-value">${this.esc(this.artifact.spec.product_type)}</span></div>
					<div class="artifact-field"><span class="af-label">Dimensions</span><span class="af-value">${this.artifact.spec.dimensions.width_mm}x${this.artifact.spec.dimensions.height_mm}mm</span></div>
					<div class="artifact-field"><span class="af-label">DPI</span><span class="af-value">${this.artifact.spec.dpi}</span></div>
					<div class="artifact-field"><span class="af-label">Color Space</span><span class="af-value">${this.artifact.spec.color_space}</span></div>
					<div class="artifact-field"><span class="af-label">Capabilities</span><span class="af-value">${(this.artifact.spec.required_capabilities || []).join(", ")}</span></div>
					<div class="artifact-field"><span class="af-label">Substrates</span><span class="af-value">${(this.artifact.spec.substrates || []).join(", ")}</span></div>
				</div>
				<div class="artifact-targets">${Object.values(this.artifact.render_targets).map((t: any) => `<span class="target-chip">${t.label}: ${t.dimensions}</span>`).join("")}</div>
				<div class="artifact-actions">${(this.artifact.next_actions || []).map((a: any) => `<span class="action-chip">${a.label}</span>`).join("")}</div>
			</div>`;

			html += `
			<div class="provider-section">
				<h3 class="provider-heading">Provider Matching <span class="buyer-loc">(buyer: Berlin)</span></h3>
				<div class="provider-table">
					<div class="pt-header"><span>Provider</span><span>Type</span><span>City</span><span>Distance</span><span>Cost</span><span>Turnaround</span></div>
					${providers.map((prov, i) => `
					<div class="pt-row ${i === 0 ? 'nearest' : ''}">
						<span class="pt-name">${prov.name}</span>
						<span><span class="badge ${prov.type === 'cosmolocal' ? 'badge-cosmo' : 'badge-global'}">${prov.type}</span></span>
						<span>${prov.city}</span>
						<span>${prov.distance === Infinity ? '--' : `~${prov.distance.toLocaleString()} km`}</span>
						<span class="pt-cost">$${prov.unitCost.toFixed(2)}</span>
						<span>${prov.turnaround}</span>
					</div>`).join("")}
				</div>
			</div>`;

			html += `
			<div class="split-section">
				<h4 class="split-heading">Revenue Split <span class="split-total">(from $${unitCost.toFixed(2)} unit cost)</span></h4>
				<div class="split-bar">
					<div class="split-seg split-provider" style="flex:50">Provider 50% <span class="split-amt">$${provAmt}</span></div>
					<div class="split-seg split-creator" style="flex:35">Creator 35% <span class="split-amt">$${creatorAmt}</span></div>
					<div class="split-seg split-community" style="flex:15"><span class="split-amt">$${communityAmt}</span></div>
				</div>
				<div class="split-legend"><span>Community 15%</span></div>
			</div>`;
		}
		html += `</section>`;
		return html;
	}

	private renderStep4(): string {
		return `
		<section class="step-section visible">
			<h3 class="pipeline-heading">Print-on-Demand Pipeline</h3>
			<div class="pipeline">
				<div class="pipe-node done"><div class="pipe-icon">${this.productIcon("tee")}</div><div class="pipe-label">rSwag Design</div><span class="pipe-check">&#10003;</span></div>
				<div class="pipe-arrow">&rarr;</div>
				<div class="pipe-node done"><div class="pipe-icon"><svg viewBox="0 0 20 20" width="24" height="24"><rect x="2" y="4" width="16" height="12" rx="2" fill="none" stroke="#818cf8" stroke-width="1.5"/><line x1="6" y1="8" x2="14" y2="8" stroke="#818cf8" stroke-width="1"/><line x1="6" y1="11" x2="12" y2="11" stroke="#818cf8" stroke-width="1"/></svg></div><div class="pipe-label">Artifact Spec</div><span class="pipe-check">&#10003;</span></div>
				<div class="pipe-arrow">&rarr;</div>
				<div class="pipe-node done"><div class="pipe-icon"><svg viewBox="0 0 20 20" width="24" height="24"><rect x="3" y="3" width="14" height="14" rx="3" fill="none" stroke="#818cf8" stroke-width="1.5"/><circle cx="10" cy="10" r="3" fill="#818cf8"/></svg></div><div class="pipe-label">rCart Catalog</div><span class="pipe-check">&#10003;</span></div>
				<div class="pipe-arrow">&rarr;</div>
				<div class="pipe-node done"><div class="pipe-icon"><svg viewBox="0 0 20 20" width="24" height="24"><circle cx="10" cy="8" r="4" fill="none" stroke="#818cf8" stroke-width="1.5"/><path d="M4,18 Q4,13 10,13 Q16,13 16,18" fill="none" stroke="#818cf8" stroke-width="1.5"/></svg></div><div class="pipe-label">Provider Match</div><span class="pipe-check">&#10003;</span></div>
				<div class="pipe-arrow">&rarr;</div>
				<div class="pipe-node done"><div class="pipe-icon"><svg viewBox="0 0 20 20" width="24" height="24"><rect x="3" y="5" width="14" height="10" rx="2" fill="none" stroke="#818cf8" stroke-width="1.5"/><path d="M3,8 L10,12 L17,8" fill="none" stroke="#818cf8" stroke-width="1.5"/></svg></div><div class="pipe-label">Local Print</div><span class="pipe-check">&#10003;</span></div>
			</div>
			<div class="pipeline-actions">
				<a class="btn btn-primary" href="/rcart?tab=catalog">Send to rCart</a>
				<button class="btn btn-secondary json-toggle-btn">View Artifact JSON</button>
			</div>
			<pre class="json-pre">${this.esc(JSON.stringify(this.artifact, null, 2))}</pre>
		</section>`;
	}

	private productIcon(id: string): string {
		const icons: Record<string, string> = {
			tee: `<svg viewBox="0 0 24 24" width="28" height="28"><path d="M7,4 L4,6 L2,12 L5,13 L6,9 L6,22 L18,22 L18,9 L19,13 L22,12 L20,6 L17,4 L15,6 Q14,8 12,8 Q10,8 9,6 Z" fill="none" stroke="#818cf8" stroke-width="1.5" stroke-linejoin="round"/></svg>`,
			sticker: `<svg viewBox="0 0 24 24" width="28" height="28"><rect x="3" y="3" width="18" height="18" rx="4" fill="none" stroke="#818cf8" stroke-width="1.5"/><circle cx="12" cy="12" r="5" fill="none" stroke="#818cf8" stroke-width="1.5"/></svg>`,
			poster: `<svg viewBox="0 0 24 24" width="28" height="28"><rect x="4" y="2" width="16" height="20" rx="2" fill="none" stroke="#818cf8" stroke-width="1.5"/><line x1="8" y1="7" x2="16" y2="7" stroke="#818cf8" stroke-width="1"/><line x1="8" y1="10" x2="16" y2="10" stroke="#818cf8" stroke-width="1"/><rect x="8" y="13" width="8" height="5" rx="1" fill="none" stroke="#818cf8" stroke-width="1"/></svg>`,
			hoodie: `<svg viewBox="0 0 24 24" width="28" height="28"><path d="M7,5 L3,7 L1,14 L4,15 L5,10 L5,22 L19,22 L19,10 L20,15 L23,14 L21,7 L17,5 L15,7 Q14,9 12,9 Q10,9 9,7 Z" fill="none" stroke="#818cf8" stroke-width="1.5" stroke-linejoin="round"/><ellipse cx="12" cy="6" rx="3" ry="2" fill="none" stroke="#818cf8" stroke-width="1"/></svg>`,
		};
		return icons[id] || icons.tee;
	}

	private getMockupSvg(): string {
		const colorHex = this.getDemoProduct().colors?.find(c => c.id === this.selectedColor)?.hex || "#0a0a0a";
		switch (this.selectedProduct) {
			case "tee": return teeMockupSvg(colorHex);
			case "hoodie": return hoodieMockupSvg(colorHex);
			case "sticker": return stickerMockupSvg();
			case "poster": return posterMockupSvg();
			default: return teeMockupSvg(colorHex);
		}
	}

	private bindDemoEvents() {
		this.shadow.querySelector("#btn-tour")?.addEventListener("click", () => this.startTour());
		this.shadow.querySelectorAll(".product").forEach(el => {
			el.addEventListener("click", () => this.demoSelectProduct((el as HTMLElement).dataset.product || "tee"));
		});
		this.shadow.querySelectorAll(".pill[data-size]").forEach(el => {
			el.addEventListener("click", () => { this.selectedSize = (el as HTMLElement).dataset.size || "M"; this.render(); });
		});
		this.shadow.querySelectorAll(".swatch[data-color]").forEach(el => {
			el.addEventListener("click", () => { this.selectedColor = (el as HTMLElement).dataset.color || "black"; this.render(); });
		});
		this.shadow.querySelector(".sample-btn")?.addEventListener("click", () => this.demoUseSample());
		this.shadow.querySelector(".generate-btn")?.addEventListener("click", () => this.demoGenerate());
		this.shadow.querySelector(".json-toggle-btn")?.addEventListener("click", () => {
			this.shadow.querySelector(".json-pre")?.classList.toggle("visible");
		});
	}

	// ── Styles ──

	private getFullStyles(): string {
		return `
			:host { display: block; padding: 1.5rem; max-width: 960px; margin: 0 auto; }
			*, *::before, *::after { box-sizing: border-box; }

			/* Live indicator */
			.live-header { display: flex; align-items: center; gap: 8px; margin-bottom: 0.5rem; }
			.live-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: #22c55e; animation: pulse-dot 2s infinite; }
			@keyframes pulse-dot { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
			.live-badge { font-size: 0.7rem; padding: 2px 8px; border-radius: 999px; background: rgba(34,197,94,0.15); color: #22c55e; font-weight: 500; display: flex; align-items: center; gap: 2px; }

			/* Tab bar */
			.tab-bar { display: flex; gap: 0; border-bottom: 2px solid var(--rs-border, #334155); margin-bottom: 1.5rem; }
			.tab-btn { padding: 0.625rem 1.25rem; border: none; background: none; color: var(--rs-text-secondary, #94a3b8); font-size: 0.875rem; font-weight: 500; cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -2px; transition: all 0.15s; font-family: inherit; }
			.tab-btn:hover { color: var(--rs-text-primary, #e2e8f0); }
			.tab-btn.active { color: var(--rs-primary, #6366f1); border-bottom-color: var(--rs-primary, #6366f1); font-weight: 600; }

			/* Common */
			.section-title { color: var(--rs-text-primary); font-weight: 600; font-size: 1rem; margin: 0 0 0.375rem; }
			.section-desc { color: var(--rs-text-secondary); font-size: 0.8125rem; margin: 0 0 1rem; }
			.loading { text-align: center; color: var(--rs-text-muted); padding: 2rem; }
			.empty-state { text-align: center; padding: 3rem 1rem; color: var(--rs-text-secondary); }
			.empty-state p { margin: 0 0 1rem; }
			.error { background: rgba(239,68,68,0.1); border: 1px solid var(--rs-error, #ef4444); border-radius: 8px; padding: 0.75rem; color: #fca5a5; font-size: 0.875rem; margin-bottom: 1rem; }

			/* Badges */
			.badge { display: inline-block; padding: 0.125rem 0.5rem; border-radius: 999px; font-size: 0.625rem; font-weight: 600; }
			.badge-stickers { background: rgba(34,197,94,0.15); color: #4ade80; }
			.badge-apparel { background: rgba(56,189,248,0.15); color: #38bdf8; }
			.badge-prints { background: rgba(249,115,22,0.15); color: #fb923c; }
			.badge-draft { background: rgba(100,116,139,0.15); color: #94a3b8; }
			.badge-active { background: rgba(34,197,94,0.15); color: #4ade80; }
			.badge-source { background: rgba(99,102,241,0.15); color: #a5b4fc; }
			.badge-cosmo { background: rgba(34,197,94,0.15); color: #4ade80; }
			.badge-global { background: rgba(56,189,248,0.15); color: #38bdf8; }
			.badge-printful { background: rgba(56,189,248,0.15); color: #38bdf8; }
			.badge-local { background: rgba(34,197,94,0.15); color: #4ade80; }
			.badge-ok { background: rgba(34,197,94,0.15); color: #4ade80; padding: 0.25rem 0.75rem; font-size: 0.75rem; }

			/* Buttons */
			.btn { padding: 0.625rem 1.25rem; border-radius: 8px; font-size: 0.875rem; font-weight: 600; cursor: pointer; border: none; text-decoration: none; display: inline-block; transition: all 0.15s; font-family: inherit; }
			.btn:disabled { opacity: 0.5; cursor: not-allowed; }
			.btn-primary { background: var(--rs-primary-hover, #4f46e5); color: #fff; }
			.btn-primary:hover:not(:disabled) { background: #4338ca; }
			.btn-secondary { background: var(--rs-bg-surface-raised, #1e293b); color: var(--rs-text-primary, #e2e8f0); }
			.btn-secondary:hover { background: var(--rs-bg-hover, #334155); }
			.btn-danger { background: rgba(239,68,68,0.15); color: #fca5a5; }
			.btn-danger:hover { background: rgba(239,68,68,0.25); }
			.btn-sm { padding: 0.375rem 0.75rem; font-size: 0.75rem; }

			/* Browse tab */
			.browse-controls { display: flex; gap: 0.75rem; margin-bottom: 1rem; }
			.search-input, .category-select { padding: 0.5rem 0.75rem; border: 1px solid var(--rs-input-border, #334155); border-radius: 8px; background: var(--rs-input-bg, #0f172a); color: var(--rs-input-text, #e2e8f0); font-size: 0.875rem; font-family: inherit; }
			.search-input { flex: 1; }
			.search-input:focus, .category-select:focus { outline: none; border-color: var(--rs-primary, #6366f1); }
			.product-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 1rem; }
			.catalog-card { background: var(--rs-bg-surface, #1e293b); border: 1px solid var(--rs-border, #334155); border-radius: 12px; overflow: hidden; transition: border-color 0.15s; }
			.catalog-card:hover { border-color: var(--rs-primary, #6366f1); }
			.catalog-img { height: 180px; background-size: cover; background-position: center; background-color: var(--rs-bg-page, #0f172a); }
			.catalog-body { padding: 0.75rem; }
			.catalog-name { font-weight: 600; font-size: 0.875rem; color: var(--rs-text-primary); margin-bottom: 0.375rem; }
			.catalog-meta { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.375rem; }
			.catalog-price { font-weight: 600; color: #4ade80; font-size: 0.875rem; }
			.catalog-variants { display: flex; gap: 0.25rem; flex-wrap: wrap; margin-bottom: 0.5rem; }
			.variant-chip { padding: 0.125rem 0.375rem; border-radius: 4px; font-size: 0.625rem; background: var(--rs-bg-page); color: var(--rs-text-muted); border: 1px solid var(--rs-border); }

			/* Create tab */
			.create-modes { display: flex; gap: 0.5rem; margin-bottom: 1rem; }
			.mode-btn { padding: 0.5rem 1rem; border-radius: 8px; border: 1px solid var(--rs-border); background: var(--rs-bg-surface); color: var(--rs-text-secondary); font-size: 0.8125rem; cursor: pointer; transition: all 0.15s; font-family: inherit; }
			.mode-btn:hover { border-color: var(--rs-border-strong); }
			.mode-btn.active { border-color: var(--rs-primary); background: rgba(99,102,241,0.1); color: var(--rs-text-primary); }
			.create-section { margin-bottom: 1.5rem; }
			.input-field { width: 100%; padding: 0.625rem 0.75rem; border: 1px solid var(--rs-input-border, #334155); border-radius: 8px; background: var(--rs-input-bg, #0f172a); color: var(--rs-input-text, #e2e8f0); font-size: 0.875rem; margin-bottom: 0.75rem; box-sizing: border-box; font-family: inherit; }
			.input-field:focus { outline: none; border-color: var(--rs-primary); }
			textarea.input-field { resize: vertical; min-height: 80px; }
			.upload-area { border: 2px dashed var(--rs-border); border-radius: 12px; padding: 2rem; text-align: center; margin-bottom: 0.75rem; cursor: pointer; transition: border-color 0.15s; background: var(--rs-bg-surface); }
			.upload-area:hover { border-color: var(--rs-primary); }
			.upload-area.has-image { border-style: solid; border-color: var(--rs-border-strong); }
			.upload-label { color: var(--rs-text-secondary); font-size: 0.875rem; }
			.preview-img { max-width: 200px; max-height: 200px; border-radius: 8px; }
			input[type="file"] { display: none; }

			/* Designs grid */
			.designs-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 0.75rem; }
			.design-card { background: var(--rs-bg-surface); border: 1px solid var(--rs-border); border-radius: 10px; overflow: hidden; transition: border-color 0.15s; }
			.design-card:hover { border-color: var(--rs-primary); }
			.design-img { height: 140px; background-size: cover; background-position: center; background-color: var(--rs-bg-page); }
			.design-card-icon { font-size: 1.5rem; padding: 1rem; text-align: center; }
			.design-body { padding: 0.75rem; }
			.design-name { font-weight: 600; font-size: 0.8125rem; color: var(--rs-text-primary); margin-bottom: 0.25rem; }
			.design-meta { display: flex; gap: 0.375rem; margin-bottom: 0.5rem; }
			.design-actions { display: flex; gap: 0.375rem; flex-wrap: wrap; }

			/* Dither tab */
			.dither-section { }
			.dither-controls { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; margin-bottom: 1rem; }
			.control-group { display: flex; flex-direction: column; gap: 0.25rem; }
			.control-group label { font-size: 0.75rem; color: var(--rs-text-muted); font-weight: 500; text-transform: uppercase; letter-spacing: 0.05em; }
			.control-group select { padding: 0.5rem; border: 1px solid var(--rs-input-border); border-radius: 6px; background: var(--rs-input-bg); color: var(--rs-input-text); font-size: 0.8125rem; font-family: inherit; }
			.control-group select:focus { outline: none; border-color: var(--rs-primary); }
			.control-group input[type="range"] { width: 100%; accent-color: var(--rs-primary); }
			.range-value { font-size: 0.75rem; color: var(--rs-text-secondary); font-weight: 600; }
			.dither-actions { display: flex; gap: 0.75rem; margin-bottom: 1.5rem; }
			.dither-preview, .separations-preview { background: var(--rs-bg-surface); border: 1px solid var(--rs-border); border-radius: 12px; padding: 1rem; margin-bottom: 1rem; }
			.dither-preview h4, .separations-preview h4 { margin: 0 0 0.75rem; color: var(--rs-text-primary); font-size: 0.9375rem; }
			.preview-row { display: flex; align-items: center; gap: 1rem; margin-bottom: 0.75rem; }
			.preview-original, .preview-dithered { text-align: center; }
			.preview-original img, .preview-dithered img { max-width: 200px; max-height: 200px; border-radius: 8px; border: 1px solid var(--rs-border); }
			.preview-original span, .preview-dithered span { display: block; font-size: 0.6875rem; color: var(--rs-text-muted); margin-top: 0.25rem; }
			.preview-arrow { font-size: 1.5rem; color: var(--rs-text-muted); }
			.palette-row { display: flex; align-items: center; gap: 0.375rem; margin-bottom: 0.75rem; }
			.palette-label { font-size: 0.75rem; color: var(--rs-text-muted); }
			.palette-swatch { display: inline-block; width: 20px; height: 20px; border-radius: 4px; border: 1px solid rgba(255,255,255,0.2); }
			.sep-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 0.5rem; }
			.sep-card { text-align: center; }
			.sep-card img { width: 100%; border-radius: 6px; border: 1px solid var(--rs-border); }
			.sep-card span { display: block; font-size: 0.6875rem; color: var(--rs-text-muted); margin-top: 0.25rem; }
			.sep-color { display: flex; align-items: center; justify-content: center; gap: 0.25rem; }

			/* Orders tab */
			.orders-list { display: flex; flex-direction: column; gap: 0.5rem; }
			.order-card { display: flex; align-items: center; gap: 1rem; background: var(--rs-bg-surface); border: 1px solid var(--rs-border); border-radius: 8px; padding: 0.75rem; }
			.order-id { font-weight: 600; font-size: 0.875rem; color: var(--rs-text-primary); }
			.order-tracking { font-size: 0.8125rem; color: var(--rs-primary); }

			@media (max-width: 768px) {
				.tab-btn { padding: 0.5rem 0.75rem; font-size: 0.75rem; }
				.product-grid, .designs-grid { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); }
				.dither-controls { grid-template-columns: 1fr; }
				.preview-row { flex-direction: column; }
				.browse-controls { flex-direction: column; }
			}
		`;
	}

	private getDemoStyles(): string {
		return `
			:host { display: block; padding: 1.5rem; max-width: 960px; margin: 0 auto; }
			*, *::before, *::after { box-sizing: border-box; }
			.steps-bar { display: flex; align-items: center; justify-content: center; gap: 0; margin-bottom: 2rem; }
			.step-dot { display: flex; flex-direction: column; align-items: center; gap: 0.25rem; }
			.step-num { width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 0.75rem; font-weight: 600; border: 2px solid var(--rs-border); color: var(--rs-text-muted); background: var(--rs-bg-surface); transition: all 0.2s; }
			.step-dot.active .step-num { border-color: var(--rs-primary); color: #fff; background: var(--rs-primary-hover); }
			.step-dot.current .step-num { box-shadow: 0 0 0 3px rgba(99,102,241,0.3); }
			.step-label { font-size: 0.6875rem; color: var(--rs-text-muted); }
			.step-dot.active .step-label { color: #a5b4fc; }
			.step-line { width: 40px; height: 2px; background: var(--rs-border); margin: 0 0.5rem; margin-bottom: 1rem; }
			.products { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.75rem; margin-bottom: 1.5rem; }
			.product { padding: 0.875rem 0.5rem; border-radius: 12px; border: 2px solid var(--rs-border); background: var(--rs-bg-surface); cursor: pointer; text-align: center; transition: all 0.15s; }
			.product:hover { border-color: var(--rs-border-strong); }
			.product.active { border-color: var(--rs-primary); background: rgba(99,102,241,0.1); }
			.product-icon { margin-bottom: 0.375rem; display: flex; justify-content: center; }
			.product-name { color: var(--rs-text-primary); font-weight: 600; font-size: 0.8125rem; }
			.product-specs { color: var(--rs-text-muted); font-size: 0.6875rem; margin-top: 0.125rem; }
			.product-cost { color: var(--rs-text-secondary); font-size: 0.75rem; margin-top: 0.25rem; }
			.badge { display: inline-block; padding: 0.125rem 0.5rem; border-radius: 999px; font-size: 0.625rem; font-weight: 600; margin-top: 0.375rem; }
			.badge-printful { background: rgba(56,189,248,0.15); color: #38bdf8; }
			.badge-local { background: rgba(34,197,94,0.15); color: #4ade80; }
			.badge-cosmo { background: rgba(34,197,94,0.15); color: #4ade80; }
			.badge-global { background: rgba(56,189,248,0.15); color: #38bdf8; }
			.badge-ok { background: rgba(34,197,94,0.15); color: #4ade80; padding: 0.25rem 0.75rem; font-size: 0.75rem; }
			.option-row { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.75rem; }
			.option-label { color: var(--rs-text-secondary); font-size: 0.8125rem; font-weight: 500; min-width: 40px; }
			.pills { display: flex; gap: 0.375rem; flex-wrap: wrap; }
			.pill { padding: 0.375rem 0.75rem; border-radius: 999px; border: 1px solid var(--rs-border); background: var(--rs-bg-surface); color: var(--rs-text-secondary); font-size: 0.75rem; cursor: pointer; transition: all 0.15s; }
			.pill:hover { border-color: var(--rs-border-strong); color: var(--rs-text-primary); }
			.pill.active { border-color: var(--rs-primary); background: rgba(99,102,241,0.15); color: #fff; }
			.color-swatches { display: flex; gap: 0.5rem; }
			.swatch { width: 28px; height: 28px; border-radius: 50%; border: 2px solid var(--rs-border); cursor: pointer; transition: all 0.15s; }
			.swatch:hover { border-color: var(--rs-border-strong); }
			.swatch.active { border-color: var(--rs-primary); box-shadow: 0 0 0 3px rgba(99,102,241,0.3); }
			.mockup-area { display: flex; gap: 1.5rem; align-items: center; background: var(--rs-bg-surface); border: 1px solid var(--rs-border); border-radius: 12px; padding: 1.5rem; margin-bottom: 1.5rem; }
			.mockup-svg { flex-shrink: 0; }
			.mockup-svg svg { width: 180px; height: auto; }
			.mockup-info { flex: 1; }
			.mockup-title { color: var(--rs-text-primary); font-weight: 600; font-size: 1rem; margin: 0 0 0.25rem; }
			.mockup-meta { color: var(--rs-text-secondary); font-size: 0.8125rem; margin-bottom: 0.75rem; }
			.btn { padding: 0.625rem 1.25rem; border-radius: 8px; font-size: 0.875rem; font-weight: 600; cursor: pointer; border: none; text-decoration: none; display: inline-block; transition: all 0.15s; font-family: inherit; }
			.btn-primary { background: var(--rs-primary-hover); color: #fff; }
			.btn-primary:hover { background: #4338ca; }
			.btn-secondary { background: var(--rs-bg-surface-raised); color: var(--rs-text-primary); }
			.btn-secondary:hover { background: var(--rs-bg-hover); }
			.generate-row { text-align: center; margin-bottom: 1.5rem; }
			.step-section { margin-bottom: 1.5rem; }
			.step-section:not(.visible) { display: none; }
			.progress-bar { height: 6px; background: var(--rs-bg-surface); border-radius: 3px; overflow: hidden; margin-bottom: 0.5rem; }
			.progress-fill { height: 100%; background: linear-gradient(90deg, var(--rs-primary), var(--rs-primary-hover)); border-radius: 3px; transition: width 0.3s ease; }
			.progress-steps { display: flex; justify-content: space-between; margin-bottom: 1.5rem; }
			.prog-label { font-size: 0.6875rem; color: var(--rs-text-muted); transition: color 0.2s; }
			.prog-label.active { color: var(--rs-primary-hover); font-weight: 600; }
			.prog-label.done { color: #4ade80; }
			.artifact-card { background: var(--rs-bg-surface); border: 1px solid var(--rs-border); border-radius: 12px; padding: 1.25rem; margin-bottom: 1rem; }
			.artifact-heading { color: var(--rs-text-primary); font-weight: 600; font-size: 0.9375rem; margin: 0 0 0.75rem; }
			.artifact-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.5rem 1rem; margin-bottom: 0.75rem; }
			.artifact-field { display: flex; flex-direction: column; }
			.af-label { color: var(--rs-text-muted); font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.05em; }
			.af-value { color: var(--rs-text-primary); font-size: 0.8125rem; }
			.artifact-targets { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 0.5rem; }
			.target-chip { background: rgba(99,102,241,0.1); color: #a5b4fc; padding: 0.25rem 0.625rem; border-radius: 6px; font-size: 0.6875rem; }
			.artifact-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; }
			.action-chip { background: var(--rs-bg-page); color: var(--rs-text-secondary); padding: 0.25rem 0.625rem; border-radius: 6px; font-size: 0.6875rem; border: 1px solid var(--rs-border); }
			.provider-section { margin-bottom: 1rem; }
			.provider-heading { color: var(--rs-text-primary); font-weight: 600; font-size: 0.9375rem; margin: 0 0 0.75rem; }
			.buyer-loc { color: var(--rs-text-muted); font-weight: 400; font-size: 0.75rem; }
			.provider-table { background: var(--rs-bg-surface); border: 1px solid var(--rs-border); border-radius: 12px; overflow: hidden; }
			.pt-header, .pt-row { display: grid; grid-template-columns: 2fr 1fr 1fr 1.2fr 0.8fr 1fr; padding: 0.625rem 1rem; font-size: 0.8125rem; align-items: center; }
			.pt-header { background: var(--rs-bg-page); color: var(--rs-text-muted); font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.05em; font-weight: 600; }
			.pt-row { color: var(--rs-text-primary); border-top: 1px solid var(--rs-border-subtle); }
			.pt-row.nearest { background: rgba(99,102,241,0.05); border-left: 3px solid var(--rs-primary); }
			.pt-name { font-weight: 500; }
			.pt-cost { color: #4ade80; font-weight: 600; }
			.split-section { margin-bottom: 1.5rem; }
			.split-heading { color: var(--rs-text-secondary); font-size: 0.8125rem; font-weight: 500; margin: 0 0 0.5rem; }
			.split-total { color: var(--rs-text-muted); font-weight: 400; }
			.split-bar { display: flex; height: 32px; border-radius: 8px; overflow: hidden; font-size: 0.6875rem; font-weight: 600; }
			.split-seg { display: flex; align-items: center; justify-content: center; gap: 0.25rem; color: #fff; }
			.split-provider { background: #16a34a; }
			.split-creator { background: #4f46e5; }
			.split-community { background: #d97706; }
			.split-amt { opacity: 0.85; }
			.split-legend { text-align: right; margin-top: 0.25rem; }
			.split-legend span { color: var(--rs-text-muted); font-size: 0.6875rem; }
			.pipeline-heading { color: var(--rs-text-primary); font-weight: 600; font-size: 0.9375rem; margin: 0 0 1rem; text-align: center; }
			.pipeline { display: flex; align-items: center; justify-content: center; gap: 0; flex-wrap: wrap; margin-bottom: 1.5rem; }
			.pipe-node { display: flex; flex-direction: column; align-items: center; gap: 0.25rem; padding: 0.75rem; background: var(--rs-bg-surface); border: 1px solid var(--rs-border); border-radius: 10px; position: relative; min-width: 90px; }
			.pipe-node.done { border-color: #22c55e; }
			.pipe-icon { display: flex; align-items: center; justify-content: center; }
			.pipe-label { color: var(--rs-text-primary); font-size: 0.6875rem; font-weight: 500; text-align: center; }
			.pipe-check { position: absolute; top: -6px; right: -6px; background: #16a34a; color: #fff; width: 16px; height: 16px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 0.5625rem; }
			.pipe-arrow { color: var(--rs-text-muted); font-size: 1.25rem; margin: 0 0.25rem; margin-bottom: 0.75rem; }
			.pipeline-actions { display: flex; gap: 0.75rem; justify-content: center; margin-bottom: 1rem; }
			.json-pre { background: var(--rs-bg-page); border: 1px solid var(--rs-border); border-radius: 8px; padding: 0.75rem; overflow-x: auto; font-size: 0.6875rem; color: var(--rs-text-secondary); max-height: 300px; display: none; white-space: pre-wrap; word-break: break-all; }
			.json-pre.visible { display: block; }
			@media (max-width: 768px) {
				.products { grid-template-columns: repeat(2, 1fr); }
				.mockup-area { flex-direction: column; text-align: center; }
				.pt-header, .pt-row { grid-template-columns: 1.5fr 0.8fr 0.8fr 1fr 0.7fr 0.8fr; font-size: 0.6875rem; padding: 0.5rem; }
				.artifact-grid { grid-template-columns: repeat(2, 1fr); }
				.pipeline { gap: 0.25rem; }
				.pipe-node { min-width: 70px; padding: 0.5rem; }
				.pipe-label { font-size: 0.5625rem; }
			}
		`;
	}

	private esc(s: string): string {
		const d = document.createElement("div");
		d.textContent = s;
		return d.innerHTML;
	}
}

customElements.define("folk-swag-designer", FolkSwagDesigner);
