/**
 * <rstack-app-switcher> — Collapsible left sidebar to switch between rSpace modules.
 *
 * Attributes:
 *   current — the active module ID (highlighted)
 *
 * Methods:
 *   setModules(list) — provide the list of available modules
 */

export interface AppSwitcherModule {
	id: string;
	name: string;
	icon: string;
	description: string;
	standaloneDomain?: string;
	scoping?: { defaultScope: 'space' | 'global'; userConfigurable: boolean };
	enabled?: boolean;
}

// Pastel badge abbreviations & colors for each module
const MODULE_BADGES: Record<string, { badge: string; color: string }> = {
	// Create
	rspace:      { badge: "r🎨", color: "#5eead4" }, // teal-300
	rdocs:       { badge: "r📄", color: "#a5b4fc" }, // indigo-300
	rnotes:      { badge: "r📝", color: "#fcd34d" }, // amber-300
	rpubs:       { badge: "r📖", color: "#fda4af" }, // rose-300
	rdesign:     { badge: "r🎨", color: "#c4b5fd" }, // violet-300
	rsheets:     { badge: "r📑", color: "#86efac" }, // green-300
	rswag:       { badge: "r👕", color: "#fda4af" }, // rose-300
	rsplat:      { badge: "r🔮", color: "#d8b4fe" }, // purple-300
	// Communicate
	rchats:      { badge: "r🗨", color: "#6ee7b7" }, // emerald-200
	rinbox:      { badge: "r📨", color: "#a5b4fc" }, // indigo-300
	rmail:       { badge: "r✉️",  color: "#93c5fd" }, // blue-200
	rforum:      { badge: "r💬", color: "#fcd34d" }, // amber-200
	rmeets:      { badge: "r📹", color: "#67e8f9" }, // cyan-300
	// Coordinate
	rcal:        { badge: "r📅", color: "#7dd3fc" }, // sky-300
	rchoices:    { badge: "r☑️",  color: "#f0abfc" }, // fuchsia-300
	rschedule:   { badge: "r⏱",  color: "#a5b4fc" }, // indigo-200
	rtasks:      { badge: "r📋", color: "#cbd5e1" }, // slate-300
	rtime:       { badge: "r⏳", color: "#a78bfa" }, // violet-400
	rvote:       { badge: "r🗳", color: "#c4b5fd" }, // violet-300
	crowdsurf:   { badge: "r🏄", color: "#fde68a" }, // amber-200
	// Connect
	rnetwork:    { badge: "r🌐", color: "#93c5fd" }, // blue-300
	rsocials:    { badge: "r📢", color: "#7dd3fc" }, // sky-200
	// Commerce
	rexchange:   { badge: "r💱", color: "#fde047" }, // yellow-300
	rflows:      { badge: "r🌊", color: "#bef264" }, // lime-300
	rwallet:     { badge: "r💰", color: "#fde047" }, // yellow-300
	rcart:       { badge: "r🛒", color: "#fdba74" }, // orange-300
	rauctions:   { badge: "r🎭", color: "#fca5a5" }, // red-300
	// Govern
	rgov:        { badge: "r⚖️",  color: "#94a3b8" }, // slate-400
	rcred:       { badge: "r⭐", color: "#d97706" }, // amber-600
	// Media
	rphotos:     { badge: "r📸", color: "#f9a8d4" }, // pink-200
	rfiles:      { badge: "r📁", color: "#67e8f9" }, // cyan-300
	rtube:       { badge: "r🎬", color: "#f9a8d4" }, // pink-300
	rbooks:      { badge: "r📚", color: "#fda4af" }, // rose-300
	// Travel & Stay
	rmaps:       { badge: "r🗺", color: "#86efac" }, // green-300
	rtrips:      { badge: "r✈️",  color: "#6ee7b7" }, // emerald-300
	rbnb:        { badge: "r🏠", color: "#fbbf24" }, // amber-300
	rvnb:        { badge: "r🚐", color: "#a5f3fc" }, // cyan-200
	// Observe
	rdata:       { badge: "r📊", color: "#d8b4fe" }, // purple-300
	// Platform
	ragents:     { badge: "r🤖", color: "#6ee7b7" }, // emerald-300
	rids:        { badge: "r🪪", color: "#6ee7b7" }, // emerald-300
	rstack:      { badge: "r✨", color: "" },         // gradient (handled separately)
};

