/**
 * <folk-group-buy-page> — Public shareable group buy page.
 *
 * Attributes: space, buy-id
 * Fetches GET /api/group-buys/:id, polls every 10s.
 * Shows product hero, tier progress, pledge panel.
 * Demo mode when buyId starts with 'demo-'.
 */

class FolkGroupBuyPage extends HTMLElement {
	private shadow: ShadowRoot;
	private space = 'default';
	private buyId = '';
	private data: any = null;
	private loading = true;
	private error = '';
	private pollTimer: ReturnType<typeof setInterval> | null = null;
	private pledgeQty = 1;
	private pledgeName = '';
	private pledging = false;
	private pledged = false;
	private simQty = 0; // "What If" simulator quantity (0 = off)
	private commonsSharePct = 20; // % of revenue to commons

	constructor() {
		super();
		this.shadow = this.attachShadow({ mode: 'open' });
	}

	connectedCallback() {
		this.space = this.getAttribute('space') || 'default';
		this.buyId = this.getAttribute('buy-id') || '';
		this.loadData();
		this.startPolling();
	}

	disconnectedCallback() {
		this.stopPolling();
	}

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

	private async loadData() {
		this.loading = true;
		this.render();

		if (this.buyId.startsWith('demo-')) {
			this.data = this.getDemoData();
			this.loading = false;
			this.render();
			return;
		}

		try {
			const res = await fetch(`${this.getApiBase()}/api/group-buys/${this.buyId}`);
			if (!res.ok) throw new Error('Group buy not found');
			this.data = await res.json();
		} catch (e) {
			this.error = e instanceof Error ? e.message : 'Failed to load group buy';
		}
		this.loading = false;
		this.render();
	}

	private startPolling() {
		this.pollTimer = setInterval(async () => {
			if (!this.buyId || this.buyId.startsWith('demo-')) return;
			try {
				const res = await fetch(`${this.getApiBase()}/api/group-buys/${this.buyId}`);
				if (res.ok) {
					const updated = await res.json();
					if (updated.totalPledged !== this.data?.totalPledged) {
						this.data = updated;
						this.render();
					}
				}
			} catch { /* silent poll fail */ }
		}, 10000);
	}

	private stopPolling() {
		if (this.pollTimer) { clearInterval(this.pollTimer); this.pollTimer = null; }
	}

	private getDemoData() {
		return {
			id: this.buyId,
			title: 'Cosmolocal Network Tee',
			productType: 'tee',
			imageUrl: '/images/catalog/catalog-cosmolocal-tee.jpg',
			description: 'Bella+Canvas 3001 tee with the Cosmolocal Network radial design in teal and coral.',
			tiers: [
				{ min_qty: 1, per_unit: 25, currency: 'USD' },
				{ min_qty: 10, per_unit: 21.25, currency: 'USD' },
				{ min_qty: 25, per_unit: 18, currency: 'USD' },
				{ min_qty: 50, per_unit: 15, currency: 'USD' },
			],
			status: 'OPEN',
			totalPledged: 17,
			currentTier: { min_qty: 10, per_unit: 21.25, currency: 'USD' },
			pledges: [
				{ id: 'p1', displayName: 'Alice', quantity: 5, pledgedAt: new Date(Date.now() - 86400000 * 3).toISOString() },
				{ id: 'p2', displayName: 'Bob', quantity: 3, pledgedAt: new Date(Date.now() - 86400000 * 2).toISOString() },
				{ id: 'p3', displayName: 'Cooperative Hub', quantity: 6, pledgedAt: new Date(Date.now() - 86400000).toISOString() },
				{ id: 'p4', displayName: 'Dana', quantity: 3, pledgedAt: new Date(Date.now() - 3600000).toISOString() },
			],
			closesAt: new Date(Date.now() + 86400000 * 14).toISOString(),
			createdAt: new Date(Date.now() - 86400000 * 5).toISOString(),
		};
	}

