/* fz-conv3: blog -> app conversion, loaded from Ghost files (code injection is full). Su 03/11/2026. 1. Mobile sticky CTA bar (QR Pay on QR pages, US$10 eSIM gift elsewhere, none on card pages). 2. Desktop: scan-to-phone QR inside the top box and the end-of-post CTA card (half our readers are on desktop). 3. Mobile tables scroll sideways instead of breaking words letter by letter. 4. Card-paused pages (AI subscriptions etc.): a light 'tell me when the card is back' email box replaces the card gift line; members get the Ghost label 'Card waitlist' (Su 03/11). 5. Desktop: an app link no longer dumps the reader on a store page they cannot install from; it opens a light 'scan with your phone' QR modal for that same link (Su 03/11). GA4: clicks carry data-fzp, read by place() in 'fizen-ga4-events'. */ (function () { if (window.__fzc3) return; window.__fzc3 = 1; var D = document, W = window, path = location.pathname; var L = (D.documentElement.lang || 'en').slice(0, 2); if (/^\/ru\//.test(path)) L = 'ru'; else if (/^\/zh\//.test(path)) L = 'zh'; else if (/^\/es\//.test(path)) L = 'es'; else if (/^\/pt\//.test(path)) L = 'pt'; if (!/^(en|ru|zh|es|pt)$/.test(L)) L = 'en'; // own shortlinks per placement (affiliate dashboard: blog|sitewide|app_download|) var LINK = { qr: 'https://link.fizen.io/s/hCO52bdSc5', esim_ph: 'https://link.fizen.io/s/BEtiZGG32O', // sticky_bar_qr, sticky_bar_esim_ph esim: 'https://link.fizen.io/s/bUy2uA-pOZ', scan: 'https://link.fizen.io/s/69CADP_GPK' }; // sticky_bar_esim, desktop_qr_box var T = { en: { qmH: 'Get Fizen on your phone', qmP: 'Scan with your phone camera. It opens the App Store or Google Play.', qmS: 'Or open', gift: 'Your US$10 travel eSIM gift is waiting', giftB: 'Get my gift', getApp: 'Get the app', scan: 'On a computer? Scan with your phone camera to get the app.' }, ru: { qmH: 'Установите Fizen на телефон', qmP: 'Наведите камеру телефона на код. Откроется App Store или Google Play.', qmS: 'Или откройте', gift: 'Ваш подарок: US$10 на eSIM для поездки', giftB: 'Забрать', getApp: 'Скачать', scan: 'Вы за компьютером? Наведите камеру телефона, чтобы скачать приложение.' }, zh: { qmH: '在手机上安装 Fizen', qmP: '用手机相机扫码,会打开 App Store 或 Google Play。', qmS: '或打开', gift: '新用户送 US$10 旅行 eSIM 礼物', giftB: '领取礼物', getApp: '下载 App', scan: '在电脑上?用手机相机扫码下载 App。' }, es: { qmH: 'Instala Fizen en tu móvil', qmP: 'Escanea con la cámara del móvil. Se abre App Store o Google Play.', qmS: 'O abre', gift: 'Tu regalo de US$10 para eSIM de viaje te espera', giftB: 'Lo quiero', getApp: 'Descargar', scan: '¿En el ordenador? Escanea con la cámara del móvil para descargar la app.' }, pt: { qmH: 'Instale o Fizen no celular', qmP: 'Aponte a câmera do celular. Abre a App Store ou o Google Play.', qmS: 'Ou abra', gift: 'Seu presente de US$10 em eSIM de viagem está esperando', giftB: 'Quero o meu', getApp: 'Baixar', scan: 'No computador? Aponte a câmera do celular para baixar o app.' } }[L]; function css(s) { var e = D.createElement('style'); e.textContent = s; D.head.appendChild(e); } css('#fz-sb{position:fixed;left:8px;right:8px;bottom:8px;z-index:9990;display:flex;align-items:center;gap:10px;background:#fff;color:#171226;border:1px solid #E6DEFF;border-radius:14px;padding:10px 10px 10px 14px;box-shadow:0 6px 20px rgba(23,18,38,.12);font:500 14px/1.3 Inter,system-ui,sans-serif;transform:translateY(140%);transition:transform .3s}' + '#fz-sb.on{transform:none}#fz-sb .t{flex:1;min-width:0}#fz-sb a.b{flex:none;background:#6E3BFF;color:#fff;font-weight:600;border-radius:999px;padding:9px 14px;text-decoration:none;white-space:nowrap}' + '#fz-sb .x{flex:none;background:none;border:0;color:#9b97ad;font-size:18px;line-height:1;padding:4px;cursor:pointer}' + '.fz-scan{display:flex;align-items:center;gap:14px;margin-top:14px;font:500 13px/1.4 Inter,system-ui,sans-serif;color:inherit;opacity:.9}.fz-scan img{width:92px;height:92px;background:#fff;padding:6px;border-radius:10px;border:1px solid #E6DEFF;flex:none}.fz-scan{color:#5c5570}' + '.kg-product-card .fz-scan{color:#6b6880;justify-content:center}' + '@media(max-width:767px){.gh-content table td,.gh-content table th{min-width:7.5em;word-break:normal!important;overflow-wrap:normal!important;hyphens:manual}.gh-content table td:first-child,.gh-content table th:first-child{min-width:6.5em}}' + '.fz-wl{background:#FAF8FF;border:1px solid #ECE6FB;border-radius:12px;padding:16px 18px;margin:18px 0 22px;font-family:Inter,system-ui,sans-serif}' + '.fz-wl b{display:block;font-size:16px;font-weight:600;color:#171226;margin-bottom:4px}.fz-wl p{margin:0 0 12px!important;font-size:14px!important;line-height:1.5!important;color:#5c5570}' + '.fz-wl form{display:flex;gap:8px;flex-wrap:wrap}.fz-wl input{flex:1 1 200px;min-width:0;border:1px solid #E0D8F5;border-radius:10px;padding:10px 12px;font:400 15px Inter,system-ui,sans-serif;color:#171226;background:#fff}' + '.fz-wl input:focus{outline:none;border-color:#6E3BFF}.fz-wl button{flex:none;background:#6E3BFF;color:#fff;border:0;border-radius:10px;padding:10px 16px;font:600 15px Inter,system-ui,sans-serif;cursor:pointer}' + '.fz-wl button[disabled]{opacity:.6}.fz-wl .ok{font-size:14px;color:#171226;margin:0}.fz-wl small{display:block;margin-top:8px;font-size:12px;color:#8b84a3}' + '#fz-qm{position:fixed;inset:0;z-index:100000;background:rgba(23,18,38,.35);display:flex;align-items:center;justify-content:center;font-family:Inter,system-ui,sans-serif}' + '#fz-qm .c{position:relative;background:#fff;border:1px solid #E6DEFF;border-radius:16px;padding:26px 28px 22px;width:340px;text-align:center;box-shadow:0 20px 50px rgba(23,18,38,.18)}' + '#fz-qm b{display:block;font-size:19px;font-weight:600;color:#171226;margin-bottom:6px}#fz-qm p{margin:0 0 14px;font-size:14px;line-height:1.5;color:#5c5570}' + '#fz-qm img{width:196px;height:196px;border:1px solid #E6DEFF;border-radius:12px;padding:8px;background:#fff}#fz-qm .s{margin-top:14px;font-size:13px;color:#8b84a3}' + '#fz-qm .s a{color:#6E3BFF!important;font-weight:500;text-decoration:none!important;margin:0 6px}#fz-qm .x{position:absolute;top:8px;right:10px;background:none;border:0;font-size:22px;color:#8b84a3;cursor:pointer}' + '@media(max-width:767px){body.fz-has-sb #fzg-box{display:none!important}}@media(max-width:999px){.fz-scan{display:none}}@media(min-width:768px){#fz-sb{display:none}}'); /* ---------- what is this page selling ---------- */ function intent() { var box = D.querySelector('.fz-qrtop'), gl = D.querySelector('.fz-gl'); if (box && !box.classList.contains('fz-esimtop')) { var h = box.innerText || ''; var txt = /Philippines/.test(h) ? 'Pay any QR Ph code with USDT' : /Vietnam/.test(h) ? 'Pay VietQR, MoMo or VNPay with USDT' : 'Pay the local QR with USDT'; return { k: 'qr', t: txt, b: T.getApp, u: LINK.qr }; } if (box) return { k: 'esim', t: T.gift, b: T.giftB, u: LINK.esim_ph }; if (gl && gl.getAttribute('data-v') === 'card') return null; // card paused: nothing to sell on these pages if (gl && gl.getAttribute('data-v') === 'qr' && L === 'en') return { k: 'qr', t: 'Pay the local QR with USDT', b: T.getApp, u: LINK.qr }; if (!D.querySelector('.gh-content, .post-content, article')) return null; return { k: 'esim', t: T.gift, b: T.giftB, u: LINK.esim }; } /* ---------- 1. mobile sticky bar ---------- */ function sticky() { if (W.innerWidth >= 768) return; try { if (sessionStorage.getItem('fzsb')) return; } catch (e) {} var it = intent(); if (!it) return; var bar = D.createElement('div'); bar.id = 'fz-sb'; bar.setAttribute('data-fzp', 'sticky_bar_' + it.k); bar.innerHTML = ''; bar.querySelector('.t').textContent = it.t; var a = bar.querySelector('a.b'); a.textContent = it.b; a.href = it.u; D.body.appendChild(bar); D.body.classList.add('fz-has-sb'); // one floating element on mobile, not two var shown = false, cardIn = false, seen = {}; // hide while the top box or the end CTA card is on screen if ('IntersectionObserver' in W) [D.querySelector('.kg-product-card'), D.querySelector('.fz-qrtop')].forEach(function (el, i) { if (el) new IntersectionObserver(function (es) { seen[i] = es[0].isIntersecting; cardIn = seen[0] || seen[1]; tick(); }).observe(el); }); function tick() { var ov = D.getElementById('fzg-ov'), pop = ov && getComputedStyle(ov).display !== 'none' && ov.offsetParent !== null; var want = W.scrollY > 600 && !cardIn && !pop; if (want !== shown) { shown = want; bar.classList.toggle('on', want); } } bar.querySelector('.x').onclick = function () { bar.remove(); try { sessionStorage.setItem('fzsb', '1'); } catch (e) {} D.body.classList.remove('fz-has-sb'); if (typeof gtag === 'function') gtag('event', 'sticky_bar_close', { kind: it.k }); }; W.addEventListener('scroll', tick, { passive: true }); setInterval(tick, 1500); tick(); } /* ---------- 2. desktop scan-to-phone QR ---------- */ function qrImg(url, cb) { function go() { try { var q = qrcode(0, 'M'); q.addData(url); q.make(); cb(q.createDataURL(5, 2)); } catch (e) {} } if (W.qrcode) return go(); var s = D.createElement('script'); s.src = 'https://cdnjs.cloudflare.com/ajax/libs/qrcode-generator/1.4.4/qrcode.min.js'; s.onload = go; D.head.appendChild(s); } function scan(host, url, where) { if (!host || host.querySelector('.fz-scan')) return; qrImg(url, function (src) { var d = D.createElement('div'); d.className = 'fz-scan'; d.setAttribute('data-fzp', where); d.innerHTML = 'QR code to get the Fizen app'; d.querySelector('img').src = src; d.querySelector('span').textContent = T.scan; host.appendChild(d); if (typeof gtag === 'function') gtag('event', 'desktop_qr_shown', { placement: where }); }); } function desktop() { if (W.innerWidth < 1000) return; var box = D.querySelector('.fz-qrtop'); if (box) scan(box, LINK.scan, 'desktop_qr_box'); var card = D.querySelector('.kg-product-card'), btn = card && card.querySelector('a.kg-product-card-button[href]'); if (btn && /link\.fizen\.io|fizen\.io\/app/.test(btn.href)) scan(btn.parentNode, btn.href.split('?')[0], 'desktop_qr_card'); } /* ---------- 4. card waitlist (card paused) ---------- */ var WL = { en: ['Waiting for the Fizen Card to come back?', 'The card is paused for now. Leave your email and we will write once, the day it reopens.', 'Notify me', 'Done. Check your inbox and confirm your email.', 'One email when the card is back. Unsubscribe any time.'], ru: ['Ждёте возвращения карты Fizen?', 'Карта временно на паузе. Оставьте email, и мы напишем один раз, в день, когда она вернётся.', 'Сообщить мне', 'Готово. Подтвердите адрес в письме.', 'Одно письмо, когда карта вернётся. Отписаться можно в любой момент.'], es: ['¿Esperas el regreso de la tarjeta Fizen?', 'La tarjeta está en pausa por ahora. Déjanos tu email y te escribiremos una sola vez, el día que vuelva.', 'Avísame', 'Listo. Revisa tu correo y confirma tu email.', 'Un solo email cuando vuelva la tarjeta. Puedes darte de baja cuando quieras.'], pt: ['Esperando o cartão Fizen voltar?', 'O cartão está pausado por enquanto. Deixe seu email e escrevemos uma única vez, no dia em que voltar.', 'Me avise', 'Pronto. Confira seu email para confirmar.', 'Um único email quando o cartão voltar. Cancele quando quiser.'] }[L]; function waitlist() { var gl = D.querySelector('.fz-gl[data-v="card"]'); if (!gl || !WL) return; var box = D.createElement('div'); box.className = 'fz-wl'; box.setAttribute('data-fzp', 'card_waitlist'); box.innerHTML = '

'; box.querySelector('b').textContent = WL[0]; box.querySelector('p').textContent = WL[1]; box.querySelector('button').textContent = WL[2]; box.querySelector('small').textContent = WL[4]; gl.parentNode.replaceChild(box, gl); D.querySelectorAll('.fz-sub').forEach(function (e) { e.remove(); }); // one email ask per page var f = box.querySelector('form'), inp = f.querySelector('input'), btn = f.querySelector('button'); f.addEventListener('submit', function (e) { e.preventDefault(); var em = inp.value.trim(); if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(em)) { inp.focus(); inp.style.borderColor = '#e5484d'; return; } btn.disabled = true; fetch('/members/api/integrity-token/', { credentials: 'same-origin' }).then(function (r) { return r.text(); }).then(function (tok) { return fetch('/members/api/send-magic-link/', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: em, emailType: 'signup', labels: ['Card waitlist'], integrityToken: tok, urlHistory: [] }) }); }).then(function (r) { if (!r.ok) throw 0; f.outerHTML = '

'; box.querySelector('.ok').textContent = WL[3]; if (typeof gtag === 'function') { gtag('event', 'sign_up', { method: 'card_waitlist' }); gtag('event', 'card_waitlist_submit', { post_slug: path.replace(/^\/|\/$/g, '') }); } }).catch(function () { btn.disabled = false; inp.style.borderColor = '#e5484d'; }); }); } /* ---------- 5. desktop: app link -> scan-to-phone modal ---------- */ var IOS = 'https://apps.apple.com/app/id1621269508', PLAY = 'https://play.google.com/store/apps/details?id=com.fizen.io.wallet'; function modal(url) { var old = D.getElementById('fz-qm'); if (old) old.remove(); var m = D.createElement('div'); m.id = 'fz-qm'; m.setAttribute('data-fzp', 'desktop_qr_modal'); m.innerHTML = ''; m.querySelector('b').textContent = T.qmH; m.querySelector('p').textContent = T.qmP; m.querySelector('.s span').textContent = T.qmS; var as = m.querySelectorAll('.s a'); as[0].href = IOS; as[1].href = PLAY; qrImg(url, function (src) { m.querySelector('img').src = src; }); function close() { m.remove(); D.removeEventListener('keydown', esc); } function esc(e) { if (e.key === 'Escape') close(); } m.addEventListener('click', function (e) { if (e.target === m || e.target.className === 'x') close(); }); D.addEventListener('keydown', esc); D.body.appendChild(m); if (typeof gtag === 'function') gtag('event', 'desktop_qr_modal', { link_url: url.split('?')[0] }); } function desktopLinks() { D.addEventListener('click', function (e) { // bubble phase: the GA click logger (capture) has already run if (W.innerWidth < 1000 || !W.matchMedia('(pointer:fine)').matches) return; var a = e.target.closest && e.target.closest('a[href]'); if (!a || a.closest('#fz-qm')) return; if (!/link\.fizen\.io\/s\/|fizen\.io\/app|apps\.apple\.com|play\.google\.com/.test(a.href)) return; if (e.metaKey || e.ctrlKey || e.shiftKey) return; e.preventDefault(); e.stopPropagation(); modal(a.href); }); } desktopLinks(); function init() { setTimeout(function () { waitlist(); sticky(); desktop(); }, 600); } // after fz-qrtop / declutter have run if (D.readyState === 'loading') D.addEventListener('DOMContentLoaded', init); else init(); })();