1/* ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 2 Glycosutra â shared analytics + Google Ads conversion tracking 3 4 ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 5 â LAST UPDATED : 2026-08-18 22:10 IST (delta-144) â 6 âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ⤠7 â 2026-08-18 delta-144 captures gclid / utm_* so leads can be traced â 8 â to a channel; strips query strings (cal.com was â 9 â forwarding the booker's EMAIL in the URL). â 10 â 2026-08-18 delta-143 chat.whatsapp.com group joins now fire â 11 â whatsapp_group_join, and are NOT counted as an â 12 â Ads conversion. A group join is not a lead. â 13 â 2026-08-15 delta-140 created â GA4 + Ads conversion tracking; one â 14 â file so the IDs cannot drift between pages. â 15 ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 16 ⸠WHEN YOU CHANGE THIS FILE, ADD A DATED LINE ABOVE. 17 Windows Explorer's "Date" column often shows Date *Created*, not Date 18 Modified, so it cannot be trusted to tell you whether the copy sitting on 19 Hostinger is current. This header is the source of truth. 20 21 ⸠PASTE YOUR THREE IDs BELOW. This is the ONLY file you need to edit â 22 every page (homepage + the db_reversal funnel) loads this one script, so 23 the IDs can never drift out of sync between pages. 24 25 GA4 : analytics.google.com ⺠Admin ⺠Data streams -> G-XXXXXXXXXX 26 ADS : ads.google.com ⺠Goals ⺠Conversions ⺠Tag setup -> AW-XXXXXXXXX 27 LABEL : the conversion action's label, e.g. "abcDEfghIJ" 28 29 Until real IDs are pasted, this file is INERT: no network request, no errors, 30 nothing breaks. Verify with tagassistant.google.com before spending ad budget. 31 32 Privacy: a health-adjacent site, so IP anonymisation is on, ad personalisation 33 is off, and ads_data_redaction is on. Lead name / email / phone are NEVER sent 34 to Google â only the fact that a conversion happened. 35 ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 36(function () { 37 'use strict'; 38 39 var GA4 = 'G-SYLK1XYPY3'; // Glycosutra GA4 property (live 2026-08-15) 40 var ADS = 'ADS_CONVERSION_ID'; // e.g. 'AW-XXXXXXXXX' 41 var LABEL = 'ADS_CONVERSION_LABEL'; // e.g. 'abcDEfghIJ' 42 43 var hasGA4 = GA4.indexOf('G-') === 0; 44 var hasAds = ADS.indexOf('AW-') === 0; 45 var hasLabel = LABEL.indexOf('ADS_') !== 0; 46 window.GLYCO_TRACKING_LIVE = hasGA4 || hasAds; 47 48 // ââ attribution capture (delta-144) ââââââââââââââââââââââââââââââââââââââ 49 // Google puts ?gclid=... on the URL when someone arrives from an ad. It was 50 // already there and nothing read it, so every lead recorded its SOURCE as 51 // "db_reversal_funnel" â the page it was filled on, not where the person came 52 // from. Google Ads, Meta, a typed URL and a shared link all looked identical, 53 // which made cost-per-lead unknowable (â¹270 or â¹450 depending on how many of 54 // the 5 leads on 2026-08-18 actually came from the ads). 55 // 56 // Captured on FIRST landing and kept in sessionStorage, because the visitor 57 // lands on index.html and submits from there â the parameters are gone from 58 // the URL by the time any later page loads. 59 var ATTR_KEYS = ['gclid','gbraid','wbraid','fbclid','msclkid', 60 'utm_source','utm_medium','utm_campaign','utm_term','utm_content']; 61 var ATTR_STORE = 'glyco_attr'; 62 63 function captureAttribution() { 64 var found = {}, qs, i, kv, k, v; 65 try { 66 qs = (window.location.search || '').replace(/^\?/, '').split('&'); 67 for (i = 0; i < qs.length; i++) { 68 kv = qs[i].split('='); 69 k = decodeURIComponent(kv[0] || ''); 70 v = decodeURIComponent((kv[1] || '')).slice(0, 200); 71 if (v && ATTR_KEYS.indexOf(k) > -1) found[k] = v; 72 }
73 if (Object.keys(found).length) { 74 found.landing_page = window.location.pathname; 75 found.referrer = (document.referrer || '').slice(0, 200); 76 sessionStorage.setItem(ATTR_STORE, JSON.stringify(found)); 77 } else if (!sessionStorage.getItem(ATTR_STORE)) { 78 // no ad parameters â record the referrer so Direct vs Organic is still known 79 sessionStorage.setItem(ATTR_STORE, JSON.stringify({ 80 referrer: (document.referrer || '').slice(0, 200), 81 landing_page: window.location.pathname 82 })); 83 } 84 } catch (e) { /* private mode / storage disabled â never break the page */ } 85 } 86 87 /** What the funnel form attaches to the lead. Always an object. */ 88 window.glycoAttribution = function () { 89 try { return JSON.parse(sessionStorage.getItem(ATTR_STORE) || '{}'); } 90 catch (e) { return {}; } 91 }; 92 93 captureAttribution(); 94 95 window.dataLayer = window.dataLayer || []; 96 function gtag() { dataLayer.push(arguments); } 97 window.gtag = window.gtag || gtag; 98 99 if (window.GLYCO_TRACKING_LIVE) { 100 var el = document.createElement('script'); 101 el.async = true; 102 el.src = 'https://www.googletagmanager.com/gtag/js?id=' + (hasGA4 ? GA4 : ADS); 103 document.head.appendChild(el); 104 } 105 106 gtag('js', new Date()); 107 gtag('set', 'ads_data_redaction', true); 108 gtag('set', 'allow_ad_personalization_signals', false); 109 // delta-144 PII GUARD: cal.com was appending the booker's email to the 110 // thank-you URL (?email=...). page_location defaults to the full URL, which 111 // would have sent that email to Google â breaking this file's own promise 112 // above. Send the path only; query strings never leave the browser. 113 var CLEAN_LOCATION = window.location.origin + window.location.pathname; 114 if (hasGA4) gtag('config', GA4, { 115 anonymize_ip: true, 116 page_location: CLEAN_LOCATION 117 }); 118 if (hasAds) gtag('config', ADS); 119 120 /** Fire an event; optionally also count it as a Google Ads conversion. */ 121 function track(name, params, isConversion) { 122 try { 123 gtag('event', name, params || {}); 124 if (isConversion && hasAds && hasLabel) { 125 gtag('event', 'conversion', { send_to: ADS + '/' + LABEL }); 126 } 127 } catch (e) { /* analytics must never break the page */ } 128 } 129 window.glycoTrack = track; // funnel pages call this on successful lead capture 130 131 // ââ link conversions (homepage: 9x cal.com + WhatsApp) ââââââââââââââââââââ
132 // Delegated, so links added later are tracked with no further edits. 133 document.addEventListener('click', function (e) { 134 var a = e.target && e.target.closest ? e.target.closest('a[href]') : null; 135 if (!a) return; 136 var href = a.getAttribute('href') || ''; 137 var label = (a.textContent || '').trim().slice(0, 60); 138 if (href.indexOf('cal.com') > -1) track('book_consultation_click', { link_text: label }, true); 139 // delta-143: a GROUP JOIN is not a lead. chat.whatsapp.com is checked FIRST 140 // and reported as a non-conversion, otherwise it fell through to the 141 // wa.me branch below and would be counted as a Google Ads conversion â 142 // re-creating the exact over-counting that made the Ads dashboard claim 143 // 27 conversions against 5 real leads on 2026-08-18. 144 else if (href.indexOf('chat.whatsapp.com') > -1) 145 track('whatsapp_group_join', { link_text: label }, false); 146 else if (href.indexOf('wa.me') > -1 || 147 href.indexOf('whatsapp') > -1) track('whatsapp_click', { link_text: label }, true); 148 else if (href.indexOf('tel:') === 0) track('phone_click', { link_text: label }, true); 149 else if (href.indexOf('mailto:') === 0) track('email_click', { link_text: label }, false); 150 }, true); 151 152 // ââ engagement depth â separates real readers from bounced ad traffic âââââ 153 var marks = [25, 50, 75, 90], fired = {}; 154 window.addEventListener('scroll', function () { 155 var h = document.documentElement; 156 var pct = (h.scrollTop + window.innerHeight) / h.scrollHeight * 100; 157 for (var i = 0; i < marks.length; i++) { 158 var m = marks[i]; 159 if (pct >= m && !fired[m]) { fired[m] = 1; track('scroll_depth', { percent: m }, false); } 160 } 161 }, { passive: true }); 162 163 setTimeout(function () { track('engaged_45s', {}, false); }, 45000); 164 165 // ââ funnel step auto-tagging âââââââââââââââââââââââââââââââââââââââââââââ 166 // video.html and thank-you.html are steps AFTER the lead is captured, so 167 // reaching them is itself the signal worth measuring. 168 var path = (location.pathname || '').toLowerCase(); 169 if (path.indexOf('thank-you') > -1) track('funnel_thank_you', {}, false); 170 else if (path.indexOf('video') > -1) track('funnel_video_reached', {}, false); 171})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.