const { useState, useEffect, useMemo, useCallback } = React;

const fmt = n => n.toLocaleString('ko-KR') + '원';

const getUnitPrice = (p, type, opt) => {
    if (opt.explicitPrice) return opt.explicitPrice;

    const tiers = Object.keys(p.tierPriceMap).filter(k => k.startsWith(type + '|'))
        .sort((a, b) => b.length - a.length);
    for (const key of tiers) {
        const targetKey = key.split('|')[1];
        const regex = new RegExp(`(^|[^0-9])${targetKey}($|[^0-9])`, 'i');
        if (regex.test(opt.name)) {
            return p.tierPriceMap[key];
        }
    }

    const rules = (p.bulkRules || []).filter(r => r.type === type || r.type === '기본');
    if (rules.length) {
        const sorted = [...rules].sort((a, b) => b.threshold - a.threshold);
        for (const r of sorted) {
            const val = (r.unit === '팩' || r.unit === '개') ? (opt.count || 1) : opt.weight;
            if (val >= r.threshold) return r.price;
        }
    }

    if (p.typePriceMap && p.typePriceMap[type]) return p.typePriceMap[type];

    const genericTiers = Object.keys(p.tierPriceMap).filter(k => k.startsWith('기본|'))
        .sort((a, b) => b.length - a.length);
    for (const key of genericTiers) {
        const targetKey = key.split('|')[1];
        const regex = new RegExp(`(^|[^0-9])${targetKey}($|[^0-9])`, 'i');
        if (regex.test(opt.name)) {
            return p.tierPriceMap[key];
        }
    }

    return p.basePrice || opt.baseUnitPrice || 0;
};

