1/* 2 * dw-consent.js â Consent Mode v2 (advanced) + banner + Global Privacy Control. 3 * 4 * CANONICAL COPY. Duplicated verbatim into the other three apps, like 5 * dw-attribution.js and dw-track.js: 6 * 7 * taurist-hub-site/public/hub-static/dw-consent.js <- canonical, edit here 8 * taurist-workers-landing/public/dw-consent.js 9 * siteworks-site/dw-consent.js 10 * conversion-works-site/public/conversion-static/dw-consent.js 11 * 12 * --------------------------------------------------------------------------- 13 * LOAD ORDER IS NOT OPTIONAL 14 * --------------------------------------------------------------------------- 15 * This file MUST be a synchronous <script> placed BEFORE the GTM snippet and 16 * before the gtag loader. Consent Mode only works if the default state is in 17 * the dataLayer before any tag reads it. Load it after GTM and every tag has 18 * already fired once with no consent signal at all â which is the failure this 19 * whole file exists to prevent, and it fails silently. 20 * 21 * This is the opposite of dw-track.js, which is order-independent on purpose. 22 * 23 * --------------------------------------------------------------------------- 24 * Why "advanced" mode 25 * --------------------------------------------------------------------------- 26 * Two ways to run Consent Mode: 27 * 28 * basic â tags do not load at all until the user accepts. Clean, and it 29 * throws away every declining visitor. No modeling, no recovery. 30 * advanced â tags load immediately and send cookieless pings carrying the 31 * consent state. Google models the conversions it cannot observe. 32 * 33 * Advanced is chosen here. Declined traffic still produces modeled conversions 34 * instead of a hole in the data, and the pings carry no identifiers. 35 * 36 * --------------------------------------------------------------------------- 37 * Regional defaults, without a geo-IP lookup 38 * --------------------------------------------------------------------------- 39 * `gtag('consent','default',{region:[...]})` lets Google apply the strict 40 * default to EEA/UK by itself, from the request it already sees. So: 41 * 42 * EEA + UK + CH -> everything denied until the visitor opts in 43 * everywhere else -> granted, with a visible way to opt out 44 * 45 * That is the "US-first, EU-ready" posture: full measurement on US traffic, 46 * automatic gating where the law requires opt-in, and no third-party geo 47 * service in the critical path of the first paint. 48 * 49 * --------------------------------------------------------------------------- 50 * The banner shows everywhere, on purpose 51 * --------------------------------------------------------------------------- 52 * Taurist Technologies Inc. is a New Jersey entity. The NJ Data Privacy Act 53 * (and the other US state laws now in force) require a documented opt-out path 54 * for targeted advertising and sale/sharing â not opt-in, but it has to exist 55 * and be reachable. A US visitor is measured by default AND can decline; an 56 * EEA visitor is not measured until they accept. One component, two behaviours, 57 * driven entirely by the region parameter above. 58 * 59 * --------------------------------------------------------------------------- 60 * Global Privacy Control 61 * --------------------------------------------------------------------------- 62 * GPC is a legally recognised opt-out signal in several US states. When 63 * navigator.globalPrivacyControl is true, advertising consent is denied 64 * immediately and permanently, the banner never appears, and no interaction is 65 * required. Honouring it is not optional and it is not a preference â it is the 66 * visitor exercising a right. 67 * 68 * ponytail: ~200 lines of vanilla JS instead of a CMP subscription. A CMP earns 69 * its price when you need IAB TCF vendor strings for programmatic display â not 70 * for GA4, Ads and Meta on a four-page marketing site. Revisit if Taurist ever 71 * buys programmatic inventory. 72 */ 73(function () { 74 "use strict"; 75 76 var STORE = "taurist_consent"; 77 var VERSION = 1; // bump to re-ask everyone after a material policy change 78 79 // Consent Mode v2. ad_user_data and ad_personalization are the v2 additions â 80 // omitting them is the single most common v2 migration miss, and Google reads 81 // a missing signal as denied for EEA traffic. 82 var AD_KEYS = ["ad_storage", "ad_user_data", "ad_personalization"]; 83 var ANALYTICS_KEYS = ["analytics_storage"]; 84 85 // Consent Mode's own EEA list, plus the UK and Switzerland. 86 var STRICT_REGIONS = [ 87 "AT","BE","BG","HR","CY","CZ","DK","EE","FI","FR","DE","GR","HU","IS","IE", 88 "IT","LV","LI","LT","LU","MT","NL","NO","PL","PT","RO","SK","SI","ES","SE", 89 "GB","CH" 90 ]; 91 92 window.dataLayer = window.dataLayer || []; 93 function gtag() { 94 // Consent Mode reads the raw `arguments` object out of the dataLayer. This 95 // is the ONE place that shape is correct â everywhere else in this codebase 96 // it is the bug (see dw-track.js). 97 window.dataLayer.push(arguments); 98 } 99 100 function all(value) { 101 return { 102 ad_storage: value, 103 ad_user_data: value, 104 ad_personalization: value, 105 analytics_storage: value, 106 functionality_storage: "granted", // never gated: it is what keeps the site working 107 security_storage: "granted", // never gated: fraud prevention and auth 108 personalization_storage: value 109 }; 110 } 111 112 /* ---- 1. Defaults, before any tag can read them --------------------- */ 113 114 var strict = all("denied"); 115 strict.region = STRICT_REGIONS; 116 strict.wait_for_update = 500;
116 // ms for our own update() below to land 117 gtag("consent", "default", strict); 118 119 var relaxed = all("granted"); 120 relaxed.analytics_storage = "granted"; 121 gtag("consent", "default", relaxed); 122 123 // Redact ads data until consent is known, so the pre-decision pings carry no 124 // identifiers even where the default is granted. 125 gtag("set", "ads_data_redaction", true); 126 gtag("set", "url_passthrough", true); 127 128 /* ---- 2. Stored decision, GPC, and the resulting state -------------- */ 129 130 function read() { 131 try { 132 var raw = localStorage.getItem(STORE); 133 if (!raw) return null; 134 var parsed = JSON.parse(raw); 135 return parsed && parsed.v === VERSION ? parsed : null; 136 } catch (e) { 137 return null; 138 } 139 } 140 141 function write(state) { 142 try { 143 localStorage.setItem( 144 STORE, 145 JSON.stringify({ v: VERSION, state: state, ts: new Date().toISOString() }) 146 ); 147 } catch (e) { 148 /* private mode â the decision holds for this page view only */ 149 } 150 } 151 152 function gpcOn() { 153 try { 154 return navigator.globalPrivacyControl === true; 155 } catch (e) { 156 return false; 157 } 158 } 159 160 // Mirrored onto window for dw-track.js, which stamps consent_ad on every 161 // event so the Worker can gate the CAPI fan-out per event. Server-side is not 162 // a consent loophole. 163 function publish(state) { 164 window.__taurist_consent = state; 165 } 166 167 function apply(state, persist) { 168 gtag("consent", "update", state); 169 if (state.ad_storage === "granted") { 170 gtag("set", "ads_data_redaction", false); 171 } 172 publish(state); 173 if (persist) write(state); 174 } 175 176 function grantAll() { 177 var s = all("granted"); 178 delete s.region; 179 return s; 180 } 181 182 function denyAds() { 183 var s = all("granted"); 184 delete s.region; 185 for (var i = 0; i < AD_KEYS.length; i++) s[AD_KEYS[i]] = "denied"; 186 s.personalization_storage = "denied"; 187 return s; 188 } 189 190 function denyAll() { 191 var s = all("denied"); 192 delete s.region; 193 s.functionality_storage = "granted"; 194 s.security_storage = "granted"; 195 return s; 196 } 197 198 /* 199 * The decision runs at the BOTTOM of this file, after window.tauristConsent 200 * is published. It used to run here and `return` out of the IIFE on the GPC 201 * and stored-decision paths â which meant the public API was never assigned 202 * for exactly the two groups that need it most: someone who already declined 203 * and wants back in, and the privacy policy's "manage choices" link. 204 */ 205 206 /* ---- 3. Banner ----------------------------------------------------- */ 207 208 function el(tag, attrs, text) { 209 var n = document.createElement(tag); 210 for (var k in attrs) { 211 if (Object.prototype.hasOwnProperty.call(attrs, k)) n.setAttribute(k, attrs[k]); 212 } 213 if (text) n.textContent = text; 214 return n; 215 } 216 217 var consentScriptSrc = document.currentScript && document.currentScript.src || ""; 218 219 function spaceCookieIcon() { 220 return el("img", { 221 class: "twc__space-cookie", 222 src: consentScriptSrc ? consentScriptSrc.replace(/dw-consent\.js(?:[?#].*)?$/, "space-cookie.png") : "space-cookie.png", 223 alt: "" 224 }); 225 } 226 227 /* Figma node 2844:31096. The browser controls below are its exported assets, 228 embedded to keep this one file portable across all four sites. */ 229 var CHROME_CLOSE = { normal: "data:image/svg+xml;base64,PHN2ZyBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA4NS40IDg1LjQiIHZpZXdCb3g9IjAgMCA4NS40IDg1LjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Im00Mi43IDg1LjRjMjMuNiAwIDQyLjctMTkuMSA0Mi43LTQyLjdzLTE5LjEtNDIuNy00Mi43LTQyLjctNDIuNyAxOS4xLTQyLjcgNDIuNyAxOS4xIDQyLjcgNDIuNyA0Mi43eiIgZmlsbD0iI2UyNGI0MSIvPjxwYXRoIGQ9Im00Mi43IDgxLjhjMjEuNiAwIDM5LjEtMTcuNSAzOS4xLTM5LjFzLTE3LjUtMzkuMS0zOS4xLTM5LjEtMzkuMSAxNy41LTM5LjEgMzkuMSAxNy41IDM5LjEgMzkuMSAzOS4xeiIgZmlsbD0iI2VkNmE1ZiIvPjwvZz48L3N2Zz4=", hover: "data:image/svg+xml;base64,PHN2ZyBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA4NS40IDg1LjQiIHZpZXdCb3g9IjAgMCA4NS40IDg1LjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Im00Mi43IDg1LjRjMjMuNiAwIDQyLjctMTkuMSA0Mi43LTQyLjdzLTE5LjEtNDIuNy00Mi43LTQyLjctNDIuNyAxOS4xLTQyLjcgNDIuNyAxOS4xIDQyLjcgNDIuNyA0Mi43eiIgZmlsbD0iI2UyNGI0MSIvPjxwYXRoIGQ9Im00Mi43IDgxLjhjMjEuNiAwIDM5LjEtMTcuNSAzOS4xLTM5LjFzLTE3LjUtMzkuMS0zOS4xLTM5LjEtMzkuMSAxNy41LTM5LjEgMzkuMSAxNy41IDM5LjEgMzkuMSAzOS4xeiIgZmlsbD0iI2VkNmE1ZiIvPjxnIGZpbGw9IiM0NjA4MDQiPjxwYXRoIGQ9Im0yMi41IDU3LjggMzUuMy0zNS4zYzEuNC0xLjQgMy42LTEuNCA1IDBsLjEuMWMxLjQgMS40IDEuNCAzLjYgMCA1bC0zNS4zIDM1LjNjLTEuNCAxLjQtMy42IDEuNC01IDBsLS4xLS4xYy0xLjMtMS40LTEuMy0zLjYgMC01eiIvPjxwYXRoIGQ9Im0yNy42IDIyLjUgMzUuMyAzNS4zYzEuNCAxLjQgMS40IDMuNiAwIDVsLS4xLjFjLTEuNCAxLjQtMy42IDEuNC01IDBsLTM1LjMtMzUuM2MtMS40LTEuNC0xLjQtMy42IDAtNWwuMS0uMWMxLjQtMS4zIDMuNi0xLjMgNSAweiIvPjwvZz48L2c+PC9zdmc+", press: "data:image/svg+xml;base64,PHN2ZyBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA4NS40IDg1LjQiIHZpZXdCb3g9IjAgMCA4NS40IDg1LjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Im00Mi43IDg1LjRjMjMuNiAwIDQyLjctMTkuMSA0Mi43LTQyLjdzLTE5LjEtNDIuNy00Mi43LTQyLjctNDIuNyAxOS4xLTQyLjcgNDIuNyAxOS4xIDQyLjcgNDIuNyA0Mi43eiIgZmlsbD0iI2ExNDIzOSIvPjxwYXRoIGQ9Im00Mi43IDgxLjdjMjEuNiAwIDM5LjEtMTcuNSAzOS4xLTM5LjFzLTE3LjUtMzkuMS0zOS4xLTM5LjEtMzkuMSAxNy41LTM5LjEgMzkuMSAxNy41IDM5LjEgMzkuMSAzOS4xeiIgZmlsbD0iI2IxNTA0OCIvPjxnIGZpbGw9IiMxNzAxMDEiPjxwYXRoIGQ9Im0yMi41IDU3LjggMzUuMy0zNS4zYzEuNC0xLjQgMy42LTEuNCA1IDBsLjEuMWMxLjQgMS40IDEuNCAzLjYgMCA1bC0zNS4zIDM1LjNjLTEuNCAxLjQtMy42IDEuNC01IDBsLS4xLS4xYy0xLjQtMS40LTEuNC0zLjcgMC01eiIvPjxwYXRoIGQ9Im0yNy41IDIyLjUgMzUuMyAzNS4zYzEuNCAxLjQgMS40IDMuNiAwIDVsLS4xLjFjLTEuNCAxLjQtMy42IDEuNC01IDBsLTM1LjMtMzUuM2MtMS40LTEuNC0xLjQtMy42IDAtNWwuMS0uMWMxLjQtMS40IDMuNy0xLjQgNSAweiIvPjwvZz48L2c+PC9zdmc+" }; 230 var CHROME_MINIMIZE = { normal: "data:image/svg+xml;base64,PHN2ZyBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA4NS40IDg1LjQiIHZpZXdCb3g9IjAgMCA4NS40IDg1LjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Im00Mi43IDg1LjRjMjMuNiAwIDQyLjctMTkuMSA0Mi43LTQyLjdzLTE5LjEtNDIuNy00Mi43LTQyLjctNDIuNyAxOS4xLTQyLjcgNDIuNyAxOS4xIDQyLjcgNDIuNyA0Mi43eiIgZmlsbD0iI2UxYTczZSIvPjxwYXRoIGQ9Im00Mi43IDgxLjhjMjEuNiAwIDM5LjEtMTcuNSAzOS4xLTM5LjFzLTE3LjUtMzkuMS0zOS4xLTM5LjEtMzkuMSAxNy41LTM5LjEgMzkuMSAxNy41IDM5LjEgMzkuMSAzOS4xeiIgZmlsbD0iI2Y2YmU1MCIvPjwvZz48L3N2Zz4=", hover: "data:image/svg+xml;base64,PHN2ZyBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA4NS40IDg1LjQiIHZpZXdCb3g9IjAgMCA4NS40IDg1LjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Im00Mi43IDg1LjRjMjMuNiAwIDQyLjctMTkuMSA0Mi43LTQyLjdzLTE5LjEtNDIuNy00Mi43LTQyLjctNDIuNyAxOS4xLTQyLjcgNDIuNyAxOS4xIDQyLjcgNDIuNyA0Mi43eiIgZmlsbD0iI2UxYTczZSIvPjxwYXRoIGQ9Im00Mi43IDgxLjhjMjEuNiAwIDM5LjEtMTcuNSAzOS4xLTM5LjFzLTE3LjUtMzkuMS0zOS4xLTM5LjEtMzkuMSAxNy41LTM5LjEgMzkuMSAxNy41IDM5LjEgMzkuMSAzOS4xeiIgZmlsbD0iI2Y2YmU1MCIvPjxwYXRoIGQ9Im0xNy44IDM5LjFoNDkuOWMxLjkgMCAzLjUgMS42IDMuNSAzLjV2LjFjMCAxLjktMS42IDMuNS0zLjUgMy41aC00OS45Yy0xLjkgMC0zLjUtMS42LTMuNS0zLjV2LS4xYzAtMS45IDEuNS0zLjUgMy41LTMuNXoiIGZpbGw9IiM5MDU5MWQiLz48L2c+PC9zdmc+", press: "data:image/svg+xml;base64,PHN2ZyBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA4NS40IDg1LjQiIHZpZXdCb3g9IjAgMCA4NS40IDg1LjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Im00Mi43IDg1LjRjMjMuNiAwIDQyLjctMTkuMSA0Mi43LTQyLjdzLTE5LjEtNDIuNy00Mi43LTQyLjctNDIuNyAxOS4xLTQyLjcgNDIuN2MwIDIzLjUgMTkgNDIuNyA0Mi43IDQyLjd6IiBmaWxsPSIjYTY3ZjM2Ii8+PHBhdGggZD0ibTQyLjcgODEuN2MyMS42IDAgMzkuMS0xNy41IDM5LjEtMzkuMXMtMTcuNS0zOS4xLTM5LjEtMzkuMS0zOS4xIDE3LjUtMzkuMSAzOS4xYy0uMSAyMS42IDE3LjQgMzkuMSAzOS4xIDM5LjF6IiBmaWxsPSIjYjg5MjNiIi8+PHBhdGggZD0ibTE3LjcgMzkuMWg0OS45YzEuOSAwIDMuNSAxLjYgMy41IDMuNXYuMWMwIDEuOS0xLjYgMy41LTMuNSAzLjVoLTQ5LjljLTEuOSAwLTMuNS0xLjYtMy41LTMuNXYtLjFjMC0xLjkgMS42LTMuNSAzLjUtMy4
2301eiIgZmlsbD0iIzUzMmEwYSIvPjwvZz48L3N2Zz4=" }; 231 var CHROME_MAXIMIZE = { normal: "data:image/svg+xml;base64,PHN2ZyBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA4NS40IDg1LjQiIHZpZXdCb3g9IjAgMCA4NS40IDg1LjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Im00Mi43IDg1LjRjMjMuNiAwIDQyLjctMTkuMSA0Mi43LTQyLjdzLTE5LjEtNDIuNy00Mi43LTQyLjctNDIuNyAxOS4xLTQyLjcgNDIuNyAxOS4xIDQyLjcgNDIuNyA0Mi43eiIgZmlsbD0iIzJkYWMyZiIvPjxwYXRoIGQ9Im00Mi43IDgxLjhjMjEuNiAwIDM5LjEtMTcuNSAzOS4xLTM5LjFzLTE3LjUtMzkuMS0zOS4xLTM5LjEtMzkuMSAxNy41LTM5LjEgMzkuMSAxNy41IDM5LjEgMzkuMSAzOS4xeiIgZmlsbD0iIzYxYzU1NSIvPjwvZz48L3N2Zz4=", hover: "data:image/svg+xml;base64,PHN2ZyBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA4NS40IDg1LjQiIHZpZXdCb3g9IjAgMCA4NS40IDg1LjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Im00Mi43IDg1LjRjMjMuNiAwIDQyLjctMTkuMSA0Mi43LTQyLjdzLTE5LjEtNDIuNy00Mi43LTQyLjctNDIuNyAxOS4xLTQyLjcgNDIuNyAxOS4xIDQyLjcgNDIuNyA0Mi43eiIgZmlsbD0iIzJkYWMyZiIvPjxwYXRoIGQ9Im00Mi43IDgxLjhjMjEuNiAwIDM5LjEtMTcuNSAzOS4xLTM5LjFzLTE3LjUtMzkuMS0zOS4xLTM5LjEtMzkuMSAxNy41LTM5LjEgMzkuMWMwIDIxLjUgMTcuNSAzOS4xIDM5LjEgMzkuMXoiIGZpbGw9IiM2MWM1NTUiLz48cGF0aCBkPSJtMzEuMiAyMC44aDI2LjdjMy42IDAgNi41IDIuOSA2LjUgNi41djI2Ljd6bTIzLjIgNDMuN2gtMjYuOGMtMy42IDAtNi41LTIuOS02LjUtNi41di0yNi44eiIgZmlsbD0iIzJhNjIxOCIvPjwvZz48L3N2Zz4=", press: "data:image/svg+xml;base64,PHN2ZyBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA4NS40IDg1LjQiIHZpZXdCb3g9IjAgMCA4NS40IDg1LjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Im00Mi43IDg1LjRjMjMuNiAwIDQyLjctMTkuMSA0Mi43LTQyLjdzLTE5LjEtNDIuNy00Mi43LTQyLjctNDIuNyAxOS4xLTQyLjcgNDIuNyAxOS4xIDQyLjcgNDIuNyA0Mi43eiIgZmlsbD0iIzQyODIzNCIvPjxwYXRoIGQ9Im00Mi43IDgxLjhjMjEuNiAwIDM5LjEtMTcuNSAzOS4xLTM5LjFzLTE3LjUtMzkuMS0zOS4xLTM5LjEtMzkuMSAxNy41LTM5LjEgMzkuMWMwIDIxLjUgMTcuNSAzOS4xIDM5LjEgMzkuMXoiIGZpbGw9IiM0YTk3NDEiLz48cGF0aCBkPSJtMzEuMiAyMC44aDI2LjdjMy42IDAgNi41IDIuOSA2LjUgNi41djI2Ljd6bTIzLjIgNDMuN2gtMjYuOGMtMy42IDAtNi41LTIuOS02LjUtNi41di0yNi44eiIgZmlsbD0iIzExMzEwNyIvPjwvZz48L3N2Zz4=" }; 232 233 /* Official button colors from the private GitHub brand guide: Aurora Purple, 234 Solar Orange, Magenta, and Signal Red. */ 235 var STYLE = 236 ".twc{--twc-button:#8C6BF0;--twc-button-shadow:rgba(140,107,240,.35);position:fixed;left:max(20px,env(safe-area-inset-left));bottom:max(20px,env(safe-area-inset-bottom));z-index:2147483000;width:min(369px,calc(100vw - 40px));box-sizing:border-box;overflow:hidden;border:1px solid rgba(255,255,255,.35);border-radius:16px;background:rgba(74,74,74,.78);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);color:#fff;box-shadow:0 20px 56px rgba(0,0,0,.34);font:400 14px/normal 'Pretendard',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif}" + 237 ".twc--conversion{--twc-button:#F22248;--twc-button-shadow:rgba(242,34,72,.35)}.twc--workers{--twc-button:#F222B0;--twc-button-shadow:rgba(242,34,176,.35)}.twc--siteworks{--twc-button:#F26422;--twc-button-shadow:rgba(242,100,34,.35)}" + 238 ".twc__chrome{height:30px;box-sizing:border-box;display:flex;align-items:center;padding:8px 20px;background:rgba(255,255,255,.07);border-radius:15px 15px 0 0}.twc__controls{display:flex;gap:5px;align-items:center;flex:none}.twc__control{display:block;width:14px;height:14px;padding:0;border:0;background:transparent;cursor:pointer}.twc__control img{display:block;width:14px;height:14px}.twc__tag{display:flex;margin-left:auto;align-items:center;justify-content:center;flex:none;padding:2.78px;background:var(--twc-button);font:600 6.95px/normal 'Pretendard',sans-serif;letter-spacing:-.139px;white-space:nowrap}" + 239 ".twc__body{display:flex;flex-direction:column;gap:16px;padding:16px 20px;background:rgba(255,255,255,.04);border-radius:0 0 15px 15px}.twc__copy{display:flex;flex-direction:column;gap:7px}.twc__h{margin:0;font:600 16px/normal 'Pretendard',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;letter-spacing:0}.twc__t{margin:0;color:#D9D9D9;font:400 14px/normal 'Pretendard',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif}.twc__t a{color:inherit;text-decoration:underline;text-underline-position:from-font}.twc__b{display:flex;gap:16px;width:100%}.twc__btn{position:relative;isolation:isolate;flex:1;min-height:43px;box-sizing:border-box;overflow:hidden;appearance:none;-webkit-appearance:none;border:1px solid #fff!important;border-radius:11px;background:transparent!important;background-image:none!important;color:#fff;cursor:pointer;font:600 16px/19px 'Pretendard',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;transition:border-color .22s ease,background-color .22s ease,background .22s ease,box-shadow .22s ease,opacity .22s ease}.twc[data-twc-consent] .twc__b:before,.twc[data-twc-consent] .twc__b:after,.twc[data-twc-consent] button.twc__btn:before,.twc[data-twc-consent] button.twc__btn:after{all:unset!important;content:''!important;display:none!important}.twc__btn--y{border-color:var(--twc-button)!important;background:var(--twc-button)!important;box-shadow:0 8px 20px var(--twc-button-shadow)}.twc__btn:hover{border-color:rgba(255,255,255,.82)!important;background:rgba(255,255,255,.1)!important;box-shadow:0 8px 20px rgba(0,0,0,.16)}.twc__btn--y:hover{border-color:var(--twc-button)!important;background:linear-gradient(rgba(255,255,255,.1),rgba(255,255,255,.1)),var(--twc-button)!important;box-shadow:0 8px 20px var(--twc-button-shadow)}.twc__btn:active{filter:brightness(.96)}.twc--confirming .twc__btn{pointer-events:none}.twc--confirming .twc__btn:not([data-confirmed]){flex:0;min-width:0;opacity:0;transform:translateY(8px) scale(.95)}.twc--confirming .twc__btn[data-confirmed]{animation:twcconfirm .42s cubic-bezier(.22,1,.36,1)}.twc--confirming .twc__chrome,.twc--confirming .twc__copy{opacity:.52;transform:translateY(-2px);transition:opacity .2s ease,transform .2s ease}@keyframes twcconfirm{0%{transform:scale(.96)}55%{transform:scale(1.025)}100%{transform:scale(1)}}.twc__btn:focus-visible,.twc__control:focus-visible,.twc-minimized:focus-visible,.twc-manage:focus-visible{outline:2px solid #fff;outline-offset:3px}" + 240 ".twc{transition:width .34s cubic-bezier(.22,1,.36,1)}.twc__prefs{display:flex;flex-direction:column;gap:8px;max-height:0;margin-top:-16px;overflow:hidden;opacity:0;pointer-events:none;transform:translateY(-8px);transition:max-height .34s cubic-bezier(.22,1,.36,1),margin .34s cubic-bezier(.22,1,.36,1),opacity .18s ease,transform .34s cubic-bezier(.22,1,.36,1)}.twc--expanded{width:min(410px,calc(100vw - 40px))}.twc--expanded .twc__prefs{max-height:300px;margin-top:0;opacity:1;pointer-events:auto;transform:none}.twc__b{max-height:43px;overflow:hidden;opacity:1;transform:none;transition:max-height .24s ease,margin .24s ease,opacity .16s ease,transform .24s ease}.twc--expanded .twc__b{max-height:0;margin-top:-16px;opacity:0;pointer-events:none;transform:translateY(-6px)}.twc__pref{display:flex;gap:12px;align-items:center;justify-content:space-between;padding:11px 12px;border:1px solid rgba(255,255,255,.16);border-radius:10px;background:rgba(0,0,0,.13)}.twc__pref-copy{display:flex;flex-direction:column;gap:2px}.twc__pref strong{font:600 14px/normal 'Pretendard',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif}.twc__pref small{color:#D9D9D9;font:400 12px/1.25 'Pretendard',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif}.twc__locked{color:#D9D9D9;font:400 12px/normal 'Pretendard',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif}.twc__switch{appearance:none;position:relative;flex:none;width:38px;height:22px;margin:0;border:1px solid rgba(255,255,255,.35);border-radius:999px;background:rgba(0,0,0,.22);cursor:pointer}.twc__switch:after{content:'';position:absolute;top:3px;left:3px;width:14px;
240height:14px;border-radius:50%;background:#fff;transition:transform .16s ease}.twc__switch:checked{border-color:var(--twc-button);background:var(--twc-button)}.twc__switch:checked:after{transform:translateX(16px)}.twc__save{width:100%;margin-top:4px}.twc-minimized{--twc-button:#8C6BF0;position:fixed;left:max(12px,env(safe-area-inset-left));bottom:max(12px,env(safe-area-inset-bottom));z-index:2147483000;display:grid;place-items:center;width:40px;height:40px;box-sizing:border-box;padding:0;border:1px solid rgba(255,255,255,.28);border-radius:50%;background:rgba(12,12,15,.9);color:#fff;box-shadow:0 8px 24px rgba(0,0,0,.3);cursor:pointer}.twc__space-cookie{display:block;width:24px;height:24px;object-fit:contain}.twc-minimized[data-brand=conversion]{--twc-button:#F22248}.twc-minimized[data-brand=workers]{--twc-button:#F222B0}.twc-minimized[data-brand=siteworks]{--twc-button:#F26422}" + 241 ".twc-manage{--twc-button:#8C6BF0;--twc-glow:rgba(140,107,240,.34);position:fixed;left:max(12px,env(safe-area-inset-left));bottom:max(12px,env(safe-area-inset-bottom));z-index:2147482000;display:grid;place-items:center;width:40px;height:40px;box-sizing:border-box;padding:0;background:rgba(12,12,15,.88);color:#fff;border:1px solid rgba(255,255,255,.24);border-radius:50%;cursor:pointer;box-shadow:0 8px 24px var(--twc-glow)}.twc-manage[data-brand=conversion]{--twc-button:#F22248;--twc-glow:rgba(242,34,72,.32)}.twc-manage[data-brand=workers]{--twc-button:#F222B0;--twc-glow:rgba(140,107,240,.34)}.twc-manage[data-brand=siteworks]{--twc-button:#F26422;--twc-glow:rgba(242,100,34,.32)}" + 242 "@media(max-width:640px){.twc{left:max(12px,env(safe-area-inset-left));bottom:max(12px,env(safe-area-inset-bottom));width:calc(100vw - 24px)}.twc__chrome{padding-left:16px;padding-right:16px}.twc__body{padding:16px}.twc__b{gap:10px}}" + 243 "@media(prefers-reduced-motion:no-preference){.twc{animation:twcarrive .78s cubic-bezier(.16,1,.3,1) both}@keyframes twcarrive{0%{opacity:0;transform:translate3d(-120%,38px,0) rotate(-4deg) scale(.94)}56%{opacity:1;transform:translate3d(13px,-7px,0) rotate(.7deg) scale(1.01)}78%{transform:translate3d(-4px,2px,0) rotate(-.18deg)}100%{opacity:1;transform:none}}}@media(prefers-reduced-motion:reduce){.twc,.twc__prefs,.twc__b,.twc__switch:after,.twc__btn,.twc__chrome,.twc__copy{transition:none!important;animation:none!important}}"; 244 245 var styled = false; 246 function injectStyle() { 247 if (styled) return; 248 styled = true; 249 var s = el("style"); 250 s.appendChild(document.createTextNode(STYLE)); 251 document.head.appendChild(s); 252 } 253 254 var bar = null; 255 256 function brand() { 257 var bodySurface = document.body && typeof document.body.getAttribute === "function" && document.body.getAttribute("data-site-surface"); 258 var location = window.location || {}; 259 var host = String(location.hostname || "").toLowerCase(); 260 var path = String(location.pathname || "").toLowerCase(); 261 if (bodySurface === "workers" || host.indexOf("workers") !== -1 || path.indexOf("/workers") === 0) return "workers"; 262 if (bodySurface === "conversion" || host.indexOf("conversion") !== -1) return "conversion"; 263 if (bodySurface === "siteworks" || host.indexOf("siteworks") !== -1 || path.indexOf("/siteworks") === 0) return "siteworks"; 264 return "taurist"; 265 } 266 267 var BRAND_COPY = { 268 taurist: { 269 label: "TRST_TCHNLGS", 270 title: "Not every signal needs to follow you.", 271 body: "Optional cookies help us see what creates momentum. You can decline without losing any functionality. " 272 }, 273 siteworks: { 274 label: "STWRKS", 275 title: "Good websites pay attention. Not too much.", 276 body: "Optional cookies help us improve the experience. You can decline without losing any functionality. " 277 }, 278 workers: { 279 label: "WRKRS", 280 title: "A small worker wants to count this visit.", 281 body: "Optional cookies help us improve the site. You can decline without losing any functionality. " 282 }, 283 conversion: { 284 label: "CNVRSN_WRKS", 285 title: "We test pagesânot your patience.", 286 body: "Optional cookies help us learn what works. You can decline without losing any functionality. " 287 } 288 }; 289 290 function chromeIcon(states) { 291 return el("img", { src: states.normal, alt: "", "aria-hidden": "true" }); 292 } 293 294 function chromeControl(states, label, onClick) { 295 var control = el("button", { type: "button", class: "twc__control", "aria-label": label, title: label }); 296 var icon = chromeIcon(states); 297 function show(state) { 298 icon.setAttribute("src", states[state]); 299 } 300 control.appendChild(icon); 301 control.addEventListener("mouseenter", function () { show("hover"); }); 302 control.addEventListener("mouseleave", function () { show("normal"); }); 303 control.addEventListener("focus", function () { show("hover"); }); 304 control.addEventListener("blur", function () { show("normal"); }); 305 control.addEventListener("mousedown", function () { show("press"); }); 306 control.addEventListener("mouseup", function () { show("hover"); }); 307 control.addEventListener("click", onClick); 308 return control; 309 } 310 311 var minimized = null; 312 function close() { 313 if (bar && bar.parentNode) bar.parentNode.removeChild(bar); 314 bar = null; 315 if (minimized && minimized.parentNode) minimized.parentNode.removeChild(minimized); 316 minimized = null; 317 } 318 319 function decide(state) { 320 apply(state, true); 321 close(); 322 } 323 324 function confirmChoice(button, state, label) { 325 if (!bar || bar.getAttribute("data-confirming") === "true") return; 326 bar.setAttribute("class", bar.getAttribute("class") + " twc--confirming"); 327 bar.setAttribute("data-confirming", "true"); 328 button.setAttribute("data-confirmed", "true"); 329 button.textContent = label; 330 apply(state, true); 331 var finish = function () { close(); }; 332 if (typeof window.setTimeout === "function") window.setTimeout(finish, 560); 333 else setTimeout(finish, 560); 334 } 335 336 function showBanner() { 337 if (bar) return; 338 if (minimized && minimized.parentNode) minimized.parentNode.removeChild(minimized); 339 minimized = null; 340 whenBody(buildBanner); 341 } 342 343 function scheduleInitialBanner() { 344 // A banner that arrives before the page has painted reads like broken UI. 345 // Real browsers provide load events; the direct fallback keeps embedded or 346 // test environments usable when they do not. 347 if (typeof window.addEventListener !== "function") { 348 showBanner(); 349 return; 350 } 351 var reveal = function () { 352 window.setTimeout(showBanner, 650); 353 }; 354 if (document.readyState === "complete") reveal(); 355 else window.addEventListener("load", reveal, { once: true }); 356 } 357 358 function suppressBannerForPath() { 359 // The Summertime Social campaign is a focused event landing page. It keeps 360 // Consent Mode's defaults, but intentionally does not interrupt its RSVP 361 // flow with the portfolio-wide prompt. 362 return /\/workers\/summertime-social-2026\/?$/i.test((window.location && window.location.pathname) || ""); 363 } 364 365 function minimizeBanner(currentBrand) { 366 if (bar && bar.parentNode) bar.parentNode.removeChild(bar); 367 bar = null; 368 if (minimized) return; 369 minimized = el("button", { 370 type: "button", 371 class: "twc-minimized", 372 "data-brand": currentBrand, 373 "aria-label": "Open privacy choices", 374 title: "Privacy choices" 375 }); 376 minimized.appendChild(spaceCookieIcon()); 377 minimized.addEventListener("click", function () { 378 if (minimized && minimized.parentNode) minimized.parentNode.removeChild(minimized); 379 minimized = null; 380 showBanner(); 381 }); 382 document.body.appendChild(minimized); 383 } 384 385 function choiceState(analytics, advertising) { 386 var state = all("denied");
387 delete state.region; 388 state.functionality_storage = "granted"; 389 state.security_storage = "granted"; 390 if (analytics) state.analytics_storage = "granted"; 391 if (advertising) { 392 for (var i = 0; i < AD_KEYS.length; i++) state[AD_KEYS[i]] = "granted"; 393 state.personalization_storage = "granted"; 394 } 395 return state; 396 } 397 398 function buildBanner() { 399 if (bar) return; 400 injectStyle(); 401 402 var currentBrand = brand(); 403 var copyData = BRAND_COPY[currentBrand]; 404 var preferencesOpen = false; 405 406 bar = el("div", { 407 class: "twc twc--" + currentBrand, 408 "data-twc-consent": "true", 409 role: "dialog", 410 "aria-modal": "false", // deliberately not modal: it must not trap the page 411 "aria-label": "Privacy choices" 412 }); 413 414 var chrome = el("div", { class: "twc__chrome" }); 415 var controls = el("div", { class: "twc__controls" }); 416 controls.appendChild(chromeControl(CHROME_CLOSE, "Decline optional cookies", function () { 417 decide(denyAll()); 418 })); 419 controls.appendChild(chromeControl(CHROME_MINIMIZE, "Minimize privacy choices", function () { 420 minimizeBanner(currentBrand); 421 })); 422 var maximize = chromeControl(CHROME_MAXIMIZE, "Open privacy preferences", function () { 423 preferencesOpen = !preferencesOpen; 424 bar.setAttribute("class", "twc twc--" + currentBrand + (preferencesOpen ? " twc--expanded" : "")); 425 maximize.setAttribute("aria-label", preferencesOpen ? "Close privacy preferences" : "Open privacy preferences"); 426 maximize.setAttribute("title", preferencesOpen ? "Close privacy preferences" : "Open privacy preferences"); 427 if (preferencesOpen) analytics.input.focus(); 428 else yes.focus(); 429 }); 430 controls.appendChild(maximize); 431 chrome.appendChild(controls); 432 chrome.appendChild(el("div", { class: "twc__tag" }, copyData.label)); 433 434 var inner = el("div", { class: "twc__body" }); 435 var content = el("div", { class: "twc__copy" }); 436 content.appendChild(el("h2", { class: "twc__h" }, copyData.title)); 437 var copy = el("p", { class: "twc__t" }); 438 copy.appendChild(document.createTextNode(copyData.body)); 439 var link = el("a", { href: "/privacy-policy" }, "Privacy policy"); 440 copy.appendChild(link); 441 content.appendChild(copy); 442 443 var preferences = el("div", { class: "twc__prefs", "aria-label": "Cookie preferences" }); 444 var essential = el("div", { class: "twc__pref" }); 445 var essentialCopy = el("div", { class: "twc__pref-copy" }); 446 essentialCopy.appendChild(el("strong", {}, "Essential")); 447 essentialCopy.appendChild(el("small", {}, "Keeps the site secure and working.")); 448 essential.appendChild(essentialCopy); 449 essential.appendChild(el("span", { class: "twc__locked" }, "Always on")); 450 451 function preference(label, detail) { 452 var row = el("label", { class: "twc__pref" }); 453 var rowCopy = el("span", { class: "twc__pref-copy" }); 454 rowCopy.appendChild(el("strong", {}, label)); 455 rowCopy.appendChild(el("small", {}, detail)); 456 var input = el("input", { type: "checkbox", class: "twc__switch", "aria-label": label + " cookies" }); 457 row.appendChild(rowCopy); 458 row.appendChild(input); 459 return { row: row, input: input }; 460 } 461 462 var analytics = preference("Analytics", "Helps us understand what is useful."); 463 var advertising = preference("Marketing", "Helps us keep marketing relevant."); 464 preferences.appendChild(essential); 465 preferences.appendChild(analytics.row); 466 preferences.appendChild(advertising.row); 467 var save = el("button", { type: "button", class: "twc__btn twc__btn--y twc__save" }, "Save choices"); 468 save.addEventListener("click", function () { 469 confirmChoice(save, choiceState(analytics.input.checked, advertising.input.checked), "Saved â"); 470 }); 471 preferences.appendChild(save); 472 473 var buttons = el("div", { class: "twc__b" }); 474 var yes = el("button", { type: "button", class: "twc__btn twc__btn--y" }, "Accept"); 475 var no = el("button", { type: "button", class: "twc__btn" }, "Decline"); 476 no.addEventListener("click", function () { 477 confirmChoice(no, denyAll(), "Declined â"); 478 }); 479 yes.addEventListener("click", function () { 480 confirmChoice(yes, grantAll(), "Accepted â"); 481 }); 482 buttons.appendChild(yes); 483 buttons.appendChild(no); 484 485 inner.appendChild(content); 486 inner.appendChild(preferences); 487 inner.appendChild(buttons); 488 bar.appendChild(chrome); 489 bar.appendChild(inner); 490 document.body.appendChild(bar); 491 yes.focus(); 492 } 493 494 /* This file is a synchronous <head> script, so document.body is usually null 495 when the returning-visitor path runs. Queue until it exists rather than 496 silently dropping the control. */ 497 function whenBody(fn) { 498 if (document.body) { 499 fn(); 500 return; 501 } 502 document.addEventListener("DOMContentLoaded", function () { 503 // Guard rather than assume: a consent control that throws would take the 504 // rest of the page's inline scripts down with it. 505 if (document.body) fn(); 506 }); 507 } 508 509 window.tauristConsent = { 510 state: function () { 511 return window.__taurist_consent; 512 }, 513 open: showBanner, 514 acceptAll: function () { 515 decide(grantAll()); 516 }, 517 denyAll: function () { 518 decide(denyAll()); 519 }, 520 denyAds: function () { 521 decide(denyAds()); 522 }, 523 reset: function () {
524 try { 525 localStorage.removeItem(STORE); 526 } catch (e) { 527 /* nothing to clear */ 528 } 529 showBanner(); 530 } 531 }; 532 533 /* ---- 4. Apply the decision, now that the API exists ---------------- */ 534 535 if (gpcOn()) { 536 // A legally recognised opt-out. Applied immediately, no banner, nothing to 537 // click, and it overrides any previously stored acceptance â an older click 538 // cannot waive a right the visitor is currently exercising. Not persisted: 539 // GPC is read fresh from the browser on every page view, and storing it 540 // would outlive the visitor turning it off. 541 var gpcState = denyAds(); 542 gpcState.gpc = true; 543 apply(gpcState, false); 544 } else { 545 var stored = read(); 546 if (stored) { 547 apply(stored.state, false); 548 } else { 549 publish(null); // pending â the Worker must not read null as consent 550 if (!suppressBannerForPath()) scheduleInitialBanner(); 551 } 552 } 553})();
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.