backups/tillval-qty2-20260420_143742/js/sales-product-modal.js

Code: DEV-D24C8A7D Size: 67.5 KB Lines: 985 Path: /home/prodconfig.wenesthosting.com/dev.solargroup.wenest.se/backups/tillval-qty2-20260420_143742/js/sales-product-modal.js

Task / Comment

Open report form
// sales-product-modal.js - Product detail modal, tillval, sprojs, price

// === SALES PRODUCT MODAL ===
let _spSelectedVariant = 0;
var _spSelectedWinModel = null;
function spSelectWinModel(key, eurPrice) { _spSelectedWinModel = key; _spSelectedWindowSize = Object.assign({}, _spSelectedWindowSize, {price: eurPrice}); spUpdateWindowSize(); }
let _spTillval = [];
let _spProduct = null;
let _spImages = [];
let _spCurrentImage = 0;

async function showProductModal(id) {
    const p = catalogProducts.find(x => x.id === id);
    if(!p) return;
    _spProduct = p;
    _spSelectedVariant = 0;
    _spImages = [p.img, ...(p.gallery || [])].filter(Boolean);
    _spCurrentImage = 0;

    const isAdmin = (gUserRole === 'systemadmin' || gUserRole === 'admin' || gUserRole === 'saljchef');
    const variants = (p.specs && p.specs.variants) || [];
    const attributes = (p.specs && p.specs.attributes) || [];
    const documents = p.documents || [];

    // Detect variant label key
    let variantLabelKey = '', variantLabelSuffix = '';
    if(variants.length) {
        const first = variants[0];
        if(first.kwh !== undefined) { variantLabelKey = 'kwh'; variantLabelSuffix = ' kWh'; }
        else if(first.paneler !== undefined) { variantLabelKey = 'paneler'; variantLabelSuffix = ' paneler'; }
        else if(first.cm !== undefined) { variantLabelKey = 'cm'; variantLabelSuffix = ' cm'; }
        else if(first.material !== undefined) { variantLabelKey = 'material'; variantLabelSuffix = ''; }
        else { const keys = Object.keys(first); variantLabelKey = keys[0]; variantLabelSuffix = ''; }
    }

    // Width-only selector (foderkloss, tröskel, utv råplan etc)
    let widthOnlyHtml = '';
    if(p.specs && p.specs.type === 'width_variants') {
        const wv = p.specs.variants;
        widthOnlyHtml = '<div style="margin-bottom:16px">'
            +'<div style="font-size:12px;font-weight:600;color:#334155;margin-bottom:8px">Välj variant</div>'
            +'<div style="display:flex;flex-wrap:wrap;gap:6px" id="spWidthOnlyChips">'
            + wv.map((v, i) => {
                const label = v.label || (v.width_mm ? v.width_mm + ' mm' : (v.length_mm ? v.length_mm + ' mm' : ''));
                const priceStr = v.price != null ? v.price.toFixed(2) + ' kr' : '–';
                return '<button onclick="spSelectWidthOnly('+i+')" class="sp-wo-chip" data-idx="'+i+'" style="padding:8px 16px;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;transition:all .15s;background:#f8f9fa;color:#334155;border:2px solid #e5e7eb;display:flex;flex-direction:column;align-items:center;gap:2px">'
                    +'<span>'+label+'</span>'
                    +'<span style="font-size:10px;font-weight:400;color:#64748b">'+priceStr+'</span>'
                    +'</button>';
            }).join('')
            +'</div></div>';
    }

    // Style + width selector (foder, smyg, fönsterbänk etc)
    let styleWidthHtml = '';
    if(p.specs && p.specs.type === 'style_width_variants') {
        const sw = p.specs.styles;
        styleWidthHtml = '<div style="margin-bottom:16px" id="spStyleWidth">'
            +'<div style="font-size:12px;font-weight:600;color:#334155;margin-bottom:8px">Välj utförande</div>'
            +'<div style="display:flex;flex-wrap:wrap;gap:6px;margin-bottom:12px" id="spStyleChips">'
            + sw.map((s, i) => '<button onclick="spSelectStyle('+i+')" class="sp-style-chip" data-idx="'+i+'" style="padding:8px 16px;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;transition:all .15s;'
                +(i===0?'background:#024550;color:#fff;border:2px solid #024550':'background:#f8f9fa;color:#334155;border:2px solid #e5e7eb')
                +'">'+s.style+(s.desc?' <span style="font-weight:400;opacity:.7;font-size:11px">('+s.desc+')</span>':'')+'</button>').join('')
            +'</div>'
            +'<div style="font-size:12px;font-weight:600;color:#334155;margin-bottom:4px">Välj bredd</div>'
            +'<div id="spWidthChips" style="display:flex;flex-wrap:wrap;gap:6px"></div>'
            +'<div id="spStyleResult" style="display:none;margin-top:10px"></div>'
            +'</div>';
    }

    // Window sizes selector
    let windowSizesHtml = '';
    if(p.specs && p.specs.type === 'window_sizes') {
        const ws = p.specs;
        windowSizesHtml = '<div style="margin-bottom:16px" id="spWindowSizes">'
            +'<div style="font-size:11px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:.5px;margin-bottom:4px">Leverantör: '+ws.supplier+' | Modell: '+ws.model+'</div>'
            +'<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin:12px 0">'
            +'<div><label style="font-size:12px;font-weight:600;color:#334155;display:block;margin-bottom:4px">Bredd (dm)</label>'
            +'<select id="spWinWidth" onchange="spUpdateWindowSize()" style="width:100%;padding:10px 12px;border:1.5px solid #e5e7eb;border-radius:8px;font-size:14px;font-family:inherit;background:#fff">'
            +'<option value="">Välj bredd...</option>'
            + ws.widths.map(w => '<option value="'+w+'">'+w+' dm ('+(w*10)+' cm)</option>').join('')
            +'</select></div>'
            +'<div><label style="font-size:12px;font-weight:600;color:#334155;display:block;margin-bottom:4px">Höjd (dm)</label>'
            +'<select id="spWinHeight" onchange="spUpdateWindowSize()" style="width:100%;padding:10px 12px;border:1.5px solid #e5e7eb;border-radius:8px;font-size:14px;font-family:inherit;background:#fff">'
            +'<option value="">Välj höjd...</option>'
            + ws.heights.map(h => '<option value="'+h+'">'+h+' dm ('+(h*10)+' cm)</option>').join('')
            +'</select></div>'
            +'</div>'
            +'<div style="margin:8px 0"><label style="font-size:12px;font-weight:600;color:#334155;display:block;margin-bottom:4px">Antal</label>'
            +'<div style="display:flex;align-items:center;gap:8px">'
            +'<button onclick="spWinQty(-1)" style="width:36px;height:36px;border-radius:8px;border:1.5px solid #e5e7eb;background:#f8f9fa;font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;font-family:inherit;color:#334155">−</button>'
            +'<input id="spWinAntal" type="number" min="1" value="1" onchange="spUpdateWindowSize()" style="width:60px;padding:8px;border:1.5px solid #e5e7eb;border-radius:8px;font-size:16px;font-weight:700;text-align:center;font-family:inherit">'
            +'<button onclick="spWinQty(1)" style="width:36px;height:36px;border-radius:8px;border:1.5px solid #e5e7eb;background:#f8f9fa;font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;font-family:inherit;color:#334155">+</button>'
            +'</div></div>'
            +'<div id="spWinResult" style="display:none"></div>'
            +'</div>';
    }

    // Variant chips
    let variantChipsHtml = '';
    if(variants.length > 1) {
        variantChipsHtml = '<div style="margin-bottom:16px">'
            +'<div style="font-size:11px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:.5px;margin-bottom:8px">Välj variant</div>'
            +'<div style="display:flex;flex-wrap:wrap;gap:6px" id="spVariantChips">'
            + variants.map((v, i) => {
                const label = v[variantLabelKey] + variantLabelSuffix;
                return '<button onclick="spSelectVariant('+i+')" class="sp-variant-chip" data-idx="'+i+'" style="padding:8px 16px;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;transition:all .15s;'
                    +(i===0 ? 'background:#024550;color:#fff;border:2px solid #024550' : 'background:#f8f9fa;color:#334155;border:2px solid #e5e7eb')
                    +'">'+label+'</button>';
            }).join('')
            +'</div></div>';
    }

    // Images
    const mainImgSrc = _spImages.length ? _spImages[0] : '';
    let imgHtml = '<div style="width:100%;aspect-ratio:4/3;background:#f8f9fa;border-radius:12px;overflow:hidden;display:flex;align-items:center;justify-content:center;margin-bottom:12px;cursor:'+(mainImgSrc?'pointer':'default')+'" id="spMainImageWrap" onclick="spOpenFullscreen()">';
    if(mainImgSrc) {
        imgHtml += '<img id="spMainImage" src="'+mainImgSrc+'" style="width:100%;height:100%;object-fit:contain">';
    } else {
        imgHtml += '<div style="text-align:center;color:#cbd5e1"><svg viewBox="0 0 24 24" style="width:48px;height:48px;stroke:currentColor;fill:none;stroke-width:1"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg></div>';
    }
    imgHtml += '</div>';

    // Thumbnails
    if(_spImages.length > 1) {
        imgHtml += '<div style="display:flex;gap:6px;flex-wrap:wrap" id="spThumbnails">'
            + _spImages.map((img, i) => '<img src="'+img+'" onclick="event.stopPropagation();spSetImage('+i+')" style="width:56px;height:56px;object-fit:cover;border-radius:8px;cursor:pointer;border:2px solid '+(i===0?'#024550':'#e5e7eb')+';transition:border-color .15s">').join('')
            +'</div>';
    }

    // Dots
    if(_spImages.length > 1) {
        imgHtml += '<div style="display:flex;justify-content:center;gap:6px;margin-top:8px" id="spDots">'
            + _spImages.map((_, i) => '<span onclick="event.stopPropagation();spSetImage('+i+')" style="width:8px;height:8px;border-radius:50%;cursor:pointer;transition:background .15s;background:'+(i===0?'#024550':'#cbd5e1')+'"></span>').join('')
            +'</div>';
    }

    // Badges
    const greenBadge = p.greenTechEligible ? '<span style="display:inline-flex;align-items:center;gap:4px;background:linear-gradient(135deg,#059669,#10b981);color:#fff;font-size:11px;font-weight:700;padding:5px 12px;border-radius:8px"><svg viewBox="0 0 24 24" style="width:14px;height:14px;fill:currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>Grönt teknik-avdrag</span>' : '';
    const rotBadge = p.rotEligible ? '<span style="display:inline-flex;align-items:center;gap:4px;background:linear-gradient(135deg,#2563eb,#3b82f6);color:#fff;font-size:11px;font-weight:700;padding:5px 12px;border-radius:8px;margin-left:6px">ROT-avdrag</span>' : '';

    // Specs table
    let specsHtml = '';
    if(attributes.length) {
        specsHtml = '<div>'
            +'<div style="font-size:13px;font-weight:700;color:#1a1a1a;margin-bottom:8px">Specifikationer</div>'
            +'<table style="width:100%;border-collapse:collapse;font-size:13px">'
            + attributes.map(a => '<tr style="border-top:1px solid #f1f5f9"><td style="padding:8px 0;color:#64748b;width:40%">'+a.key+'</td><td style="padding:8px 0;font-weight:600">'+a.value+'</td></tr>').join('')
            +'</table></div>';
    }

    // Documents
    let docsHtml = '';
    if(documents.length) {
        const typeLabels = {manual:'Manual',datasheet:'Datablad',certificate:'Certifikat',warranty:'Garanti',other:'Dokument'};
        docsHtml = '<div>'
            +'<div style="font-size:13px;font-weight:700;color:#1a1a1a;margin-bottom:8px">Dokument</div>'
            + documents.map(d => {
                const tl = typeLabels[d.type] || d.type || 'Dokument';
                return '<a href="'+d.file+'" target="_blank" download style="display:flex;align-items:center;gap:10px;padding:10px 12px;background:#f8f9fa;border-radius:8px;margin-bottom:6px;text-decoration:none;color:#334155;transition:background .15s" onmouseover="this.style.background=\'#f1f5f9\'" onmouseout="this.style.background=\'#f8f9fa\'">'
                    +'<svg viewBox="0 0 24 24" style="width:20px;height:20px;stroke:#0284c7;fill:none;stroke-width:2;flex-shrink:0"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>'
                    +'<div style="flex:1;min-width:0"><div style="font-weight:600;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">'+(d.name||'Dokument')+'</div>'
                    +'<span style="font-size:10px;color:#94a3b8">'+tl+'</span></div>'
                    +'<svg viewBox="0 0 24 24" style="width:16px;height:16px;stroke:#94a3b8;fill:none;stroke-width:2;flex-shrink:0"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>'
                    +'</a>';
            }).join('')
            +'</div>';
    }

    // Bottom section
    let bottomHtml = '';
    if(specsHtml || docsHtml) {
        bottomHtml = '<div style="border-top:1px solid #f1f5f9;padding:20px 24px 24px;display:grid;grid-template-columns:'+(specsHtml && docsHtml ? '1fr 1fr' : '1fr')+';gap:24px">'
            + specsHtml + docsHtml +'</div>';
    }

    // Remove existing
    document.getElementById('productSalesModal')?.remove();
    const modal = document.createElement('div');
    modal.id = 'productSalesModal';
    modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:9999;display:flex;align-items:center;justify-content:center;padding:20px';
    modal.onclick = e => { if(e.target === modal) modal.remove(); };

    modal.innerHTML = '<div style="background:#fff;border-radius:16px;max-width:1100px;width:100%;max-height:95vh;overflow-y:auto;box-shadow:0 25px 60px rgba(0,0,0,.3)">'
        // Header
        +'<div style="padding:14px 24px;border-bottom:1px solid #f1f5f9;display:flex;justify-content:space-between;align-items:center">'
        +'<button onclick="document.getElementById(\'productSalesModal\').remove()" style="display:flex;align-items:center;gap:6px;background:none;border:none;cursor:pointer;color:#64748b;font-size:13px;font-weight:600;font-family:inherit;padding:4px 0"><svg viewBox="0 0 24 24" style="width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:2"><polyline points="15 18 9 12 15 6"/></svg>Tillbaka</button>'
        +(isAdmin ? '<button onclick="document.getElementById(\'productSalesModal\').remove();editProduct(\''+p.id+'\')" style="display:flex;align-items:center;gap:6px;background:#f8f9fa;border:1px solid #e5e7eb;border-radius:8px;padding:7px 14px;cursor:pointer;font-size:12px;font-weight:600;color:#334155;font-family:inherit"><svg viewBox="0 0 24 24" style="width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>Redigera</button>' : '')
        +'</div>'
        // Main: bild + info + tillval vertikalt
        +'<div style="padding:24px">'
        // TOP ROW: bild + produktinfo
        +'<div style="display:grid;grid-template-columns:1fr 1fr;gap:24px;margin-bottom:24px">'
        +'<div>'+imgHtml+'</div>'
        +'<div>'
        +'<div style="font-size:11px;font-weight:600;color:#94a3b8;text-transform:uppercase;letter-spacing:1px;margin-bottom:4px">'+p.catLabel+'</div>'
        +'<h2 style="font-size:24px;font-weight:800;color:#1a1a1a;margin:0 0 8px 0;line-height:1.2">'+p.name+'</h2>'
        +(greenBadge || rotBadge ? '<div style="margin-bottom:12px">'+greenBadge+rotBadge+'</div>' : '')
        +(p.desc ? '<p style="font-size:14px;color:#64748b;line-height:1.6;margin:0 0 20px 0">'+p.desc+'</p>' : '')
        + (widthOnlyHtml || styleWidthHtml || windowSizesHtml || variantChipsHtml)
        +'<div id="spQtyArea"></div>'
        +'<div id="spPriceArea" style="background:#f8f9fa;border-radius:12px;padding:16px"></div>'
        +'</div>'
        +'</div>'
        // TILLVAL: 2 kolumner
        +'<div id="spTillvalArea" style="margin-bottom:16px"></div>'
        +'<div id="spColorSection" style="margin-top:16px">'
        +'<div style="font-size:11px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:.5px;margin-bottom:10px">F\u00e4rg</div>'
        +'<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px">'
        +'<div style="padding:12px;background:#f8f9fa;border:1.5px solid #e5e7eb;border-radius:10px;cursor:pointer;transition:all .15s" onclick="spOpenColorPicker(\'insida\')" onmouseover="this.style.borderColor=\'#94a3b8\'" onmouseout="this.style.borderColor=\'#e5e7eb\'">'
        +'<div style="font-size:11px;font-weight:600;color:#64748b;margin-bottom:6px">F\u00e4rg insida</div>'
        +'<div style="display:flex;align-items:center;gap:8px"><div id="spColorInsidaSwatch" style="width:36px;height:36px;border-radius:6px;background:#f0ece4;border:1px solid #ddd"></div><div><div id="spColorInsidaName" style="font-size:12px;font-weight:600;color:#1a1a1a">Vit</div><div id="spColorInsidaCode" style="font-size:10px;color:#94a3b8">RAL 9010</div></div></div>'
        +'</div>'
        +'<div style="padding:12px;background:#f8f9fa;border:1.5px solid #e5e7eb;border-radius:10px;cursor:pointer;transition:all .15s" onclick="spOpenColorPicker(\'utsida\')" onmouseover="this.style.borderColor=\'#94a3b8\'" onmouseout="this.style.borderColor=\'#e5e7eb\'">'
        +'<div style="font-size:11px;font-weight:600;color:#64748b;margin-bottom:6px">F\u00e4rg utsida</div>'
        +'<div style="display:flex;align-items:center;gap:8px"><div id="spColorUtsidaSwatch" style="width:36px;height:36px;border-radius:6px;background:#f0ece4;border:1px solid #ddd"></div><div><div id="spColorUtsidaName" style="font-size:12px;font-weight:600;color:#1a1a1a">Vit</div><div id="spColorUtsidaCode" style="font-size:10px;color:#94a3b8">RAL 9010</div></div></div>'
        +'</div>'
        +'</div>'
        +'</div>'
        +'<div id="spSprojsSection" style="margin-top:16px">'
        +'<div style="font-size:11px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:.5px;margin-bottom:10px">Spr\u00f6js</div>'
        +'<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:8px" id="spSprojsGrid">'
        +'<div onclick="spSelectSprojs(0)" class="sp-sprojs-opt" data-idx="0" style="border:2px solid #024550;border-radius:10px;padding:10px;text-align:center;cursor:pointer;background:#f0fdf4">'
        +'<svg viewBox="0 0 60 80" style="width:48px;height:64px;margin:0 auto 6px"><rect x="2" y="2" width="56" height="76" rx="2" fill="none" stroke="#64748b" stroke-width="2"/><rect x="8" y="8" width="44" height="64" fill="#e0f2fe" stroke="#94a3b8" stroke-width="1"/></svg>'
        +'<div style="font-size:11px;font-weight:600;color:#1a1a1a">Inga</div></div>'
        +'<div onclick="spSelectSprojs(1)" class="sp-sprojs-opt" data-idx="1" style="border:2px solid #e5e7eb;border-radius:10px;padding:10px;text-align:center;cursor:pointer">'
        +'<svg viewBox="0 0 60 80" style="width:48px;height:64px;margin:0 auto 6px"><rect x="2" y="2" width="56" height="76" rx="2" fill="none" stroke="#64748b" stroke-width="2"/><rect x="8" y="8" width="44" height="64" fill="#e0f2fe" stroke="#94a3b8" stroke-width="1"/><line x1="30" y1="8" x2="30" y2="72" stroke="#94a3b8" stroke-width="1.5"/><line x1="8" y1="40" x2="52" y2="40" stroke="#94a3b8" stroke-width="1.5"/></svg>'
        +'<div style="font-size:10px;font-weight:500;color:#334155">1V, 1H</div></div>'
        +'<div onclick="spSelectSprojs(2)" class="sp-sprojs-opt" data-idx="2" style="border:2px solid #e5e7eb;border-radius:10px;padding:10px;text-align:center;cursor:pointer">'
        +'<svg viewBox="0 0 60 80" style="width:48px;height:64px;margin:0 auto 6px"><rect x="2" y="2" width="56" height="76" rx="2" fill="none" stroke="#64748b" stroke-width="2"/><rect x="8" y="8" width="44" height="64" fill="#e0f2fe" stroke="#94a3b8" stroke-width="1"/><line x1="30" y1="8" x2="30" y2="72" stroke="#94a3b8" stroke-width="1.5"/><line x1="8" y1="30" x2="52" y2="30" stroke="#94a3b8" stroke-width="1.5"/><line x1="8" y1="50" x2="52" y2="50" stroke="#94a3b8" stroke-width="1.5"/></svg>'
        +'<div style="font-size:10px;font-weight:500;color:#334155">1V, 2H</div></div>'
        +'<div onclick="spSelectSprojs(3)" class="sp-sprojs-opt" data-idx="3" style="border:2px solid #e5e7eb;border-radius:10px;padding:10px;text-align:center;cursor:pointer">'
        +'<svg viewBox="0 0 60 80" style="width:48px;height:64px;margin:0 auto 6px"><rect x="2" y="2" width="56" height="76" rx="2" fill="none" stroke="#64748b" stroke-width="2"/><rect x="8" y="8" width="44" height="64" fill="#e0f2fe" stroke="#94a3b8" stroke-width="1"/><line x1="22" y1="8" x2="22" y2="72" stroke="#94a3b8" stroke-width="1.5"/><line x1="38" y1="8" x2="38" y2="72" stroke="#94a3b8" stroke-width="1.5"/><line x1="8" y1="30" x2="52" y2="30" stroke="#94a3b8" stroke-width="1.5"/><line x1="8" y1="50" x2="52" y2="50" stroke="#94a3b8" stroke-width="1.5"/></svg>'
        +'<div style="font-size:10px;font-weight:500;color:#334155">2V, 2H</div></div>'
        +'</div>'
        +'</div>'
        +'<button onclick="cartAddFromModal()" style="width:100%;padding:12px;background:linear-gradient(135deg,#059669,#10b981);color:#fff;border:none;border-radius:10px;font-size:14px;font-weight:700;cursor:pointer;font-family:inherit;display:flex;align-items:center;justify-content:center;gap:8px;transition:opacity .15s;box-shadow:0 4px 12px rgba(16,185,129,.3)" onmouseover="this.style.opacity=\'0.9\'" onmouseout="this.style.opacity=\'1\'"><svg viewBox="0 0 24 24" style="width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:2"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/></svg>Lägg till i kalkyl</button>'
        +'</div>'
        +'</div>'
        + bottomHtml
        +'</div>';

    document.body.appendChild(modal);

    // Load tillval
    // Init width-only
    _spSelectedWidthOnly = null;
    // Init style+width chips
    if(p.specs && p.specs.type === 'style_width_variants') {
        _spSelectedStyle = 0;
        _spSelectedWidth = null;
        spRenderWidthChips();
    }
    if(!["fonster","dorrar"].includes(p.cat)){var _cs=document.getElementById("spColorSection");if(_cs)_cs.style.display="none";var _ss=document.getElementById("spSprojsSection");if(_ss)_ss.style.display="none";}
    // KVM input for per-unit products
    var qtyArea=document.getElementById('spQtyArea');
    if(qtyArea && p.unit && p.unit!=='st'){
        var unitLabel=p.unit==='kvm'?'m²':p.unit;
        qtyArea.innerHTML='<div style="margin-bottom:12px"><label style="font-size:11px;font-weight:600;color:#64748b;display:block;margin-bottom:4px">Antal '+unitLabel+'</label><input type="number" id="spQtyInput" value="1" min="1" placeholder="150" oninput="spUpdatePrice()" style="width:160px;padding:10px 14px;border:1.5px solid #e5e7eb;border-radius:8px;font-size:14px;font-family:inherit"></div>';
    }
    await spLoadTillval(p.id);
    spUpdatePrice();
}