const parseJinaMarkdown = (text) => {
    const results = [];
    const blocks = text.split(/(?:\n|^)\*\*\d+\.\s*/);
    blocks.forEach((block, idx) => {
        if (!block.trim() || idx === 0) return;
        const titleMatch = block.match(/^(.+?)\*\*/);
        if (!titleMatch) return;
        const rawTitle = titleMatch[1].trim();
        const isOutOfStock = rawTitle.includes('품절');
        const imgMatch = block.match(/!\[.*?\]\((https:\/\/[^\)]+)\)/);
        const imageUrl = imgMatch ? imgMatch[1] : '';
        const priceLineMatch = block.match(/단가\s*:\s*([\s\S]+?)(?=\n\s*\*|\n\s*\d+\s*[.\|)]|\n\s*[0-9.]+\s*(?:kg|팩)|$)/);
        let priceLineRaw = priceLineMatch ? priceLineMatch[1].trim() : '';
        const firstOptIdx = priceLineRaw.search(/[0-9.]+\s*(?:kg|팩)/);
        if (firstOptIdx > 10) priceLineRaw = priceLineRaw.substring(0, firstOptIdx).trim();
        let priceLine = priceLineRaw.split(/[.!?\n]/).map(s => s.trim()).map(s => s.replace(/!\[.*?\]\(.*?\)/g, '').replace(/\[.*?\]\(.*?\)/g, '').replace(/https?:\/\/[^\s)]+/g, '')).filter(s => s.includes('원') || s.includes('단가') || s.includes('kg') || s.includes('팩')).join('. ').replace(/\s+/g, ' ').replace(/[A-Za-z0-9_-]{15,}/g, '').trim();
        if (priceLine && !priceLine.endsWith('.')) priceLine += '.';
        const weightMatch = rawTitle.match(/\((?:약\s*)?([0-9.]+)(kg|g)(?:\s*~\s*(?:약\s*)?([0-9.]+)(kg|g))?\)/i) || rawTitle.match(/(?:약\s*)?([0-9.]+)(kg|g)(?:\s*~\s*(?:약\s*)?([0-9.]+)(kg|g))?/i);
        let avgWeight = 1.0;
        let isMedianUsed = false;
        if (weightMatch) {
            const lo = parseFloat(weightMatch[1]);
            const unitLo = (weightMatch[2] || '').toLowerCase();
            const hi = weightMatch[3] ? parseFloat(weightMatch[3]) : null;
            const unitHi = (weightMatch[4] || '').toLowerCase() || unitLo;
            if (unitLo === 'kg' || unitLo === 'g') {
                const loKg = unitLo === 'g' ? lo / 1000 : lo;
                if (hi !== null) {
                    const hiKg = unitHi === 'g' ? hi / 1000 : hi;
                    avgWeight = hiKg;
                    isMedianUsed = true;
                } else {
                    avgWeight = loKg;
                }
            }
        }
        if (isMedianUsed) priceLine += " (최댓값 기준으로 계산되었습니다)";
        const isPerPack = (priceLine.includes('팩당') || priceLine.includes('개당') || rawTitle.includes('팩')) && !priceLine.includes('kg당');
        const bulletPoints = [...block.matchAll(/\*\s+(.+)/g)].map(m => m[1].trim());
        const types = bulletPoints.filter(b => !b.match(/\d+(팩|kg|g|개|BOX|박스)/i) && !b.includes('단가') && b.length < 20);
        const allLines = block.split('\n').map(l => l.trim()).filter(l => l);
        const optionStrs = allLines.filter(l =>
            !l.startsWith('**') &&
            !l.startsWith('!') &&
            !l.includes('http') &&
            !priceLineRaw.includes(l) &&
            !l.includes('원 입니다') &&
            l.match(/[0-9.]+\s*(팩|kg|g|개|BOX|박스)/i)
        );
        const extractedOptions = [];
        optionStrs.forEach(l => {
            const cleanTitle = rawTitle.replace(/\[.*?\]/g, '').trim();
            let s = l.replace(cleanTitle, '').replace(/^\*\s*/, '').replace(/^[\d.]+\s*[.\|)]\s*/, '').trim();

            const explicitPriceMatch = s.match(/[:\-\|]\s*([0-9,]+)원|([0-9,]+)원/);
            let explicitPrice = null;
            if (explicitPriceMatch) {
                const priceStr = explicitPriceMatch[1] || explicitPriceMatch[2];
                explicitPrice = parseInt(priceStr.replace(/,/g, ''));
                s = s.replace(explicitPriceMatch[0], '').replace(/[:\-\|]\s*$/, '').trim();
            }

            if (s && s.length < 50) {
                extractedOptions.push({ text: s, explicitPrice });
            }
        });
        const typePriceMap = {};
        const tierPriceMap = {};
        const bulkRules = [];
        const priceSegments = priceLine.split(/\s*[\/\n]|\.\s*(?=[가-힣A-Z])/);
        priceSegments.forEach(segment => {
            let segmentType = '기본';
            let bestTypeScore = -1;
            types.forEach(t => {
                const keywords = t.replace(/육|등급/g, '').split(/[\s\(\)]+/).filter(k => k.length >= 2);
                const matchCount = keywords.filter(k => segment.includes(k)).length;
                if (matchCount > 0) {
                    const score = (matchCount * 10) + keywords.join('').length;
                    if (score > bestTypeScore) {
                        bestTypeScore = score;
                        segmentType = t;
                    }
                }
            });
            const segPriceMatches = [...segment.matchAll(/([0-9,]+)원/g)];
            let lastMatchEnd = 0;
            segPriceMatches.forEach(m => {
                const price = parseInt(m[1].replace(/,/g, ''));
                const chunk = segment.substring(lastMatchEnd, m.index + m[0].length);
                lastMatchEnd = m.index + m[0].length;
                let matchedType = segmentType;
                let bestMatchScore = -1;
                types.forEach(t => {
                    const keywords = t.replace(/육|등급/g, '').split(/[\s\(\)]+/).filter(k => k.length >= 2);
                    const foundKeywords = keywords.filter(k => chunk.includes(k));
                    const matchCount = foundKeywords.length;
                    if (matchCount > 0) {
                        const score = (matchCount * 10) + keywords.join('').length;
                        if (score > bestMatchScore) {
                            bestMatchScore = score;
                            matchedType = t;
                        }
                    }
                });
                const targetType = matchedType;
                const bulkMatches = [...chunk.matchAll(/(\d+)\s*(팩|kg|개|g)\s*이상/ig)];
                const bulkMatch = bulkMatches.length > 0 ? bulkMatches.pop() : null;
                const weightMatches = [...chunk.matchAll(/([0-9.]+kg|BOX|박스|개내외)/ig)];
                const weightKey = weightMatches.length > 0 ? weightMatches.pop()[0] : null;
                if (bulkMatch) {
                    bulkRules.push({ type: targetType, threshold: parseInt(bulkMatch[1]), unit: bulkMatch[2], price });
                } else if (weightKey) {
                    tierPriceMap[`${targetType}|${weightKey}`] = price;
                    tierPriceMap[`${targetType}|${weightKey} 내외`] = price;
                } else {
                    if (!typePriceMap[targetType]) typePriceMap[targetType] = price;
                }
            });
        });
        if (bulkRules.length === 0) {
            const globalBulkMatch = priceLine.match(/(\d+)\s*(팩|kg|개|g)\s*이상.*?([0-9,]+)원/i);
            if (globalBulkMatch) {
                bulkRules.push({ type: '기본', threshold: parseInt(globalBulkMatch[1]), unit: globalBulkMatch[2], price: parseInt(globalBulkMatch[3].replace(/,/g, '')) });
            }
        }
        const basePriceMatch = priceLine.match(/([0-9,]+)원/);
        const basePrice = basePriceMatch ? parseInt(basePriceMatch[1].replace(/,/g, '')) : 0;
        const filteredOptions = extractedOptions.filter(os => {
            const isTooLong = os.text.length > 25;
            const isNoise = os.text.includes('특징') || os.text.includes('등급') || os.text.includes('선택') || os.text.includes('감사') || os.text.includes('포장') || os.text.includes('입니다') || os.text.includes('패키지') || os.text.includes('동봉') || os.text.includes('단가') || (os.text.length > 10 && (os.text.includes('이상') || os.text.includes('이하')));
            const hasWeightOrPack = os.text.match(/[0-9.]+\s*(팩|kg|g|개|BOX|박스)/i);
            return !isTooLong && !isNoise && hasWeightOrPack;
        });
        const uniqueOptions = [];
        const seenKeys = new Set();
        const baseOptions = filteredOptions.length > 0 ? filteredOptions : [{ text: '1팩', explicitPrice: null }];

        baseOptions.forEach(os => {
            const cMatch = os.text.match(/(\d+)(팩|개|BOX|박스)/i);
            const wMatch = os.text.match(/([0-9.]+)(kg|g)/i);
            const count = cMatch ? parseInt(cMatch[1]) : 1;
            let weight = avgWeight * count;
            if (wMatch) {
                const val = parseFloat(wMatch[1]);
                weight = wMatch[2].toLowerCase() === 'g' ? val / 1000 : val;
            }

            const key = `${count}-${weight.toFixed(3)}`;
            if (!seenKeys.has(key)) {
                seenKeys.add(key);
                uniqueOptions.push({
                    name: os.text,
                    weight,
                    count,
                    explicitPrice: os.explicitPrice,
                    baseUnitPrice: basePrice
                });
            }
        });

        Object.keys(tierPriceMap).forEach(key => {
            const weightStr = key.split('|')[1];
            const wMatch = weightStr.match(/([0-9.]+)\s*kg/i);
            if (wMatch) {
                const weight = parseFloat(wMatch[1]);
                const optKey = `1-${weight.toFixed(3)}`;
                if (!seenKeys.has(optKey)) {
                    seenKeys.add(optKey);
                    uniqueOptions.push({
                        name: weightStr, weight, count: 1, explicitPrice: null, baseUnitPrice: basePrice
                    });
                }
            }
        });

        results.push({
            id: results.length + 1,
            title: rawTitle.replace(/\[.*?\]/g, '').trim(),
            imageUrl, desc: priceLine, isPerPack, isOutOfStock,
            types: types.length > 0 ? types : ['기본'],
            typePriceMap, tierPriceMap, bulkRules,
            options: uniqueOptions,
            selectedTypeIdx: 0, selectedOptionIdx: 0, isSelected: false
        });
    });
    return results;
};