// Category definitions for the rApp dropdown (display-only grouping)
const MODULE_CATEGORIES: Record<string, string> = {
	// Create
	rdocs: "Create",
	rdesign: "Create",
	rnotes: "Create",
	rpubs: "Create",
	rsheets: "Create",
	rsplat: "Create",
	rspace: "Create",
	rswag: "Create",
	// Communicate
	rchats: "Communicate",
	rforum: "Communicate",
	rinbox: "Communicate",
	rmail: "Communicate",
	rmeets: "Communicate",
	// Coordinate
	crowdsurf: "Coordinate",
	rcal: "Coordinate",
	rchoices: "Coordinate",
	rschedule: "Coordinate",
	rtasks: "Coordinate",
	rtime: "Coordinate",
	rvote: "Coordinate",
	// Connect
	rnetwork: "Connect",
	rsocials: "Connect",
	// Commerce
	rauctions: "Commerce",
	rcart: "Commerce",
	rexchange: "Commerce",
	rflows: "Commerce",
	rwallet: "Commerce",
	// Govern
	rgov: "Govern",
	// Media
	rbooks: "Media",
	rfiles: "Media",
	rphotos: "Media",
	rtube: "Media",
	// Travel & Stay
	rbnb: "Travel & Stay",
	rmaps: "Travel & Stay",
	rtrips: "Travel & Stay",
	rvnb: "Travel & Stay",
	// Observe
	rdata: "Observe",
	// Platform
	ragents: "Platform",
	rids: "Platform",
	rstack: "Platform",
};

/** Color the "r" prefix orange in rApp names. */
function brandR(name: string): string {
	if (name.startsWith("r") && name.length > 1 && name[1] === name[1].toUpperCase()) {
		return `<span style="color:#dc8300">r</span>${name.slice(1)}`;
	}
	return name;
}

const CATEGORY_ORDER = [
	"Create",
	"Communicate",
	"Coordinate",
	"Connect",
	"Commerce",
	"Govern",
	"Media",
	"Travel & Stay",
	"Observe",
	"Platform",
];

import { rspaceNavUrl, getCurrentSpace, isStandaloneDomain } from "../url-helpers";

export class RStackAppSwitcher extends HTMLElement {
	#shadow: ShadowRoot;
	#modules: AppSwitcherModule[] = [];
	#allModules: AppSwitcherModule[] = []; // Full catalog including disabled
	#isOpen = false;
	#catalogOpen = false;
	#catalogBusy = false;
	#sortMode: 'function' | 'alpha' = 'function';
	#pinnedIds: Set<string> = new Set();
	#outsideClickHandler: ((e: PointerEvent) => void) | null = null;

	constructor() {
		super();
		this.#shadow = this.attachShadow({ mode: "open" });
		// Restore persisted preferences
		try {
			const sort = localStorage.getItem("rspace-sort-mode");
			if (sort === "alpha" || sort === "function") this.#sortMode = sort;
		} catch {}
		try {
			const pinned = JSON.parse(localStorage.getItem("rspace-pinned-modules") || "[]");
			if (Array.isArray(pinned)) this.#pinnedIds = new Set(pinned);
		} catch {}
	}

	static get observedAttributes() {
		return ["current"];
	}

	get current(): string {
		return this.getAttribute("current") || "";
	}