async function spLoadTillval(productId) {
    try {
        const [svcRes, accRes, linkedRes] = await Promise.all([
            fetch('/api/products.php?cat=tjanster&active=1'),
            fetch('/api/products.php?cat=tillbehor&active=1'),
            fetch('/api/products.php?services_for='+productId)
        ]);
        const svcData = await svcRes.json();
        const accData = await accRes.json();
        const linkedData = await linkedRes.json();
        const linkedMap = {}; if(linkedData.success) (linkedData.services||[]).forEach(s=>{linkedMap[s.service_id]={default_on:parseInt(s.default_on),hidden:parseInt(s.hidden||0),mandatory:parseInt(s.mandatory||0),default_variant:s.default_variant||null,default_qty:s.default_qty!=null?parseFloat(s.default_qty):null,min_qty:s.min_qty!=null?parseFloat(s.min_qty):null,max_qty:s.max_qty!=null?parseFloat(s.max_qty):null};});
        const linked = Object.keys(linkedMap);
        const allProducts = [...(svcData.success ? svcData.products : []), ...(accData.success ? accData.products : [])];
        _spTillval = allProducts
            .filter(s => linked.includes(s.id))
            .map(s => {
                let specs = null;
                try { if(s.specs) specs = typeof s.specs === 'string' ? JSON.parse(s.specs) : s.specs; } catch(e){}
                const lnk=linkedMap[s.id]||{}; const isOblig=parseInt(s.tillval_obligatorisk)||0; const isHid=parseInt(s.tillval_hidden)||0;
                const linkMin = lnk.min_qty!=null ? lnk.min_qty : null;
                const linkMax = lnk.max_qty!=null ? lnk.max_qty : null;
                const linkDef = lnk.default_qty!=null ? lnk.default_qty : null;
                const seedUserQty = linkDef!=null ? linkDef : 0;
                return { id: s.id, name: s.name, price: parseFloat(s.price), unit: s.unit || 'st', checked: !!(lnk.default_on || isOblig || lnk.mandatory), included: !!(isOblig || lnk.mandatory), hidden: !!(lnk.hidden || isHid), qty: 1, userQty: seedUserQty, userL: 0, userW: 0, computedPrice: null, excluded: false, included: false, specs: specs, selectedStyle: 0, selectedVariant: null, linkMin: linkMin, linkMax: linkMax, linkDefaultQty: linkDef };
            });
        // Resolve default_variant to selectedStyle + selectedVariant
        _spTillval.forEach(function(tv) {
            var lnk = linkedMap[tv.id] || {};
            if (lnk.default_variant && tv.specs && tv.specs.type === 'style_width_variants' && tv.specs.styles) {
                var parts = lnk.default_variant.split('|');
                for (var si = 0; si < tv.specs.styles.length; si++) {
                    if (tv.specs.styles[si].style === parts[0]) {
                        tv.selectedStyle = si;
                        if (parts[1]) {
                            for (var wi = 0; wi < tv.specs.styles[si].variants.length; wi++) {
                                if (String(tv.specs.styles[si].variants[wi].width_mm) === parts[1]) { tv.selectedVariant = wi; break; }
                            }
                        }
                        break;
                    }
                }
            }
        });
        spApplyRulesAndRender();
    } catch(e) { console.error('Load tillval error:', e); }
}