	private async submitPledge() {
		if (this.pledging || this.pledgeQty < 1) return;

		if (this.buyId.startsWith('demo-')) {
			this.data.totalPledged += this.pledgeQty;
			this.data.pledges.push({
				id: `p-${Date.now()}`,
				displayName: this.pledgeName || 'Anonymous',
				quantity: this.pledgeQty,
				pledgedAt: new Date().toISOString(),
			});
			// Recalculate current tier
			const tiers = this.data.tiers;
			this.data.currentTier = [...tiers].reverse().find((t: any) => this.data.totalPledged >= t.min_qty) || tiers[0];
			this.pledged = true;
			this.render();
			return;
		}

		this.pledging = true;
		this.render();
		try {
			const res = await fetch(`${this.getApiBase()}/api/group-buys/${this.buyId}/pledge`, {
				method: 'POST',
				headers: {
					'Content-Type': 'application/json',
					...(localStorage.getItem('encryptid-token') ? { 'Authorization': `Bearer ${localStorage.getItem('encryptid-token')}` } : {}),
				},
				body: JSON.stringify({ quantity: this.pledgeQty, displayName: this.pledgeName || 'Anonymous' }),
			});
			if (res.ok) {
				this.pledged = true;
				await this.loadData();
			}
		} catch (e) {
			console.error('Failed to pledge:', e);
		}
		this.pledging = false;
		this.render();
	}

