// 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 && (
SERIÖST?
5 appar.
5 lösen.
för
alltid
)}
); } function ReceiptGood() { const C = COPY[window.locale]; return (
TVMOMENTO
58 {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}
Pris

Du sparar
7 884 kr per år.

Det är vad du betalar extra med fem separata abonnemang. Inte hypotetiskt. Per år.

Nuläget715 kr/mån8 580 kr/år
Tvmomento58 kr/mån696 kr/år
Du sparar657 kr/mån7 884 kr/år
{C.punch1}
{C.punch2}
); } function HowItWorks() { 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 (
Så fungerar det

Tre steg.
Femton minuter.

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

Välj din plan.

Tre nivåer, samma innehåll. Inga dolda avgifter, ingen bindningstid.

3 månader
349 kr
1 år
699 kr Populärast
3 år + 6 mån gratis
1 299 kr
{/* Step 02 — payment sheet */}
02

Betala säkert.

Visa, Mastercard, Swish eller Apple Pay. Shopify Payments-skyddad betalning, kvitto direkt.

Tvmomento1 år
Plan699 kr
Att betala699 kr
Pay
VISA
S wish
Pay
link
{/* Step 03 — cinematic image */}
03

Tryck play.

Inloggning till din e-post inom 15 minuter. Funkar på TV, mobil, surfplatta och dator.

Streama på TV och mobil
); } 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]; React.useEffect(() => { const grid = document.querySelector('.dvc-grid'); if (grid && window.innerWidth <= 560) { const origCards = Array.from(grid.querySelectorAll('.dvc-card')); origCards.forEach(c => { c.classList.add('visible'); }); origCards.forEach(c => grid.appendChild(c.cloneNode(true))); return; } const io = new IntersectionObserver(entries => { entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add('visible'); io.unobserve(e.target); } }); }, { threshold: 0.08, rootMargin: '0px 0px -40px 0px' }); document.querySelectorAll('.dvc-card').forEach(el => io.observe(el)); return () => io.disconnect(); }, []); return (
Kompatibilitet

{C.deviceHeadline}
{C.deviceSub}

{C.deviceIntro}

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

{d.name}

{d.desc}

{C.devicesLogosLabel[i]}
{DEVICE_LOGOS[i]}
))}
); } function Pricing() { const ref = React.useRef(null); React.useEffect(() => { const root = ref.current; if (!root) return; const CO_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' }, chipUnit1: 'skärm', chipUnitN: 'skärmar', chipIncluded: 'Ingår', sumExtra: n => '+' + n + ' skärm' + (n > 1 ? 'ar' : ''), numLocale: 'sv-SE', }, NO: { planNames: { '3m': '3 Måneder', '1y': '1 År', '3y': '3 År + 6 mnd gratis' }, planSub: { '3m': 'Engangsbetaling', '1y': 'Spar vs. månedlig', '3y': 'Beste verdi — 3,5 år totalt' }, chipUnit1: 'skjerm', chipUnitN: 'skjermer', chipIncluded: 'Inkludert', sumExtra: n => '+' + n + ' skjerm' + (n > 1 ? 'er' : ''), numLocale: 'nb-NO', }, }; const C = CO_COPY[window.locale || 'SE']; const PLANS = { 'SE': { '3m': { base: 349, extra: 175 }, '1y': { base: 699, extra: 350 }, '3y': { base: 1299, extra: 650 }, }, 'NO': { '3m': { base: 379, extra: 189 }, '1y': { base: 749, extra: 379 }, '3y': { base: 1399, extra: 699 }, } }[window.locale || 'SE']; let selPlan = '1y', selExtra = 0, planTapped = false, step2Active = false; function fmt(n) { return n.toLocaleString(C.numLocale); } function isMob() { return window.matchMedia('(max-width: 860px)').matches; } function q(id) { return root.querySelector('#' + id); } function updateChipPrices() { const ppu = PLANS[selPlan].extra; for (let i = 1; i <= 5; i++) { const el = q('chip-price-' + i); if (el) el.textContent = '+' + fmt(i * ppu) + ' kr'; } } function updateSummary() { const plan = PLANS[selPlan], extra = selExtra * plan.extra, total = plan.base + extra; if (q('sum-plan-name')) q('sum-plan-name').textContent = C.planNames[selPlan]; if (q('sum-base-price')) q('sum-base-price').textContent = fmt(plan.base) + ' kr'; if (q('sum-total')) q('sum-total').textContent = fmt(total); if (q('cta-price')) q('cta-price').textContent = fmt(total); const extraRow = q('sum-extra-row'); if (extraRow) { if (selExtra > 0) { extraRow.classList.add('visible'); q('sum-extra-label').textContent = C.sumExtra(selExtra); q('sum-extra-price').textContent = '+' + fmt(extra) + ' kr'; } else { extraRow.classList.remove('visible'); } } if (q('ps-name')) q('ps-name').textContent = C.planNames[selPlan]; if (q('ps-price')) q('ps-price').textContent = fmt(plan.base) + ' kr'; const mcbTotal = root.querySelector('.m-cta-total'); const mcbAmt = root.querySelector('.m-cta-amount'); if (mcbTotal && mcbAmt) { mcbTotal.classList.add('updating'); requestAnimationFrame(() => { mcbAmt.textContent = fmt(total); requestAnimationFrame(() => mcbTotal.classList.remove('updating')); }); } } const mCtaEl = root.querySelector('.m-cta'); const mCtaBtn = root.querySelector('.m-cta-btn'); 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 ctaHeight = mCtaEl && mCtaEl.classList.contains('m-cta-up') ? mCtaEl.getBoundingClientRect().height : 0; const viewportSpace = Math.max(320, window.innerHeight - ctaHeight - 20); const target = window.scrollY + rect.top - Math.max(14, (viewportSpace - rect.height) / 2); window.scrollTo({ top: Math.max(0, target), behavior: 'smooth' }); } 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'; if (mCtaEl) mCtaEl.classList.add('m-cta-up'); syncCheckoutCtaState(); step2Active = true; 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'); if (mCtaEl) mCtaEl.classList.remove('m-cta-up'); syncCheckoutCtaState(); step2Active = false; planTapped = false; root.querySelector('#mstep-1').className = 'm-step active'; root.querySelector('#mstep-2').className = 'm-step'; 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(); } function goToCheckout() { const links = window.CHECKOUT_LINKS; const url = links && links[selPlan] && links[selPlan][selExtra]; if (url) window.location.href = url; } 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 (!step2Active) { mCtaBtn.textContent = 'Välj antal skärmar →'; mCtaBtn.onclick = revealDevices; } else { mCtaEl.classList.add('m-cta-up'); mCtaBtn.textContent = 'Gå till kassa →'; mCtaBtn.onclick = goToCheckout; } syncCheckoutCtaState(); } root.querySelectorAll('.plan-tile').forEach(t => t.addEventListener('click', () => selectPlan(t))); root.querySelectorAll('.chip').forEach(c => c.addEventListener('click', () => selectExtra(c))); root.querySelector('.ps-change')?.addEventListener('click', changePlan); root.querySelector('#cta-btn')?.addEventListener('click', goToCheckout); updateChipPrices(); updateSummary(); let snapCooled = false; let snapTimer = null; let 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 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'); }; }, []); return (
Priser

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

Välj ditt paket
Abonnemang
1 År
699 kr
3 Månader
349kr
Engångsbelopp
1 År
Populärast ★
699kr
Spara vs månadsvis
3 År + 6 mån gratis 🎁
1 299kr
Bästa värdet — 3,5 år totalt
Antal skärmar
1
skärm
Ingår
Vanligast
2
skärmar
3
skärmar
4
skärmar
5
skärmar
6
skärmar

Varje skärm kan titta på olika kanaler samtidigt.

Säker Shopify-betalning
Inloggning inom 15 minuter
Krypterad anslutning (TLS 1.3)
Din beställning
Paket1 År
Baspris699 kr
Tilläggsenheter+0 kr
Totalt att betala
699kr
Engångsbetalning — ingen prenumeration
Säker Shopify-betalning
Inloggning inom 15 minuter
Krypterad anslutning (TLS 1.3)
699 kr
Engångsbetalning
); } 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', 'Kontakta oss på WhatsApp'); 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}

); } function Footer() { const C = COPY[window.locale]; return ( ); } function RecentlyBought() { const purchases = [ { name:"Erik J.", plan:"1-årsplan", where:"Stockholm", ago:"just nu", color:"#6e6e73" }, { name:"Amina K.", plan:"3-årsplan", where:"Göteborg", ago:"1 min", color:"#8e8e93" }, { name:"Lars P.", plan:"3-månadersplan", where:"Oslo", ago:"3 min", color:"#636366" }, { name:"Sara H.", plan:"1-årsplan", where:"Malmö", ago:"5 min", color:"#8e8e93" }, { name:"Mohammed A.", plan:"3-årsplan", where:"Linköping", ago:"7 min", color:"#6e6e73" }, { name:"Ingrid N.", plan:"1-årsplan", 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]}
Nytt köp
{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 = [ { name: 'Maria S.', text: 'Fantastisk tjänst! Kanalkvaliteten är överlägsen och priset är helt obeatable.' }, { name: 'Johan K.', text: 'Fungerar perfekt på min Fire TV. Inga avbrott, skarp bild i 4K.' }, { name: 'Anna L.', text: 'Har haft Tvmomento i 8 månader. Aldrig haft problem. Rekommenderar varmt.' }, { name: 'Erik M.', text: 'Priset är löjligt lågt jämfört med vad man får. 10/10 utan tvekan.' }, { name: 'Sara B.', text: 'Support svarade på 5 minuter och löste mitt problem direkt. Imponerande.' }, { name: 'Thomas N.', text: 'Bästa köpet jag gjort. Hela familjen är nöjd med kanalutbudet.' }, { name: 'Karin P.', text: '10 000+ kanaler är inte ett skämt. Det är bokstavligen allt man önskar sig.' }, { name: 'Mikael R.', text: 'Installationen tog 10 minuter. Nu ser jag all sport live utan avbrott.' }, { name: 'Lena A.', text: 'Bytte från Viaplay och sparar 400 kr i månaden. Bättre utbud dessutom!' }, { name: 'Peter H.', text: 'Extremt stabil anslutning. Inga buffringsproblem ens under rusningstid.' }, { name: 'Cecilia M.', text: 'Superenkel installation och supporten är otroligt hjälpsam och snabb.' }, { name: 'Anders L.', text: 'Har provat allt — inget slår Tvmomento på pris och kanalutbud.' }, ]; 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; } 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 = 'Kundomdöme'; 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 = 'Verifierad köp'; 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 = [ 'téléchargement (1).webp', 'téléchargement (2).webp', 'téléchargement (3).webp', 'téléchargement (4).webp', 'téléchargement.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); }; }, []); return (
Omdömen

2 400+ nöjda kunder.
De pratar för oss.

); } 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, });