const ProductCard = ({ p, onTypeChange, onOptionChange, onToggle, openCart }) => {
    const type = p.types[p.selectedTypeIdx];
    const opt = p.options[p.selectedOptionIdx];
    const unitPrice = getUnitPrice(p, type, opt);
    const isGrassFedOnly = p.types.length === 1 && p.types[0].includes('목초육');
    const optSubtotal = unitPrice * (p.isPerPack ? opt.count : opt.weight);

    return (
        <div className="product-card" id={`product-${p.id}`} style={p.isSelected ? { boxShadow: '0 0 0 2px var(--primary)' } : {}}>
            <div className="product-header">
                <img src={p.imageUrl} className="product-img" onError={(e) => e.target.style.display = 'none'} />
                <div className="product-main-info" style={{ opacity: p.isOutOfStock ? 0.5 : 1 }}>
                    <div className="product-title">
                        {p.isOutOfStock && <span className="sold-out-badge">[품절]</span>}
                        {p.title}
                    </div>
                    <div className="price-row">
                        <span className="main-price" style={{ fontSize: '1.0rem', letterSpacing: '-0.5px', color: 'var(--primary)' }}>
                            <span style={{ fontSize: '0.85rem', color: '#666', fontWeight: 700 }}>[{type}]</span> 계산식: {fmt(unitPrice)} × {p.isPerPack ? opt.count + '팩' : opt.weight.toFixed(2) + 'kg'}
                        </span>
                    </div>
                </div>
            </div>
            <div className="product-desc">📋 {p.desc}</div>
            
            {p.types.length > 1 ? (
                <div className="selector-section">
                    <div className="selector-label">종류 선택</div>
                    <div className="type-selector">
                        {p.types.map((t, i) => (
                            <button key={i} 
                                className={`type-btn ${i === p.selectedTypeIdx ? 'active' : ''}`} 
                                onClick={() => onTypeChange(p.id, i)} 
                                disabled={p.isOutOfStock}>
                                {t}
                            </button>
                        ))}
                    </div>
                </div>
            ) : (
                isGrassFedOnly && (
                    <div className="selector-section">
                        <div className="grass-fed-only">🌿 목초육 전용 상품</div>
                    </div>
                )
            )}
            
            <div className="selector-section">
                <div className="selector-label">중량/수량 선택</div>
                <select className="opt-select" 
                    value={p.selectedOptionIdx}
                    onChange={(e) => onOptionChange(p.id, parseInt(e.target.value))} 
                    disabled={p.isOutOfStock}>
                    {p.options.map((o, i) => {
                        const optUP = getUnitPrice(p, type, o);
                        const sub = optUP * (p.isPerPack ? o.count : o.weight);
                        return <option key={i} value={i}>{o.name} - {fmt(sub)}</option>;
                    })}
                </select>
            </div>
            
            <button className={`btn-toggle ${p.isSelected ? 'selected' : ''}`} 
                    onClick={() => {
                        onToggle(p.id);
                        if (!p.isSelected) openCart();
                    }} 
                    disabled={p.isOutOfStock}>
                {p.isOutOfStock ? '품절된 상품입니다' : (p.isSelected ? '담기 취소' : '장바구니 담기')}
            </button>
        </div>
    );
};