function spApplyRulesAndRender() {
    const p = _spProduct;
    const variants = (p.specs && p.specs.variants) || [];
    const rules = (p.specs && p.specs.rules) || [];
    const selectedVariant = _spSelectedWindowSize || variants[_spSelectedVariant] || {};
    // 1) Kör föräldraproduktens regler
    const ruleResult = peApplyRules(rules, selectedVariant, _spTillval);
    // Bygg berikad data med alla tillvals valda bredder
    const enrichedVariant = Object.assign({}, selectedVariant);
    _spTillval.forEach(tv => {
        if(tv.checked && tv.specs && tv.selectedVariant !== null) {
            const key = tv.id.replace(/-/g, '_');
            if(tv.specs.type === 'style_width_variants') {
                const style = tv.specs.styles[tv.selectedStyle] || tv.specs.styles[0];
                const v = style && style.variants[tv.selectedVariant];
                if(v) { enrichedVariant[key + '_width'] = v.width_mm || 0; enrichedVariant[key + '_style'] = tv.selectedStyle; }
            } else if(tv.specs.type === 'width_variants') {
                const v = tv.specs.variants[tv.selectedVariant];
                if(v) enrichedVariant[key + '_width'] = v.width_mm || 0;
            }
        }
    });
    _spTillval.forEach(tv => {
        // 2) Kör tillvalets EGNA regler med berikad variant-data (inkl andra tillvals val)
        const tvRules = (tv.specs && tv.specs.rules) || [];
        const tvResult = tvRules.length ? peApplyRules(tvRules, enrichedVariant, []) : {};
        const selfResult = tvResult[''] || tvResult['__product'] || null;
        // Merge: förälderns regel först, sedan tillvalets egen
        const r = ruleResult[tv.id] || {};
        if(selfResult) { Object.keys(selfResult).forEach(k => { if(selfResult[k] !== null && selfResult[k] !== undefined) r[k] = selfResult[k]; }); }
        if(Object.keys(r).length) {
            tv.excluded = !!r.exkluderad;
            tv.included = !!r.inkluderad;
            tv.computedPrice = r.pris !== null && r.pris !== undefined ? r.pris : null;
            if(r.antal !== null && r.antal !== undefined) tv.qty = r.antal; else tv.qty = 1;
            if(r.max !== undefined) tv.max = r.max;
            if(r.min !== undefined) tv.min = r.min;
            if(r.langd !== undefined) { tv.computedLangd = r.langd; if(tv.unit === "m" || tv.unit === "löpmeter") tv.userQty = Math.round(r.langd) / 1000; }
            if(r.bredd !== undefined) tv.computedBredd = r.bredd;
            if(tv.included) tv.checked = true;
            if(tv.excluded) tv.checked = false;
        } else {
            tv.excluded = false; tv.included = false; tv.computedPrice = null; tv.qty = 1;
            delete tv.max; delete tv.min; delete tv.computedLangd; delete tv.computedBredd;
        }
    });
    spRenderTillval();
    spUpdatePrice();
}