	private render() {
		const styles = this.getStyles();

		if (this.loading) {
			this.shadow.innerHTML = `<style>${styles}</style><div class="loading">Loading group buy...</div>`;
			return;
		}
		if (this.error) {
			this.shadow.innerHTML = `<style>${styles}</style><div class="error">${this.esc(this.error)}</div>`;
			return;
		}

		const d = this.data;
		if (!d) return;

		const nextTier = d.tiers.find((t: any) => t.min_qty > d.totalPledged);
		const nextTierQty = nextTier ? nextTier.min_qty : d.tiers[d.tiers.length - 1]?.min_qty || 0;
		const progressPct = nextTier ? Math.min(100, Math.round((d.totalPledged / nextTier.min_qty) * 100)) : 100;
		const remaining = nextTier ? nextTier.min_qty - d.totalPledged : 0;
		const closesDate = new Date(d.closesAt);
		const daysLeft = Math.max(0, Math.ceil((closesDate.getTime() - Date.now()) / 86400000));

		// "What If" simulator calculations
		const simTotal = this.simQty > 0 ? d.totalPledged + this.simQty : d.totalPledged;
		const simTier = [...d.tiers].reverse().find((t: any) => simTotal >= t.min_qty) || d.tiers[0];
		const simNextTier = d.tiers.find((t: any) => t.min_qty > simTotal);
		const simProgressPct = simNextTier ? Math.min(100, Math.round((simTotal / simNextTier.min_qty) * 100)) : 100;
		const maxTierQty = d.tiers[d.tiers.length - 1]?.min_qty || 50;

		// Revenue & commons calculations
		const currentRevenue = d.totalPledged * (d.currentTier?.per_unit || d.tiers[0]?.per_unit || 0);
		const simRevenue = simTotal * simTier.per_unit;
		const commonsRevenue = simRevenue * (this.commonsSharePct / 100);
		const currentCommons = currentRevenue * (this.commonsSharePct / 100);

		// Overall progress toward max tier (for the fill-up visual)
		const overallPct = Math.min(100, Math.round((d.totalPledged / maxTierQty) * 100));

		// People SVG icons for social proof
		const peopleCount = d.pledges?.length || 0;

		this.shadow.innerHTML = `
		<style>${styles}</style>
		<div class="page">
			<div class="hero">
				${d.imageUrl ? `<img class="hero-img" src="${this.esc(d.imageUrl)}" alt="${this.esc(d.title)}" />` : ''}
				<div class="hero-info">
					<h1 class="hero-title">${this.esc(d.title)}</h1>
					<div class="hero-tags">
						${d.productType ? `<span class="tag tag-type">${this.esc(d.productType)}</span>` : ''}
						<span class="status status-${d.status.toLowerCase()}">${d.status}</span>
					</div>
					${d.description ? `<p class="hero-desc">${this.esc(d.description)}</p>` : ''}
					<div class="hero-stats">
						<div class="hero-stat">
							<div class="hero-stat-value">${d.totalPledged}</div>
							<div class="hero-stat-label">pledged</div>
						</div>
						<div class="hero-stat">
							<div class="hero-stat-value">${peopleCount}</div>
							<div class="hero-stat-label">backers</div>
						</div>
						<div class="hero-stat">
							<div class="hero-stat-value">${daysLeft}</div>
							<div class="hero-stat-label">days left</div>
						</div>
						<div class="hero-stat">
							<div class="hero-stat-value">$${(d.currentTier?.per_unit || d.tiers[0]?.per_unit || 0).toFixed(2)}</div>
							<div class="hero-stat-label">per unit</div>
						</div>
					</div>
				</div>
			</div>

			<div class="main-grid">
				<div class="tiers-section">
					<!-- Fill-up progress visual -->
					<div class="fill-visual">
						<div class="fill-visual__container">
							<div class="fill-visual__liquid" style="height:${overallPct}%">
								<div class="fill-visual__wave"></div>
							</div>
							<div class="fill-visual__label">
								<span class="fill-visual__qty">${d.totalPledged}</span>
								<span class="fill-visual__of">/ ${maxTierQty}</span>
							</div>
							${d.tiers.map((t: any) => {
								const tierPct = Math.round((t.min_qty / maxTierQty) * 100);
								return `<div class="fill-visual__marker" style="bottom:${tierPct}%" title="${t.min_qty}+ = $${t.per_unit.toFixed(2)}/ea">
									<span class="fill-visual__marker-line"></span>
									<span class="fill-visual__marker-label">${t.min_qty}+</span>
								</div>`;
							}).join('')}
						</div>
						<div class="fill-visual__people">
							${Array.from({length: Math.min(peopleCount, 8)}, (_, i) =>
								`<svg class="fill-visual__person" style="animation-delay:${i * 0.1}s" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="8" r="4"/><path d="M20 21a8 8 0 10-16 0"/></svg>`
							).join('')}
							${peopleCount > 8 ? `<span class="fill-visual__more">+${peopleCount - 8}</span>` : ''}
						</div>
					</div>

					<h3>Volume Pricing Tiers</h3>
					<div class="tier-table">
						${d.tiers.map((t: any, i: number) => {
							const active = d.currentTier && t.min_qty === d.currentTier.min_qty;
							const reached = d.totalPledged >= t.min_qty;
							const simReached = simTotal >= t.min_qty;
							const simActive = this.simQty > 0 && simTier && t.min_qty === simTier.min_qty;
							const savings = i > 0 ? Math.round((1 - t.per_unit / d.tiers[0].per_unit) * 100) : 0;
							const tierRevenue = t.min_qty * t.per_unit;
							const tierCommons = tierRevenue * (this.commonsSharePct / 100);
							return `<div class="tier-row ${active ? 'tier-current' : ''} ${reached ? 'tier-reached' : ''} ${this.simQty > 0 && simActive ? 'tier-sim-active' : ''} ${this.simQty > 0 && simReached && !reached ? 'tier-sim-reached' : ''}">
								<span class="tier-qty">${t.min_qty}+</span>
								<span class="tier-price">$${t.per_unit.toFixed(2)}/ea</span>
								${savings > 0 ? `<span class="tier-savings">-${savings}%</span>` : `<span class="tier-savings tier-savings--base">base</span>`}
								<span class="tier-commons" title="Commons revenue at this tier">$${tierCommons.toFixed(0)} commons</span>
								${reached ? `<span class="tier-check">&#10003;</span>` : this.simQty > 0 && simReached ? `<span class="tier-check tier-check-sim">&#10003;</span>` : `<span class="tier-check"></span>`}
							</div>`;
						}).join('')}
					</div>

					${nextTier ? `
					<div class="progress-section">
						<div class="progress-label">
							<span>${remaining} more to unlock <strong>$${nextTier.per_unit.toFixed(2)}/ea</strong></span>
							<span class="progress-meta">${d.totalPledged} / ${nextTierQty}</span>
						</div>
						<div class="progress-bar"><div class="progress-fill" style="width:${progressPct}%"></div></div>
					</div>` : `<div class="progress-section"><div class="progress-label text-green">&#127881; Best tier unlocked!</div></div>`}

					<div class="sim-section">
						<h3>What If...?</h3>
						<p class="sim-desc">Explore how more pledges unlock better pricing and grow the commons.</p>
						<div class="sim-controls">
							<label class="sim-label">Add pledges</label>
							<input type="range" class="sim-slider" data-field="sim-qty" min="0" max="${Math.max(maxTierQty * 1.5, 100)}" value="${this.simQty}" />
							<input type="number" class="qty-input sim-qty-input" data-field="sim-qty-num" value="${this.simQty}" min="0" />
						</div>
						${this.simQty > 0 ? `
						<div class="sim-results">
							<div class="sim-row">
								<span>Total pledged</span>
								<span class="sim-value">${d.totalPledged} + ${this.simQty} = <strong>${simTotal}</strong></span>
							</div>
							<div class="sim-row">
								<span>Tier unlocked</span>
								<span class="sim-value"><strong>$${simTier.per_unit.toFixed(2)}/ea</strong>${simTier.per_unit < (d.currentTier?.per_unit || d.tiers[0].per_unit) ? ` <span class="sim-better">(better!)</span>` : ''}</span>
							</div>
							<div class="sim-row">
								<span>Total revenue</span>
								<span class="sim-value">$${simRevenue.toFixed(2)}</span>
							</div>
							${simNextTier ? `
							<div class="sim-row">
								<span>Next unlock</span>
								<span class="sim-value">${simNextTier.min_qty - simTotal} more for $${simNextTier.per_unit.toFixed(2)}/ea</span>
							</div>
							<div class="progress-bar"><div class="progress-fill sim-fill" style="width:${simProgressPct}%"></div></div>
							` : `<div class="sim-row"><span class="text-green" style="font-weight:600">&#127881; Best tier unlocked!</span></div>`}
						</div>
						` : ''}

						<div class="commons-section">
							<h4>Commons Revenue</h4>
							<div class="commons-controls">
								<label class="sim-label">Commons share</label>
								<input type="range" class="sim-slider commons-slider" data-field="commons-pct" min="0" max="50" value="${this.commonsSharePct}" />
								<span class="commons-pct-label">${this.commonsSharePct}%</span>
							</div>
							<div class="commons-stats">
								<div class="commons-stat">
									<div class="commons-stat-value">$${currentCommons.toFixed(2)}</div>
									<div class="commons-stat-label">Current</div>
								</div>
								<div class="commons-stat-arrow">${this.simQty > 0 ? '&#8594;' : ''}</div>
								${this.simQty > 0 ? `
								<div class="commons-stat commons-stat-projected">
									<div class="commons-stat-value">$${commonsRevenue.toFixed(2)}</div>
									<div class="commons-stat-label">Projected</div>
								</div>
								<div class="commons-stat">
									<div class="commons-stat-value text-green">+$${(commonsRevenue - currentCommons).toFixed(2)}</div>
									<div class="commons-stat-label">Growth</div>
								</div>` : ''}
							</div>
						</div>
					</div>

					<h3>Pledges (${d.pledges.length})</h3>
					<div class="pledges-list">
						${d.pledges.map((p: any) => `
						<div class="pledge-row">
							<span class="pledge-avatar">${this.esc(p.displayName.charAt(0).toUpperCase())}</span>
							<span class="pledge-name">${this.esc(p.displayName)}</span>
							<span class="pledge-qty">${p.quantity} unit${p.quantity > 1 ? 's' : ''}</span>
						</div>`).join('')}
					</div>
				</div>

				<div class="pledge-panel">
					<div class="pledge-panel__header">
						<h3>Join this Group Buy</h3>
						<div class="pledge-panel__social">${peopleCount} backer${peopleCount !== 1 ? 's' : ''} so far</div>
					</div>
					${this.pledged ? `
						<div class="pledge-success">
							<div class="pledge-success-icon">&#10003;</div>
							<p>Pledge submitted! Share this link to bring more people in.</p>
							<button class="btn btn-primary btn-lg" data-action="copy-link">&#128279; Copy Share Link</button>
						</div>
					` : `
						<div class="pledge-form">
							<label>Your name (optional)</label>
							<input class="input" type="text" data-field="pledge-name" placeholder="Anonymous" value="${this.esc(this.pledgeName)}" />
							<label>Quantity</label>
							<div class="qty-controls">
								<button class="btn btn-sm" data-action="qty-dec">&#8722;</button>
								<input class="qty-input" type="number" data-field="pledge-qty" value="${this.pledgeQty}" min="1" />
								<button class="btn btn-sm" data-action="qty-inc">&#43;</button>
							</div>
							${d.currentTier ? `
							<div class="pledge-price-box">
								<div class="pledge-price-amount">$${(d.currentTier.per_unit * this.pledgeQty).toFixed(2)}</div>
								<div class="pledge-price-label">at current tier ($${d.currentTier.per_unit.toFixed(2)}/ea)</div>
							</div>` : ''}
							<button class="btn btn-pledge btn-lg" data-action="submit-pledge" ${this.pledging ? 'disabled' : ''}>
								${this.pledging ? 'Pledging...' : `Pledge ${this.pledgeQty} unit${this.pledgeQty > 1 ? 's' : ''}`}
							</button>
							<p class="pledge-disclaimer">No payment collected now. You'll be notified when the group buy closes.</p>
						</div>
					`}
				</div>
			</div>
		</div>`;

		this.bindEvents();
	}

	private bindEvents() {
		this.shadow.querySelector("[data-action='qty-dec']")?.addEventListener("click", () => {
			if (this.pledgeQty > 1) { this.pledgeQty--; this.render(); }
		});
		this.shadow.querySelector("[data-action='qty-inc']")?.addEventListener("click", () => {
			this.pledgeQty++; this.render();
		});
		this.shadow.querySelector("[data-field='pledge-qty']")?.addEventListener("change", (e) => {
			this.pledgeQty = Math.max(1, parseInt((e.target as HTMLInputElement).value) || 1);
			this.render();
		});
		this.shadow.querySelector("[data-field='pledge-name']")?.addEventListener("input", (e) => {
			this.pledgeName = (e.target as HTMLInputElement).value;
		});
		this.shadow.querySelector("[data-action='submit-pledge']")?.addEventListener("click", () => {
			this.submitPledge();
		});
		this.shadow.querySelector("[data-action='copy-link']")?.addEventListener("click", () => {
			navigator.clipboard.writeText(window.location.href);
			const btn = this.shadow.querySelector("[data-action='copy-link']") as HTMLElement;
			if (btn) { btn.textContent = 'Copied!'; setTimeout(() => { btn.textContent = 'Copy Share Link'; }, 2000); }
		});

		// "What If" simulator controls
		const simSlider = this.shadow.querySelector("[data-field='sim-qty']") as HTMLInputElement;
		const simNum = this.shadow.querySelector("[data-field='sim-qty-num']") as HTMLInputElement;
		simSlider?.addEventListener("input", (e) => {
			this.simQty = parseInt((e.target as HTMLInputElement).value) || 0;
			this.render();
		});
		simNum?.addEventListener("change", (e) => {
			this.simQty = Math.max(0, parseInt((e.target as HTMLInputElement).value) || 0);
			this.render();
		});
		// Commons share slider
		this.shadow.querySelector("[data-field='commons-pct']")?.addEventListener("input", (e) => {
			this.commonsSharePct = parseInt((e.target as HTMLInputElement).value) || 0;
			this.render();
		});
	}

	private getStyles(): string {
		return `
			:host { display: block; padding: 2rem 1.5rem; width: 100%; max-width: 960px; margin: 0 auto; -webkit-tap-highlight-color: transparent; overflow-wrap: break-word; }
			* { box-sizing: border-box; }
			button, a, input, select, textarea, [role="button"] { touch-action: manipulation; }

			.loading { text-align: center; padding: 3rem; color: var(--rs-text-secondary); }
			.error { background: rgba(239,68,68,0.1); border: 1px solid var(--rs-error); border-radius: 8px; padding: 1.5rem; color: #fca5a5; text-align: center; }

			.hero { display: flex; gap: 2rem; margin-bottom: 2.5rem; padding: 1.5rem; background: var(--rs-bg-surface); border: 1px solid var(--rs-border); border-radius: 16px; }
			.hero-img { width: 220px; height: 220px; border-radius: 12px; object-fit: cover; flex-shrink: 0; }
			.hero-title { color: var(--rs-text-primary); font-size: 1.75rem; font-weight: 700; margin: 0 0 0.5rem; line-height: 1.2; }
			.hero-tags { display: flex; gap: 0.5rem; align-items: center; margin-bottom: 0.5rem; }
			.hero-desc { color: var(--rs-text-secondary); font-size: 0.9375rem; line-height: 1.6; margin: 0.5rem 0 1rem; }
			.hero-stats { display: flex; gap: 1.5rem; padding-top: 0.75rem; border-top: 1px solid var(--rs-border-subtle); }
			.hero-stat { text-align: center; }
			.hero-stat-value { color: var(--rs-text-primary); font-size: 1.25rem; font-weight: 700; }
			.hero-stat-label { color: var(--rs-text-muted); font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.05em; }

			.tag { display: inline-block; padding: 0.1875rem 0.625rem; border-radius: 6px; font-size: 0.6875rem; font-weight: 500; }
			.tag-type { background: rgba(99,102,241,0.1); color: var(--rs-primary-hover); }

			.status { padding: 0.1875rem 0.625rem; border-radius: 999px; font-size: 0.6875rem; font-weight: 600; }
			.status-open { background: rgba(34,197,94,0.15); color: #4ade80; }
			.status-locked { background: rgba(251,191,36,0.15); color: #fbbf24; }
			.status-ordered { background: rgba(99,102,241,0.15); color: #a5b4fc; }
			.status-cancelled { background: rgba(239,68,68,0.15); color: #f87171; }

			.main-grid { display: grid; grid-template-columns: 1fr 340px; gap: 2rem; align-items: start; }

			h3 { color: var(--rs-text-primary); font-size: 1.0625rem; font-weight: 600; margin: 0 0 0.75rem; }

			/* Fill-up progress visual */
			.fill-visual { display: flex; gap: 1.5rem; align-items: flex-end; margin-bottom: 2rem; }
			.fill-visual__container {
				position: relative; width: 80px; height: 180px;
				border: 2px solid var(--rs-border); border-radius: 12px;
				overflow: hidden; background: var(--rs-bg-surface);
				flex-shrink: 0;
			}
			.fill-visual__liquid {
				position: absolute; bottom: 0; left: 0; right: 0;
				background: linear-gradient(180deg, #f59e0b 0%, #22c55e 100%);
				transition: height 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
				border-radius: 0 0 10px 10px;
			}
			.fill-visual__wave {
				position: absolute; top: -4px; left: -10%; right: -10%; height: 10px;
				background: radial-gradient(ellipse at 50% 100%, transparent 60%, currentColor 61%);
				opacity: 0.2;
				animation: wave 3s ease-in-out infinite;
			}
			@keyframes wave { 0%,100% { transform: translateX(-5%); } 50% { transform: translateX(5%); } }
			.fill-visual__label {
				position: absolute; inset: 0; display: flex; flex-direction: column;
				align-items: center; justify-content: center; z-index: 1;
				text-shadow: 0 1px 3px rgba(0,0,0,0.3);
			}
			.fill-visual__qty { color: #fff; font-size: 1.5rem; font-weight: 800; }
			.fill-visual__of { color: rgba(255,255,255,0.7); font-size: 0.6875rem; }
			.fill-visual__marker {
				position: absolute; left: 0; right: 0; display: flex; align-items: center; z-index: 2;
			}
			.fill-visual__marker-line {
				flex: 1; height: 1px; background: rgba(255,255,255,0.3);
				border-top: 1px dashed var(--rs-border);
			}
			.fill-visual__marker-label {
				position: absolute; right: -3rem; font-size: 0.625rem; color: var(--rs-text-muted);
				white-space: nowrap;
			}
			.fill-visual__people { display: flex; flex-wrap: wrap; gap: 0.25rem; align-items: flex-end; }
			.fill-visual__person {
				width: 24px; height: 24px; color: var(--rs-text-muted); opacity: 0.6;
				animation: personPop 0.3s ease-out both;
			}
			.fill-visual__more {
				font-size: 0.6875rem; color: var(--rs-text-muted); font-weight: 600;
				padding: 0.125rem 0.375rem; background: var(--rs-bg-surface-raised);
				border-radius: 999px;
			}
			@keyframes personPop {
				from { transform: scale(0); opacity: 0; }
				to { transform: scale(1); opacity: 0.6; }
			}

			.tier-table { background: var(--rs-bg-surface); border: 1px solid var(--rs-border); border-radius: 12px; overflow: hidden; margin-bottom: 1.25rem; }
			.tier-row { display: flex; align-items: center; padding: 0.75rem 1rem; border-bottom: 1px solid var(--rs-border-subtle); gap: 1rem; transition: background 0.15s; }
			.tier-row:last-child { border-bottom: none; }
			.tier-current { background: rgba(99,102,241,0.1); border-left: 3px solid var(--rs-primary-hover); }
			.tier-reached { }
			.tier-qty { color: var(--rs-text-primary); font-weight: 700; min-width: 3rem; font-size: 0.9375rem; }
			.tier-price { color: var(--rs-text-primary); flex: 1; font-size: 0.9375rem; }
			.tier-savings { color: #4ade80; font-size: 0.8125rem; font-weight: 600; min-width: 3.5rem; }
			.tier-savings--base { color: var(--rs-text-muted); font-weight: 400; }
			.tier-check { color: #4ade80; font-size: 0.9375rem; min-width: 1.25rem; text-align: center; }

			.progress-section { margin-bottom: 1.5rem; }
			.progress-label { color: var(--rs-text-primary); font-size: 0.875rem; margin-bottom: 0.5rem; display: flex; justify-content: space-between; align-items: center; }
			.progress-meta { color: var(--rs-text-muted); font-size: 0.75rem; }
			.progress-bar { background: var(--rs-bg-surface-raised); border-radius: 999px; height: 12px; overflow: hidden; }
			.progress-fill { background: linear-gradient(90deg, #f59e0b, #22c55e); height: 100%; border-radius: 999px; transition: width 0.5s cubic-bezier(0.34, 1.56, 0.64, 1); }
			.text-green { color: #4ade80; }

			.pledges-list { margin-bottom: 1.5rem; }
			.pledge-row { display: flex; align-items: center; gap: 0.75rem; padding: 0.5rem 0; border-bottom: 1px solid var(--rs-border-subtle); }
			.pledge-row:last-child { border-bottom: none; }
			.pledge-avatar {
				width: 28px; height: 28px; border-radius: 999px; flex-shrink: 0;
				background: linear-gradient(135deg, #6366f1, #a855f7);
				color: #fff; font-size: 0.75rem; font-weight: 700;
				display: flex; align-items: center; justify-content: center;
			}
			.pledge-name { color: var(--rs-text-primary); font-size: 0.875rem; flex: 1; }
			.pledge-qty { color: var(--rs-text-secondary); font-size: 0.8125rem; font-weight: 600; white-space: nowrap; }

			.pledge-panel {
				background: var(--rs-bg-surface); border: 1px solid var(--rs-border);
				border-radius: 16px; padding: 1.5rem; height: fit-content;
				position: sticky; top: 1.5rem;
				box-shadow: 0 4px 24px rgba(0,0,0,0.1);
			}
			.pledge-panel__header { margin-bottom: 1rem; }
			.pledge-panel__header h3 { margin-bottom: 0.25rem; }
			.pledge-panel__social { color: var(--rs-text-muted); font-size: 0.75rem; }
			.pledge-form { display: flex; flex-direction: column; gap: 0.875rem; }
			.pledge-form label { color: var(--rs-text-secondary); font-size: 0.8125rem; font-weight: 500; }
			.input { padding: 0.5rem 0.75rem; border-radius: 8px; border: 1px solid var(--rs-input-border); background: var(--rs-input-bg); color: var(--rs-input-text); font-size: 0.875rem; }
			.input:focus { outline: none; border-color: var(--rs-primary); box-shadow: 0 0 0 2px rgba(99,102,241,0.15); }

			.qty-controls { display: flex; align-items: center; gap: 0.5rem; }
			.qty-input { width: 60px; text-align: center; padding: 0.375rem; border-radius: 8px; border: 1px solid var(--rs-input-border); background: var(--rs-input-bg); color: var(--rs-input-text); font-size: 0.875rem; }

			.pledge-price-box {
				background: rgba(99,102,241,0.05); border: 1px solid rgba(99,102,241,0.15);
				border-radius: 10px; padding: 0.75rem; text-align: center;
			}
			.pledge-price-amount { color: var(--rs-text-primary); font-size: 1.375rem; font-weight: 700; }
			.pledge-price-label { color: var(--rs-text-muted); font-size: 0.75rem; margin-top: 0.125rem; }

			.btn { padding: 0.5rem 1rem; border-radius: 8px; border: 1px solid var(--rs-border); background: var(--rs-bg-surface); color: var(--rs-text-primary); cursor: pointer; font-size: 0.875rem; transition: all 0.15s; }
			.btn:hover { border-color: var(--rs-border-strong); }
			.btn-primary { background: var(--rs-primary-hover); border-color: var(--rs-primary); color: #fff; }
			.btn-primary:hover { background: #4338ca; }
			.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
			.btn-pledge {
				background: linear-gradient(135deg, #22c55e, #16a34a); border: none; color: #fff;
				font-weight: 700; font-size: 1.0625rem; padding: 0.875rem; border-radius: 12px;
				box-shadow: 0 2px 8px rgba(34,197,94,0.3);
			}
			.btn-pledge:hover { background: linear-gradient(135deg, #16a34a, #15803d); box-shadow: 0 4px 12px rgba(34,197,94,0.4); }
			.btn-pledge:disabled { opacity: 0.5; cursor: not-allowed; }
			.btn-sm { padding: 0.375rem 0.75rem; font-size: 0.8125rem; min-height: 36px; min-width: 36px; }
			.btn-lg { padding: 0.875rem; font-size: 1rem; width: 100%; }

			.pledge-disclaimer { color: var(--rs-text-muted); font-size: 0.6875rem; text-align: center; margin: 0; line-height: 1.4; }

			.pledge-success { text-align: center; padding: 0.5rem 0; }
			.pledge-success-icon { font-size: 2.5rem; color: #4ade80; margin-bottom: 0.75rem; }
			.pledge-success p { color: var(--rs-text-secondary); font-size: 0.875rem; margin: 0.5rem 0 1.25rem; line-height: 1.5; }

			/* Tier commons column */
			.tier-commons { color: var(--rs-text-muted); font-size: 0.6875rem; min-width: 5rem; text-align: right; }

			/* Simulated tier states */
			.tier-sim-active { background: rgba(168,85,247,0.1); border-left: 3px solid #a855f7; }
			.tier-sim-reached { }
			.tier-check-sim { color: #a855f7; opacity: 0.7; }

			/* "What If" simulator */
			.sim-section { background: var(--rs-bg-surface); border: 1px solid var(--rs-border); border-radius: 16px; padding: 1.5rem; margin: 1.5rem 0; }
			.sim-desc { color: var(--rs-text-secondary); font-size: 0.8125rem; margin: 0 0 1rem; line-height: 1.4; }
			.sim-controls { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.75rem; }
			.sim-label { color: var(--rs-text-secondary); font-size: 0.8125rem; font-weight: 500; white-space: nowrap; }
			.sim-slider { flex: 1; accent-color: #a855f7; height: 6px; cursor: pointer; }
			.sim-qty-input { width: 60px; }
			.sim-results { background: rgba(168,85,247,0.05); border: 1px solid rgba(168,85,247,0.2); border-radius: 10px; padding: 1rem; margin-bottom: 1rem; }
			.sim-row { display: flex; justify-content: space-between; padding: 0.3125rem 0; color: var(--rs-text-primary); font-size: 0.875rem; }
			.sim-value { text-align: right; }
			.sim-better { color: #4ade80; font-weight: 600; font-size: 0.8125rem; }
			.sim-fill { background: linear-gradient(90deg, #a855f7, #d946ef); }

			/* Commons revenue */
			.commons-section { border-top: 1px solid var(--rs-border-subtle); padding-top: 1rem; margin-top: 0.75rem; }
			.commons-section h4 { color: var(--rs-text-primary); font-size: 0.9375rem; font-weight: 600; margin: 0 0 0.75rem; }
			.commons-controls { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.75rem; }
			.commons-slider { flex: 1; accent-color: #4ade80; height: 6px; cursor: pointer; }
			.commons-pct-label { color: #4ade80; font-weight: 600; font-size: 0.875rem; min-width: 2.5rem; text-align: right; }
			.commons-stats { display: flex; align-items: center; gap: 1rem; }
			.commons-stat { text-align: center; }
			.commons-stat-value { color: var(--rs-text-primary); font-weight: 700; font-size: 1.125rem; }
			.commons-stat-label { color: var(--rs-text-muted); font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.05em; margin-top: 0.125rem; }
			.commons-stat-projected .commons-stat-value { color: #a855f7; }
			.commons-stat-arrow { color: var(--rs-text-muted); font-size: 1.25rem; }

			@media (max-width: 960px) and (min-width: 769px) {
				.main-grid { grid-template-columns: 1fr 280px; }
				.hero-img { width: 160px; height: 160px; }
				.tier-commons { display: none; }
			}
			@media (max-width: 768px) {
				:host { padding: 1.25rem 1rem; }
				.hero { flex-direction: column; padding: 1.25rem; }
				.hero-img { width: 100%; height: auto; aspect-ratio: 1; }
				.hero-stats { flex-wrap: wrap; gap: 1rem; }
				.main-grid { grid-template-columns: 1fr; }
				.pledge-panel { position: static; }
				.commons-stats { flex-wrap: wrap; gap: 0.5rem; }
				.fill-visual { flex-direction: column; align-items: center; }
				.fill-visual__marker-label { display: none; }
				.sim-controls { flex-direction: column; align-items: stretch; gap: 0.5rem; }
				.sim-label { white-space: normal; }
				.commons-controls { flex-direction: column; align-items: stretch; gap: 0.5rem; }
			}
			@media (max-width: 480px) {
				:host { padding: 1rem 0.75rem; }
				.hero { padding: 1rem; gap: 1rem; }
				.hero-title { font-size: 1.375rem; }
				.hero-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; }
				.hero-stat-value { font-size: 1.125rem; }
				.tier-commons { display: none; }
				.tier-row { padding: 0.625rem 0.75rem; gap: 0.5rem; }
				.tier-qty { font-size: 0.8125rem; min-width: 2.5rem; }
				.tier-price { font-size: 0.8125rem; }
				.pledge-panel { padding: 1rem; }
				h3 { font-size: 0.9375rem; }
			}
		`;
	}

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

customElements.define("folk-group-buy-page", FolkGroupBuyPage);