const Cart = ({ products, onShare }) => {
    const [collapsed, setCollapsed] = useState(true);

    const selectedProducts = products.filter(p => p.isSelected);
    
    let total = 0, totalWeight = 0;
    
    const cartItems = selectedProducts.map(p => {
        const type = p.types[p.selectedTypeIdx];
        const opt = p.options[p.selectedOptionIdx];
        const unitPrice = getUnitPrice(p, type, opt);
        const sub = unitPrice * (p.isPerPack ? opt.count : opt.weight);
        
        total += sub;
        totalWeight += opt.weight;
        
        const calcDesc = p.isPerPack ? `${fmt(unitPrice)} × ${opt.count}팩` : `${fmt(unitPrice)} × ${opt.weight.toFixed(2)}kg`;
        
        return { p, type, opt, sub, calcDesc };
    });

    let shippingFee = 0;
    if (totalWeight > 0) {
        if (totalWeight <= 2) shippingFee = 4500;
        else if (totalWeight <= 8) shippingFee = 8000;
        else if (totalWeight <= 18) shippingFee = 10000;
        else shippingFee = 18000;
    }

    const scrollToProduct = (id) => {
        const el = document.getElementById(`product-${id}`);
        if (el) {
            setCollapsed(true);
            el.scrollIntoView({ behavior: 'smooth', block: 'center' });
            el.style.transition = 'all 0.3s';
            el.style.boxShadow = '0 0 20px rgba(211, 47, 47, 0.4)';
            el.style.transform = 'scale(1.02)';
            setTimeout(() => { el.style.boxShadow = 'var(--shadow)'; el.style.transform = 'scale(1)'; }, 1500);
        }
    };

    const isVisible = selectedProducts.length > 0;

    return (
        <div className={`floating-cart ${isVisible ? 'visible' : ''} ${collapsed ? 'collapsed' : ''}`}>
            <button className="cart-toggle-btn" onClick={() => setCollapsed(!collapsed)}>
                {collapsed ? '▲' : '▼'}
            </button>
            
            {isVisible && (
                <div id="cartItemsList" style={{ maxHeight: '200px', overflowY: 'auto', marginBottom: '12px', borderBottom: '1px solid #eee', display: collapsed ? 'none' : 'block', paddingRight: '5px' }}>
                    {cartItems.map((item, idx) => (
                        <div key={idx} style={{ marginBottom: '8px', borderBottom: '1px solid #f1f1f1', paddingBottom: '4px' }}>
                            <div style={{ display: 'flex', justifyContent: 'space-between', fontWeight: 700 }}>
                                <span onClick={() => scrollToProduct(item.p.id)} style={{ cursor: 'pointer', textDecoration: 'underline', textUnderlineOffset: '3px', textDecorationColor: 'rgba(211,47,47,0.3)' }}>
                                    {item.p.title}{item.type !== '기본' ? ` [${item.type}]` : ''}
                                </span>
                                <span>{fmt(item.sub)}</span>
                            </div>
                            <div style={{ fontSize: '0.75rem', color: 'var(--sub)' }}>{item.opt.name} | {item.calcDesc}</div>
                        </div>
                    ))}
                </div>
            )}

            <div className="cart-breakdown">
                <div style={{ fontWeight: 600, color: '#444' }}>
                    상품 {fmt(total)} + 배송 {fmt(shippingFee)} (총 {totalWeight.toFixed(2)}kg)
                </div>
                <div className="shipping-info-box">
                    🚚 배송비: 2kg↓ 4,500원 / 8kg↓ 8,000원 / 18kg↓ 10,000원 / 38kg↓ 18,000원
                </div>
            </div>

            <div className="summary-row">
                <span className="total-label">총 예상 견적</span>
                <span className="total-price">{fmt(total + shippingFee)}</span>
            </div>

            <button className="btn-share" onClick={() => onShare(selectedProducts, total, shippingFee, totalWeight)}>
                <span>📋 예상 견적서 복사 및 공유</span>
            </button>
        </div>
    );
};