function spRenderTillval() {
    const el = document.getElementById('spTillvalArea');
    if(!el) return;
    const visible = _spTillval.filter(tv => !tv.excluded && !tv.hidden);
    if(!visible.length) { el.innerHTML = ''; return; }
    el.innerHTML = '<div style="font-size:11px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:.5px;margin-bottom:8px">Tillval</div><div style="display:grid;grid-template-columns:1fr 1fr;gap:6px">'
        + visible.map(tv => {
            const realIdx = _spTillval.indexOf(tv);
            let price = tv.computedPrice !== null ? tv.computedPrice : tv.price;
            // Override price from selected variant
            if(tv.specs && tv.selectedVariant !== null && tv.selectedVariant !== undefined) {
                if(tv.specs.type === 'style_width_variants') {
                    const sv = tv.specs.styles[tv.selectedStyle]?.variants[tv.selectedVariant];
                    if(sv && sv.price != null) price = sv.price;
                } else if(tv.specs.type === 'width_variants') {
                    const wv = tv.specs.variants[tv.selectedVariant];
                    if(wv && wv.price != null) price = wv.price;
                }
            }
            const stHasUserQty = tv.unit === 'st' && (tv.linkMax > 1 || tv.linkDefaultQty != null) && tv.userQty > 0;
            const effectiveQty = tv.unit === 'm²' ? (tv.userL * tv.userW) : (tv.unit !== 'st' && tv.userQty > 0 ? tv.userQty : (stHasUserQty ? tv.userQty : tv.qty));
            const totalPrice = price * (effectiveQty || (tv.unit === 'st' ? 1 : 0));
            const unitLabel = tv.unit && tv.unit !== 'st' ? ' <span style="color:#94a3b8;font-weight:400;font-size:11px">('+price.toLocaleString('sv-SE')+' kr/'+tv.unit+')</span>' : '';

            // Build variant selector for tillval with specs
            let variantSelector = '';
            if(tv.specs && tv.specs.type === 'style_width_variants' && tv.checked) {
                const styles = tv.specs.styles;
                const selStyle = styles[tv.selectedStyle] || styles[0];
                variantSelector = '<div style="margin-top:8px;display:flex;flex-direction:column;gap:6px" onclick="event.preventDefault();event.stopPropagation()">'
                    +'<div style="display:flex;gap:4px;flex-wrap:wrap">'
                    + styles.map((s, si) => '<button onclick="event.preventDefault();spSetTillvalStyle('+realIdx+','+si+')" style="padding:4px 10px;border-radius:6px;font-size:11px;font-weight:600;cursor:pointer;font-family:inherit;border:1.5px solid '+(si===tv.selectedStyle?'#024550':'#e5e7eb')+';background:'+(si===tv.selectedStyle?'#024550':'#fff')+';color:'+(si===tv.selectedStyle?'#fff':'#334155')+'">'+s.style+'</button>').join('')
                    +'</div>'
                    +'<select onchange="spSetTillvalVariant('+realIdx+',this.value)" style="padding:6px 10px;border:1.5px solid '+(tv.selectedVariant===null?'#f59e0b':'#e5e7eb')+';border-radius:6px;font-size:12px;font-family:inherit;background:'+(tv.selectedVariant===null?'#fffbeb':'#fff')+'">'
                    +'<option value="">⚠ Välj bredd (obligatorisk)...</option>'
                    + selStyle.variants.map((v, vi) => {
                        const lbl = (v.label || (v.width_mm ? v.width_mm + ' mm' : (v.length_mm ? v.length_mm + ' mm' : '')));
                        const pr = v.price != null ? ' — ' + v.price.toFixed(2) + ' kr/' + tv.unit : ' — pris saknas';
                        var isDefault = tv.selectedVariant === null && tv.specs.default_width && v.width_mm == tv.specs.default_width; if(isDefault) tv.selectedVariant = vi;
                        return '<option value="'+vi+'"'+((vi===tv.selectedVariant)?' selected':'')+'>'+lbl+pr+'</option>';
                    }).join('')
                    +'</select></div>';
            } else if(tv.specs && tv.specs.type === 'width_variants' && tv.checked) {
                variantSelector = '<div style="margin-top:8px" onclick="event.preventDefault();event.stopPropagation()">'
                    +'<select onchange="spSetTillvalVariant('+realIdx+',this.value)" style="padding:6px 10px;border:1.5px solid #e5e7eb;border-radius:6px;font-size:12px;font-family:inherit;background:#fff;width:100%">'
                    +'<option value="">Välj variant...</option>'
                    + tv.specs.variants.map((v, vi) => {
                        const lbl = v.label || (v.width_mm ? v.width_mm + ' mm' : (v.length_mm ? v.length_mm + ' mm' : ''));
                        const pr = v.price != null ? ' — ' + v.price.toFixed(2) + ' kr' : ' — pris saknas';
                        return '<option value="'+vi+'"'+(vi===tv.selectedVariant?' selected':'')+'>'+lbl+pr+'</option>';
                    }).join('')
                    +'</select></div>';
            }

            // Build quantity input based on unit
            let qtyInput = '';
            if(tv.unit === 'm²') {
                qtyInput = '<div style="display:flex;align-items:center;gap:4px;margin-top:6px" onclick="event.preventDefault();event.stopPropagation()">'
                    +'<input type="number" min="0" step="0.1" value="'+(tv.userL||'')+'" placeholder="L" oninput="spSetTillvalDim('+realIdx+',\'L\',this.value)" style="width:56px;padding:5px 6px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;font-family:inherit;text-align:center">'
                    +'<span style="font-size:11px;color:#94a3b8">×</span>'
                    +'<input type="number" min="0" step="0.1" value="'+(tv.userW||'')+'" placeholder="B" oninput="spSetTillvalDim('+realIdx+',\'W\',this.value)" style="width:56px;padding:5px 6px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;font-family:inherit;text-align:center">'
                    +'<span style="font-size:11px;color:#64748b">= '+(tv.userL&&tv.userW?(tv.userL*tv.userW).toFixed(1):'0')+' m²</span>'
                    +'</div>';
            } else if(tv.unit !== 'st') {
                qtyInput = '<div style="display:flex;align-items:center;gap:6px;margin-top:6px" onclick="event.preventDefault();event.stopPropagation()">'
                    +'<input type="number" min="0" step="0.1" value="'+(tv.userQty||'')+'" placeholder="Antal" oninput="spSetTillvalQty('+realIdx+',this.value)" style="width:70px;padding:5px 8px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;font-family:inherit">'
                    +'<span style="font-size:11px;color:#64748b">'+tv.unit+'</span>'
                    +'</div>';
            } else if(tv.checked && (tv.linkMax > 1 || tv.linkDefaultQty != null)) {
                const minAttr = tv.linkMin != null ? tv.linkMin : 1;
                const maxAttr = tv.linkMax != null ? ' max="'+tv.linkMax+'"' : '';
                const valNow = tv.userQty > 0 ? tv.userQty : (tv.linkDefaultQty != null ? tv.linkDefaultQty : 1);
                qtyInput = '<div style="display:flex;align-items:center;gap:6px;margin-top:6px" onclick="event.preventDefault();event.stopPropagation()">'
                    +'<label style="font-size:11px;color:#64748b">Antal</label>'
                    +'<input type="number" min="'+minAttr+'"'+maxAttr+' step="1" value="'+valNow+'" oninput="spSetTillvalQty('+realIdx+',this.value)" style="width:70px;padding:5px 8px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;font-family:inherit;text-align:center">'
                    +(tv.linkMax != null ? '<span style="font-size:11px;color:#94a3b8">/ max '+tv.linkMax+'</span>' : '')
                    +'</div>';
            } else if(tv.qty > 1) {
                qtyInput = '';
                // show qty from rules inline
            }

            const qtyLabel = (tv.unit === 'st' && tv.qty > 1) ? ' <span style="color:#94a3b8;font-weight:400">×'+tv.qty+'</span>' : '';

            return '<div style="padding:10px 14px;background:'+(tv.checked?'#f0fdf4':'#f8f9fa')+';border:1px solid '+(tv.checked?'#bbf7d0':'#e5e7eb')+';border-radius:10px;margin-bottom:6px;transition:all .15s">'
                +'<label style="display:flex;align-items:'+(qtyInput?'flex-start':'center')+';gap:10px;cursor:'+(tv.included?'default':'pointer')+'">'
                +'<input type="checkbox" '+(tv.checked?'checked':'')+' '+(tv.included?'disabled':'')+' onchange="spToggleTillval('+realIdx+')" style="width:18px;height:18px;accent-color:#059669;flex-shrink:0;margin-top:'+(qtyInput?'2px':'0')+'">'
                +'<div style="flex:1"><div style="display:flex;align-items:center;justify-content:space-between">'
                +'<span style="font-size:13px;font-weight:500;color:#334155">'+tv.name+qtyLabel+unitLabel+(tv.computedBredd?' <span style="color:#0284c7;font-size:11px;font-weight:400">B:'+Math.round(tv.computedBredd)+' mm</span>':'')+(tv.computedLangd?' <span style="color:#0284c7;font-size:11px;font-weight:400">L:'+Math.round(tv.computedLangd)+' mm</span>':'')+'</span>'
                +'<span style="font-size:13px;font-weight:700;color:'+(tv.checked?'#059669':'#94a3b8')+'">'+totalPrice.toLocaleString('sv-SE')+' kr</span>'
                +'</div>'+(variantSelector||'')+(qtyInput||'')+'</div>'
                +'</label>'
                +'</div>';
        }).join('') + '</div>';
}

