1"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[2027],{22027:(e,t,o)=>{o.d(t,{gG:()=>g,fP:()=>h,u4:()=>_});let r=new Set(["PROJECT_REVIEW","BOQ_REVIEW","DRAWING_REVIEW","TECHNICAL_REVIEW","WHATSAPP_DISCUSSION","REQUEST_MODEL","CONTACT","CONSULTANT_RESOURCE"]),i={tool_start:{label:"Tool started",category:"tool",allowedKeys:["tool_slug","tool_category","tool_title"],description:"Fires the first time a tool component renders to the user."},tool_complete:{label:"Tool completed",category:"tool",allowedKeys:["tool_slug","tool_category","result_summary","duration_ms"],description:"Fires when the tool produces a final result for the user."},tool_abandon:{label:"Tool abandoned",category:"tool",allowedKeys:["tool_slug","tool_category","last_step","duration_ms"],description:"Fires when the user navigates away from a tool mid-flow."},preset_load:{label:"Preset loaded",category:"tool",allowedKeys:["tool_slug","preset_id"],description:"Fires when a curated preset page hydrates its calculator with the preset state. Identifiers only â the preset's input values never enter the payload."},brief_prefill:{label:"Brief wizard prefilled",category:"tool",allowedKeys:["source"],description:"Fires when the brief wizard consumes a toolâbrief prefill from query params. source is a whitelisted tool slug token (av-room-studio or a discovery-console slug) â never free text, never brief content."},contact_submit:{label:"Contact form submitted",category:"contact",allowedKeys:["form_id","service_slug","sector_slug","origin_path"],description:"Fires after the contact form completes a successful POST. No form-field values are captured."},whatsapp_click:{label:"WhatsApp click",category:"contact",allowedKeys:["origin_path","cta_context","cta_purpose"],description:"Fires when any WhatsApp link or button is clicked. Not every click is a contact intent: the tool share panel's arm is a recipient-less `wa.me/?text=â¦` forward that never reaches the practice. `cta_purpose` separates the two â WHATSAPP_DISCUSSION is an approach to TechnoGuru, CONSULTANT_RESOURCE is a peer forward."},phone_click:{label:"Phone click",category:"contact",allowedKeys:["origin_path","cta_context"],description:"Fires when a phone link is clicked."},email_click:{label:"Email click",category:"contact",allowedKeys:["origin_path","cta_context"],description:"Fires when an email link is clicked."},cta_click:{label:"CTA click",category:"conversion",allowedKeys:["cta_id","cta_label","cta_context","origin_path","target_path","cta_purpose"],description:"Fires when a tracked CTA is clicked. Two independent axes: `cta_context` is WHERE the control sat (placement â historical values preserved) and `cta_purpose` is WHAT was being asked for (the closed CTA_PURPOSES union). `cta_id` identifies the control itself."},faq_expand:{label:"FAQ expanded",category:"engagement",allowedKeys:["faq_question","origin_path"],description:"Fires when a single FAQ item opens."},engineering_topic_view:{label:"Engineering topic viewed",category:"engineering",allowedKeys:["topic_slug","topic_kind"],description:"Fires on hydration of an engineering topic page."},deployment_view:{label:"Deployment viewed",category:"engineering",allowedKeys:["deployment_slug","deployment_family"],description:"Fires on hydration of a deployment archetype page."},interoperability_view:{label:"Interoperability matrix viewed",category:"engineering",allowedKeys:["matrix_slug"],description:"Fires on hydration of an interoperability matrix page."},project_view:{label:"Project viewed",category:"content",allowedKeys:["project_slug","project_sector","project_city"],description:"Fires on hydration of a project case-study page."},compare_interaction:{label:"Compare interaction",category:"compare",allowedKeys:["compare_id","interaction_kind","selection_a","selection_b"],description:"Fires when the user changes selections inside a compare tool."},resource_download:{label:"Resource downloaded",category:"resource",allowedKeys:["resource_id","resource_kind","origin_path"],description:"Fires when a downloadable resource is opened in a new tab."},resource_request:{label:"Resource requested",category:"resource",allowedKeys:["resource_id","resource_kind","origin_path"],description:"Fires when a visitor asks for a resource: a request-only CTA (mailto) click, or an email-gated tool export where the deliverable opens locally and the request reaches the practice inbox. Captures the resource id only â never the requester's details. This is the SOFT conversion; a gated export is deliberately not counted as contact_submit."},model_request:{label:"Catalogue model requested",category:"resource",allowedKeys:["tool_slug","catalogue","requested_brand"],description:"Fires when a visitor tells a calculator which product record
1is missing from its catalogue. Identifiers only: the tool, the catalogue and the BRAND CHOSEN FROM THE CLOSED SELECT. The free-text model string, the free-text question and the optional email never enter the payload â the request reaches the practice inbox through /api/contact, and this event exists only to rank which catalogues are asked for. Not a Key Event and deliberately not contact_submit: expressing demand costs the visitor nothing, and counting it as a lead would inflate the number the funnel is judged on."},pdf_generate:{label:"PDF generated",category:"resource",allowedKeys:["pdf_kind","tool_slug","origin_path"],description:"Fires when a PDF asset is generated from a tool flow."},search_query:{label:"Search query",category:"search",allowedKeys:["query_token_count","origin_path"],description:"Fires when a search is executed. Only the token count is captured â no query string."},search_result_click:{label:"Search result clicked",category:"search",allowedKeys:["result_kind","result_slug","result_rank","origin_path"],description:"Fires when a search result is clicked."},form_start:{label:"Form started",category:"contact",allowedKeys:["form_id","origin_path"],description:"Fires once per mount on the first focus of any lead-form field. No field values are captured â enables the startâsubmit conversion rate."},form_error:{label:"Form error",category:"contact",allowedKeys:["form_id","error_kind","origin_path"],description:"Fires when a lead-form submission fails. error_kind is a fixed enum (captcha | rate_limit | delivery | validation) â never a message string, never field values."},not_found_view:{label:"404 viewed",category:"engagement",allowedKeys:["origin_path"],description:"Fires when the 404 page mounts. origin_path is the missing path; GA4 auto-captures page_referrer â surfaces broken internal/external links."},tool_error:{label:"Tool error",category:"tool",allowedKeys:["tool_slug","error_kind"],description:"Fires when an error boundary catches a client exception on a tool surface. error_kind is a fixed enum (render_crash | route_crash) â never a message string."}},a=[/email/i,/\bphone\b/i,/^name$/i,/\baddress\b/i,/password/i,/\btoken$/i,/first_?name/i,/last_?name/i,/\bipv?4?\b/i,/\bipv?6\b/i],n={cta_purpose:e=>"string"==typeof e&&r.has(e)};function s(){return!!window.__tgAnalyticsConsent}function l(){let e=window.google_tag_manager;return!!e&&"object"==typeof e&&Object.keys(e).some(e=>e.startsWith("GTM-"))}function c(){return!!(l()&&Array.isArray(window.dataLayer))||"function"==typeof window.gtag}function d(e,t){let o=(window.__tgAnalyticsQueue||(window.__tgAnalyticsQueue=[]),window.__tgAnalyticsQueue);o.length>=50&&o.shift(),o.push({name:e,payload:t})}function u(e,t){let o=i[e];if(!o)return;let r={event:e,event_category:o.category,...t};return l()&&Array.isArray(window.dataLayer)?void window.dataLayer.push(r):"function"==typeof window.gtag?void window.gtag("event",e,{event_category:o.category,...t}):Array.isArray(window.dataLayer)?void window.dataLayer.push(r):void 0}function p(){window.__tgAnalyticsDraining||function e(){var t,o;let r=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0;if(window.__tgAnalyticsDraining=!1,!s())return;if(!c()&&r<15){window.__tgAnalyticsDraining=!0,window.setTimeout(()=>e(r+1),400);return}c()||(window.dataLayer=null!=(t=window.dataLayer)?t:[]);let i=null!=(o=window.__tgAnalyticsQueue)?o:[];for(let{name:e,payload:t}of(window.__tgAnalyticsQueue=[],i))u(e,t)}(0)}function _(e){var t,o;let r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},l=function(e,t){let o=[],r=i[e];if(!r)return o.push("Unknown analytics event: ".concat(e)),o;for(let i of Object.keys(t)){(function(e){return a.some(t=>t.test(e))})(i)&&o.push("Event ".concat(e,": forbidden PII-like key ").concat(i)),r.allowedKeys.includes(i)||o.push("Event ".concat(e,": unregistered key ").concat(i));let s=n[i];s&&!s(t[i])&&o.push("Event ".concat(e,": key ").concat(i," carries a value outside its closed union â ")+"only the registered constants are permitted, so a runtime-derived string cannot travel under it")}return o}(e,r);if(l.length>
10)return void console.warn("[analytics] dropping invalid event '".concat(e,"': ").concat(l.join("; ")));if(!s())return void d(e,r);if(!c()||(null!=(o=null==(t=window.__tgAnalyticsQueue)?void 0:t.length)?o:0)>0){d(e,r),p();return}u(e,r)}function g(){window.__tgAnalyticsConsent=!0,p()}function h(){window.__tgAnalyticsConsent=!1,window.__tgAnalyticsQueue=[]}}}]);
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.