1(function () { 2 var details = document.querySelector('.site-nav-details'); 3 if (!details) return; 4 var mediaQuery = window.matchMedia('(min-width: 960px)'); 5 function sync() { 6 var isDesktop = mediaQuery.matches; 7 if (isDesktop) { 8 details.open = true; 9 } else { 10 details.open = false; 11 } 12 } 13 sync(); 14 if (typeof mediaQuery.addEventListener === 'function') { 15 mediaQuery.addEventListener('change', sync); 16 } else if (typeof mediaQuery.addListener === 'function') { 17 mediaQuery.addListener(sync); 18 } 19})(); 20 21(function () { 22 if (!document.body || !document.body.classList.contains('page-home')) return; 23 24 var prefersReduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; 25 if (prefersReduced) return; 26 27 var statEls = document.querySelectorAll('.section--trusted .impact-card__figure'); 28 if (!statEls.length) return; 29 30 function parseTarget(rawText) { 31 var raw = (rawText || '').trim(); 32 var value = parseFloat(raw.replace(/[^0-9.]/g, '')); 33 if (isNaN(value)) return null; 34 return { 35 raw: raw, 36 value: value, 37 hasDollar: raw.indexOf('$') >= 0, 38 hasPercent: raw.indexOf('%') >= 0, 39 hasDecimal: raw.indexOf('.') >= 0, 40 hasMo: /\/mo/i.test(raw), 41 hasCountries: /countries/i.test(raw) 42 }; 43 } 44 45 function formatValue(meta, n) { 46 var out = meta.hasDecimal ? n.toFixed(1) : Math.round(n).toString(); 47 if (meta.hasDollar) out = '$' + out; 48 if (meta.hasPercent) out += '%'; 49 if (meta.hasMo) out += '/mo'; 50 if (meta.hasCountries) out += ' countries'; 51 return out; 52 } 53 54 function animate(el, meta) { 55 var start = null; 56 var duration = 1100; 57 function step(ts) { 58 if (!start) start = ts; 59 var t = Math.min(1, (ts - start) / duration); 60 var eased = 1 - Math.pow(1 - t, 3); 61 el.textContent = formatValue(meta, meta.value * eased); 62 if (t < 1) { 63 window.requestAnimationFrame(step); 64 } else { 65 el.textContent = meta.raw; 66 } 67 } 68 window.requestAnimationFrame(step); 69 } 70 71 if (!('IntersectionObserver' in window)) return; 72 73 var observer = new IntersectionObserver(function (entries, obs) { 74 for (var i = 0; i < entries.length; i++) { 75 if (entries[i].isIntersecting) { 76 var el = entries[i].target; 77 var meta = parseTarget(el.textContent); 78 if (meta) animate(el, meta); 79 obs.unobserve(el); 80 } 81 } 82 }, { threshold: 0.45 }); 83 84 for (var j = 0; j < statEls.length; j++) observer.observe(statEls[j]); 85})(); 86 87(function () { 88 if (!document.body || !document.body.classList.contains('page-home')) return; 89 90 var reducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; 91 92 var style = document.createElement('style'); 93 style.textContent = [ 94 ':root{--anim-enter:cubic-bezier(0.22,1,0.36,1);--anim-move:cubic-bezier(0.25,1,0.5,1)}', 95 '.anim-reveal{opacity:0;transform:translateY(10px);transition:transform 240ms var(--anim-enter),opacity 240ms var(--anim-enter)}', 96 '.anim-reveal.is-visible{opacity:1;transform:translateY(0)}', 97 '.anim-stagger>*{transition-delay:calc(var(--stagger-index,0)*32ms)}', 98 '@media (hover:hover) and (pointer:fine){.anim-lift{transition:transform 160ms var(--anim-enter),box-shadow 160ms var(--anim-enter)}.anim-lift:hover{transform:translateY(-2px)}.btn.anim-lift:active{transform:translateY(0)}}', 99 '.anim-faq summary::after{transition:transform 160ms var(--anim-move)}.anim-faq[open] summary::after{transform:rotate(90deg)}', 100 '.site-nav__panel{opacity:0;transform:translateY(-4px);transition:opacity 160ms var(--anim-enter),transform 160ms var(--anim-enter)}', 101 '.site-nav__dropdown[open]>.site-nav__panel{opacity:1;transform:translateY(0)}', 102 '.anim-progress{position:fixed;inset:0 auto auto 0;width:100%;height:3px;z-index:9999;pointer-events:none;background:transparent}', 103 '.anim-progress__bar{height:100%;width:0%;background:var(--pi-site-kinetic-blue, #002fa7);transform-origin:left center;transition:width 120ms linear}', 104 '.anim-magnetic{transition:transform 100ms var(--anim-enter);will-change:transform}', 105 /* Preserve fixed phone rendering: do not transform phone shot */ 106 '.coo-hero__bg-photo{transition:transform 180ms var(--anim-move);will-change:transform}', 107 '@media (prefers-reduced-motion:reduce){.anim-reveal,.anim-lift,.anim-faq summary::after,.anim-magnetic,.anim-progress__bar,.site-nav__panel,.coo-hero__bg-photo{transition:none !important;animation:none !important;transform:none !important;opacity:1 !important}}' 108 ].join(''); 109 document.head.appendChild(style); 110 111 var progressWrap = document.createElement('div'); 112 progressWrap.className = 'anim-progress'; 113 progressWrap.setAttribute('aria-hidden', 'true'); 114 progressWrap.innerHTML = '<div class="anim-progress__bar" id="anim-progress-bar"></div>'; 115 document.body.appendChild(progressWrap); 116 117 var revealTargets = document.querySelectorAll([ 118 '.coo-hero__copy', 119 '.module-card', 120 '.impact-card', 121 '.ceo-case__visual', 122 '.ceo-case__copy', 123 '.persona-sticky-card', 124 '.steps-replit__item', 125 '.faq-details details' 126 ].join(',')); 127 for (var i = 0; i < revealTargets.length; i++) revealTargets[i].classList.add('anim-reveal'); 128 129 var staggerGroups = document.querySelectorAll('.module-grid, .impact-grid, .persona-sticky-stack, .steps-replit'); 130 for (var g = 0; g < staggerGroups.length; g++) { 131 var group = staggerGroups[g]; 132 group.classList.add('anim-stagger'); 133 for (var c = 0; c < group.children.length; c++) group.children[c].style.setProperty('--stagger-index', c); 134 } 135 136 var liftTargets = document.querySelectorAll('.btn, .module-card, .impact-card, .persona-sticky-card, .ceo-case__img, .card'); 137 for (var l = 0; l < liftTargets.length; l++) liftTargets[l].classList.add('anim-lift'); 138 139 var faqTargets = document.querySelectorAll('.faq-details details'); 140 for (var f = 0; f < faqTargets.length; f++) faqTargets[f].classList.add('anim-faq'); 141 142 if (reducedMotion) { 143 for (var r = 0; r < revealTargets.length; r++) revealTargets[r].classList.add('is-visible'); 144 return; 145 } 146 147 if ('IntersectionObserver' in window) { 148 var observer = new IntersectionObserver(function (entries, obs) { 149 for (var e = 0; e < entries.length; e++) { 150 if (entries[e].isIntersecting) {
151 entries[e].target.classList.add('is-visible'); 152 obs.unobserve(entries[e].target); 153 } 154 } 155 }, { threshold: 0.12, rootMargin: '0px 0px -8% 0px' }); 156 for (var t = 0; t < revealTargets.length; t++) observer.observe(revealTargets[t]); 157 } else { 158 for (var rr = 0; rr < revealTargets.length; rr++) revealTargets[rr].classList.add('is-visible'); 159 } 160 161 var progressBar = document.getElementById('anim-progress-bar'); 162 if (progressBar) { 163 var progressTicking = false; 164 var updateProgress = function () { 165 var doc = document.documentElement; 166 var total = doc.scrollHeight - window.innerHeight; 167 var pct = total > 0 ? (window.scrollY / total) * 100 : 0; 168 progressBar.style.width = Math.max(0, Math.min(100, pct)) + '%'; 169 progressTicking = false; 170 }; 171 window.addEventListener('scroll', function () { 172 if (!progressTicking) { 173 progressTicking = true; 174 window.requestAnimationFrame(updateProgress); 175 } 176 }, { passive: true }); 177 updateProgress(); 178 } 179 180 /* Parallax on background only (phone remains untouched) */ 181 var hero = document.querySelector('.coo-hero--replit'); 182 var heroBg = document.querySelector('.coo-hero__bg-photo'); 183 if (hero && heroBg) { 184 var parallaxTicking = false; 185 var updateParallax = function () { 186 var rect = hero.getBoundingClientRect(); 187 var viewport = window.innerHeight || 1; 188 var progress = Math.max(-1, Math.min(1, (rect.top + rect.height * 0.5 - viewport * 0.5) / viewport)); 189 heroBg.style.transform = 'translateY(' + (-progress * 6).toFixed(2) + 'px)'; 190 parallaxTicking = false; 191 }; 192 window.addEventListener('scroll', function () { 193 if (!parallaxTicking) { 194 parallaxTicking = true; 195 window.requestAnimationFrame(updateParallax); 196 } 197 }, { passive: true }); 198 updateParallax(); 199 } 200 201 if (window.matchMedia && window.matchMedia('(hover: hover) and (pointer: fine)').matches) { 202 var magneticTargets = document.querySelectorAll('.coo-hero__actions .btn, .coo-adoption__cta .btn, .section--blue .btn'); 203 for (var m = 0; m < magneticTargets.length; m++) { 204 (function (btn) { 205 btn.classList.add('anim-magnetic'); 206 btn.addEventListener('mousemove', function (ev) { 207 var rect = btn.getBoundingClientRect(); 208 var x = (ev.clientX - rect.left) / rect.width - 0.5; 209 var y = (ev.clientY - rect.top) / rect.height - 0.5; 210 btn.style.transform = 'translate(' + (x * 2.5).toFixed(2) + 'px,' + (y * 2).toFixed(2) + 'px)'; 211 }); 212 btn.addEventListener('mouseleave', function () { 213 btn.style.transform = 'translate(0,0)'; 214 }); 215 })(magneticTargets[m]); 216 } 217 } 218})(); 219 220(function () { 221 if (!document.body) return; 222 var isSolutions = 223 document.body.classList.contains('page-coo') || 224 document.body.classList.contains('page-ceo') || 225 document.body.classList.contains('page-cto'); 226 if (!isSolutions) return; 227 228 var reducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; 229 230 var style = document.createElement('style'); 231 style.textContent = [ 232 ':root{--sol-enter:cubic-bezier(0.22,1,0.36,1);--sol-move:cubic-bezier(0.25,1,0.5,1)}', 233 '.sol-reveal{opacity:0;transform:translateY(10px);transition:transform 240ms var(--sol-enter),opacity 240ms var(--sol-enter)}', 234 '.sol-reveal.is-visible{opacity:1;transform:translateY(0)}', 235 '.sol-stagger>*{transition-delay:calc(var(--sol-index,0)*34ms)}', 236 '@media (hover:hover) and (pointer:fine){.sol-lift{transition:transform 160ms var(--sol-enter),box-shadow 160ms var(--sol-enter)}.sol-lift:hover{transform:translateY(-2px)}.btn.sol-lift:active{transform:translateY(0)}}', 237 '.sol-hero-media{opacity:0;transform:translateY(10px) scale(.985);will-change:transform,opacity}', 238 '@keyframes solHeroPop{0%{opacity:0;transform:translateY(10px) scale(.985)}58%{opacity:1;transform:translateY(-1px) scale(1.013)}100%{opacity:1;transform:translateY(0) scale(1)}}', 239 '.sol-hero-media.is-visible{animation:solHeroPop 520ms var(--sol-enter) both;will-change:auto}', 240 '@media (prefers-reduced-motion:reduce){.sol-reveal,.sol-lift,.sol-hero-media{transition:none !important;animation:none !important;transform:none !important;opacity:1 !important}}' 241 ].join(''); 242 document.head.appendChild(style); 243 244 var heroMedia = document.querySelectorAll('.coo-hero__media, .ceo-hero__media, .cto-hero__media'); 245 for (var hm = 0; hm < heroMedia.length; hm++) heroMedia[hm].classList.add('sol-hero-media'); 246 247 var revealTargets = document.querySelectorAll([ 248 '.coo-hero__copy', 249 '.ceo-hero__copy', 250 '.cto-hero__copy', 251 '.coo-adoption__copy', 252 '.coo-adoption__device', 253 '.coo-case__quote-block', 254 '.coo-case__actions', 255 '.ceo-stats__intro', 256 '.ceo-stat', 257 '.ceo-case__visual', 258 '.ceo-case__copy', 259 '.cto-zero__intro-row', 260 '.cto-zero__lead', 261 '.cto-bento__card', 262 '.coo-cta__inner', 263 '.ceo-cta__inner', 264 '.cto-brands' 265 ].join(',')); 266 for (var i = 0; i < revealTargets.length; i++) revealTargets[i].classList.add('sol-reveal'); 267 268 var staggerGroups = document.querySelectorAll('.ceo-stats__grid, .cto-bento, .coo-adoption__phone-pair'); 269 for (var g = 0; g < staggerGroups.length; g++) { 270 var group = staggerGroups[g]; 271 group.classList.add('sol-stagger'); 272 for (var c = 0; c < group.children.length; c++) group.children[c].style.setProperty('--sol-index', c); 273 } 274 275 var liftTargets = document.querySelectorAll('.btn, .ceo-stat, .ceo-case__stat, .cto-bento__card, .coo-case__actions .btn'); 276 for (var l = 0; l < liftTargets.length; l++) liftTargets[l].classList.add('sol-lift'); 277 278 if (reducedMotion) { 279 for (var r = 0; r < revealTargets.length; r++) revealTargets[r].classList.add('is-visible'); 280 for (var rh = 0; rh < heroMedia.length; rh++) heroMedia[rh].classList.add('is-visible'); 281 return; 282 } 283 284 /* Ensure hero media pop is always visible on page load */ 285 window.requestAnimationFrame(function () { 286 setTimeout(function () { 287 for (var h = 0; h < heroMedia.length; h++) heroMedia[h].classList.add('is-visible'); 288 }, 90); 289 }); 290 291 if ('IntersectionObserver' in window) { 292 var observer = new IntersectionObserver(function (entries, obs) { 293 for (var e = 0; e < entries.length; e++) { 294 if (entries[e].isIntersecting) {
295 entries[e].target.classList.add('is-visible'); 296 obs.unobserve(entries[e].target); 297 } 298 } 299 }, { threshold: 0.14, rootMargin: '0px 0px -8% 0px' }); 300 for (var t = 0; t < revealTargets.length; t++) observer.observe(revealTargets[t]); 301 } else { 302 for (var rr = 0; rr < revealTargets.length; rr++) revealTargets[rr].classList.add('is-visible'); 303 } 304 305 /* Solutions number count-up */ 306 var stats = document.querySelectorAll('.ceo-stat__num, .ceo-case__stat-value'); 307 function parseNum(text) { 308 var raw = (text || '').trim(); 309 var v = parseFloat(raw.replace(/[^0-9.]/g, '')); 310 if (isNaN(v)) return null; 311 return { 312 raw: raw, 313 value: v, 314 hasPercent: raw.indexOf('%') >= 0, 315 hasDecimal: raw.indexOf('.') >= 0 316 }; 317 } 318 function formatNum(meta, n) { 319 var s = meta.hasDecimal ? n.toFixed(1) : Math.round(n).toString(); 320 if (meta.hasPercent) s += '%'; 321 return s; 322 } 323 function animateNum(el, meta) { 324 var start = null; 325 var duration = 1000; 326 function step(ts) { 327 if (!start) start = ts; 328 var p = Math.min(1, (ts - start) / duration); 329 var eased = 1 - Math.pow(1 - p, 3); 330 el.textContent = formatNum(meta, meta.value * eased); 331 if (p < 1) window.requestAnimationFrame(step); 332 else el.textContent = meta.raw; 333 } 334 window.requestAnimationFrame(step); 335 } 336 if (stats.length && 'IntersectionObserver' in window) { 337 var statObserver = new IntersectionObserver(function (entries, obs) { 338 for (var s = 0; s < entries.length; s++) { 339 if (entries[s].isIntersecting) { 340 var meta = parseNum(entries[s].target.textContent); 341 if (meta) animateNum(entries[s].target, meta); 342 obs.unobserve(entries[s].target); 343 } 344 } 345 }, { threshold: 0.4 }); 346 for (var si = 0; si < stats.length; si++) statObserver.observe(stats[si]); 347 } 348})(); 349 350(function () { 351 if (!document.body) return; 352 var isTarget = 353 document.body.classList.contains('page-product') || 354 document.body.classList.contains('page-services'); 355 if (!isTarget) return; 356 357 var reducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; 358 359 var style = document.createElement('style'); 360 style.textContent = [ 361 ':root{--ps-enter:cubic-bezier(0.22,1,0.36,1);--ps-move:cubic-bezier(0.25,1,0.5,1)}', 362 '.ps-reveal{opacity:0;transform:translateY(10px);transition:transform 240ms var(--ps-enter),opacity 240ms var(--ps-enter)}', 363 '.ps-reveal.is-visible{opacity:1;transform:translateY(0)}', 364 '.ps-stagger>*{transition-delay:calc(var(--ps-index,0)*34ms)}', 365 '@media (hover:hover) and (pointer:fine){.ps-lift{transition:transform 160ms var(--ps-enter),box-shadow 160ms var(--ps-enter)}.ps-lift:hover{transform:translateY(-2px)}.btn.ps-lift:active{transform:translateY(0)}}', 366 '.ps-hero-media{opacity:0;transform:translateY(10px) scale(.985);will-change:transform,opacity}', 367 '@keyframes psHeroPop{0%{opacity:0;transform:translateY(10px) scale(.985)}58%{opacity:1;transform:translateY(-1px) scale(1.013)}100%{opacity:1;transform:translateY(0) scale(1)}}', 368 '.ps-hero-media.is-visible{animation:psHeroPop 520ms var(--ps-enter) both;will-change:auto}', 369 '.ps-parallax-layer{transition:transform 180ms var(--ps-move);will-change:transform}', 370 '@media (prefers-reduced-motion:reduce){.ps-reveal,.ps-lift,.ps-hero-media,.ps-parallax-layer{transition:none !important;animation:none !important;transform:none !important;opacity:1 !important}}' 371 ].join(''); 372 document.head.appendChild(style); 373 374 var heroMedia = document.querySelectorAll('.prod-hero__media, .prod-hero__visual, .serv-hero__media'); 375 for (var hm = 0; hm < heroMedia.length; hm++) heroMedia[hm].classList.add('ps-hero-media'); 376 377 var revealTargets = document.querySelectorAll([ 378 '.prod-hero__left', 379 '.prod-philosophy__card', 380 '.prod-chats__intro', 381 '.prod-chats__screen', 382 '.prod-trust__lead', 383 '.prod-persona-card', 384 '.prod-cta__box-content', 385 '.serv-hero__inner', 386 '.serv-strategic__intro', 387 '.serv-strategic__card', 388 '.serv-roadmap__intro', 389 '.serv-roadmap__phase', 390 '.serv-cta__copy', 391 '.serv-cta__stat' 392 ].join(',')); 393 for (var i = 0; i < revealTargets.length; i++) revealTargets[i].classList.add('ps-reveal'); 394 395 var staggerGroups = document.querySelectorAll('.prod-philosophy__grid, .prod-ch
395ats__screens, .prod-personas__grid, .serv-strategic__cards, .serv-roadmap__grid'); 396 for (var g = 0; g < staggerGroups.length; g++) { 397 var group = staggerGroups[g]; 398 group.classList.add('ps-stagger'); 399 for (var c = 0; c < group.children.length; c++) group.children[c].style.setProperty('--ps-index', c); 400 } 401 402 var liftTargets = document.querySelectorAll('.btn, .prod-philosophy__card, .prod-persona-card, .serv-strategic__card, .serv-roadmap__phase, .serv-cta__stat'); 403 for (var l = 0; l < liftTargets.length; l++) liftTargets[l].classList.add('ps-lift'); 404 405 if (reducedMotion) { 406 for (var r = 0; r < revealTargets.length; r++) revealTargets[r].classList.add('is-visible'); 407 for (var rh = 0; rh < heroMedia.length; rh++) heroMedia[rh].classList.add('is-visible'); 408 return; 409 } 410 411 window.requestAnimationFrame(function () { 412 setTimeout(function () { 413 for (var h = 0; h < heroMedia.length; h++) heroMedia[h].classList.add('is-visible'); 414 }, 90); 415 }); 416 417 if ('IntersectionObserver' in window) { 418 var observer = new IntersectionObserver(function (entries, obs) { 419 for (var e = 0; e < entries.length; e++) { 420 if (entries[e].isIntersecting) { 421 entries[e].target.classList.add('is-visible'); 422 obs.unobserve(entries[e].target); 423 } 424 } 425 }, { threshold: 0.14, rootMargin: '0px 0px -8% 0px' }); 426 for (var t = 0; t < revealTargets.length; t++) observer.observe(revealTargets[t]); 427 } else { 428 for (var rr = 0; rr < revealTargets.length; rr++) revealTargets[rr].classList.add('is-visible'); 429 } 430 431 var parallaxConfigs = [ 432 { section: '.prod-hero', bg: '.prod-hero__media img', fg: '.prod-hero__phone-shot, .prod-hero .home-hero__phone-shot' }, 433 { section: '.serv-hero', bg: '.serv-hero__media img', fg: '.serv-hero__inner' } 434 ]; 435 var pairs = []; 436 for (var p = 0; p < parallaxConfigs.length; p++) { 437 var cfg = parallaxConfigs[p]; 438 var section = document.querySelector(cfg.section); 439 if (!section) continue; 440 var bg = section.querySelector(cfg.bg); 441 var fg = cfg.fg ? section.querySelector(cfg.fg) : null; 442 if (bg) bg.classList.add('ps-parallax-layer'); 443 if (fg) fg.classList.add('ps-parallax-layer'); 444 if (bg || fg) pairs.push({ section: section, bg: bg, fg: fg }); 445 } 446 if (pairs.length) { 447 var tick = false; 448 var update = function () { 449 for (var pi = 0; pi < pairs.length; pi++) { 450 var pair = pairs[pi]; 451 var rect = pair.section.getBoundingClientRect(); 452 var viewport = window.innerHeight || 1; 453 var progress = Math.max(-1, Math.min(1, (rect.top + rect.height * 0.5 - viewport * 0.5) / viewport)); 454 if (pair.bg) pair.bg.style.transform = 'translateY(' + (-progress * 6).toFixed(2) + 'px)'; 455 if (pair.fg) pair.fg.style.transform = 'translateY(' + (progress * 3).toFixed(2) + 'px)'; 456 } 457 tick = false; 458 }; 459 window.addEventListener('scroll', function () { 460 if (!tick) { 461 tick = true; 462 window.requestAnimationFrame(update); 463 } 464 }, { passive: true }); 465 update(); 466 } 467})(); 468 469(function initPageJourney() { 470 if (!document.body) return; 471 var nav = document.querySelector('.page-journey'); 472 if (!nav) return; 473 var trackFill = nav.querySelector('.page-journey__track-fill'); 474 if (!trackFill) return; 475 var links = nav.querySelectorAll('a.page-journey__link[href^="#"]'); 476 var sections = []; 477 for (var ji = 0; ji < links.length; ji++) { 478 var href = links[ji].getAttribute('href'); 479 if (!href || href.length < 2) continue; 480 var target = document.getElementById(href.slice(1)); 481 if (target) sections.push({ link: links[ji], el: target }); 482 } 483 if (!sections.length) return; 484 485 function updateJourney() { 486 var first = sections[0].el; 487 var last = sections[sections.length - 1].el; 488 var start = first.getBoundingClientRect().top + window.scrollY; 489 var end = last.getBoundingClientRect().bottom + window.scrollY; 490 var mid = window.scrollY + window.innerHeight * 0.33; 491 var span = end - start; 492 var p = span > 0 ? (mid - start) / span : 0; 493 nav.style.setProperty('--journey-fill', Math.max(0, Math.min(1, p)).toFixed(4)); 494 495 var marker = window.innerHeight * 0.38; 496 var activeIdx = 0; 497 for (var a = 0; a < sections.length; a++) { 498 var r = sections[a].el.getBoundingClientRect(); 499 if (r.top <= marker) activeIdx = a; 500 } 501 for (var b = 0; b < sections.length; b++) { 502 sections[b].link.classList.toggle('is-active', b === activeIdx); 503 } 504 } 505 506 var journeyTick = false; 507 var heroEl = sections.length ? sections[0].el : null; 508 function updateVisibility() { 509 if (!heroEl) return; 510 var heroBottom = heroEl.getBoundingClientRect().bottom; 511 nav.classList.toggle('is-hidden', heroBottom >
511 80); 512 } 513 514 function onJourneyFrame() { 515 updateJourney(); 516 updateVisibility(); 517 } 518 519 window.addEventListener('scroll', function() { 520 if (!journeyTick) { 521 journeyTick = true; 522 window.requestAnimationFrame(function () { onJourneyFrame(); journeyTick = false; }); 523 } 524 }, { passive: true }); 525 window.addEventListener('resize', function() { 526 if (!journeyTick) { 527 journeyTick = true; 528 window.requestAnimationFrame(function () { onJourneyFrame(); journeyTick = false; }); 529 } 530 }, { passive: true }); 531 onJourneyFrame(); 532 533 var smoothScroll = !(window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches); 534 for (var c = 0; c < sections.length; c++) { 535 (function (link, el) { 536 link.addEventListener('click', function (e) { 537 e.preventDefault(); 538 el.scrollIntoView({ behavior: smoothScroll ? 'smooth' : 'auto', block: 'start' }); 539 window.setTimeout(updateJourney, smoothScroll ? 480 : 0); 540 }); 541 })(sections[c].link, sections[c].el); 542 } 543})(); 544 545(function () { 546 if (!document.body) return; 547 var isTarget = 548 document.body.classList.contains('page-about') || 549 document.body.classList.contains('page-contact'); 550 if (!isTarget) return; 551 552 var reducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; 553 554 var style = document.createElement('style'); 555 style.textContent = [ 556 ':root{--ac-enter:cubic-bezier(0.22,1,0.36,1);--ac-move:cubic-bezier(0.25,1,0.5,1)}', 557 '.ac-reveal{opacity:0;transform:translateY(10px);transition:transform 240ms var(--ac-enter),opacity 240ms var(--ac-enter)}', 558 '.ac-reveal.is-visible{opacity:1;transform:translateY(0)}', 559 '.ac-stagger>*{transition-delay:calc(var(--ac-index,0)*34ms)}', 560 '@media (hover:hover) and (pointer:fine){.ac-lift{transition:transform 160ms var(--ac-enter),box-shadow 160ms var(--ac-enter)}.ac-lift:hover{transform:translateY(-2px)}.btn.ac-lift:active{transform:translateY(0)}}', 561 '.ac-hero-media{opacity:0;transform:translateY(10px) scale(.985);will-change:transform,opacity}', 562 '@keyframes acHeroPop{0%{opacity:0;transform:translateY(10px) scale(.985)}58%{opacity:1;transform:translateY(-1px) scale(1.013)}100%{opacity:1;transform:translateY(0) scale(1)}}', 563 '.ac-hero-media.is-visible{animation:acHeroPop 520ms var(--ac-enter) both;will-change:auto}', 564 '.ac-parallax-layer{transition:transform 180ms var(--ac-move);will-change:transform}', 565 '@media (prefers-reduced-motion:reduce){.ac-reveal,.ac-lift,.ac-hero-media,.ac-parallax-layer{transition:none !important;animation:none !important;transform:none !important;opacity:1 !important}}' 566 ].join(''); 567 document.head.appendChild(style); 568 569 var heroMedia = document.querySelectorAll('.about-hero__inner, .contact-hero__inner'); 570 for (var hm = 0; hm < heroMedia.length; hm++) heroMedia[hm].classList.add('ac-hero-media'); 571 572 var revealTargets = document.querySelectorAll([ 573 '.about-story__grid > div', 574 '.about-story__figure', 575 '.about-institutional__map', 576 '.about-institutional__copy', 577 '.about-gutfeel__grid > div:first-child', 578 '.about-gutfeel__panel', 579 '.about-cta__title', 580 '.about-cta__lead', 581 '.about-cta__actions', 582 '.about-people__h2', 583 '.about-people__lead', 584 '.about-people__card', 585 '.contact-aside', 586 '.contact-form-wrap', 587 '.contact-next__intro', 588 '.contact-next__step' 589 ].join(',')); 590 for (var i = 0; i < revealTargets.length; i++) revealTargets[i].classList.add('ac-reveal'); 591 592 var staggerGroups = document.querySelectorAll('.about-gutfeel__list, .about-people__grid, .contact-next__grid'); 593 for (var g = 0; g < staggerGroups.length; g++) { 594 var group = staggerGroups[g]; 595 group.classList.add('ac-stagger'); 596 for (var c = 0; c < group.children.length; c++) group.children[c].style.setProperty('--ac-index', c); 597 } 598 599 var liftTargets = document.querySelectorAll('.btn, .about-people__card, .about-gutfeel__item'); 600 for (var l = 0; l < liftTargets.length; l++) liftTargets[l].classList.add('ac-lift'); 601 602 if (reducedMotion) { 603 for (var r = 0; r < revealTargets.length; r++) revealTargets[r].classList.add('is-visible'); 604 for (var rh = 0; rh < heroMedia.length; rh++) heroMedia[rh].classList.add('is-visible'); 605 return; 606 } 607 608 window.requestAnimationFrame(function () { 609 setTimeout(function () { 610 for (var h = 0; h < heroMedia.length; h++) heroMedia[h].classList.add('is-visible'); 611 }, 90); 612 }); 613 614 if ('IntersectionObserver' in window) { 615 var observer = new IntersectionObserver(function (entries, obs) { 616 for (var e = 0; e < entries.length; e++) { 617 if (entries[e].isIntersecting) {
618 entries[e].target.classList.add('is-visible'); 619 obs.unobserve(entries[e].target); 620 } 621 } 622 }, { threshold: 0.14, rootMargin: '0px 0px -8% 0px' }); 623 for (var t = 0; t < revealTargets.length; t++) observer.observe(revealTargets[t]); 624 } else { 625 for (var rr = 0; rr < revealTargets.length; rr++) revealTargets[rr].classList.add('is-visible'); 626 } 627 628 var parallaxConfigs = [ 629 { section: '.about-hero', bg: '.about-hero__bg' }, 630 { section: '.contact-hero', bg: '.contact-hero__bg-photo' } 631 ]; 632 var pairs = []; 633 for (var p = 0; p < parallaxConfigs.length; p++) { 634 var cfg = parallaxConfigs[p]; 635 var section = document.querySelector(cfg.section); 636 if (!section) continue; 637 var bg = section.querySelector(cfg.bg); 638 if (bg) { 639 bg.classList.add('ac-parallax-layer'); 640 pairs.push({ section: section, bg: bg }); 641 } 642 } 643 if (pairs.length) { 644 var tick = false; 645 var updateAc = function () { 646 for (var pi = 0; pi < pairs.length; pi++) { 647 var pair = pairs[pi]; 648 var rect = pair.section.getBoundingClientRect(); 649 var viewport = window.innerHeight || 1; 650 var progress = Math.max(-1, Math.min(1, (rect.top + rect.height * 0.5 - viewport * 0.5) / viewport)); 651 pair.bg.style.transform = 'translateY(' + (-progress * 6).toFixed(2) + 'px)'; 652 } 653 tick = false; 654 }; 655 window.addEventListener('scroll', function () { 656 if (!tick) { 657 tick = true; 658 window.requestAnimationFrame(updateAc); 659 } 660 }, { passive: true }); 661 updateAc(); 662 } 663})(); 664 665/** HubSpot funnel: persist UTMs + first-touch URL, fill native form hidden fields, behavioral event on /contact links. See docs/HUBSPOT_FUNNEL_SETUP.md */ 666(function () { 667 var STORAGE_KEY = 'pi_hs_attr_v1'; 668 var UTM_KEYS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term']; 669 670 function readStore() { 671 try { 672 var raw = localStorage.getItem(STORAGE_KEY); 673 if (!raw) return {}; 674 var o = JSON.parse(raw); 675 return typeof o === 'object' && o ? o : {}; 676 } catch (e) { 677 return {}; 678 } 679 } 680 681 function writeStore(o) { 682 try { 683 localStorage.setItem(STORAGE_KEY, JSON.stringify(o)); 684 } catch (e) {} 685 } 686 687 function parseQuery() { 688 var params = {}; 689 try { 690 var search = window.location.search.replace(/^\?/, ''); 691 if (!search) return params; 692 search.split('&').forEach(function (pair) { 693 var i = pair.indexOf('='); 694 if (i < 0) return; 695 var k = decodeURIComponent(pair.slice(0, i).replace(/\+/g, ' ')); 696 var v = decodeURIComponent(pair.slice(i + 1).replace(/\+/g, ' ')); 697 if (k) params[k] = v; 698 }); 699 } catch (e) {} 700 return params; 701 } 702 703 function refreshAttribution() { 704 var data = readStore(); 705 var q = parseQuery(); 706 var i; 707 for (i = 0; i < UTM_KEYS.length; i++) { 708 var key = UTM_KEYS[i]; 709 if (q[key]) data[key] = q[key]; 710 } 711 if (!data.pi_first_touch_url) { 712 data.pi_first_touch_url = window.location.href.split('#')[0]; 713 data.pi_first_touch_referrer = document.referrer || ''; 714 } 715 data.pi_last_seen_url = window.location.href.split('#')[0]; 716 writeStore(data); 717 } 718 719 refreshAttribution(); 720 721 function getAttributionForForm() { 722 var data = readStore(); 723 var out = {}; 724 var i; 725 for (i = 0; i < UTM_KEYS.length; i++) { 726 var k = UTM_KEYS[i]; 727 if (data[k]) out[k] = data[k]; 728 } 729 if (data.pi_first_touch_url) out.pi_first_touch_url = data.pi_first_touch_url; 730 if (data.pi_first_touch_referrer != null) out.pi_first_touch_referrer = data.pi_first_touch_referrer; 731 return out; 732 } 733 734 function fillHubspotForm(form) { 735 if (!form || form.nodeName !== 'FORM') return; 736 var data = getAttributionForForm(); 737 var url = window.location.href.split('#')[0]; 738 data.pi_submission_page_url = url; 739 var keys = Object.keys(data); 740 for (var j = 0; j < keys.length; j++) { 741 var name = keys[j]; 742 var val = data[name]; 743 if (val == null || val === '') continue; 744 var fields = form.querySelectorAll('[name="' + name + '"]'); 745 for (var f = 0; f < fields.length; f++) { 746 var el = fields[f]; 747 if (el.disabled) continue; 748 if (!el.value) el.value = val; 749 } 750 } 751 } 752 753 function scanForms() { 754 var forms = document.querySelectorAll('form.hs-form, form[data-form-id]'); 755 for (var i = 0; i < forms.length; i++) fillHubspotForm(forms[i]); 756 } 757 758 document.addEventListener( 759 'submit', 760 function (e) { 761 var form = e.target; 762 if (!form || form.nodeName !== 'FORM') return; 763 var cls = form.getAttribute('class') || ''; 764 if (cls.indexOf('hs-form') === -1 && !form.hasAttribute('data-form-id')) return; 765 fillHubspotForm(form); 766 }, 767 true 768 ); 769 770 var formWatchStarted = false; 771 function bootstrapFormWatch() { 772 if (formWatchStarted) return; 773 var wrap = document.querySelector('.contact-form-wrap'); 774 if (!wrap) return; 775 formWatchStarted = true; 776 scanForms();
777 var obs = new MutationObserver(function () { 778 scanForms(); 779 }); 780 obs.observe(wrap, { childList: true, subtree: true }); 781 var n = 0; 782 var timer = setInterval(function () { 783 scanForms(); 784 n++; 785 if (n >= 15) clearInterval(timer); 786 }, 400); 787 } 788 789 if (document.readyState === 'loading') { 790 document.addEventListener('DOMContentLoaded', bootstrapFormWatch); 791 } else { 792 bootstrapFormWatch(); 793 } 794 795 document.body.addEventListener( 796 'click', 797 function (e) { 798 var t = e.target.closest && e.target.closest('a[href]'); 799 if (!t || !window._hsq) return; 800 var href = (t.getAttribute('href') || '').trim(); 801 if (!href || href.charAt(0) === '#') return; 802 var path; 803 try { 804 if (/^https?:\/\//i.test(href)) { 805 var u = new URL(href); 806 path = u.pathname || ''; 807 } else { 808 path = href.split('?')[0].split('#')[0]; 809 } 810 } catch (err) { 811 return; 812 } 813 if (!path || !/\/contact\/?$/.test(path)) return; 814 try { 815 window._hsq.push([ 816 'trackCustomBehavioralEvent', 817 { 818 name: 'pe147149326_pi_book_demo_cta', 819 properties: { 820 href: href, 821 text: (t.textContent || '').trim().slice(0, 120) 822 } 823 } 824 ]); 825 } catch (err2) {} 826 }, 827 true 828 ); 829})(); 830 831(function initCookieSettingsButton() { 832 function trackBehaviorEvent(name, properties) { 833 if (!window._hsq) return; 834 try { 835 window._hsq.push([ 836 'trackCustomBehavioralEvent', 837 { 838 name: name, 839 properties: properties || {} 840 } 841 ]); 842 } catch (err) {} 843 } 844 845 function tryShowCookieBanner() { 846 var didAttempt = false; 847 try { 848 var queue = window._hsp = window._hsp || []; 849 if (queue && typeof queue.push === 'function') { 850 queue.push(['showBanner']); 851 didAttempt = true; 852 } 853 } catch (err) {} 854 855 try { 856 if (window.hsCookieBanner && typeof window.hsCookieBanner.showBanner === 'function') { 857 window.hsCookieBanner.showBanner(); 858 didAttempt = true; 859 } 860 } catch (err2) {} 861 862 try { 863 if (window.HSCookieBanner && typeof window.HSCookieBanner.showBanner === 'function') { 864 window.HSCookieBanner.showBanner(); 865 didAttempt = true; 866 } 867 } catch (err3) {} 868 869 return didAttempt; 870 } 871 872 function showCookieBannerWithRetry() { 873 var attempts = 0; 874 var maxAttempts = 25; 875 tryShowCookieBanner(); 876 var timer = setInterval(function () { 877 attempts++; 878 tryShowCookieBanner(); 879 if (attempts >= maxAttempts) clearInterval(timer); 880 }, 200); 881 } 882 883 function bind() { 884 var btn = document.getElementById('hs_show_banner_button'); 885 if (!btn) return; 886 btn.addEventListener('click', function () { 887 showCookieBannerWithRetry(); 888 trackBehaviorEvent('pe147149326_pi_cookie_settings_click', { 889 page_path: window.location.pathname 890 }); 891 }); 892 } 893 894 if (document.readyState === 'loading') { 895 document.addEventListener('DOMContentLoaded', bind); 896 } else { 897 bind(); 898 } 899})(); 900 901(function () { 902 /** Canonical CDN for the Nando's case study PDF (HubSpot rewrites uploaded theme hrefs via domain mapping). */ 903 var NANDOS_CANON = 904 'https://147149326.fs1.hubspotusercontent-eu1.net/hubfs/147149326/PI%20-%20Nandos%20Case%20Study.pdf'; 905 906 function canonicalNandosUrl(u) { 907 try { 908 var parsed = new URL(u, window.location.href); 909 var qs = parsed.searchParams; 910 qs.delete('hsLang'); 911 qs.delete('_hsmi'); 912 qs.delete('_hsenc'); 913 var q = qs.toString(); 914 return NANDOS_CANON + (q ? '?' + q : ''); 915 } catch (e) { 916 return NANDOS_CANON; 917 } 918 } 919 920 function sameNandosFile(href) { 921 if (!href || href.indexOf('Nandos') < 0) return false; 922 923 /** Legacy primary-domain URLs */ 924 try { 925 var a = document.createElement('a'); 926 a.href = href; 927 if (a.pathname.indexOf('%20-%20Nandos%20Case%20Study.pdf') >= 0) return true; 928 if ((a.pathname + '').indexOf('PI - Nandos Case Study.pdf') >= 0) return true; 929 } catch (e2) {} 930 931 return href.indexOf('PI%20-%20Nandos%20Case%20Study.pdf') >= 0; 932 } 933 934 function normalize(el) { 935 if (!el) return false; 936 var href = el.getAttribute('href'); 937 if (!href) return false; 938 if (!sameNandosFile(href)) return false; 939 940 var next = canonicalNandosUrl(href); 941 if (href === next) return false; 942 el.setAttribute('href', next); 943 return true; 944 } 945 946 function scan(root) { 947 var scope = root || document; 948 var links = scope.querySelectorAll('a[href]'); 949 for (var i = 0; i < links.length; i++) normalize(links[i]); 950 } 951 952 scan(document); 953 954 document.addEventListener( 955 'click', 956 function (e) { 957 var t = e.target && e.target.closest ? e.target.closest('a[href]') : null; 958 if (!t) return; 959 if (!sameNandosFile(t.getAttribute('href'))) return; 960 if (normalize(t)) { 961 /** href updated before navigation */ 962 } 963 }, 964 true 965 ); 966 967 try { 968 var mo = new MutationObserver(function (mutations) { 969 for (var m = 0; m < mutations.length; m++) { 970 var mut = mutations[m]; 971 if (mut.type === 'attributes' && mut.target && mut.target.matches && mut.target.matches('a[href]')) { 972 normalize(mut.target); 973 continue; 974 } 975 if (mut.addedNodes && mut.addedNodes.length) { 976 for (var n = 0; n < mut.addedNodes.length; n++) { 977 var node = mut.addedNodes[n]; 978 if (node.nodeType !== 1) continue; 979 if (node.matches && node.matches('a[href]')) normalize(node); 980 if (node.querySelectorAll) scan(node); 981 } 982 } 983 } 984 });
985 mo.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['href'] }); 986 } catch (e3) {} 987})(); 988 989 990/** US / CA / SA spelling locale â timezone default + manual header override. */ 991(function () { 992 var STORAGE_KEY = 'pi_locale'; 993 /** Longest-first. CA shares Commonwealth -our/-ll with SA, but keeps US -ize. */ 994 var TERMS = [ 995 { us: 'optimization', ca: 'optimization', sa: 'optimisation' }, 996 { us: 'optimized', ca: 'optimized', sa: 'optimised' }, 997 { us: 'optimize', ca: 'optimize', sa: 'optimise' }, 998 { us: 'modeling', ca: 'modelling', sa: 'modelling' }, 999 { us: 'modeled', ca: 'modelled', sa: 'modelled' }, 1000 { us: 'behavior', ca: 'behaviour', sa: 'behaviour' }, 1001 { us: 'labor', ca: 'labour', sa: 'labour' } 1002 ]; 1003 var CA_TIMEZONES = { 1004 'America/Toronto': 1, 1005 'America/Vancouver': 1, 1006 'America/Edmonton': 1, 1007 'America/Winnipeg': 1, 1008 'America/Halifax': 1, 1009 'America/St_Johns': 1, 1010 'America/Whitehorse': 1, 1011 'America/Yellowknife': 1, 1012 'America/Regina': 1, 1013 'America/Moncton': 1, 1014 'America/Iqaluit': 1, 1015 'America/Goose_Bay': 1, 1016 'America/Glace_Bay': 1, 1017 'America/Blanc-Sablon': 1, 1018 'America/Dawson': 1, 1019 'America/Dawson_Creek': 1, 1020 'America/Fort_Nelson': 1, 1021 'America/Creston': 1, 1022 'America/Cambridge_Bay': 1, 1023 'America/Rankin_Inlet': 1, 1024 'America/Inuvik': 1, 1025 'America/Resolute': 1, 1026 'America/Swift_Current': 1, 1027 'America/Thunder_Bay': 1, 1028 'America/Nipigon': 1, 1029 'America/Rainy_River': 1, 1030 'America/Atikokan': 1 1031 }; 1032 1033 var applied = null; 1034 1035 function isValidLocale(v) { 1036 return v === 'us' || v === 'ca' || v === 'sa'; 1037 } 1038 1039 function formFor(term, locale) { 1040 if (locale === 'ca') return term.ca; 1041 if (locale === 'sa') return term.sa; 1042 return term.us; 1043 } 1044 1045 function htmlLang(locale) { 1046 if (locale === 'sa') return 'en-GB'; 1047 if (locale === 'ca') return 'en-CA'; 1048 return 'en-US'; 1049 } 1050 1051 function timezoneDefault() { 1052 try { 1053 var tz = (Intl.DateTimeFormat().resolvedOptions().timeZone) || ''; 1054 if (CA_TIMEZONES[tz]) return 'ca'; 1055 if (/^Africa\//.test(tz) || /^Europe\//.test(tz)) return 'sa'; 1056 } catch (e) {} 1057 return 'us'; 1058 } 1059 1060 function readStored() { 1061 try { 1062 var v = localStorage.getItem(STORAGE_KEY); 1063 if (isValidLocale(v)) return v; 1064 } catch (e2) {} 1065 return null; 1066 } 1067 1068 function resolveLocale() { 1069 return readStored() || timezoneDefault(); 1070 } 1071 1072 function persist(locale) { 1073 try { 1074 localStorage.setItem(STORAGE_KEY, locale); 1075 } catch (e3) {} 1076 } 1077 1078 function matchCase(sample, replacement) { 1079 if (!sample) return replacement; 1080 if (sample === sample.toUpperCase()) return replacement.toUpperCase(); 1081 if (sample[0] === sample[0].toUpperCase()) { 1082 return replacement.charAt(0).toUpperCase() + replacement.slice(1); 1083 } 1084 return replacement; 1085 } 1086 1087 function buildRegex(word) { 1088 return new RegExp('\\b' + word + '\\b', 'gi'); 1089 } 1090 1091 function rewriteText(text, fromLocale, toLocale) { 1092 if (!text || fromLocale === toLocale) return text; 1093 var out = text; 1094 var i; 1095 var from; 1096 var to; 1097 var re; 1098 for (i = 0; i < TERMS.length; i++) { 1099 from = formFor(TERMS[i], fromLocale); 1100 to = formFor(TERMS[i], toLocale); 1101 if (from === to) continue; 1102 re = buildRegex(from); 1103 out = out.replace(re, function (match) { 1104 return matchCase(match, to);
1105 }); 1106 } 1107 return out; 1108 } 1109 1110 function shouldSkip(node) { 1111 if (!node || node.nodeType !== 1) return true; 1112 var tag = (node.tagName || '').toLowerCase(); 1113 if (tag === 'script' || tag === 'style' || tag === 'noscript' || tag === 'code' || tag === 'pre') return true; 1114 if (tag === 'input' || tag === 'textarea' || tag === 'select' || tag === 'option') return true; 1115 if (node.closest && node.closest('[data-pi-locale-skip]')) return true; 1116 return false; 1117 } 1118 1119 function rewriteTree(root, fromLocale, toLocale) { 1120 if (!root || fromLocale === toLocale) return; 1121 var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, { 1122 acceptNode: function (node) { 1123 if (!node || !node.nodeValue || !node.nodeValue.trim()) return NodeFilter.FILTER_REJECT; 1124 var parent = node.parentNode; 1125 if (shouldSkip(parent)) return NodeFilter.FILTER_REJECT; 1126 return NodeFilter.FILTER_ACCEPT; 1127 } 1128 }); 1129 var node; 1130 var next; 1131 var nodes = []; 1132 while ((node = walker.nextNode())) nodes.push(node); 1133 for (var i = 0; i < nodes.length; i++) { 1134 next = nodes[i]; 1135 next.nodeValue = rewriteText(next.nodeValue, fromLocale, toLocale); 1136 } 1137 } 1138 1139 function syncToggle(locale) { 1140 var buttons = document.querySelectorAll('.pi-locale__btn[data-pi-locale]'); 1141 for (var i = 0; i < buttons.length; i++) { 1142 var btn = buttons[i]; 1143 var isActive = btn.getAttribute('data-pi-locale') === locale; 1144 btn.setAttribute('aria-pressed', isActive ? 'true' : 'false'); 1145 } 1146 } 1147 1148 /** Fixed figures that are authored per market rather than derived - currency 1149 conversions that would be wrong to produce by swapping a symbol. */ 1150 function applyMoney(locale) { 1151 var els = document.querySelectorAll('[data-pi-money]'); 1152 for (var i = 0; i < els.length; i++) { 1153 var v = els[i].getAttribute('data-money-' + locale); 1154 if (v) els[i].textContent = v; 1155 } 1156 } 1157 1158 function notifyCalculatorIframes(locale) { 1159 var iframes = document.querySelectorAll('iframe.res-calculator__iframe, iframe#calculator-iframe'); 1160 for (var i = 0; i < iframes.length; i++) { 1161 var frame = iframes[i]; 1162 try { 1163 if (frame.contentWindow) { 1164 frame.contentWindow.postMessage({ type: 'pi:locale', locale: locale }, '*'); 1165 } 1166 } catch (e4) {} 1167 try { 1168 if (frame.src) { 1169 var url = new URL(frame.src, window.location.href); 1170 url.searchParams.set('locale', locale); 1171 if (frame.src !== url.href) frame.src = url.href; 1172 } 1173 } catch (e5) {} 1174 } 1175 } 1176 1177 function applyLocale(locale, opts) { 1178 opts = opts || {}; 1179 if (!isValidLocale(locale)) locale = 'us'; 1180 var from = applied || 'us'; 1181 document.documentElement.setAttribute('data-pi-locale', locale); 1182 document.documentElement.lang = htmlLang(locale); 1183 if (from !== locale) { 1184 rewriteTree(document.body || document.documentElement, from, locale); 1185 } 1186 applied = locale; 1187 syncToggle(locale); 1188 if (opts.persist) persist(locale); 1189 applyMoney(locale); 1190 if (opts.notifyFrames !== false) notifyCalculatorIframes(locale); 1191 try { 1192 window.dispatchEvent(new CustomEvent('pi:localechange', { detail: { locale: locale } })); 1193 } catch (e6) {} 1194 } 1195 1196 /* Something downstream resets <html lang> back to the page default after 1197 main.js has run, so the attribute disagreed with the chosen locale on every 1198 page. Put it back whenever that happens. The equality check stops the 1199 observer retriggering itself. */ 1200 function guardLang() { 1201 try { 1202 var obs = new MutationObserver(function () { 1203 var want = htmlLang(applied || resolveLocale()); 1204 if (document.documentElement.lang !== want) document.documentElement.lang = want; 1205 }); 1206 obs.observe(document.documentElement, { attributes: true, attributeFilter: ['lang'] }); 1207 } catch (e7) {} 1208 } 1209 1210 function bindToggle() { 1211 var root = document.querySelector('.pi-locale'); 1212 if (!root) return; 1213 root.addEventListener('click', function (ev) { 1214 var btn = ev.target && ev.target.closest ? ev.target.closest('.pi-locale__btn[data-pi-locale]') : null; 1215 if (!btn || !root.contains(btn)) return; 1216 var next = btn.getAttribute('data-pi-locale'); 1217 if (!isValidLocale(next)) return; 1218 applyLocale(next, { persist: true, notifyFrames: true });
1219 }); 1220 } 1221 1222 window.PILocale = { 1223 resolve: resolveLocale, 1224 apply: applyLocale, 1225 get: function () { 1226 return applied || resolveLocale(); 1227 } 1228 }; 1229 1230 var initial = resolveLocale(); 1231 if (document.body) { 1232 applyLocale(initial, { persist: true, notifyFrames: true }); 1233 bindToggle(); 1234 guardLang(); 1235 } else { 1236 document.addEventListener('DOMContentLoaded', function () { 1237 applyLocale(initial, { persist: true, notifyFrames: true }); 1238 bindToggle(); 1239 guardLang(); 1240 }); 1241 } 1242})();
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.