function spSetTillvalStyle(idx, styleIdx) {
    _spTillval[idx].selectedStyle = styleIdx;
    _spTillval[idx].selectedVariant = null;
    spApplyRulesAndRender();
}
function spSetTillvalVariant(idx, val) {
    _spTillval[idx].selectedVariant = val !== '' ? parseInt(val) : null;
    spApplyRulesAndRender();
}

function spSetTillvalQty(idx, val) {
    var tv = _spTillval[idx];
    var n = parseFloat(val);
    if (!isFinite(n)) n = 0;
    if (tv.linkMin != null && n < tv.linkMin) n = tv.linkMin;
    if (tv.linkMax != null && n > tv.linkMax) n = tv.linkMax;
    tv.userQty = n;
    spRenderTillval();
    spUpdatePrice();
}

function spSetTillvalDim(idx, dim, val) {
    if(dim === 'L') _spTillval[idx].userL = parseFloat(val) || 0;
    else _spTillval[idx].userW = parseFloat(val) || 0;
    spRenderTillval();
    spUpdatePrice();
}

function spToggleTillval(idx) {
    if(_spTillval[idx].included || _spTillval[idx].excluded) return;
    _spTillval[idx].checked = !_spTillval[idx].checked;
    spApplyRulesAndRender();
}

function spSelectVariant(idx) {
    _spSelectedVariant = idx;
    document.querySelectorAll('.sp-variant-chip').forEach(btn => {
        const i = parseInt(btn.dataset.idx);
        btn.style.background = i===idx ? '#024550' : '#f8f9fa';
        btn.style.color = i===idx ? '#fff' : '#334155';
        btn.style.borderColor = i===idx ? '#024550' : '#e5e7eb';
    });
    spApplyRulesAndRender();
}

let _spSelectedWidthOnly = null;

function spSelectWidthOnly(idx) {
    _spSelectedWidthOnly = idx;
    document.querySelectorAll('.sp-wo-chip').forEach((btn, i) => {
        btn.style.background = i===idx ? '#024550' : '#f8f9fa';
        btn.style.color = i===idx ? '#fff' : '#334155';
        btn.style.borderColor = i===idx ? '#024550' : '#e5e7eb';
    });
    spUpdatePrice();
}

let _spSelectedWindowSize = null;
let _spSelectedStyle = 0;
let _spSelectedWidth = null;

function spSelectStyle(idx) {
    _spSelectedStyle = idx;
    _spSelectedWidth = null;
    document.querySelectorAll('.sp-style-chip').forEach((btn, i) => {
        btn.style.background = i===idx ? '#024550' : '#f8f9fa';
        btn.style.color = i===idx ? '#fff' : '#334155';
        btn.style.borderColor = i===idx ? '#024550' : '#e5e7eb';
    });
    spRenderWidthChips();
    document.getElementById('spStyleResult').style.display = 'none';
    spUpdatePrice();
}

function spRenderWidthChips() {
    const el = document.getElementById('spWidthChips');
    if(!el || !_spProduct || !_spProduct.specs || _spProduct.specs.type !== 'style_width_variants') return;
    const style = _spProduct.specs.styles[_spSelectedStyle];
    if(!style) return;
    el.innerHTML = style.variants.map((v, i) => {
        const label = v.label || (v.width_mm ? v.width_mm + ' mm' : (v.length_mm ? v.length_mm + ' mm' : ''));
        const priceStr = v.price != null ? v.price.toFixed(2) + ' kr' : '–';
        return '<button onclick="spSelectWidth('+i+')" class="sp-width-chip" data-idx="'+i+'" style="padding:6px 14px;border-radius:8px;font-size:12px;font-weight:600;cursor:pointer;font-family:inherit;transition:all .15s;background:#f8f9fa;color:#334155;border:2px solid #e5e7eb;display:flex;flex-direction:column;align-items:center;gap:2px">'
            +'<span>'+label+'</span>'
            +'<span style="font-size:10px;font-weight:400;color:#64748b">'+priceStr+'</span>'
            +'</button>';
    }).join('');
}

function spSelectWidth(idx) {
    _spSelectedWidth = idx;
    document.querySelectorAll('.sp-width-chip').forEach((btn, i) => {
        btn.style.background = i===idx ? '#024550' : '#f8f9fa';
        btn.style.color = i===idx ? '#fff' : '#334155';
        btn.style.borderColor = i===idx ? '#024550' : '#e5e7eb';
    });
    const style = _spProduct.specs.styles[_spSelectedStyle];
    const v = style.variants[idx];
    const resEl = document.getElementById('spStyleResult');
    if(resEl && v) {
        const label = v.label || (v.width_mm ? v.width_mm + ' mm' : (v.length_mm ? v.length_mm + ' mm' : ''));
        if(v.price != null) {
            resEl.innerHTML = '<div style="background:#f0fdf4;border:1px solid #bbf7d0;border-radius:10px;padding:12px;display:flex;justify-content:space-between;align-items:center">'
                +'<div><span style="font-weight:600;color:#334155">'+style.style+' '+label+'</span></div>'
                +'<div style="font-size:20px;font-weight:800;color:#024550">'+v.price.toFixed(2)+' kr/'+(_spProduct.unit||'st')+'</div></div>';
        } else {
            resEl.innerHTML = '<div style="background:#fef3c7;border:1px solid #fde68a;border-radius:10px;padding:12px;text-align:center;color:#d97706;font-weight:600;font-size:13px">Pris ej angivet – kontakta Hedlunds</div>';
        }
        resEl.style.display = 'block';
    }
    spUpdatePrice();
}

