PageSourceSearch

https://www.taurist.com/hub-static/dw-consent.js

js taurist.com collected 2026-10-08 20:47:33 UTC 35,400 bytes, 553 lines download raw bytes

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.