PageSourceSearch

https://predictiveinsights.net/hubfs/raw_assets/388/public/@projec…/pi-theme/js/main.js?v=mobile-locale-14

js predictiveinsights.net collected 2026-10-09 01:38:44 UTC 46,821 bytes, 1,242 lines download raw bytes

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.