function spWinQty(delta) {
    const el = document.getElementById('spWinAntal');
    if(!el) return;
    let v = parseInt(el.value) || 1;
    v = Math.max(1, v + delta);
    el.value = v;
    spUpdateWindowSize();
}

function spUpdateWindowSize() {
    const p = _spProduct;
    if(!p || !p.specs || p.specs.type !== 'window_sizes') return;
    const wSel = document.getElementById('spWinWidth');
    const hSel = document.getElementById('spWinHeight');
    const resEl = document.getElementById('spWinResult');
    if(!wSel || !hSel || !resEl) return;

    const w = parseInt(wSel.value);
    const h = parseInt(hSel.value);

    if(!w || !h) {
        resEl.style.display = 'none';
        _spSelectedWindowSize = null;
        spUpdatePrice();
        return;
    }

    // Filter heights based on selected width
    const availH = p.specs.sizes.filter(s => s.w === w).map(s => s.h);
    Array.from(hSel.options).forEach(opt => {
        if(!opt.value) return;
        const hv = parseInt(opt.value);
        opt.disabled = !availH.includes(hv);
        opt.style.color = availH.includes(hv) ? '' : '#ccc';
    });

    // Filter widths based on selected height
    const availW = p.specs.sizes.filter(s => s.h === h).map(s => s.w);
    Array.from(wSel.options).forEach(opt => {
        if(!opt.value) return;
        const wv = parseInt(opt.value);
        opt.disabled = !availW.includes(wv);
        opt.style.color = availW.includes(wv) ? '' : '#ccc';
    });

    const match = p.specs.sizes.find(s => s.w === w && s.h === h);
    if(match) {
        _spSelectedWindowSize = match;
        // Set price from selected model or auto-select single model
        if(p.specs.models && p.specs.models.length) {
            var availModels = p.specs.models.filter(function(m){ return match[m.key] != null; });
            if(availModels.length === 1) _spSelectedWinModel = availModels[0].key;
            if(_spSelectedWinModel && match[_spSelectedWinModel] != null) _spSelectedWindowSize.price = match[_spSelectedWinModel];
        }
        let noteHtml = '';
        if(match.note && match.note !== 'None' && match.note !== '') {
            const noteColor = match.note.toLowerCase().includes('laminated') ? '#d97706' :
                             match.note.toLowerCase().includes("can't") || match.note.toLowerCase().includes('no guarantee') ? '#dc2626' : '#64748b';
            noteHtml = '<div style="margin-top:6px;padding:6px 10px;border-radius:6px;font-size:12px;font-weight:600;color:'+noteColor+';background:'+(noteColor==='#dc2626'?'#fef2f2':'#fffbeb')+'">'+match.note+'</div>';
        }
        const antal = parseInt(document.getElementById('spWinAntal')?.value) || 1;
        const _cr = _currencyRates[p.costCurrency||'EUR'] || _currencyRates['EUR'] || 1;
        const _mk = p.markupType==='percent'&&p.markupValue ? (1+p.markupValue/100) : 1;
        const _mkA = p.markupType==='amount'&&p.markupValue ? p.markupValue*antal : 0;
        if(p.specs.models && p.specs.models.length) {
            var modelRows = '';
            var hasAny = false;
            p.specs.models.forEach(function(m) {
                var mp = match[m.key];
                if(mp != null) {
                    hasAny = true;
                    var mTotal = Math.round(mp * antal * _cr * _mk + _mkA);
                    var selected = _spSelectedWinModel === m.key;
                    modelRows += '<div onclick="spSelectWinModel(\''+m.key+'\','+mp+')" style="display:flex;justify-content:space-between;align-items:center;padding:10px 14px;border:2px solid '+(selected?'#3b82f6':'#e5e7eb')+';border-radius:8px;cursor:pointer;background:'+(selected?'#f0f9ff':'#fff')+'">'
                        +'<div style="font-weight:600;font-size:13px;color:#334155">'+m.label+'</div>'
                        +'<div style="font-weight:800;font-size:15px;color:#024550">'+mTotal.toLocaleString('sv-SE')+' kr</div>'
                        +'</div>';
                }
            });
            if(hasAny) {
                resEl.innerHTML = '<div style="margin-top:8px">'
                    +'<div style="font-size:12px;font-weight:600;color:#334155;margin-bottom:6px">'+w+' \u00d7 '+h+' dm ('+(w*10)+' \u00d7 '+(h*10)+' cm) \u2014 v\u00e4lj modell:</div>'
                    +'<div style="display:grid;gap:6px">'+modelRows+'</div></div>';
            } else {
                resEl.innerHTML = '<div style="background:#fef2f2;border:1px solid #fecaca;border-radius:10px;padding:14px;margin-top:8px;text-align:center;color:#dc2626;font-size:13px;font-weight:600">Inga modeller i '+w+'\u00d7'+h+'</div>';
            }
            if(_spSelectedWinModel && _spSelectedWindowSize && _spSelectedWindowSize[_spSelectedWinModel] != null) _spSelectedWindowSize.price = _spSelectedWindowSize[_spSelectedWinModel];
            resEl.style.display = 'block';
            spApplyRulesAndRender();
            return;
        }
        var unitPrice = match.price || 0;
        const totalEur = unitPrice * antal;
        const totalSek = Math.round(totalEur * _cr * _mk + _mkA);
        resEl.innerHTML = '<div style="background:#f0fdf4;border:1px solid #bbf7d0;border-radius:10px;padding:14px;margin-top:8px">'
            +'<div style="display:flex;justify-content:space-between;align-items:center">'
            +'<div><div style="font-size:13px;font-weight:600;color:#334155">'+w+' \u00d7 '+h+' dm <span style="color:#94a3b8;font-weight:400">('+(w*10)+' \u00d7 '+(h*10)+' cm)</span></div>'
            +'<div style="font-size:11px;color:#64748b;margin-top:2px">Area: '+((w*h)/100).toFixed(2)+' m\u00b2 | Art.nr: '+(match.article_id||'')+'</div></div>'
            +'<div style="text-align:right"><div style="font-size:22px;font-weight:800;color:#024550">'+totalSek.toLocaleString('sv-SE')+' kr</div>'
            +(antal > 1 ? '<div style="font-size:11px;color:#64748b">'+antal+' st \u00d7 '+Math.round(unitPrice*_cr*_mk).toLocaleString('sv-SE')+' kr</div>' : '')
            +'</div></div>'
            +noteHtml
        resEl.style.display = 'block';
    } else {
        _spSelectedWindowSize = null;
        resEl.innerHTML = '<div style="background:#fef2f2;border:1px solid #fecaca;border-radius:10px;padding:14px;margin-top:8px;text-align:center;color:#dc2626;font-size:13px;font-weight:600">Denna storlekskombination finns ej tillgänglig</div>';
        resEl.style.display = 'block';
    }
    spApplyRulesAndRender();
}

// === SPRÖJS ===
let _spSelectedSprojs = 0;
function spSelectSprojs(idx) {
    _spSelectedSprojs = idx;
    document.querySelectorAll('.sp-sprojs-opt').forEach(el => {
        const i = parseInt(el.dataset.idx);
        el.style.borderColor = i === idx ? '#024550' : '#e5e7eb';
        el.style.background = i === idx ? '#f0fdf4' : '';
    });
}

// === FÄRGVÄLJARE ===
const _spColors = [
    { name:'Vit', code:'RAL 9010', hex:'#f0ece4', group:'standard' },
    { name:'Röd', code:'RAL 3011', hex:'#781f1c', group:'kulör' },
    { name:'Antracitgrå', code:'RAL 7016', hex:'#383e42', group:'kulör' },
    { name:'Matt svart', code:'RAL 9005M', hex:'#1a1a1a', group:'kulör' },
    { name:'Grafitsvart', code:'RAL 9011', hex:'#262626', group:'kulör' },
    { name:'Ljusgrå', code:'RAL 7035', hex:'#b0b0a8', group:'kulör' },
    { name:'Kvartsgrå', code:'RAL 7039', hex:'#6b6560', group:'kulör' },
    { name:'Basaltgrå', code:'RAL 7012', hex:'#585c5e', group:'kulör' },
    { name:'Mörkbrun', code:'RAL 8019', hex:'#3b3332', group:'kulör' },
    { name:'Svart', code:'RAL 9005', hex:'#0e0e10', group:'kulör' },
    { name:'Grön', code:'RAL 6009', hex:'#1f3a28', group:'kulör' },
    { name:'Blå', code:'RAL 5011', hex:'#1a2b3c', group:'kulör' },
];
let _spSelectedColors = { insida: _spColors[0], utsida: _spColors[0] };