	static #getRecentModules(): string[] {
		try {
			const raw = localStorage.getItem("rspace-recent-modules");
			if (!raw) return [];
			const arr = JSON.parse(raw);
			return Array.isArray(arr) ? arr : [];
		} catch { return []; }
	}

	static #recordRecentModule(id: string) {
		try {
			const recent = RStackAppSwitcher.#getRecentModules().filter((x) => x !== id);
			recent.unshift(id);
			localStorage.setItem("rspace-recent-modules", JSON.stringify(recent.slice(0, 5)));
		} catch {}
	}

	connectedCallback() {
		if (this.current) RStackAppSwitcher.#recordRecentModule(this.current);
		this.#render();
	}

	disconnectedCallback() {
		// Clean up body class and outside-click listener on removal
		document.body.classList.remove("rstack-sidebar-open");
		if (this.#outsideClickHandler) {
			document.removeEventListener("pointerdown", this.#outsideClickHandler);
			this.#outsideClickHandler = null;
		}
	}

	attributeChangedCallback() {
		this.#render();
	}

	setModules(modules: AppSwitcherModule[]) {
		this.#modules = modules;
		this.#render();
	}

	/** Provide the full module catalog (enabled + disabled) for the "Manage rApps" panel */
	setAllModules(modules: AppSwitcherModule[]) {
		this.#allModules = modules;
	}

	#renderGroupedModules(current: string): string {
		const alpha = (a: AppSwitcherModule, b: AppSwitcherModule) =>
			a.name.localeCompare(b.name);

		// rStack header (clickable)
		let html = `
			<a class="rstack-header" href="https://rstack.online" target="_blank" rel="noopener">
				<span class="rstack-badge">r✨</span>
				<div class="rstack-info">
					<span class="rstack-title">rStack</span>
					<span class="rstack-subtitle">Self-hosted community app suite</span>
				</div>
			</a>
		`;

		// Pinned section
		const pinnedModules = this.#modules
			.filter((m) => this.#pinnedIds.has(m.id))
			.sort(alpha);
		if (pinnedModules.length > 0) {
			html += `<div class="category-header recent-header">Pinned</div>`;
			html += pinnedModules.map((m) => this.#renderItem(m, current)).join("");
		}

		// Recently Used section
		const recentIds = RStackAppSwitcher.#getRecentModules().filter((id) => id !== current);
		const recentModules = recentIds
			.map((id) => this.#modules.find((m) => m.id === id))
			.filter((m): m is AppSwitcherModule => !!m);
		if (recentModules.length > 0) {
			html += `<div class="category-header recent-header">Recent</div>`;
			html += recentModules.map((m) => this.#renderItem(m, current)).join("");
		}

		if (this.#sortMode === 'alpha') {
			// Flat alphabetical list
			const sorted = [...this.#modules].sort(alpha);
			html += `<div class="category-header">All rApps</div>`;
			html += sorted.map((m) => this.#renderItem(m, current)).join("");
		} else {
			// Group by category, alphabetical within each group
			const groups = new Map<string, AppSwitcherModule[]>();
			const uncategorized: AppSwitcherModule[] = [];

			for (const m of this.#modules) {
				const cat = MODULE_CATEGORIES[m.id];
				if (cat) {
					if (!groups.has(cat)) groups.set(cat, []);
					groups.get(cat)!.push(m);
				} else {
					uncategorized.push(m);
				}
			}

			for (const cat of CATEGORY_ORDER) {
				const items = groups.get(cat);
				if (!items || items.length === 0) continue;
				items.sort(alpha);
				html += `<div class="category-header">${cat}</div>`;
				html += items.map((m) => this.#renderItem(m, current)).join("");
			}
			if (uncategorized.length > 0) {
				uncategorized.sort(alpha);
				html += `<div class="category-header">Other</div>`;
				html += uncategorized.map((m) => this.#renderItem(m, current)).join("");
			}
		}

		// "Manage rApps" catalog section
		const disabledModules = this.#allModules.filter(
			m => m.enabled === false && m.id !== 'rspace'
		);
		// Only show the Manage section when there are disabled modules to add,
		// or when enabledModules is actively configured (not null/all-enabled)
		const hasRestrictions = disabledModules.length > 0;
		if (hasRestrictions || this.#allModules.length > this.#modules.length) {
			html += `
				<div class="catalog-divider">
					<button class="catalog-toggle" id="catalog-toggle">
						${this.#catalogOpen ? '▾' : '▸'} Manage rApps
						${disabledModules.length > 0 ? `<span class="catalog-count">${disabledModules.length} more</span>` : ''}
					</button>
				</div>
			`;
			if (this.#catalogOpen) {
				html += `<div class="catalog-panel" id="catalog-panel">`;
				// Show disabled modules with add option
				if (disabledModules.length > 0) {
					html += `<div class="catalog-section-label">Available to Add</div>`;
					for (const m of disabledModules) {
						html += this.#renderCatalogItem(m, false);
					}
				}
				// Show enabled modules with remove option (compact section below)
				const enabledNonCore = this.#allModules.filter(
					m => m.enabled !== false && m.id !== 'rspace'
				);
				if (enabledNonCore.length > 0) {
					html += `<div class="catalog-section-label">Remove from Space</div>`;
					for (const m of enabledNonCore) {
						html += this.#renderCatalogItem(m, true);
					}
				}
				html += `</div>`;
			}
		}

		// Sort toggle + Footer
		html += `
			<div class="sort-toggle">
				<button class="sort-btn ${this.#sortMode === 'function' ? 'sort-active' : ''}" id="sort-function" title="Sort by function">By Function</button>
				<button class="sort-btn ${this.#sortMode === 'alpha' ? 'sort-active' : ''}" id="sort-alpha" title="Sort alphabetically">A–Z</button>
			</div>
			<div class="rstack-footer">
				<a href="https://rstack.online" target="_blank" rel="noopener">rstack.online — self-hosted, community-run</a>
			</div>
		`;

		return html;
	}

	#renderCatalogItem(m: AppSwitcherModule, enabled: boolean): string {
		const badgeInfo = MODULE_BADGES[m.id];
		const badgeHtml = badgeInfo
			? `<span class="item-badge" style="background:${badgeInfo.color}${enabled ? '' : ';opacity:0.5'}"><span style="color:#dc8300">r</span>${badgeInfo.badge.slice(1)}</span>`
			: `<span class="item-icon" style="${enabled ? '' : 'opacity:0.5'}">${m.icon}</span>`;
		const cat = MODULE_CATEGORIES[m.id] || '';

		return `
			<div class="catalog-item ${enabled ? '' : 'catalog-item--disabled'}">
				${badgeHtml}
				<div class="item-text" style="flex:1;min-width:0;">
					<span class="item-name">${m.name}</span>
					<span class="item-desc">${m.description}</span>
				</div>
				<button class="catalog-btn ${enabled ? 'catalog-btn--remove' : 'catalog-btn--add'}"
					data-catalog-toggle="${m.id}" data-catalog-enabled="${enabled}"
					title="${enabled ? 'Remove from space' : 'Add to space'}"
					${this.#catalogBusy ? 'disabled' : ''}>
					${enabled ? '−' : '+'}
				</button>
			</div>
		`;
	}

	#renderItem(m: AppSwitcherModule, current: string): string {
		const badgeInfo = MODULE_BADGES[m.id];
		const badgeHtml = badgeInfo
			? `<span class="item-badge" style="background:${badgeInfo.color}"><span style="color:#dc8300">r</span>${badgeInfo.badge.slice(1)}</span>`
			: `<span class="item-icon">${m.icon}</span>`;

		const space = this.#getSpaceSlug();
		const host = window.location.host.split(":")[0];
		const isBareDomain = host === "rspace.online" || host === "www.rspace.online";
		const href =
			(isBareDomain || isStandaloneDomain())
				? `${window.location.protocol}//rspace.online/${m.id}`
				: rspaceNavUrl(space, m.id);

		const scopeBadge = m.scoping?.defaultScope === "global"
			? `<span class="scope-badge scope-global" title="Global data (shared across spaces)">G</span>`
			: "";

		const isPinned = this.#pinnedIds.has(m.id);

		return `
			<div class="item-row ${m.id === current ? "active" : ""}">
				<a class="item"
				   href="${href}"
				   data-id="${m.id}">
					${badgeHtml}
					<div class="item-text">
						<span class="item-name-row">
							<span class="item-name">${m.name}</span>
							${scopeBadge}
						</span>
						<span class="item-desc">${m.description}</span>
					</div>
				</a>
				<button class="pin-btn ${isPinned ? 'pin-active' : ''}"
					data-pin-id="${m.id}"
					title="${isPinned ? 'Unpin' : 'Pin to top'}">★</button>
			</div>
		`;
	}

	#render() {
		const current = this.current;
		const currentMod = this.#modules.find((m) => m.id === current);
		const badgeInfo = currentMod ? MODULE_BADGES[currentMod.id] : null;

		const triggerContent = badgeInfo
			? `<span class="trigger-badge" style="background:${badgeInfo.color}"><span style="color:#dc8300">r</span>${badgeInfo.badge.slice(1)}</span> ${currentMod!.name}`
			: currentMod
				? `${currentMod.icon} ${currentMod.name}`
				: `<span class="trigger-badge rstack-gradient"><span style="color:#dc8300">r</span>✨</span> rSpace`;

		this.#shadow.innerHTML = `
			<style>${STYLES}</style>
			<div class="switcher">
				<button class="trigger" id="trigger">${triggerContent} <span class="caret">▾</span></button>
				<div class="sidebar-backdrop" id="backdrop"></div>
				<div class="sidebar" id="sidebar">
					<button class="collapse-btn" id="collapse" title="Close sidebar">‹</button>
					${this.#renderGroupedModules(current)}
				</div>
			</div>
		`;

		const trigger = this.#shadow.getElementById("trigger")!;
		const sidebar = this.#shadow.getElementById("sidebar")!;
		const collapse = this.#shadow.getElementById("collapse")!;
		const backdrop = this.#shadow.getElementById("backdrop")!;

		// Sync sidebar DOM with tracked state (survives re-renders)
		if (this.#isOpen) {
			sidebar.classList.add("open");
			backdrop.classList.add("visible");
			document.body.classList.add("rstack-sidebar-open");
		} else {
			document.body.classList.remove("rstack-sidebar-open");
		}

		const open = () => {
			this.#isOpen = true;
			sidebar.classList.add("open");
			backdrop.classList.add("visible");
			document.body.classList.add("rstack-sidebar-open");
		};
		const close = () => {
			this.#isOpen = false;
			sidebar.classList.remove("open");
			backdrop.classList.remove("visible");
			document.body.classList.remove("rstack-sidebar-open");
		};
		const toggle = () => {
			if (this.#isOpen) close(); else open();
		};

		trigger.addEventListener("click", (e) => {
			e.stopPropagation();
			toggle();
		});

		collapse.addEventListener("click", () => close());
		backdrop.addEventListener("click", () => close());

		// Close sidebar when clicking outside (on main content)
		if (this.#outsideClickHandler) {
			document.removeEventListener("pointerdown", this.#outsideClickHandler);
		}
		this.#outsideClickHandler = (e: PointerEvent) => {
			if (!this.#isOpen) return;
			const path = e.composedPath();
			if (!path.includes(sidebar) && !path.includes(trigger)) {
				close();
			}
		};
		document.addEventListener("pointerdown", this.#outsideClickHandler);

		// Prefetch module fragments on hover for faster tab switching.
		// Uses low-priority fetch so it doesn't compete with user-initiated requests.
		if ((window as any).__rspaceTabBar) {
			this.#shadow.querySelectorAll("a.item").forEach((el) => {
				el.addEventListener("mouseenter", () => {
					const moduleId = (el as HTMLElement).dataset.id;
					if (!moduleId || moduleId === current) return;
					const space = this.#getSpaceSlug();
					const href = `/${space}/${moduleId}?fragment=1`;
					fetch(href, { priority: "low" } as RequestInit).catch(() => {});
				}, { once: true });
			});
		}

		// Intercept same-origin module links → dispatch event for tab system
		// Only intercept when the shell tab system is active (window.__rspaceTabBar).
		// On landing pages (rspace.online/, rspace.online/{moduleId}), let links
		// navigate normally since there's no module-select listener.
		this.#shadow.querySelectorAll("a.item").forEach((el) => {
			el.addEventListener("click", (e) => {
				const moduleId = (el as HTMLElement).dataset.id;
				if (!moduleId) return;
				RStackAppSwitcher.#recordRecentModule(moduleId);
				// Skip interception if tab system isn't active (landing pages)
				if (!(window as any).__rspaceTabBar) return;
				// Only intercept same-origin links
				const href = (el as HTMLAnchorElement).href;
				try {
					const url = new URL(href, window.location.href);
					if (url.origin !== window.location.origin) return;
				} catch { return; }
				e.preventDefault();
				close();
				this.dispatchEvent(new CustomEvent("module-select", {
					detail: { moduleId },
					bubbles: true,
					composed: true,
				}));
			});
		});

		// Sort toggle
		this.#shadow.getElementById("sort-function")?.addEventListener("click", () => {
			this.#sortMode = 'function';
			try { localStorage.setItem("rspace-sort-mode", "function"); } catch {}
			this.#render();
		});
		this.#shadow.getElementById("sort-alpha")?.addEventListener("click", () => {
			this.#sortMode = 'alpha';
			try { localStorage.setItem("rspace-sort-mode", "alpha"); } catch {}
			this.#render();
		});

		// Pin/unpin buttons
		this.#shadow.querySelectorAll<HTMLElement>("[data-pin-id]").forEach((btn) => {
			btn.addEventListener("click", (e) => {
				e.stopPropagation();
				e.preventDefault();
				const id = btn.dataset.pinId!;
				if (this.#pinnedIds.has(id)) {
					this.#pinnedIds.delete(id);
				} else {
					this.#pinnedIds.add(id);
				}
				try { localStorage.setItem("rspace-pinned-modules", JSON.stringify([...this.#pinnedIds])); } catch {}
				this.#render();
			});
		});

		// Catalog toggle
		this.#shadow.getElementById("catalog-toggle")?.addEventListener("click", () => {
			this.#catalogOpen = !this.#catalogOpen;
			this.#render();
		});

		// Catalog enable/disable buttons
		this.#shadow.querySelectorAll("[data-catalog-toggle]").forEach((btn) => {
			btn.addEventListener("click", async (e) => {
				e.stopPropagation();
				const moduleId = (btn as HTMLElement).dataset.catalogToggle!;
				const wasEnabled = (btn as HTMLElement).dataset.catalogEnabled === "true";
				await this.#toggleModule(moduleId, !wasEnabled);
			});
		});
	}

	async #toggleModule(moduleId: string, enable: boolean) {
		const space = this.#getSpaceSlug();
		if (!space || space === "demo") {
			// Demo mode: toggle locally
			this.#toggleModuleLocal(moduleId, enable);
			return;
		}

		// Get auth token
		let token: string | null = null;
		try {
			const session = JSON.parse(localStorage.getItem("encryptid_session") || "");
			token = session?.accessToken;
		} catch {}
		if (!token) {
			alert("Sign in to manage rApps for this space.");
			return;
		}

		this.#catalogBusy = true;
		this.#render();

		try {
			// Build new enabled list
			const currentEnabled = this.#allModules
				.filter(m => m.enabled !== false)
				.map(m => m.id);

			let newEnabled: string[];
			if (enable) {
				newEnabled = [...currentEnabled, moduleId];
			} else {
				newEnabled = currentEnabled.filter(id => id !== moduleId && id !== "rspace");
			}
			// Always include rspace
			if (!newEnabled.includes("rspace")) newEnabled.push("rspace");

			const res = await fetch(`/api/spaces/${encodeURIComponent(space)}/modules`, {
				method: "PATCH",
				headers: {
					"Content-Type": "application/json",
					Authorization: `Bearer ${token}`,
				},
				body: JSON.stringify({ enabledModules: newEnabled }),
			});

			if (res.ok) {
				// Update local state
				this.#toggleModuleLocal(moduleId, enable);
				// Dispatch event for shell to know
				this.dispatchEvent(new CustomEvent("modules-changed", {
					detail: { moduleId, enabled: enable, enabledModules: newEnabled },
					bubbles: true, composed: true,
				}));
			} else {
				const err = await res.json().catch(() => ({}));
				alert((err as any).error || "Failed to update modules");
			}
		} catch (err) {
			console.error("[AppSwitcher] Toggle failed:", err);
			alert("Failed to update modules. Check your connection.");
		}

		this.#catalogBusy = false;
		this.#render();
	}

	#toggleModuleLocal(moduleId: string, enable: boolean) {
		// Update allModules
		const mod = this.#allModules.find(m => m.id === moduleId);
		if (mod) mod.enabled = enable;

		// Update visible modules list
		if (enable) {
			if (!this.#modules.find(m => m.id === moduleId) && mod) {
				this.#modules.push(mod);
			}
		} else {
			this.#modules = this.#modules.filter(m => m.id !== moduleId);
		}

		this.#render();
	}

	/** Open the sidebar programmatically (used by tab bar [+] button) */
	open() {
		this.#isOpen = true;
		this.#render();
	}

	#getSpaceSlug(): string {
		// Read from the space switcher or URL
		const spaceSwitcher = document.querySelector("rstack-space-switcher");
		if (spaceSwitcher) return spaceSwitcher.getAttribute("current") || "demo";
		return getCurrentSpace();
	}

	static define(tag = "rstack-app-switcher") {
		if (!customElements.get(tag)) customElements.define(tag, RStackAppSwitcher);
	}
}

const STYLES = `
:host { display: contents; }

.switcher { position: relative; }

.trigger {
  display: flex; align-items: center; gap: 6px;
  padding: 6px 14px; border-radius: 8px; border: none;
  font-size: 0.9rem; font-weight: 600; cursor: pointer;
  transition: background 0.15s;
  background: var(--rs-btn-secondary-bg); color: var(--rs-text-primary);
  white-space: nowrap; min-width: 0; flex-shrink: 1;
  overflow: hidden; text-overflow: ellipsis;
}
.trigger:hover, .trigger:active { background: var(--rs-bg-hover); }

.trigger-badge {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 5px;
  font-size: 0.65rem; font-weight: 900; color: #1e293b;
  line-height: 1; flex-shrink: 0; white-space: nowrap;
}
.trigger-badge.rstack-gradient {
  background: linear-gradient(135deg, #67e8f9, #c4b5fd, #fda4af);
}

.caret { font-size: 0.7em; opacity: 0.6; }

/* ── Sidebar backdrop (mobile) ── */
.sidebar-backdrop {
  display: none; position: fixed; inset: 0; z-index: 10000;
  background: rgba(0,0,0,0.4); opacity: 0; transition: opacity 0.25s ease;
  -webkit-tap-highlight-color: transparent;
}
@media (max-width: 640px) {
  .sidebar-backdrop.visible { display: block; opacity: 1; }
}

/* ── Sidebar panel ── */
.sidebar {
  position: fixed;
  top: 56px; left: 0; bottom: 0;
  width: 280px;
  overflow-y: auto;
  touch-action: pan-y;
  z-index: 10001;
  background: var(--rs-bg-surface);
  border-right: 1px solid var(--rs-border);
  box-shadow: var(--rs-shadow-lg);
  transform: translateX(-100%);
  transition: transform 0.25s ease;
}
.sidebar.open {
  transform: translateX(0);
}

/* Collapse button */
.collapse-btn {
  position: absolute;
  top: 8px; right: 8px;
  width: 28px; height: 28px;
  border-radius: 6px;
  border: 1px solid var(--rs-border);
  background: var(--rs-bg-surface);
  color: var(--rs-text-secondary);
  font-size: 1rem; line-height: 1;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background 0.15s, color 0.15s;
  z-index: 1;
}
.collapse-btn:hover, .collapse-btn:active {
  background: var(--rs-bg-hover);
  color: var(--rs-text-primary);
}

/* rStack header */
a.rstack-header {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 14px; border-bottom: 1px solid var(--rs-border-subtle);
  text-decoration: none; color: inherit; cursor: pointer;
  transition: background 0.12s;
}
a.rstack-header:hover, a.rstack-header:active { background: var(--rs-bg-hover); }
.rstack-badge {
  display: flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 8px;
  background: linear-gradient(135deg, #67e8f9, #c4b5fd, #fda4af);
  font-size: 0.7rem; font-weight: 900; color: #1e293b; line-height: 1;
  flex-shrink: 0;
}
.rstack-info { display: flex; flex-direction: column; }
.rstack-title { font-size: 0.875rem; font-weight: 700; color: var(--rs-text-primary); }
.rstack-subtitle { font-size: 0.65rem; opacity: 0.5; }

/* Footer */
.rstack-footer {
  padding: 10px 14px; text-align: center;
  border-top: 1px solid var(--rs-border-subtle);
}
.rstack-footer a {
  font-size: 0.7rem; opacity: 0.4; text-decoration: none; color: inherit;
  transition: opacity 0.15s;
}
.rstack-footer a:hover { opacity: 0.8; color: var(--rs-accent); }

.item-row {
  display: flex; align-items: center;
  transition: background 0.12s;
  color: var(--rs-text-primary);
}
.item-row:hover, .item-row:active { background: var(--rs-bg-hover); }
.item-row.active { background: var(--rs-bg-active); }

.item {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 14px; text-decoration: none;
  cursor: pointer; flex: 1; min-width: 0; color: inherit;
}

.item-badge {
  display: flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 6px;
  font-size: 0.7rem; font-weight: 900; color: #1e293b;
  line-height: 1; flex-shrink: 0; white-space: nowrap;
}
.item-icon { font-size: 1.3rem; width: 28px; text-align: center; flex-shrink: 0; }
.item-text { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.item-name-row { display: flex; align-items: center; gap: 6px; }
.item-name { font-size: 0.875rem; font-weight: 600; }
.item-desc { font-size: 0.7rem; opacity: 0.5; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.scope-badge {
  font-size: 0.55rem; font-weight: 700; padding: 1px 4px; border-radius: 3px;
  text-transform: uppercase; letter-spacing: 0.04em; line-height: 1; flex-shrink: 0;
}
.scope-global { background: rgba(139,92,246,0.2); color: #a78bfa; }

.category-header {
  padding: 8px 14px 4px; font-size: 0.6rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.08em; opacity: 0.5;
  user-select: none;
}
.category-header:not(:first-child) {
  border-top: 1px solid var(--rs-border-subtle);
  margin-top: 4px; padding-top: 10px;
}
.recent-header {
  border-top: none; margin-top: 0; padding-top: 8px;
}

/* ── Catalog / Manage rApps ── */
.catalog-divider {
  border-top: 1px solid var(--rs-border-subtle);
  margin-top: 4px;
}
.catalog-toggle {
  display: flex; align-items: center; gap: 6px;
  width: 100%; padding: 10px 14px;
  background: none; border: none; cursor: pointer;
  font-size: 0.75rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--rs-accent, #6366f1);
  transition: background 0.12s;
}
.catalog-toggle:hover { background: var(--rs-bg-hover); }
.catalog-count {
  font-size: 0.6rem; font-weight: 600;
  padding: 1px 6px; border-radius: 8px;
  background: rgba(99,102,241,0.15); color: var(--rs-accent, #6366f1);
  text-transform: none; letter-spacing: 0;
}
.catalog-panel {
  padding-bottom: 8px;
}
.catalog-section-label {
  padding: 6px 14px 2px; font-size: 0.6rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.08em; opacity: 0.4;
  user-select: none;
}
.catalog-item {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 14px; transition: background 0.12s;
}
.catalog-item:hover { background: var(--rs-bg-hover); }
.catalog-item--disabled { opacity: 0.6; }
.catalog-btn {
  width: 26px; height: 26px; border-radius: 6px;
  border: 1px solid var(--rs-border); background: var(--rs-bg-surface);
  color: var(--rs-text-primary); font-size: 1rem; line-height: 1;
  cursor: pointer; display: flex; align-items: center; justify-content: center;
  transition: background 0.12s, border-color 0.12s;
  flex-shrink: 0;
}
.catalog-btn:hover { background: var(--rs-bg-hover); }
.catalog-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.catalog-btn--add {
  border-color: rgba(99,102,241,0.4); color: var(--rs-accent, #6366f1);
}
.catalog-btn--add:hover { border-color: var(--rs-accent, #6366f1); background: rgba(99,102,241,0.1); }
.catalog-btn--remove {
  border-color: rgba(239,68,68,0.3); color: #ef4444;
}
.catalog-btn--remove:hover { border-color: #ef4444; background: rgba(239,68,68,0.1); }

/* ── Pin star button ── */
.pin-btn {
  width: 28px; height: 28px; flex-shrink: 0;
  border: none; background: none; cursor: pointer;
  font-size: 0.85rem; line-height: 1;
  color: var(--rs-text-secondary); opacity: 0;
  transition: opacity 0.15s, color 0.15s;
  display: flex; align-items: center; justify-content: center;
  border-radius: 4px;
}
.item-row:hover .pin-btn,
.pin-btn.pin-active { opacity: 1; }
.pin-btn.pin-active { color: #fbbf24; }
.pin-btn:hover { color: #fbbf24; background: rgba(251,191,36,0.1); }

/* ── Sort toggle ── */
.sort-toggle {
  display: flex; gap: 4px; padding: 6px 14px;
  border-top: 1px solid var(--rs-border-subtle);
}
.sort-btn {
  flex: 1; padding: 4px 8px; border-radius: 5px;
  border: 1px solid var(--rs-border); background: none;
  font-size: 0.65rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--rs-text-secondary); cursor: pointer;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.sort-btn:hover { background: var(--rs-bg-hover); }
.sort-btn.sort-active {
  background: var(--rs-bg-active); color: var(--rs-text-primary);
  border-color: var(--rs-accent, #6366f1);
}

/* Mobile: sidebar overlays instead of pushing */
@media (max-width: 640px) {
  .sidebar { box-shadow: 4px 0 20px rgba(0,0,0,0.3); }
}
`;
