// Receipt comparison, features, how it works, pricing, FAQ, final CTA, footer. function ChipSvg({ type }) { const s = 28, r = 6; const chips = { netflix: ( <> ), viaplay: ( <> via ), disney: ( <> D+ ), max: ( <> MAX ), tv4: ( <> TV4 ), }; const key = /netflix/i.test(type) ? 'netflix' : /viaplay/i.test(type) ? 'viaplay' : /disney/i.test(type) ? 'disney' : /hbo|max/i.test(type) ? 'max' : /tv4/i.test(type) ? 'tv4' : 'netflix'; return ( ); } function BarcodeSvg() { const pat = [2,1,2,1,3,2,1,2,2,1,3,1,2,2,1,3,2,1,1,2,3,1,2,1,1,3,2,1,3,1,1,2,2,3,1,2,1,1,3,2,1,2,3,1,2,1,1,2,3,2]; let x = 8; const bars = []; pat.forEach((w, i) => { const bw = w * 2.6; if (i % 2 === 0) bars.push(); x += bw; }); return ( ); } function ReceiptBad({ mini = false }) { const C = COPY[window.locale]; const fmtPrice = p => p.replace(/\s*kr$/, ',00'); return (
{/* Handwritten annotations (full version only) */} {!mini && (
{C.rbtAnnot1}
{C.rbtAnnot2}
{C.rbtAnnot3}
)}
); } function ReceiptGood() { const C = COPY[window.locale]; const monthlyPrice = 58; const annualPrice = 699; return (
TVMOMENTO
{monthlyPrice} {C.perMnd}
vid 1-årsplan
    {C.goodFeatures.map(f => (
  • {f}
  • ))}
{C.cta}
); } function ReceiptCompare() { return null; } function FeatureSavings() { const C = COPY[window.locale]; return (
{C.badEyebrow}
{C.compareEyebrow}

{C.compareSaveH1}
{C.compareSaveH2}

{C.compareSaveBody}

{C.compareRows[0][0]}{C.compareRows[0][1]}{C.compareRows[0][2]}
{C.compareRows[1][0]}{C.compareRows[1][1]}{C.compareRows[1][2]}
{C.compareRows[2][0]}{C.compareRows[2][1]}{C.compareRows[2][2]}
{C.punch1}
{C.punch2}
); } function HowItWorks() { const C = COPY[window.locale]; const hwPrices = { m3: 349, y1: 699, y3: 1299 }; React.useEffect(() => { const io = new IntersectionObserver(entries => { entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add('visible'); io.unobserve(e.target); } }); }, { threshold: 0.12, rootMargin: '0px 0px -40px 0px' }); document.querySelectorAll('.how-step').forEach(s => io.observe(s)); return () => io.disconnect(); }, []); return (
{C.howEyebrow}

{C.howH1}
{C.howH2}

{/* Step 01 — plan comparison */}
01

{C.howS1Verb}

{C.howS1Body}

{C.howPlans[0]}
{hwPrices.m3} kr
{C.howPlans[1]}
{hwPrices.y1} kr {C.howPopular}
{C.howPlans[2]}
{hwPrices.y3} kr
{/* Step 02 — payment sheet */}
02

{C.howS2VerbA} {C.howS2VerbB}

{C.howS2Body}

Tvmomento{C.howPlans[1]}
{C.howPayLabel}{hwPrices.y1} kr
{C.howPayTotal}{hwPrices.y1} kr
Pay
VISA
S wish
Pay
link
{/* Step 03 — cinematic image */}
03

{C.howS3VerbA} {C.howS3VerbB}

{C.howS3Body}

{C.howS3Alt}
); } const DEVICE_IMAGES = ['assets/devices/tv.webp', 'assets/devices/phone.webp', 'assets/devices/tablet.webp', 'assets/devices/laptop.webp']; const DEVICE_ALT = ['Smart TV', 'Phone', 'Tablet', 'Laptop']; const DEVICE_LOGOS = [ /* TV */
SAMSUNG
LG
PHILIPS
Android TV
, /* Mobile */
SAMSUNG
Huawei
Xiaomi
, /* Tablet */
SAMSUNG
iPad
Lenovo
Surface
, /* Laptop */
Windows
macOS
Linux
ChromeOS
, ]; function DeviceCompat() { const C = COPY[window.locale]; const headerRef = React.useRef(null); React.useEffect(() => { const el = headerRef.current; if (!el) return; const io = new IntersectionObserver(([entry]) => { if (entry.isIntersecting) { el.classList.add('visible'); io.disconnect(); } }, { threshold: 0.05 }); io.observe(el); return () => io.disconnect(); }, []); return (
{C.deviceEyebrow}

{C.deviceHeadline}
{C.deviceSub}

{C.deviceIntro}

{C.devices.map((d, i) => (
{DEVICE_ALT[i]}

{d.name}

{d.desc}

{C.devicesLogosLabel[i]}
{DEVICE_LOGOS[i]}
))}
); } const CHECKOUT_WEBHOOK_URL = (typeof window !== 'undefined' && window.CHECKOUT_WEBHOOK_URL) ? window.CHECKOUT_WEBHOOK_URL : 'https://script.google.com/macros/s/AKfycbx_h_RdOCwuWzqOOf9P3I3g_1en18nHc8Bz81urBZS6S8Sc6rHfloSpYirtfdT7jQDS/exec'; const PRICING_COPY = { SE: { planNames: { '3m': '3 Månader', '1y': '1 År', '3y': '3 År + 6 mån gratis' }, planSub: { '3m': 'Engångsbelopp', '1y': 'Spara vs månadsvis', '3y': 'Bästa värdet — 3,5 år totalt' }, popularBadge: 'Populärast ★', chipUnit1: 'skärm', chipUnitN: 'skärmar', chipIncluded: 'Ingår', sumExtra: n => '+' + n + ' skärm' + (n > 1 ? 'ar' : ''), numLocale: 'sv-SE', currency: 'SEK', locale: 'SE', defaultCC: '+46', mCtaStep1: 'Välj antal skärmar →', mCtaStep2: 'Fortsätt →', mCtaStep3: 'Slutför beställning →', mCtaNote: 'Engångsbetalning', scrollUpBtn: 'Välj plan ↑', trustItems: ['Vi ringer inom 15 min', 'Inga kortuppgifter krävs', 'Krypterad anslutning (TLS 1.3)'], panelLabel: 'Välj ditt paket', summaryLabel: 'Din beställning', sectionLabel: 'Abonnemang', changeBtn: 'Ändra ↩', devicesTitle: 'Antal skärmar', devicesNote: 'Varje skärm kan titta på olika kanaler samtidigt.', chipPop: 'Vanligast', sumPlan: 'Paket', sumBase: 'Baspris', sumTotal: 'Totalt att betala', sumNote: 'Engångsbetalning — ingen prenumeration', orderNote: 'Engångsbetalning — ingen prenumeration', viewersSuffix: ' ser på detta just nu', slotsSuffix: ' platser kvar', formPhoneLabel: 'Telefonnummer', formEmailLabel: 'E-postadress', formPhoneErr: 'Ange ett giltigt telefonnummer', formEmailErr: 'Ange en giltig e-postadress', formPrivacy: 'Vi använder dina uppgifter för att skicka inloggningen. Inget spam.', formError: 'Något gick fel. Försök igen.', submitLabel: 'Slutför beställning', successBadge: 'PRISET LÅST', successHeading: 'Du är inne.', successSavingsHero: 'Du sparar 7 884 kr det här året.', successFromBefore: '715 kr/mån', successFromAfter: '58 kr/mån', successFromNote: 'Från 5 separata appar till 1.', successCallLine: 'Vi hör av oss på {phone} inom 15 min.', successHumanNote: 'Ingen bot — en riktig människa hjälper dig igång på 10 minuter.', successWelcome: 'Välkommen till 2 400+ smarta streamers. Nu är du en av dem.', successFootnote: 'Bekräftelse skickas till {email}.', successBeforeLbl: 'Tidigare', successAfterLbl: 'Nu', }, }; const PRICING_PLANS = { SE: { '3m': { base: 349, extra: 175 }, '1y': { base: 699, extra: 350 }, '3y': { base: 1299, extra: 650 }, }, }; function Pricing() { const ref = React.useRef(null); const loc = window.locale || 'SE'; const TC = PRICING_COPY[loc]; const TP = PRICING_PLANS[loc]; React.useEffect(() => { const root = ref.current; if (!root) return; const C = PRICING_COPY[window.locale || 'SE']; const PLANS = PRICING_PLANS[window.locale || 'SE']; let selPlan = '1y', selExtra = 0, planTapped = false, step2Active = false, step3Active = false; let emailRevealed = false; let selectedCC = C.defaultCC; function fmt(n) { return n.toLocaleString(C.numLocale); } function isMob() { return window.matchMedia('(max-width: 860px)').matches; } function updateSummary() { const plan = PLANS[selPlan], extra = selExtra * plan.extra, total = plan.base + extra; const qe = id => root.querySelector('#' + id); if (qe('sum-plan-name')) qe('sum-plan-name').textContent = C.planNames[selPlan]; if (qe('sum-base-price')) qe('sum-base-price').textContent = fmt(plan.base) + ' kr'; if (qe('sum-total')) qe('sum-total').textContent = fmt(total); const lfPrePrice = root.querySelector('#lf-pre-price'); if (lfPrePrice) lfPrePrice.textContent = fmt(total); const lfPrice = root.querySelector('#lf-price'); if (lfPrice) lfPrice.textContent = fmt(total); const extraRow = qe('sum-extra-row'); if (extraRow) { if (selExtra > 0) { extraRow.classList.add('visible'); root.querySelector('#sum-extra-label').textContent = C.sumExtra(selExtra); root.querySelector('#sum-extra-price').textContent = '+' + fmt(extra) + ' kr'; } else { extraRow.classList.remove('visible'); } } if (qe('ps-name')) qe('ps-name').textContent = C.planNames[selPlan]; if (qe('ps-price')) qe('ps-price').textContent = fmt(plan.base) + ' kr'; updateTopBar(); } function updateChipPrices() { const ppu = PLANS[selPlan].extra; for (let i = 1; i <= 5; i++) { const el = root.querySelector('#chip-price-' + i); if (el) el.textContent = '+' + fmt(i * ppu) + ' kr'; } } const mCtaEl = root.querySelector('.m-cta'); const mCtaBtn = root.querySelector('.m-cta-btn'); const mTopBar = root.querySelector('#m-top-bar'); const checkoutPanel = root.querySelector('.checkout-grid .co-panel'); function syncCheckoutCtaState() { const active = !!(mCtaEl && isMob() && mCtaEl.classList.contains('m-cta-up')); document.body.classList.toggle('checkout-cta-active', active); document.documentElement.classList.toggle('checkout-cta-active', active); } function keepCheckoutInView() { if (!isMob() || !checkoutPanel) return; const rect = checkoutPanel.getBoundingClientRect(); const ctaH = mCtaEl && mCtaEl.classList.contains('m-cta-up') ? mCtaEl.getBoundingClientRect().height : 0; const vp = Math.max(320, window.innerHeight - ctaH - 20); const target = window.scrollY + rect.top - Math.max(14, (vp - rect.height) / 2); window.scrollTo({ top: Math.max(0, target), behavior: 'smooth' }); } function updateTopBar() { if (!mTopBar) return; var plan = PLANS[selPlan], extra = selExtra * plan.extra, total = plan.base + extra; var nameEl = root.querySelector('#mtb-plan'); var priceEl = root.querySelector('#mtb-price'); if (nameEl) nameEl.textContent = C.planNames[selPlan]; if (priceEl) priceEl.textContent = fmt(total) + ' kr'; if (step2Active || step3Active) mTopBar.classList.add('mtb-visible'); else mTopBar.classList.remove('mtb-visible'); } function revealDevices() { root.querySelector('#plan-grid').classList.add('collapsed'); root.querySelector('#plan-summary').classList.add('visible'); root.querySelector('#xdev-block').classList.add('xdev-revealed'); root.querySelector('#mstep-1').className = 'm-step done'; root.querySelector('#mstep-2').className = 'm-step active'; root.querySelector('#mstep-3').className = 'm-step'; if (mCtaEl) mCtaEl.classList.add('m-cta-up'); syncCheckoutCtaState(); step2Active = true; step3Active = false; updateTopBar(); refreshMobileCta(); keepCheckoutInView(); } function changePlan() { root.querySelector('#plan-grid').classList.remove('collapsed'); root.querySelector('#plan-summary').classList.remove('visible'); root.querySelector('#xdev-block').classList.remove('xdev-revealed'); var _form = root.querySelector('#lead-form'); var _pre = root.querySelector('#lf-pre-form'); var _div = root.querySelector('#lf-form-divider'); if (_form) _form.classList.remove('lf-shown'); if (_pre) _pre.classList.remove('co-hidden'); if (_div) _div.style.display = 'none'; // Restore form elements to right panel in case they were moved on mobile var rightPanel = root.querySelector('.summary-panel > .co-panel'); if (rightPanel) { if (_pre) rightPanel.appendChild(_pre); if (_div) rightPanel.appendChild(_div); if (_form) rightPanel.appendChild(_form); } if (mCtaEl) mCtaEl.classList.remove('m-cta-up'); syncCheckoutCtaState(); step2Active = false; step3Active = false; planTapped = false; root.querySelector('#mstep-1').className = 'm-step active'; root.querySelector('#mstep-2').className = 'm-step'; root.querySelector('#mstep-3').className = 'm-step'; updateTopBar(); refreshMobileCta(); keepCheckoutInView(); } function selectPlan(tile) { root.querySelectorAll('.plan-tile').forEach(t => t.classList.remove('selected')); tile.classList.add('selected'); selPlan = tile.dataset.plan; updateChipPrices(); updateSummary(); if (isMob()) { if (!planTapped) { planTapped = true; revealDevices(); } else if (mCtaEl) { mCtaEl.classList.add('m-cta-up'); syncCheckoutCtaState(); } } } function selectExtra(chip) { root.querySelectorAll('.chip').forEach(c => c.classList.remove('selected')); chip.classList.add('selected'); selExtra = parseInt(chip.dataset.extra, 10); updateSummary(); } // ── Form logic ────────────────────────────────────────────────────────── function showForm() { var pre = root.querySelector('#lf-pre-form'); var form = root.querySelector('#lead-form'); var divider = root.querySelector('#lf-form-divider'); if (!form || form.classList.contains('lf-shown')) return; if (pre) pre.classList.add('co-hidden'); if (divider) divider.style.display = ''; form.classList.add('lf-shown'); if (isMob()) { // Move form into left panel so step 3 is in the same viewport as steps 1 and 2 var leftPanel = root.querySelector('.checkout-grid > .co-panel'); if (leftPanel) { if (pre) leftPanel.appendChild(pre); if (divider) leftPanel.appendChild(divider); leftPanel.appendChild(form); } // Collapse chips — form replaces them var xdev = root.querySelector('#xdev-block'); if (xdev) xdev.classList.remove('xdev-revealed'); root.querySelector('#mstep-2').className = 'm-step done'; root.querySelector('#mstep-3').className = 'm-step active'; step2Active = false; step3Active = true; if (mCtaEl) { mCtaEl.classList.add('m-cta-up'); syncCheckoutCtaState(); } updateTopBar(); refreshMobileCta(); window.scrollTo({ top: 0, behavior: 'smooth' }); } else { if (mCtaEl) mCtaEl.classList.remove('m-cta-up'); syncCheckoutCtaState(); } } function revealEmailField(focusAfter) { if (emailRevealed) { if (focusAfter) { var e = root.querySelector('#lf-email'); if (e) e.focus(); } return; } var step = root.querySelector('#lf-step-email'); var submitBtn = root.querySelector('#lf-submit'); if (!step) return; emailRevealed = true; step.classList.remove('hidden'); void step.offsetWidth; step.classList.add('visible'); if (submitBtn) submitBtn.classList.add('lf-submit-shown'); if (focusAfter) { setTimeout(function() { var e = root.querySelector('#lf-email'); if (e) e.focus(); }, 360); } } function shakeEl(el) { if (!el) return; el.classList.remove('co-lf-shaking'); void el.offsetWidth; el.classList.add('co-lf-shaking'); el.addEventListener('animationend', function() { el.classList.remove('co-lf-shaking'); }, { once: true }); } function autoCapture(phone, email) { try { localStorage.setItem('tvm_lead', JSON.stringify({ phone: phone || '', email: email || '', locale: C.locale, ts: new Date().toISOString() })); } catch(e) {} } const AC_DOMAINS = ['gmail.com','hotmail.com','outlook.com','yahoo.com','icloud.com','me.com','live.com','protonmail.com','msn.com','telia.com','bredband.net']; function attachEmailAC(input, onAccept) { if (!input) return; function suggest() { var val = input.value; if (input.selectionStart !== val.length) return; var atIdx = val.indexOf('@'); if (atIdx < 0 && val.length > 0) { var s = val + '@gmail.com'; input.value = s; input.setSelectionRange(val.length, s.length); return; } if (atIdx < 1) return; var partial = val.slice(atIdx + 1).toLowerCase(); if (!partial) { var s2 = val + 'gmail.com'; input.value = s2; input.setSelectionRange(val.length, s2.length); return; } var match = AC_DOMAINS.find(function(d) { return d.startsWith(partial) && d !== partial; }); if (!match) return; var s3 = val.slice(0, atIdx + 1) + match; input.value = s3; input.setSelectionRange(val.length, s3.length); } input.addEventListener('input', suggest); input.addEventListener('keydown', function(e) { var sel = input.selectionStart, end = input.selectionEnd, hasSug = sel < end; if (hasSug) { if (e.key === 'Tab' || e.key === 'ArrowRight' || e.key === 'Enter') { e.preventDefault(); input.setSelectionRange(end, end); if (e.key === 'Enter' && onAccept) onAccept(); return; } if (e.key === 'Escape') { e.preventDefault(); input.value = input.value.slice(0, sel); input.setSelectionRange(sel, sel); return; } } else { if (e.key === 'Enter' && onAccept) { e.preventDefault(); onAccept(); } } }); } function escapeHtml(s) { return String(s).replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"'); } function initPhoneField() { var phone = root.querySelector('#lf-phone'); var phoneField = root.querySelector('#lf-phone-field'); if (!phone || !phoneField) return; function checkPhoneValidity() { var val = phone.value.trim(), digits = val.replace(/\D/g, '').length, valid = digits >= 8; phoneField.classList.toggle('co-valid', valid); phoneField.classList.toggle('co-invalid', !valid && val.length > 2); return valid; } phone.addEventListener('input', function() { var pos = phone.selectionStart, raw = phone.value.replace(/[^\d\s+\-()]/g, ''); if (raw !== phone.value) { phone.value = raw; try { phone.setSelectionRange(pos, pos); } catch(x){} } var valid = checkPhoneValidity(); var err = root.querySelector('#lf-phone-err'); if (err && valid) err.classList.remove('show'); if (valid) revealEmailField(false); }); phone.addEventListener('blur', function() { if (checkPhoneValidity()) { autoCapture(selectedCC + ' ' + phone.value.trim(), ''); revealEmailField(false); } }); phone.addEventListener('keydown', function(e) { if (e.key === 'Enter') { e.preventDefault(); if (checkPhoneValidity()) { revealEmailField(true); } else { shakeEl(phoneField); var err = root.querySelector('#lf-phone-err'); if (err) err.classList.add('show'); } } }); } function initEmailField() { var email = root.querySelector('#lf-email'); if (!email) return; email.addEventListener('blur', function() { if (/[^@\s]+@[^@\s]+\.[^@\s]+/.test(email.value.trim())) { autoCapture(selectedCC + ' ' + (root.querySelector('#lf-phone') || {value:''}).value.trim(), email.value.trim()); } }); email.addEventListener('input', function() { var err = root.querySelector('#lf-email-err'); if (err && /[^@\s]+@[^@\s]+\.[^@\s]+/.test(email.value.trim())) err.classList.remove('show'); }); attachEmailAC(email, function() { var btn = root.querySelector('#lf-submit'); if (btn) btn.focus(); }); } function initCCPicker() { var btn = root.querySelector('#lf-cc-btn'); var dd = root.querySelector('#lf-cc-dd'); var phoneField = root.querySelector('#lf-phone-field'); var searchInput = root.querySelector('#lf-cc-search'); var list = root.querySelector('#lf-cc-list'); if (!btn || !dd || !phoneField) return; function openDD() { dd.classList.add('open'); phoneField.classList.add('co-lf-cc-open'); btn.setAttribute('aria-expanded','true'); if (searchInput) { searchInput.value=''; filterItems(''); setTimeout(function(){searchInput.focus();},60); } } function closeDD() { dd.classList.remove('open'); phoneField.classList.remove('co-lf-cc-open'); btn.setAttribute('aria-expanded','false'); } function filterItems(q) { var items = (list||dd).querySelectorAll('.co-lf-cc-item'), lq = q.toLowerCase().trim(); items.forEach(function(item) { var name=(item.dataset.name||item.querySelector('.co-lf-cc-item-name').textContent).toLowerCase(); item.classList.toggle('co-lf-cc-hidden', !(!lq||name.includes(lq)||(item.dataset.code||'').includes(lq))); }); } btn.addEventListener('click', function(e) { e.stopPropagation(); dd.classList.contains('open')?closeDD():openDD(); }); if (searchInput) { searchInput.addEventListener('input', function(e){filterItems(e.target.value);}); searchInput.addEventListener('click', function(e){e.stopPropagation();}); searchInput.addEventListener('keydown', function(e){if(e.key==='Escape'){closeDD();root.querySelector('#lf-phone').focus();}}); } dd.addEventListener('click', function(e) { var item = e.target.closest('.co-lf-cc-item'); if (!item) return; selectedCC = item.dataset.code; root.querySelector('#lf-cc-flag').textContent = item.dataset.flag; root.querySelector('#lf-cc-code').textContent = selectedCC; dd.querySelectorAll('.co-lf-cc-item').forEach(function(i){i.classList.toggle('selected',i.dataset.code===selectedCC);}); closeDD(); root.querySelector('#lf-phone').focus(); }); var _ddClose = function(e) { if (!phoneField.contains(e.target)) closeDD(); }; var _ddEsc = function(e) { if (e.key==='Escape') closeDD(); }; document.addEventListener('click', _ddClose); document.addEventListener('keydown', _ddEsc); return function() { document.removeEventListener('click', _ddClose); document.removeEventListener('keydown', _ddEsc); }; } function buildPayload() { var plan = PLANS[selPlan], total = plan.base + selExtra * plan.extra; var emailEl = root.querySelector('#lf-email'); return { plan: selPlan, planLabel: C.planNames[selPlan], screens: 1+selExtra, totalPrice: total, currency: C.currency, phone: selectedCC+' '+(root.querySelector('#lf-phone')||{value:''}).value.trim(), email: emailEl ? emailEl.value.trim() : '', locale: C.locale, timestamp: new Date().toISOString(), pageUrl: location.href, captureType: 'full' }; } async function postToWebhook(payload) { console.log('[tvm lead]', JSON.stringify(payload, null, 2)); if (!CHECKOUT_WEBHOOK_URL) return { skipped: true }; try { await fetch(CHECKOUT_WEBHOOK_URL, { method:'POST', mode:'no-cors', headers:{'Content-Type':'text/plain;charset=utf-8'}, body: JSON.stringify(payload) }); return { ok: true }; } catch(err) { console.error('[tvm webhook error]', err); return { error: err.message }; } } async function submitLead(e) { e.preventDefault(); var form = e.target; if (form.website && form.website.value) return; var phoneVal = (root.querySelector('#lf-phone')||{value:''}).value.trim(); if (phoneVal.replace(/\D/g,'').length < 8) { shakeEl(root.querySelector('#lf-phone-field')); var pErr = root.querySelector('#lf-phone-err'); if (pErr) pErr.classList.add('show'); root.querySelector('#lf-phone').focus(); return; } var emailEl = root.querySelector('#lf-email'), emailVal = emailEl ? emailEl.value.trim() : ''; if (!/[^@\s]+@[^@\s]+\.[^@\s]+/.test(emailVal)) { if (!emailRevealed) { revealEmailField(true); return; } shakeEl(root.querySelector('.co-lf-email-wrap')); var eErr = root.querySelector('#lf-email-err'); if (eErr) eErr.classList.add('show'); if (emailEl) emailEl.focus(); return; } if (document.activeElement) document.activeElement.blur(); var btn = root.querySelector('#lf-submit'); btn.disabled = true; btn.innerHTML = ''; var payload = buildPayload(); autoCapture(payload.phone, payload.email); await postToWebhook(payload); showSuccess(payload); } function showSuccess(p) { var formEl = root.querySelector('#lead-form'); var panel = (formEl && formEl.closest('.co-panel')) || root.querySelector('.summary-panel .co-panel'); if (!panel) return; var confColors = ['#ff3b30','#34c759','#0071e3','#ff9500','#af52de','#5ac8fa','#ff2d55','#ffd60a']; var confHtml = ''; for (var ci = 0; ci < 14; ci++) { var angle=(ci/14)*2*Math.PI, dist=38+Math.round(Math.random()*28); var tx=Math.round(Math.sin(angle)*dist), ty=Math.round(-Math.abs(Math.cos(angle))*dist-10); var r=Math.round(Math.random()*360), d=(ci*0.045).toFixed(2); confHtml += '
'; } var callLine = C.successCallLine.replace('{phone}',''+escapeHtml(p.phone)+''); var footLine = C.successFootnote.replace('{email}',''+escapeHtml(p.email)+''); panel.innerHTML = '
'+ '
'+confHtml+ ''+ ''+ ''+ ''+ '
'+ '
'+C.successBadge+'
'+ '

'+C.successHeading+'

'+ '

'+C.successSavingsHero.replace('7 884 kr','7 884 kr')+'

'+ '
'+ '
'+C.successBeforeLbl+'
'+C.successFromBefore+'
'+ '
'+ '
'+C.successAfterLbl+'
'+C.successFromAfter+'
'+ '
'+ '

'+C.successFromNote+'

'+ '
'+ '
'+ '

'+callLine+'

'+C.successHumanNote+'

'+ '
'+ '

'+C.successWelcome.replace('2 400+','2 400+')+'

'+ '

'+footLine+'

'+ '
'; if (mTopBar) mTopBar.classList.remove('mtb-visible'); if (mCtaEl) mCtaEl.classList.remove('m-cta-up'); step2Active = false; step3Active = false; syncCheckoutCtaState(); try { localStorage.removeItem('tvm_lead'); } catch(x) {} } // Event wiring root.querySelectorAll('.plan-tile').forEach(t => t.addEventListener('click', () => selectPlan(t))); root.querySelectorAll('.chip').forEach(c => c.addEventListener('click', () => selectExtra(c))); root.querySelector('.plan-summary')?.addEventListener('click', changePlan); root.querySelector('#mtb-edit-btn')?.addEventListener('click', changePlan); var buyBtn = root.querySelector('#lf-buy-btn'); if (buyBtn) buyBtn.addEventListener('click', showForm); var leadForm = root.querySelector('#lead-form'); if (leadForm) leadForm.addEventListener('submit', submitLead); updateChipPrices(); updateSummary(); initPhoneField(); var cleanupCC = initCCPicker(); initEmailField(); (function initViewers() { var el = root.querySelector('#co-viewers-count'); if (!el) return; var n = 14 + Math.floor(Math.random() * 18); el.textContent = n; setInterval(function() { n = Math.max(8, Math.min(40, n + Math.floor(Math.random() * 5) - 2)); el.textContent = n; }, 3500 + Math.random() * 3000); })(); (function initSlots() { var el = root.querySelector('#co-slots-count'); if (!el) return; var n = 5 + Math.floor(Math.random() * 4); el.textContent = n; function tick() { if (n <= 2) return; n--; el.textContent = n; setTimeout(tick, 60000 + Math.random() * 60000); } setTimeout(tick, 45000 + Math.random() * 30000); })(); let snapCooled = false, snapTimer = null, lastClickInSection = 0; function noteCheckoutClick() { lastClickInSection = Date.now(); snapCooled = true; setTimeout(() => { snapCooled = false; }, 1800); } root.addEventListener('pointerdown', noteCheckoutClick, { passive: true }); root.addEventListener('click', noteCheckoutClick); function snapToSection() { if (snapCooled || !isMob()) return; if (Date.now() - lastClickInSection < 2000) return; const rect = root.getBoundingClientRect(); const partial = rect.top < window.innerHeight * 0.7 && rect.top > -rect.height * 0.3; if (partial && Math.abs(rect.top) > 4) { snapCooled = true; keepCheckoutInView(); setTimeout(() => { snapCooled = false; }, 1200); } } function refreshMobileCta() { if (!mCtaEl || !mCtaBtn) return; if (!isMob()) { mCtaEl.classList.remove('m-cta-up'); syncCheckoutCtaState(); return; } const rect = (checkoutPanel || root).getBoundingClientRect(); const inView = rect.top < window.innerHeight - 80 && rect.bottom > 80; if (!inView) { mCtaEl.classList.remove('m-cta-up'); syncCheckoutCtaState(); return; } if (step3Active) { mCtaEl.classList.add('m-cta-up'); mCtaBtn.textContent = C.mCtaStep3; mCtaBtn.onclick = function() { var f = root.querySelector('#lead-form'); if (f) f.dispatchEvent(new Event('submit', {bubbles:true,cancelable:true})); }; } else if (step2Active) { mCtaEl.classList.add('m-cta-up'); mCtaBtn.textContent = C.mCtaStep2; mCtaBtn.onclick = showForm; } else { mCtaBtn.textContent = C.mCtaStep1; mCtaBtn.onclick = revealDevices; } syncCheckoutCtaState(); } function onScroll() { refreshMobileCta(); clearTimeout(snapTimer); snapTimer = setTimeout(snapToSection, 120); } window.addEventListener('scroll', onScroll, { passive: true }); window.addEventListener('resize', refreshMobileCta); refreshMobileCta(); return () => { window.removeEventListener('scroll', onScroll); window.removeEventListener('resize', refreshMobileCta); root.removeEventListener('pointerdown', noteCheckoutClick); root.removeEventListener('click', noteCheckoutClick); clearTimeout(snapTimer); document.body.classList.remove('checkout-cta-active'); document.documentElement.classList.remove('checkout-cta-active'); if (typeof cleanupCC === 'function') cleanupCC(); }; }, []); const PC = PRICING_COPY[window.locale || 'SE']; const PP = PRICING_PLANS[window.locale || 'SE']; return (
1 År 699 kr
Priser

Välj ditt paket.
Börja streama direkt.

{PC.panelLabel}
{PC.sectionLabel}
1 År
699 kr
{PC.planNames['3m']}
{PP['3m'].base.toLocaleString(PC.numLocale)}kr
{PC.planSub['3m']}
~116 kr/mån –84%
{PC.planNames['1y']}
{PC.popularBadge}
{PP['1y'].base.toLocaleString(PC.numLocale)}kr
{PC.planSub['1y']}
~58 kr/mån –92%
{PC.planNames['3y']} 🎁
{PP['3y'].base.toLocaleString(PC.numLocale)}kr
{PC.planSub['3y']}
~31 kr/mån –96%
{PC.devicesTitle}
1
{PC.chipUnit1}
{PC.chipIncluded}
{PC.chipPop}
2
{PC.chipUnitN}
3
{PC.chipUnitN}
4
{PC.chipUnitN}
5
{PC.chipUnitN}
6
{PC.chipUnitN}

{PC.devicesNote}

{PC.trustItems[0]}
{PC.trustItems[1]}
{PC.trustItems[2]}
{PC.summaryLabel}
{PC.sumPlan}1 År
{PC.sumBase}699 kr
+0 kr
{PC.sumTotal}
699kr
{PC.orderNote}
{PC.trustItems[0]}
{PC.trustItems[1]}
{PC.trustItems[2]}

23 ser på detta just nu · 5 platser kvar


{PC.formPhoneErr}

{PC.formEmailErr}

{PC.formPrivacy}

); } function FAQ() { const listRef = React.useRef(null); const C = COPY[window.locale]; React.useEffect(() => { const list = listRef.current; if (!list) return; const cards = Array.from(list.querySelectorAll('.faq-item')); const faqSection = list.closest('.faq'); let openCount = 0; let ctaUpgraded = false; function upgradeWhatsappCta() { if (ctaUpgraded || !faqSection) return; const el = faqSection.querySelector('.faq-wa-link'); if (!el) return; ctaUpgraded = true; el.classList.add('faq-wa-primary'); el.setAttribute('aria-label', COPY[window.locale].waAriaLabel); if (!el.querySelector('svg')) { const icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); icon.setAttribute('width', '18'); icon.setAttribute('height', '18'); icon.setAttribute('viewBox', '0 0 24 24'); icon.setAttribute('fill', 'white'); icon.setAttribute('aria-hidden', 'true'); icon.setAttribute('style', 'flex-shrink:0'); icon.innerHTML = ''; el.insertBefore(icon, el.firstChild); } } cards.forEach(card => { const trigger = card.querySelector('.faq-q'); function toggle(e) { e.stopPropagation(); const isOpen = card.classList.contains('open'); cards.forEach(c => { c.classList.remove('open'); c.querySelector('.faq-q').setAttribute('aria-expanded', 'false'); }); if (!isOpen) { card.classList.add('open'); trigger.setAttribute('aria-expanded', 'true'); openCount += 1; if (openCount >= 3) upgradeWhatsappCta(); } } trigger.addEventListener('click', toggle); trigger.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggle(e); } }); }); }, []); return (

{C.faqTitle}

{C.faqItems.map(([q, a], i) => (
))}
{C.faqNotFound} {C.faqAsk}
); } function FinalCta() { const C = COPY[window.locale]; return (

{C.finalTitle1} {C.finalTitle2}

{C.finalSub}

{C.finalQuestions} {C.faqAsk} {' · '} tvmomentoservice@gmail.com

); } function Footer() { const C = COPY[window.locale]; return ( ); } function RecentlyBought() { const _C = COPY[window.locale]; const [p0, p1, p2] = _C.rbPlans; const purchases = [ { name:"Erik J.", plan: p0, where:"Stockholm", ago: _C.rbNow, color:"#6e6e73" }, { name:"Amina K.", plan: p1, where:"Göteborg", ago:"1 min", color:"#8e8e93" }, { name:"Lars P.", plan: p2, where:"Oslo", ago:"3 min", color:"#636366" }, { name:"Sara H.", plan: p0, where:"Malmö", ago:"5 min", color:"#8e8e93" }, { name:"Mohammed A.", plan: p1, where:"Linköping", ago:"7 min", color:"#6e6e73" }, { name:"Ingrid N.", plan: p0, where:"Bergen", ago:"9 min", color:"#8e8e93" }, ]; const CYCLE = 4500; const STEP_MS = 80; const [idx, setIdx] = React.useState(0); const [out, setOut] = React.useState(false); const [barPct, setBarPct] = React.useState(0); const barTimer = React.useRef(null); const startBar = React.useCallback(() => { clearInterval(barTimer.current); setBarPct(0); let pct = 0; barTimer.current = setInterval(() => { pct = Math.min(100, pct + (STEP_MS / CYCLE) * 100); setBarPct(pct); if (pct >= 100) clearInterval(barTimer.current); }, STEP_MS); }, []); React.useEffect(() => { startBar(); const cycle = setInterval(() => { setOut(true); setTimeout(() => { setIdx(i => (i + 1) % purchases.length); setOut(false); startBar(); }, 400); }, CYCLE); return () => { clearInterval(cycle); clearInterval(barTimer.current); }; }, [startBar]); const p = purchases[idx]; return (
{p.name[0]}
{_C.rbEyebrow}
{p.name}
{p.plan} · {p.where}
{p.ago}
); } function Testimonials() { React.useEffect(() => { const DIR = 'assets/ugc/'; const SPEED = 0.45; const GAP = 14; const VID_RE = /\.(webm|mp4|mov)$/i; const IMG_RE = /\.(webp|jpg|jpeg|png)$/i; const REVIEWS = COPY[window.locale].ugcReviews; const track1 = document.getElementById('ugc-track-1'); const track2 = document.getElementById('ugc-track-2'); const wrap1 = document.getElementById('ugc-wrap-1'); const wrap2 = document.getElementById('ugc-wrap-2'); if (!track1 || !track2) return; let offset1 = 0, setWidth1 = 0; let offset2 = 0, setWidth2 = 0; let rafId = null, isPaused = false; function tick() { if (!isPaused) { offset1 -= SPEED; if (offset1 <= -setWidth1) offset1 += setWidth1; track1.style.transform = `translateX(${offset1}px)`; offset2 += SPEED; if (offset2 >= 0) offset2 -= setWidth2; track2.style.transform = `translateX(${offset2}px)`; } rafId = requestAnimationFrame(tick); } function wireVideo(vid, bg, btn, cardEl) { vid.muted = true; vid.loop = true; ['muted','playsinline','webkit-playsinline','x5-playsinline'].forEach(a => vid.setAttribute(a, '')); vid.setAttribute('preload', 'none'); const show = () => { if (bg) bg.style.opacity='0'; if (btn) btn.classList.add('ugc-play-hidden'); }; const hide = () => { if (btn) btn.classList.remove('ugc-play-hidden'); }; vid.addEventListener('playing', show); vid.addEventListener('pause', hide); vid.addEventListener('waiting', hide); vid.addEventListener('stalled', hide); vid.addEventListener('error', () => { vid.style.display='none'; if(bg) bg.style.opacity='1'; if(btn) btn.style.display='none'; }); const io = new IntersectionObserver(entries => { entries.forEach(e => { if (e.isIntersecting) { if (!vid.querySelector('source')) { if (vid.dataset.mp4) { const s=document.createElement('source'); s.src=vid.dataset.mp4; s.type='video/mp4'; vid.appendChild(s); } if (vid.dataset.webm) { const s=document.createElement('source'); s.src=vid.dataset.webm; s.type='video/webm; codecs="vp9,opus"'; vid.appendChild(s); } vid.load(); } vid.play().catch(() => {}); } else { vid.pause(); } }); }, { threshold: 0.1, rootMargin: '0px 80px' }); io.observe(cardEl); cardEl.addEventListener('click', () => { vid.paused ? vid.play().catch(()=>{}) : vid.pause(); }); } function makeVideo(src) { const card = document.createElement('div'); card.className = 'ugc-card ugc-video'; const bg = document.createElement('div'); bg.className = 'ugc-vid-bg'; card.appendChild(bg); const vid = document.createElement('video'); if (/\.(mp4|mov)$/i.test(src)) { vid.dataset.mp4 = DIR + src; } else if (/\.webm$/i.test(src)) { vid.dataset.mp4 = DIR + src.replace(/\.webm$/i, '.mp4'); vid.dataset.webm = DIR + src; } card.appendChild(vid); const btn = document.createElement('div'); btn.className = 'ugc-play'; btn.innerHTML = ''; card.appendChild(btn); wireVideo(vid, bg, btn, card); return card; } let imgIdx = 0; function makeImage(src) { const card = document.createElement('div'); card.className = 'ugc-card ugc-img'; const img = document.createElement('img'); img.src = DIR + src; img.alt = 'Kundrecension Tvmomento ' + (++imgIdx); img.loading = 'lazy'; img.decoding = 'async'; card.appendChild(img); return card; } const STAR = ''; function makeTpCard(r) { const card = document.createElement('div'); card.className = 'ugc-card ugc-tp'; const head = document.createElement('div'); head.className = 'ugc-tp-head'; const stars = document.createElement('div'); stars.className = 'ugc-tp-stars'; stars.innerHTML = STAR.repeat(5); const logo = document.createElement('span'); logo.className = 'ugc-tp-logo'; logo.textContent = 'Trustpilot'; head.appendChild(stars); head.appendChild(logo); card.appendChild(head); const txt = document.createElement('p'); txt.className = 'ugc-tp-text'; txt.textContent = '“' + r.text + '”'; card.appendChild(txt); const foot = document.createElement('div'); foot.className = 'ugc-tp-foot'; const name = document.createElement('span'); name.className = 'ugc-tp-name'; name.textContent = r.name; const badge = document.createElement('span'); badge.className = 'ugc-tp-badge'; badge.textContent = COPY[window.locale].ugcVerified; foot.appendChild(name); foot.appendChild(badge); card.appendChild(foot); return card; } function cloneCard(orig) { const clone = orig.cloneNode(true); const ov = orig.querySelector('video'), cv = clone.querySelector('video'); if (ov && cv) { cv.dataset.mp4 = ov.dataset.mp4 || ''; cv.dataset.webm = ov.dataset.webm || ''; Array.from(cv.querySelectorAll('source')).forEach(s => s.remove()); cv.setAttribute('preload', 'none'); wireVideo(cv, clone.querySelector('.ugc-vid-bg'), clone.querySelector('.ugc-play'), clone); } return clone; } async function init() { let files = [ 'customer-review-1.webp', 'customer-review-2.webp', 'customer-review-3.webp', 'customer-review-4.webp', 'customer-review-5.webp' ]; if (!files.length) return; const vids = files.filter(f => VID_RE.test(f)), imgs = files.filter(f => IMG_RE.test(f)); const mediaList = []; let vi = 0, ii = 0; while (vi < vids.length || ii < imgs.length) { if (vi < vids.length) mediaList.push({ v: true, s: vids[vi++] }); if (ii < imgs.length) mediaList.push({ v: false, s: imgs[ii++] }); } // pattern: [media, tp, tp] per group; phaseOffset shifts which slot starts function buildCards(phaseOffset) { const cards = []; const groupCount = Math.max(mediaList.length, 4); let mi = 0, ri = 0; for (let g = 0; g < groupCount; g++) { for (let p = 0; p < 3; p++) { const slot = (p + phaseOffset) % 3; if (slot === 0) { const m = mediaList[mi % mediaList.length]; mi++; cards.push(m.v ? makeVideo(m.s) : makeImage(m.s)); } else { cards.push(makeTpCard(REVIEWS[ri % REVIEWS.length])); ri++; } } } return cards; } // row 1: media → tp → tp → … row 2: tp → tp → media → … const cards1 = buildCards(0); cards1.forEach(c => track1.appendChild(c)); const cards2 = buildCards(1); cards2.forEach(c => track2.appendChild(c)); requestAnimationFrame(() => requestAnimationFrame(() => { setWidth1 = cards1.reduce((sum, c) => sum + c.offsetWidth + GAP, 0); setWidth2 = cards2.reduce((sum, c) => sum + c.offsetWidth + GAP, 0); const minW = (window.innerWidth || 800) * 3; let n1 = 0; while (n1 * setWidth1 < minW) { cards1.forEach(c => track1.appendChild(cloneCard(c))); n1++; } let n2 = 0; while (n2 * setWidth2 < minW) { cards2.forEach(c => track2.appendChild(cloneCard(c))); n2++; } offset2 = -setWidth2; // row 2 starts right, scrolls left-to-right tick(); })); } document.addEventListener('visibilitychange', () => { isPaused = document.hidden; }); init(); return () => { if (rafId) cancelAnimationFrame(rafId); }; }, []); const C_ugc = COPY[window.locale]; return (
{C_ugc.ugcEyebrow}

{C_ugc.ugcH1}
{C_ugc.ugcH2}

); } function WhatsAppFloat() { return ( { e.currentTarget.style.transform='scale(1.1)'; e.currentTarget.style.boxShadow='0 8px 32px rgba(37,211,102,0.5), 0 4px 12px rgba(0,0,0,0.18)'; }} onMouseLeave={e => { e.currentTarget.style.transform='scale(1)'; e.currentTarget.style.boxShadow='0 4px 20px rgba(37,211,102,0.4), 0 2px 8px rgba(0,0,0,0.15)'; }} > ); } Object.assign(window, { ReceiptBad, ReceiptGood, ReceiptCompare, FeatureSavings, HowItWorks, DeviceCompat, Testimonials, Pricing, FAQ, FinalCta, Footer, RecentlyBought, WhatsAppFloat, });