function spOpenColorPicker(side) {
    document.getElementById('spColorPickerModal')?.remove();
    const isInsida = side === 'insida';
    const current = _spSelectedColors[side];
    const title = isInsida ? 'Kulör insida' : 'Kulör utsida';
    const note = isInsida ? 'Insidan är normalt alltid vit.' : 'Välj vilken färg du ska ha på utsidan.';

    const modal = document.createElement('div');
    modal.id = 'spColorPickerModal';
    modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:99999;display:flex;align-items:center;justify-content:center;padding:20px';
    modal.onclick = e => { if(e.target === modal) modal.remove(); };

    modal.innerHTML = '<div style="background:#fff;border-radius:16px;max-width:600px;width:100%;max-height:80vh;overflow-y:auto;box-shadow:0 25px 60px rgba(0,0,0,.3)">'
        +'<div style="padding:20px 24px;border-bottom:1px solid #f1f5f9;display:flex;justify-content:space-between;align-items:center">'
        +'<div><h3 style="font-size:18px;font-weight:700;margin:0;color:#1a1a1a">'+title+'</h3>'
        +'<div style="font-size:12px;color:#94a3b8;margin-top:2px">'+current.name+'</div></div>'
        +'<button onclick="document.getElementById(\'spColorPickerModal\').remove()" style="display:flex;align-items:center;gap:4px;background:none;border:none;cursor:pointer;font-size:14px;font-weight:700;color:#334155;font-family:inherit">Stäng <svg viewBox="0 0 24 24" style="width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:2"><polyline points="18 15 12 9 6 15"/></svg></button>'
        +'</div>'
        +'<div style="padding:20px 24px">'
        +'<p style="font-size:13px;color:#64748b;margin:0 0 16px">'+note+'</p>'
        +'<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(100px,1fr));gap:10px">'
        + _spColors.map((c, i) => {
            const selected = current.code === c.code;
            return '<div onclick="spSelectColor(\''+side+'\','+i+')" style="cursor:pointer;border:2px solid '+(selected?'#024550':'#e5e7eb')+';border-radius:10px;padding:8px;text-align:center;transition:all .15s;background:'+(selected?'#f0fdf4':'#fff')+'" onmouseover="this.style.borderColor=\'#94a3b8\'" onmouseout="this.style.borderColor=\''+(selected?'#024550':'#e5e7eb')+'\'">'
                +'<div style="width:100%;aspect-ratio:1;background:'+c.hex+';border-radius:6px;border:1px solid rgba(0,0,0,.1);margin-bottom:6px"></div>'
                +'<div style="font-size:11px;font-weight:600;color:#1a1a1a">'+c.name+'</div>'
                +'<div style="font-size:9px;color:#94a3b8">'+c.code+'</div>'
                +'</div>';
        }).join('')
        +'</div></div></div>';

    document.body.appendChild(modal);
}

function spSelectColor(side, idx) {
    _spSelectedColors[side] = _spColors[idx];
    const c = _spColors[idx];
    document.getElementById('spColor'+(side==='insida'?'Insida':'Utsida')+'Swatch').style.background = c.hex;
    document.getElementById('spColor'+(side==='insida'?'Insida':'Utsida')+'Name').textContent = c.name;
    document.getElementById('spColor'+(side==='insida'?'Insida':'Utsida')+'Code').textContent = c.code;
    document.getElementById('spColorPickerModal')?.remove();
}

