Go beyond the news. Get the insights.

By signing up, I agree to the terms of service and community guidelines.

Success! Now check your email

To complete your subscription, click the confirmation link in your inbox. If it doesn’t arrive within 3 minutes, kindly check your spam folder.

Follow Us

/ Consumer Tech View All

Techloy Originals
View all →

Stories you won't read anywhere else. Investigations, interviews, reviews and docuseries.

/ Entertainment View All

/ Startups View All

The numbers shaping technology and the industries around it. Our data-led newsletter for premium members.

Get Insight Loop in your inbox. Try it free for 7 days.

Start free trial

/ Artificial Intelligence View All

Subscribe to Techloy.com

Don't just read the news.
Understand it.

Original reporting, sharp analysis and the insights behind the headlines, delivered to your inbox.

Subscribe to Techloy.com
Start free, upgrade anytime.
Join 150K+ monthly readers.
(function () { /* ================= SETTINGS ================= */ var CONFIG = { // Ghost Portal signup link for the free Techloy Weekly newsletter signupUrl: "/#/portal/signup/free", // Show ONLY on posts with at least one of these tag slugs. Leave empty [] to show on all posts. includeTags: [], // NEVER show on posts with any of these tag slugs. // Tip: add the internal tag "#no-newsletter-cta" to any post to switch it off (slug: hash-no-newsletter-cta). excludeTags: ["insight-loop", "sponsored", "hash-no-newsletter-cta"], // Vertical-specific messages. "tags" lists every slug that belongs to that vertical // (check each slug at techloy.com/tag// and add any aliases your team uses). // The post's PRIMARY tag is checked first, then its other tags, in order. verticals: [ { tags: ["news", "tech-news"], message: "Stay ahead of the tech news that matters, without the noise. Join 150,000+ monthly readers!" }, { tags: ["products", "consumer-tech", "gadgets", "reviews", "smartphones"], message: "Get the latest gadget news, launches and honest reviews before you buy. Join 150,000+ monthly readers!" }, { tags: ["startups", "startup", "funding"], message: "Track the startups, funding rounds and founders shaping what's next. Join 150,000+ monthly readers!" }, { tags: ["fintech", "payments"], message: "Follow the fintech moves reshaping how the world pays, saves and borrows. Join 150,000+ monthly readers!" }, { tags: ["money", "personal-finance"], message: "Get smarter about money, tech and the tools that help you earn, save and grow. Join 150,000+ monthly readers!" }, { tags: ["ai", "artificial-intelligence"], message: "Keep up with AI: the biggest launches, breakthroughs and what they mean for you. Join 150,000+ monthly readers!" }, { tags: ["entertainment", "gaming", "streaming", "music", "film", "tv", "books"], message: "Get the best of gaming, streaming, film, TV and music where it meets tech. Join 150,000+ monthly readers!" }, { tags: ["cybersecurity", "security", "privacy"], message: "Stay on top of the breaches, threats and fixes that keep you safe online. Join 150,000+ monthly readers!" }, { tags: ["cryptocurrency", "crypto", "web3", "blockchain"], message: "Cut through the hype on crypto and web3 with clear, useful analysis. Join 150,000+ monthly readers!" }, { tags: ["evs", "electric-vehicles", "mobility"], message: "Follow the EVs and mobility tech changing how we move. Join 150,000+ monthly readers!" }, { tags: ["careers", "jobs", "opportunities"], message: "Never miss the tech jobs, programmes and opportunities worth your time. Join 150,000+ monthly readers!" }, { tags: ["tech-guides", "how-to", "how-tos"], message: "Get simple, step-by-step tech guides that help you do more with your devices and apps. Join 150,000+ monthly readers!" }, { tags: ["insights"], message: "Go beyond the headlines with sharp analysis on where tech, business and markets are heading. Join 150,000+ monthly readers!" }, // priority: true = wins whenever the post has this tag, regardless of tag order { tags: ["the-founder-room", "founder-room"], priority: true, message: "Get our original interviews, stories and videos with the people building tech. Join 150,000+ monthly readers!" }, { tags: ["techloy-originals", "videos"], message: "Get our original interviews, stories and videos with the people building tech. Join 150,000+ monthly readers!" } ], defaultMessage: "Don't miss the biggest technology stories, insights and opportunities. Join 150,000+ monthly readers!", linkText: "", // optional text link after the message; leave empty for none buttonText: "Subscribe for free", // Also show a copy mid-article after this paragraph number (0 = off). midArticleAfterParagraph: 4, // Ghost's paywall box (shown to non-members on members-only/paid posts). // When it's on the page, our box is skipped so readers see one clear ask, not two. // If your theme's paywall uses a different class, add it here. paywallSelectors: [".gh-post-upgrade-cta", ".post-upgrade-cta", ".content-cta", ".gh-cta"], // Signed-in members are already subscribed, so they get a REFERRAL box instead, // placed at the END of the article (after they've enjoyed it, when sharing feels natural). referral: { enabled: true, pageUrl: "/referrals/", message: "Enjoying Techloy? Share it with your network and get rewarded when they join.", buttonText: "Refer now" } }; /* ============================================ */ // Every decision is logged to the browser console (filter by "TL CTA") so you can see why a box did or didn't show function log(msg) { try { console.info("[TL CTA] " + msg); } catch (e) {} } // Run once only, even if the code ends up pasted in more than one place if (window.__tlCtaLoaded) { log("skipped: another copy of this script already ran. Remove the extra copy from Code injection."); return; } window.__tlCtaLoaded = true; var body = document.body; if (!body || !body.classList.contains("post-template")) { log("skipped: not a post page (body classes: " + (body ? body.className : "none") + ")"); return; } // Ghost adds a "tag-" class to for every tag on the post var tags = []; body.className.split(/\s+/).forEach(function (c) { if (c.indexOf("tag-") === 0) tags.push(c.slice(4)); }); var has = function (list) { return list.some(function (t) { return tags.indexOf(t) !== -1; }); }; log("post tags: " + (tags.join(", ") || "none")); if (has(CONFIG.excludeTags)) { log("skipped: post has an excluded tag"); return; } if (CONFIG.includeTags.length && !has(CONFIG.includeTags)) { log("skipped: post has none of the includeTags"); return; } // Find the article body (covers Casper/Source/Headline and most custom themes) var selectors = [".gh-content", ".post-content", ".post-full-content", ".article-content", ".single-content", ".kg-canvas"]; var content = null; for (var i = 0; i < selectors.length && !content; i++) content = document.querySelector(selectors[i]); if (!content) { log("skipped: couldn't find the article body. Tell Claude the class of the element that wraps the article text."); return; } log("article body found: " + content.tagName.toLowerCase() + "." + String(content.className).trim().split(/\s+/).join(".")); // Paywalled post seen by a non-member: Ghost puts its signup box INSIDE the article body, // so only look there (site-wide subscribe blocks in the header/footer won't trigger this). // On a paywalled post we still show the mid-article box (above the paywall), but skip the end box. var paywall = content.querySelector(CONFIG.paywallSelectors.join(",")); if (paywall) log("paywall found (" + paywall.className + "): mid-article box only"); // Pick the message by vertical. Ghost lists tag classes in the post's tag order, // so the primary tag gets first say; falls back to the default message. var message = CONFIG.defaultMessage; var matched = false; // Priority verticals first (e.g. The Founder Room), whatever position the tag is in for (var q = 0; q < CONFIG.verticals.length && !matched; q++) { if (CONFIG.verticals[q].priority && has(CONFIG.verticals[q].tags)) { message = CONFIG.verticals[q].message; matched = true; } } if (!matched) outer: for (var j = 0; j < tags.length; j++) { for (var k = 0; k < CONFIG.verticals.length; k++) { if (CONFIG.verticals[k].tags.indexOf(tags[j]) !== -1) { message = CONFIG.verticals[k].message; break outer; } } } function buildBox() { var box = document.createElement("aside"); box.className = "tl-nl-cta"; var p = document.createElement("p"); p.appendChild(document.createTextNode(message)); if (CONFIG.linkText) { p.appendChild(document.createTextNode(" ")); var link = document.createElement("a"); link.href = CONFIG.signupUrl; link.setAttribute("data-portal", "signup"); link.textContent = CONFIG.linkText; p.appendChild(link); } var btn = document.createElement("a"); btn.className = "tl-nl-btn"; btn.href = CONFIG.signupUrl; btn.setAttribute("data-portal", "signup"); btn.textContent = CONFIG.buttonText; box.appendChild(p); box.appendChild(btn); return box; } var inserted = []; // Line the box up with the article's text column (themes often give the body a wider grid than the text) var refPara = content.querySelector(":scope > p") || content.querySelector("p"); function align(box) { if (!refPara) return; box.style.marginLeft = ""; box.style.marginRight = ""; box.style.maxWidth = ""; var pr = refPara.getBoundingClientRect(); if (!pr.width) return; box.style.boxSizing = "border-box"; box.style.width = "100%"; box.style.maxWidth = pr.width + "px"; var br = box.getBoundingClientRect(); var currentLeft = parseFloat(getComputedStyle(box).marginLeft) || 0; box.style.marginLeft = (currentLeft + (pr.left - br.left)) + "px"; box.style.marginRight = "0"; } // Real body paragraphs only (skip quotes, captions, cards and very short lines), // and only those above the paywall when there is one. var paras = Array.prototype.filter.call(content.querySelectorAll("p"), function (p) { if (p.closest("blockquote, figure, aside, .kg-card, .tl-nl-cta")) return false; if (p.textContent.trim().length < 40) return false; if (paywall && !(p.compareDocumentPosition(paywall) & Node.DOCUMENT_POSITION_FOLLOWING)) return false; return true; }); // ONE box per article: mid-article when the post is long enough, // otherwise at the end (end is skipped on paywalled posts, where Ghost's signup box sits). var n = CONFIG.midArticleAfterParagraph; var needed = paywall ? n + 1 : n + 3; // leave some text after the box if (n > 0 && paras.length >= needed) { // insert after the paragraph's top-level block inside the article body var anchor = paras[n - 1]; while (anchor.parentElement && anchor.parentElement !== content) anchor = anchor.parentElement; var midBox = buildBox(); anchor.insertAdjacentElement("afterend", midBox); inserted.push(midBox); log("placed mid-article, after paragraph " + n); } else if (!paywall) { var endBox = buildBox(); content.appendChild(endBox); inserted.push(endBox); log("placed at the end (post too short for mid-article)"); } else { log("no box: only " + paras.length + " body paragraphs above the paywall"); } if (!inserted.length) { log("nothing to show on this post"); return; } inserted.forEach(align); window.addEventListener("resize", function () { inserted.forEach(align); }); /* ---------- Signed-in members: referral box at the end of the article ---------- */ var R = CONFIG.referral; function buildReferralBox() { var box = document.createElement("aside"); box.className = "tl-nl-cta tl-ref-cta"; var p = document.createElement("p"); p.textContent = R.message; var btn = document.createElement("a"); btn.className = "tl-nl-btn"; btn.href = R.pageUrl; btn.textContent = R.buttonText; box.appendChild(p); box.appendChild(btn); return box; } function showReferral() { inserted.forEach(function (b) { b.remove(); }); // members don't need the newsletter ask inserted = []; // A free member on a paid post sees Ghost's upgrade box instead; don't add a second ask if (paywall || content.querySelector(CONFIG.paywallSelectors.join(","))) { log("member signed in, paywall on this post: no referral box"); return; } var box = buildReferralBox(); content.appendChild(box); inserted.push(box); align(box); log("member signed in: referral box placed at the end"); } // Ghost returns 204 / empty when nobody is signed in if (window.fetch) { fetch("/members/api/member/", { credentials: "same-origin" }) .then(function (r) { return r.status === 200 ? r.json() : null; }) .then(function (member) { if (member && member.email) { if (R.enabled) showReferral(); else { inserted.forEach(function (b) { b.remove(); }); log("member signed in: box hidden"); } } else { log("shown: newsletter box"); } }) .catch(function () {}); } })();