const App = () => {
    const [url, setUrl] = useState('');
    const [loading, setLoading] = useState(false);
    const [products, setProducts] = useState([]);
    const [fallbackVisible, setFallbackVisible] = useState(false);
    const [fallbackText, setFallbackText] = useState('');
    const [initMsg, setInitMsg] = useState('준비 중...');
    const [orderPeriod, setOrderPeriod] = useState(null);

    useEffect(() => {
        // Auto-detect Linktree
        const checkLinktree = async () => {
            try {
                const r = await fetch('https://r.jina.ai/https://linktr.ee/meatsmanager');
                const md = await r.text();
                const m = md.match(/https:\/\/(?:naver\.me|form\.naver\.com)\/[a-zA-Z0-9_]+/);
                if (m) {
                    setUrl(m[0]);
                    fetchData(m[0]);
                }
            } catch (e) { console.error(e); }
        };
        checkLinktree();
    }, []);

    const fetchData = async (targetUrl = url) => {
        if (!targetUrl.trim()) return alert('URL을 입력해주세요.');

        setLoading(true);
        setFallbackVisible(false);
        setInitMsg('데이터 분석 중...');

        try {
            const r = await fetch(`https://r.jina.ai/${targetUrl}`, { headers: { 'Accept': 'text/markdown' } });
            const text = await r.text();

            const dateMatch = text.match(/(\d+월\s*\d+일\s*~\s*\d+월\s*\d+일)|(적용날짜[:\s]+[0-9.\s~]+)/);
            setOrderPeriod({
                url: targetUrl,
                text: dateMatch ? `📢 유효 적용 날짜: ${dateMatch[0]} (원본 이동)` : `🔗 원본 주문서 바로가기`
            });

            const parsedProducts = parseJinaMarkdown(text);
            if (parsedProducts.length) {
                setProducts(parsedProducts);
            } else {
                throw new Error('상품을 찾을 수 없습니다.');
            }
        } catch (e) {
            setInitMsg('불러오기 실패. 텍스트를 직접 붙여넣어주세요.');
            setFallbackVisible(true);
        } finally {
            setLoading(false);
        }
    };

    const handleParseFallback = () => {
        const parsedProducts = parseJinaMarkdown(fallbackText);
        if (parsedProducts.length) {
            setProducts(parsedProducts);
            setFallbackVisible(false);
            setOrderPeriod(null);
            setInitMsg('분석 완료!');
        } else {
            alert('데이터를 분석할 수 없습니다.');
        }
    };

    const updateProduct = (id, fields) => {
        setProducts(prev => prev.map(p => p.id === id ? { ...p, ...fields } : p));
    };

    const handleShare = (selectedProducts, total, deliveryFee, totalWeight) => {
        if (selectedProducts.length === 0) return alert('선택된 상품이 없습니다.');

        let t = '🛒 [미트매니저 Made Easy 견적서]\n\n';

        selectedProducts.forEach(p => {
            const type = p.types[p.selectedTypeIdx];
            const opt = p.options[p.selectedOptionIdx];
            const unitPrice = getUnitPrice(p, type, opt);
            const sub = unitPrice * (p.isPerPack ? opt.count : opt.weight);
            const calcDesc = p.isPerPack ? `${fmt(unitPrice)} × ${opt.count}팩` : `${fmt(unitPrice)} × ${opt.weight.toFixed(2)}kg`;

            t += `• ${p.title}${type !== '기본' ? ' [' + type + ']' : ''}\n  └ ${opt.name} | ${calcDesc} = ${fmt(sub)}\n`;
        });

        if (totalWeight > 0) {
            t += `\n📦 예상 배송비: ${fmt(deliveryFee)} (약 ${totalWeight.toFixed(2)}kg)\n`;
        }
        t += `\n💰 총 합계: ${fmt(total + deliveryFee)}`;

        navigator.clipboard.writeText(t).then(() => {
            if (navigator.share) {
                navigator.share({ title: '미트매니저 견적서', text: t }).catch(() => {});
            } else {
                alert('견적서가 복사되었습니다! 카카오톡이나 문자 메시지창에 붙여넣어 공유하세요.');
            }
        });
    };

    return (
        <>
            <header>
                <h1>🥩 미트매니저 Made Easy</h1>
                <p>복잡한 주문서에서 예상 견적을 한눈에 확인하세요</p>
                {orderPeriod && (
                    <a className="order-period-badge" href={orderPeriod.url} target="_blank" rel="noreferrer">
                        {orderPeriod.text}
                    </a>
                )}
            </header>

            <div className="container">
                <div className="input-card">
                    <div className="input-group">
                        <input 
                            type="text" 
                            value={url} 
                            onChange={e => setUrl(e.target.value)} 
                            placeholder="네이버 폼 URL을 입력하거나 링크트리 자동감지" 
                        />
                        <button className="btn-fetch" onClick={() => fetchData(url)} disabled={loading}>
                            불러오기
                        </button>
                    </div>
                    
                    {loading && <div id="loading" style={{ display: 'block' }}><span className="spinner"></span>데이터 분석 중...</div>}
                    
                    {fallbackVisible && (
                        <div id="fallbackArea" style={{ marginTop: '15px' }}>
                            <p style={{ fontSize: '0.85rem', color: 'var(--primary)', marginBottom: '10px', fontWeight: 600 }}>
                                ⚠️ 자동 불러오기 실패 시 아래에 텍스트를 붙여넣어주세요.
                            </p>
                            <textarea 
                                value={fallbackText}
                                onChange={e => setFallbackText(e.target.value)}
                                style={{ width: '100%', border: '2px solid var(--border)', borderRadius: '12px', minHeight: '100px', padding: '12px', fontSize: '0.9rem', outline: 'none' }}
                                placeholder="주문서 텍스트를 복사해서 붙여넣으세요..."
                            />
                            <button className="btn-fetch" onClick={handleParseFallback} style={{ width: '100%', marginTop: '10px', height: '50px' }}>
                                텍스트 분석하기
                            </button>
                        </div>
                    )}
                </div>

                <div className="product-list">
                    {products.length === 0 ? (
                        <div style={{ textAlign: 'center', padding: '60px 20px', color: 'var(--sub)', fontWeight: 600 }}>
                            <span>{initMsg}</span>
                        </div>
                    ) : (
                        products.map(p => (
                            <ProductCard 
                                key={p.id} 
                                p={p} 
                                onTypeChange={(id, idx) => updateProduct(id, { selectedTypeIdx: idx })}
                                onOptionChange={(id, idx) => updateProduct(id, { selectedOptionIdx: idx })}
                                onToggle={(id) => updateProduct(id, { isSelected: !p.isSelected })}
                                openCart={() => {}}
                            />
                        ))
                    )}
                </div>
            </div>

            <Cart products={products} onShare={handleShare} />
        </>
    );
};

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