function spUpdatePrice() {
    const el = document.getElementById('spPriceArea');
    if(!el) return;
    const p = _spProduct;
    const variants = (p.specs && p.specs.variants) || [];
    const rules = (p.specs && p.specs.rules) || [];
    const sv = variants[_spSelectedVariant] || {};
    const ruleResult = peApplyRules(rules, sv, _spTillval);

    let displayPrice = p.price, greenDeduction = 0, isPerUnit = false, unitLabel = '';

    // Width-only override
    if(p.specs && p.specs.type === 'width_variants') {
        if(_spSelectedWidthOnly !== null) {
            const v = p.specs.variants[_spSelectedWidthOnly];
            if(v && v.price != null) {
                displayPrice = v.price;
                unitLabel = 'kr/' + (p.unit || 'st');
                isPerUnit = true;
            } else {
                el.innerHTML = '<div style="text-align:center;color:#d97706;font-size:13px;padding:8px">Pris ej angivet</div>';
                return;
            }
        } else {
            el.innerHTML = '<div style="text-align:center;color:#94a3b8;font-size:13px;padding:8px">Välj variant för att se pris</div>';
            return;
        }
    }

    // Style+width override
    if(p.specs && p.specs.type === 'style_width_variants') {
        if(_spSelectedWidth !== null) {
            const style = p.specs.styles[_spSelectedStyle];
            const v = style && style.variants[_spSelectedWidth];
            if(v && v.price != null) {
                displayPrice = v.price;
                unitLabel = 'kr/' + (p.unit || 'st');
                isPerUnit = true;
            } else {
                el.innerHTML = '<div style="text-align:center;color:#d97706;font-size:13px;padding:8px">Pris ej angivet</div>';
                return;
            }
        } else {
            el.innerHTML = '<div style="text-align:center;color:#94a3b8;font-size:13px;padding:8px">Välj utförande och bredd för att se pris</div>';
            return;
        }
    }

    // Window sizes override
    if(p.specs && p.specs.type === 'window_sizes') {
        if(_spSelectedWindowSize) {
            // If models exist but none selected, show message
            if(p.specs.models && p.specs.models.length && !_spSelectedWinModel) {
                el.innerHTML = '<div style="text-align:center;color:#d97706;font-size:13px;padding:12px;background:#fef3c7;border-radius:8px">Välj en modell ovan för att se pris</div>';
                return;
            }
            const winAntal = parseInt(document.getElementById('spWinAntal')?.value) || 1;
            let eurPrice = (_spSelectedWindowSize.price || 0) * winAntal;
            // Konvertera till SEK
            const currency = p.costCurrency || 'EUR';
            const rate = _currencyRates[currency] || _currencyRates['EUR'] || 1;
            let sekPrice = eurPrice * rate;
            // Applicera påslag
            if(p.markupType === 'percent' && p.markupValue) {
                sekPrice = sekPrice * (1 + p.markupValue / 100);
            } else if(p.markupType === 'amount' && p.markupValue) {
                sekPrice = sekPrice + (p.markupValue * winAntal);
            }
            displayPrice = Math.round(sekPrice);
            unitLabel = 'kr';
            isPerUnit = true;
        } else {
            el.innerHTML = '<div style="text-align:center;color:#94a3b8;font-size:13px;padding:8px">Välj bredd och höjd för att se pris</div>';
            return;
        }
    }

    var spQtyEl=document.getElementById('spQtyInput');var spQty=spQtyEl?parseInt(spQtyEl.value)||1:1;
    if(variants.length) {
        if(sv.totalt !== undefined) {
            displayPrice = parseFloat(sv.totalt) || 0;
            if(sv.gron_teknik !== undefined) greenDeduction = parseFloat(sv.gron_teknik) || 0;
        } else if(sv.pris_per_m2 !== undefined) {
            displayPrice = parseFloat(sv.pris_per_m2) || 0;
            isPerUnit = true; unitLabel = 'kr/m²';
        } else if(sv.price !== undefined) {
            displayPrice = parseFloat(sv.price) || 0;
        } else {
            displayPrice = p.price;
        }
    }
    if(p.unit && p.unit!=='st' && spQty>1 && !isPerUnit) displayPrice=displayPrice*spQty;
        if(ruleResult['__product'] && ruleResult['__product'].pris !== null) {
        displayPrice = ruleResult['__product'].pris;
    }

    let tillvalTotal = 0;
    _spTillval.filter(tv => tv.checked && !tv.excluded).forEach(tv => {
        let pr = tv.computedPrice !== null ? tv.computedPrice : tv.price;
        if(tv.specs && tv.selectedVariant !== null && tv.selectedVariant !== undefined) {
            if(tv.specs.type === 'style_width_variants') {
                const sv = tv.specs.styles[tv.selectedStyle]?.variants[tv.selectedVariant];
                if(sv && sv.price != null) pr = sv.price;
            } else if(tv.specs.type === 'width_variants') {
                const wv = tv.specs.variants[tv.selectedVariant];
                if(wv && wv.price != null) pr = wv.price;
            }
        }
        const stHasUserQty = (tv.unit === 'st' || !tv.unit) && (tv.linkMax > 1 || tv.linkDefaultQty != null) && tv.userQty > 0;
        const eq = tv.unit === 'm²' ? (tv.userL * tv.userW) : (tv.unit && tv.unit !== 'st' && tv.userQty > 0 ? tv.userQty : (stHasUserQty ? tv.userQty : tv.qty));
        tillvalTotal += pr * (eq || (tv.unit === 'st' || !tv.unit ? 1 : 0));
    });

    let html = '';
    if(isPerUnit) {
        var unitTotal = displayPrice * spQty;
        html = '<div style="display:flex;justify-content:space-between;align-items:baseline">'
            +'<span style="font-size:15px;font-weight:700;color:#1a1a1a">Pris</span>'
            +'<span style="font-size:24px;font-weight:800;color:#024550">'+displayPrice.toLocaleString('sv-SE')+' '+unitLabel+'</span>'
            +'</div>';
        if(tillvalTotal > 0) {
            html += '<div style="display:flex;justify-content:space-between;align-items:center;margin-top:8px;padding-top:8px;border-top:1px solid #e5e7eb">'
                +'<span style="font-size:13px;color:#64748b">Tillval</span>'
                +'<span style="font-size:14px;font-weight:600;color:#334155">+'+tillvalTotal.toLocaleString('sv-SE')+' kr</span>'
                +'</div>';
        }
        // Skatteavdrag baserat på produktens taxType
        var tax = typeof getTax === 'function' ? getTax() : {gt20:20, gt50:50, rot:30, rotMax:50000};
        var productTaxType = p.taxType || p.tax_type || 'NONE';
        var unitTaxDeduction = 0;
        var unitTotalWithTillval = unitTotal + tillvalTotal;
        if(productTaxType === 'GT20') {
            unitTaxDeduction = Math.min(Math.round(unitTotalWithTillval * tax.gt20 / 100), 50000);
            html += '<div style="display:flex;justify-content:space-between;align-items:center;margin-top:6px"><span style="font-size:13px;color:#059669">Grönt teknik (GT20)</span><span style="font-size:15px;font-weight:600;color:#059669">-'+unitTaxDeduction.toLocaleString('sv-SE')+' kr</span></div>';
        } else if(productTaxType === 'GT50') {
            unitTaxDeduction = Math.min(Math.round(unitTotalWithTillval * tax.gt50 / 100), 50000);
            html += '<div style="display:flex;justify-content:space-between;align-items:center;margin-top:6px"><span style="font-size:13px;color:#059669">Skattereduktion (GT50)</span><span style="font-size:15px;font-weight:600;color:#059669">-'+unitTaxDeduction.toLocaleString('sv-SE')+' kr</span></div>';
        } else if(productTaxType === 'ROT') {
            var laborEst = Math.round(unitTotalWithTillval * 0.5);
            unitTaxDeduction = Math.min(Math.round(laborEst * tax.rot / 100), tax.rotMax);
            html += '<div style="display:flex;justify-content:space-between;align-items:center;margin-top:6px"><span style="font-size:13px;color:#2563eb">ROT-avdrag (' + tax.rot + '% på arbete)</span><span style="font-size:15px;font-weight:600;color:#2563eb">-'+unitTaxDeduction.toLocaleString('sv-SE')+' kr</span></div>';
        }
        var perUnitGrand = unitTotalWithTillval - unitTaxDeduction;
        html += '<div style="border-top:2px solid #e5e7eb;padding-top:10px;margin-top:8px;display:flex;justify-content:space-between;align-items:center">'
            +'<span style="font-size:15px;font-weight:700;color:#1a1a1a">Att betala</span>'
            +'<span style="font-size:24px;font-weight:800;color:#024550">'+perUnitGrand.toLocaleString('sv-SE')+' kr</span>'
            +'</div>';
    } else {
        // Hidden tillval (installation etc)
        var hiddenTotal=0, hiddenItems=[];
        _spTillval.filter(function(tv){return tv.checked && !tv.excluded && tv.hidden;}).forEach(function(tv){
            var hiPrice=tv.price*(tv.unit===p.unit&&spQty>1?spQty:1); hiddenTotal+=hiPrice; hiddenItems.push({name:tv.name,price:hiPrice});
            if(tv.childTillval) tv.childTillval.filter(function(ch){return ch.obligatorisk;}).forEach(function(ch){
                hiddenTotal+=ch.price; hiddenItems.push({name:ch.name,price:ch.price,isChild:true});
            });
        });
        // Product name + hidden items + totalt
        if(hiddenTotal > 0) {
            var arbete = displayPrice - hiddenTotal;
            html += '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px"><span style="font-size:13px;color:#64748b">'+p.name+'</span><span style="font-size:15px;font-weight:600;color:#334155">'+arbete.toLocaleString('sv-SE')+' kr</span></div>';
            hiddenItems.forEach(function(hi){
                html += '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;padding-left:'+(hi.isChild?'12':'0')+'px"><span style="font-size:13px;color:#64748b">'+hi.name+'</span><span style="font-size:15px;font-weight:600;color:#334155">'+hi.price.toLocaleString('sv-SE')+' kr</span></div>';
            });
            html += '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;padding-top:6px;border-top:1px solid #e5e7eb"><span style="font-size:13px;font-weight:600;color:#334155">Totalt</span><span style="font-size:15px;font-weight:700;color:#334155">'+displayPrice.toLocaleString('sv-SE')+' kr</span></div>';
        } else {
            html += '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px"><span style="font-size:13px;color:#64748b">'+p.name+'</span><span style="font-size:15px;font-weight:600;color:#334155">'+displayPrice.toLocaleString('sv-SE')+' kr</span></div>';
        }
        // Skatteavdrag baserat på produktens taxType (EN typ per installation)
        var tax = typeof getTax === 'function' ? getTax() : {gt20:20, gt50:50, rot:30, rotMax:50000};
        var productTaxType = p.taxType || p.tax_type || 'NONE';
        var taxDeduction = 0;
        var taxLabel = '';
        var totalWithTillval = displayPrice + tillvalTotal;

        if(productTaxType === 'GT20') {
            taxDeduction = Math.min(Math.round(totalWithTillval * tax.gt20 / 100), 50000);
            taxLabel = 'Grönt teknik-avdrag (GT20)';
        } else if(productTaxType === 'GT50') {
            taxDeduction = Math.min(Math.round(totalWithTillval * tax.gt50 / 100), 50000);
            taxLabel = 'Skattereduktion (GT50)';
        } else if(productTaxType === 'ROT') {
            // ROT enbart på arbete — uppskatta 50% arbete
            var laborEst = Math.round(totalWithTillval * 0.5);
            taxDeduction = Math.min(Math.round(laborEst * tax.rot / 100), tax.rotMax);
            taxLabel = 'ROT-avdrag (' + tax.rot + '% på arbete)';
        }
        // Bakåtkompatibilitet: om greenDeduction kommer från variant-data, använd den
        if(greenDeduction > 0 && taxDeduction === 0) {
            taxDeduction = greenDeduction;
            taxLabel = 'Grönt teknik-avdrag';
        }
        if(taxDeduction > 0) {
            html += '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px"><span style="font-size:13px;color:#059669">'+taxLabel+'</span><span style="font-size:15px;font-weight:600;color:#059669">-'+taxDeduction.toLocaleString('sv-SE')+' kr</span></div>';
        }
        var rotDeduction = 0; // Behåll variabelnamnet för grandTotal-beräkningen nedan
        // Itemized tillval
        if(tillvalTotal > 0) {
            html += '<div style="font-size:11px;font-weight:600;color:#94a3b8;text-transform:uppercase;letter-spacing:.5px;margin:4px 0">Tillval</div>';
            _spTillval.filter(function(tv){return tv.checked && !tv.excluded && !tv.hidden;}).forEach(function(tv){
                var tvPr = tv.price;
                if(tv.specs && tv.selectedVariant !== null && tv.selectedVariant !== undefined && tv.specs.variants && tv.specs.variants[tv.selectedVariant]) {
                    var sv2 = tv.specs.variants[tv.selectedVariant];
                    tvPr = sv2.totalt || sv2.price || sv2.att_betala || tvPr;
                }
                var stHasUserQty2 = (tv.unit === 'st' || !tv.unit) && (tv.linkMax > 1 || tv.linkDefaultQty != null) && tv.userQty > 0;
                var tvEq = (tv.unit && tv.unit !== 'st' && tv.userQty > 0) ? tv.userQty : (stHasUserQty2 ? tv.userQty : (tv.qty||1));
                var tvLine = tvPr * tvEq;
                html += '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:3px;padding-left:8px"><span style="font-size:12px;color:#64748b">'+tv.name+'</span><span style="font-size:13px;font-weight:600;color:#334155">+'+tvLine.toLocaleString('sv-SE')+' kr</span></div>';
                if(tv.childTillval) tv.childTillval.filter(function(ch){return ch.obligatorisk;}).forEach(function(ch){
                    html += '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:3px;padding-left:16px"><span style="font-size:11px;color:#94a3b8">'+ch.name+'</span><span style="font-size:12px;font-weight:600;color:#94a3b8">+'+ch.price.toLocaleString('sv-SE')+' kr</span></div>';
                });
            });
        }
        const grandTotal = displayPrice + tillvalTotal - taxDeduction;
        html += '<div style="border-top:2px solid #e5e7eb;padding-top:10px;margin-top:4px;display:flex;justify-content:space-between;align-items:center">'
            +'<span style="font-size:15px;font-weight:700;color:#1a1a1a">Att betala</span>'
            +'<span style="font-size:24px;font-weight:800;color:#024550">'+grandTotal.toLocaleString('sv-SE')+' kr</span>'
            +'</div>';
    }
    el.innerHTML = html;
}

function spSetImage(idx) {
    _spCurrentImage = idx;
    const mainImg = document.getElementById('spMainImage');
    if(mainImg) mainImg.src = _spImages[idx];
    document.querySelectorAll('#spThumbnails img').forEach((t, i) => { t.style.borderColor = i===idx ? '#024550' : '#e5e7eb'; });
    document.querySelectorAll('#spDots span').forEach((d, i) => { d.style.background = i===idx ? '#024550' : '#cbd5e1'; });
}

// === KALKYL VARUKORG (CART) ===
let _cartItems = [];