PageSourceSearch

https://friscocharterbuscompany.com/assets/site-ui.js?v=bbc83b4168

js friscocharterbuscompany.com collected 2026-10-07 06:24:36 UTC 24,410 bytes, 633 lines download raw bytes

1// The desktop quick-actions widget is dismissible: the injected close button hides it for the
2// rest of the page view.
3(function () {
4  "use strict";
5  document.addEventListener("click", function (e) {
6    var btn = e.target && e.target.closest ? e.target.closest(".desktop-cta-close") : null;
7    if (!btn) return;
8    var widget = document.getElementById("desktopCtaWidget");
9    if (widget) widget.style.display = "none";
10  });
11})();
12
13// Native quote dialog controller. showModal() supplies top-layer placement, modal inertness,
14// Escape handling, and browser-managed focus containment; this controller only owns triggers,
15// backdrop dismissal, body scroll locking, initial focus, and explicit focus restoration.
16(function () {
17  "use strict";
18
19  var dialog = document.getElementById("fullScreenModal");
20  if (!dialog || dialog.tagName !== "DIALOG" || typeof dialog.showModal !== "function") return;
21
22  var returnFocus = null;
23
24  function openDialog(trigger) {
25    if (dialog.open) return;
26    returnFocus = trigger instanceof HTMLElement ? trigger : document.activeElement;
27    document.body.classList.add("overflow-hidden");
28    dialog.showModal();
29    window.requestAnimationFrame(function () {
30      var first = dialog.querySelector(".modal-close, button, [href], input, select, textarea, iframe, [tabindex]:not([tabindex=\"-1\"])");
31      if (first && typeof first.focus === "function") first.focus();
32    });
33  }
34
35  function closeDialog() {
36    if (dialog.open) dialog.close();
37  }
38
39  document.addEventListener("click", function (event) {
40    var trigger = event.target && event.target.closest
41      ? event.target.closest(".quote-trigger, #instantQuoteBtn")
42      : null;
43    if (trigger) {
44      event.preventDefault();
45      openDialog(trigger);
46      return;
47    }
48
49    var closer = event.target && event.target.closest
50      ? event.target.closest('[data-modal-toggle="fullScreenModal"]')
51      : null;
52    if (closer && dialog.contains(closer)) {
53      event.preventDefault();
54      closeDialog();
55    }
56  });
57
58  dialog.addEventListener("click", function (event) {
59    if (event.target === dialog) closeDialog();
60  });
61
62  document.addEventListener("keydown", function (event) {
63    if (event.key !== "Escape" || !dialog.open) return;
64    event.preventDefault();
65    closeDialog();
66  });
67
68  dialog.addEventListener("close", function () {
69    document.body.classList.remove("overflow-hidden");
70    var target = returnFocus;
71    returnFocus = null;
72    if (target && target.isConnected && typeof target.focus === "function") target.focus();
73  });
74})();
75
76// One shared controller owns both the mobile drawer and the navigation disclosures. Destination
77// links always navigate; on mobile each adjacent disclosure becomes the full labeled parent row.
78(function () {
79  "use strict";
80
81  var nav = document.getElementById("primaryNav");
82  var button = document.querySelector(".hbg[aria-controls=\"primaryNav\"]");
83  var navWrap = document.querySelector("#nav .nav-wrap");
84  var desktopQuery = window.matchMedia("(min-width: 1025px)");
85  var suppressFocusOpen = false;
86  if (!nav || !button || !navWrap) return;
87
88  function isMobile() {
89    return !desktopQuery.matches;
90  }
91
92  function getSubmenu(item) {
93    return item ? item.querySelector(":scope > .nav-dropdown") : null;
94  }
95
96  function getToggle(item) {
97    return item ? item.querySelector(
98      ":scope > .nav-submenu-toggle, :scope > .nav-main-group > .nav-submenu-toggle"
99    ) : null;
100  }
101
102  function setDrawerState(isOpen) {
103    button.classList.toggle("active", isOpen);
104    button.setAttribute("aria-expanded", isOpen ? "true" : "false");
105    button.setAttribute("aria-label", isOpen ? "Close mobile menu" : "Open mobile menu");
106    nav.classList.toggle("open", isOpen);
107    document.body.classList.toggle("nav-open", isOpen);
108  }
109
110  function setSubmenuState(item, isOpen) {
111    var toggle = getToggle(item);
112    var menu = getSubmenu(item);
113    if (!item || !toggle || !menu) return;
114    item.classList.toggle("open", isOpen);
115    toggle.setAttribute("aria-expanded", isOpen ? "true" : "false");
116    menu.classList.toggle("open", isOpen);
117  }
118
119  function closeAllSubmenus(exceptItem) {
120    Array.prototype.forEach.call(nav.querySelectorAll(".nav-item.has-submenu"), function (item) {
121      if (item !== exceptItem) setSubmenuState(item, false);
122    });
123  }
124
125  function closeDrawer() {
126    setDrawerState(false);
127    closeAllSubmenus();
128  }
129
130  button.setAttribute("aria-label", "Open mobile menu");
131  button.addEventListener("click", function () {
132    var willOpen = !nav.classList.contains("open");
133    if (!willOpen) closeAllSubmenus();
134    setDrawerState(willOpen);
135  });
136
137  Array.prototype.forEach.call(nav.querySelectorAll(".nav-item.has-submenu"), function (item, index) {
138    var toggle = getToggle(item);
139    var menu = getSubmenu(item);
140    var openedByPointerHover = false;
141    if (!toggle || !menu) return;
142    if (!menu.id) menu.id = "nav-submenu-" + (index + 1);
143    toggle.setAttribute("aria-controls", menu.id);
144    toggle.setAttribute("aria-expanded", "false");
145
146    item.addEventListener("mouseenter", function () {
147      if (isMobile()) return;
148      openedByPointerHover = !item.classList.contains("open");
149      closeAllSubmenus(item);
150      setSubmenuState(item, true);
151    });
152
153    item.addEventListener("mouseleave", function () {
154      openedByPointerHover = false;
155      if (isMobile() || item.matches(":focus-within")) return;
156      setSubmenuState(item, false);
157    });
158
159    item.addEventListener("focusin", function (event) {
160      if (isMobile() || suppressFocusOpen) return;
161      // The disclosure button owns its activation state. Opening it on focus and then toggling it
162      // again in the click handler made a first pointer click immediately close the panel. The
163      // adjacent destination link still opens the disclosure for desktop keyboard exploration.
164      if (event.target === toggle) return;
165      openedByPointerHover = false;
166      closeAllSubmenus(item);
167      setSubmenuState(item, true);
168    });
169
170    item.addEventListener("focusout", function (event) {
171      if (isMobile() || item.contains(event.relatedTarget)) return;
172      setSubmenuState(item, false);
173    });
174
175    toggle.addEventListener("click", function (event) {
176      event.preventDefault();
177      // A first pointer click is preceded by mouseenter, which already synchronized the visible
178      // panel and aria-expanded. Keep that first activation open; later activations still toggle.
179      if (!isMobile() && openedByPointerHover) {
180        openedByPointerHover = false;
181        setSubmenuState(item, true);
182        return;
183      }
184      var isOpen = item.classList.contains("open");
185      closeAllSubmenus(item);
186      setSubmenuState(item, !isOpen);
187    });
188  });
189
190  Array.prototype.forEach.call(nav.querySelectorAll("a[href]"), function (link) {
191    link.addEventListener("click", function () {
192      if (isMobile()) closeDrawer();
193    });
194  });
195
196  Array.prototype.forEach.call(nav.querySelectorAll("button.quote-trigger"), function (trigger) {
197    trigger.addEventListener("click", function () {
198      if (isMobile()) closeDrawer();
199    });
200  });
201
202  document.addEventListener("click", function (event) {
203    if (navWrap.contains(event.target)) return;
204    closeAllSubmenus();
205    if (isMobile()) closeDrawer();
206  });
207
208  document.addEventListener("keydown", function (event) {
209    if (event.key !== "Escape") return;
210    var openToggle = getToggle(nav.querySelector(".nav-item.has-submenu.open"));
211    closeDrawer();
212    if (!isMobile() && openToggle) {
213      suppressFocusOpen = true;
214      openToggle.focus();
215      window.setTimeout(function () { suppressFocusOpen = false; }, 0);
216    }
217  });
218
219  window.addEventListener("resize", closeDrawer);
220})();
221
222// Add focus containment, focus restoration, and inert background handling to the shared
223// controller so every generated mobile drawer exposes the same accessible modal behavior.
224(function () {
225  "use strict";
226
227  var nav = document.getElementById("primaryNav");
228  var button = document.querySelector(".hbg[aria-controls=\"primaryNav\"]");
229  var navWrap = document.querySelector("#nav .nav-wrap");
230  var desktopQuery = window.matchMedia("(min-width: 1025px)");
231  if (!nav || !button || !navWrap || !("inert" in HTMLElement.prototype)) return;
232
233  var isEnhancedOpen = false;
234  var returnFocus = null;
235  var activationClose = false;
236  var managedInert = [];
237  var overlayStyle = document.createElement("style");
238  overlayStyle.textContent = "body.nav-open > [data-nav-drawer-overlay]{visibility:hidden!important}";
239  document.head.appendChild(overlayStyle);
240
241  function isMenuOpen() {
242    return !desktopQuery.matches && nav.classList.contains("open");
243  }
244
245  function isVisible(element) {
246    if (!element || element.closest("[inert]")) return false;
247    var style = window.getComputedStyle(element);
248    return style.display !== "none" &&
249      style.visibility !== "hidden" &&
250      element.getClientRects().length > 0;
251  }
252
253  function getNavFocusables() {
254    var selector = "a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not
254([tabindex=\"-1\"])";
255    return Array.prototype.filter.call(nav.querySelectorAll(selector), isVisible);
256  }
257
258  function getFocusLoop() {
259    var focusables = getNavFocusables();
260    focusables.unshift(button);
261    return focusables;
262  }
263
264  function rememberInert(element) {
265    if (!element || element.inert || element.hasAttribute("data-nav-drawer-inert")) return;
266    element.inert = true;
267    element.setAttribute("data-nav-drawer-inert", "");
268    if (element.parentElement === document.body) {
269      var position = window.getComputedStyle(element).position;
270      if (position === "fixed" || position === "sticky") {
271        element.setAttribute("data-nav-drawer-overlay", "");
272      }
273    }
274    managedInert.push(element);
275  }
276
277  function applyBackgroundInert() {
278    Array.prototype.forEach.call(document.body.children, function (element) {
279      if (element.contains(nav) || /^(SCRIPT|STYLE|NOSCRIPT|TEMPLATE)$/.test(element.tagName)) return;
280      rememberInert(element);
281    });
282
283    Array.prototype.forEach.call(navWrap.children, function (element) {
284      if (element === nav || element === button || element.contains(nav) || element.contains(button)) return;
285      rememberInert(element);
286    });
287  }
288
289  function releaseBackgroundInert() {
290    managedInert.forEach(function (element) {
291      if (!element.hasAttribute("data-nav-drawer-inert")) return;
292      element.inert = false;
293      element.removeAttribute("data-nav-drawer-inert");
294      element.removeAttribute("data-nav-drawer-overlay");
295    });
296    managedInert = [];
297  }
298
299  function focusFirstNavItem() {
300    window.requestAnimationFrame(function () {
301      if (!isMenuOpen()) return;
302      var focusables = getNavFocusables();
303      (focusables[0] || button).focus();
304    });
305  }
306
307  function restoreMenuFocus() {
308    window.requestAnimationFrame(function () {
309      var current = document.activeElement;
310      var modalHasFocus = current && current !== document.body && !navWrap.contains(current);
311      if (modalHasFocus) return;
312      var target = returnFocus && returnFocus.isConnected ? returnFocus : button;
313      target.focus();
314    });
315  }
316
317  function syncAccessibleState() {
318    var open = isMenuOpen();
319    if (open === isEnhancedOpen) return;
320
321    isEnhancedOpen = open;
322    if (open) {
323      returnFocus = document.activeElement;
324      activationClose = false;
325      applyBackgroundInert();
326      focusFirstNavItem();
327      return;
328    }
329
330    releaseBackgroundInert();
331    if (!activationClose) restoreMenuFocus();
332    activationClose = false;
333  }
334
335  // Release inert synchronously when a menu action launches another UI (for example,
336  // the quote modal). This lets that action move focus before the class observer runs.
337  document.addEventListener("click", function (event) {
338    if (!isEnhancedOpen) return;
339    var action = event.target && event.target.closest ?
340      event.target.closest("a[href], button.quote-trigger") : null;
341    if (!action || !nav.contains(action) || action.matches("[data-nav-toggle=\"submenu\"]")) return;
342    activationClose = true;
343    releaseBackgroundInert();
344    window.setTimeout(function () {
345      if (isMenuOpen()) {
346        activationClose = false;
347        applyBackgroundInert();
348      }
349    }, 0);
350  }, true);
351
352  document.addEventListener("keydown", function (event) {
353    if (event.key !== "Tab" || !isMenuOpen()) return;
354    var focusables = getFocusLoop();
355    if (!focusables.length) return;
356    var currentIndex = focusables.indexOf(document.activeElement);
357    var nextIndex;
358    if (event.shiftKey) {
359      nextIndex = currentIndex <= 0 ? focusables.length - 1 : currentIndex - 1;
360    } else {
361      nextIndex = currentIndex < 0 || currentIndex === focusables.length - 1 ? 0 : currentIndex + 1;
362    }
363    event.preventDefault();
364    focusables[nextIndex].focus();
365  });
366
367  var stateObserver = new MutationObserver(syncAccessibleState);
368  stateObserver.observe(nav, { attributes: true, attributeFilter: ["class"] });
369  stateObserver.observe(button, { attributes: true, attributeFilter: ["aria-expanded"] });
370
371  var backgroundObserver = new MutationObserver(function () {
372    if (isEnhancedOpen) applyBackgroundInert();
373  });
374  backgroundObserver.observe(document.body, { childList: true });
375  backgroundObserver.observe(navWrap, { childList: true });
376
377  syncAccessibleState();
378})();
379
380// Keep third-party quote-form microcopy cleanup scoped to the stable widget hosts. The widget
381// can inject duplicate consent text or its own sharing sentence; process only initial content
382// and added nodes, then disconnect after the widget's bounded startup window.
383(function () {
384  "use strict";
385
386  var sharingSentence = /we will never share your information\.?/gi;
387  var hostSelector = "#fullScreenModal, #booking-form-modal-container, #hero-quote, #instant-quote";
388
389  function normalize(element) {
390    return (element.textContent || "").replace(/\s+/g, " ").trim().toLowerCase();
391  }
392
393  function stripSharingSentence(root) {
394    if (!root) return;
395    if (root.nodeType === Node.TEXT_NODE) {
396      root.nodeValue = (root.nodeValue || "").replace(sharingSentence, "").replace(/\s{2,}/g, " ");
397      sharingSentence.lastIndex = 0;
398      return;
399    }
400    if (root.nodeType !== Node.ELEMENT_NODE) return;
401    var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
402    var node;
403    while ((node = walker.nextNode())) {
404      node.nodeValue = (node.nodeValue || "").replace(sharingSentence, "").replace(/\s{2,}/g, " ");
405      sharingSentence.lastIndex = 0;
406    }
407  }
408
409  function dedupeConsent(container) {
410    var candidates = container.querySelectorAll("p, small, div");
411    var seen = Object.create(null);
412    for (var i = 0; i < candidates.length; i++) {
413      var element = candidates[i];
414      if (element.children.length > 0 && !/^(p|small)$/i.test(element.tagName)) continue;
415      var text = normalize(element);
416      if (text.indexOf("by submitting this form") !== 0) continue;
417      if (seen[text]) {
418        if (element.style.display !== "none") element.style.display = "none";
419      } else {
420        seen[text] = true;
421      }
422    }
423  }
424
425  function repairBookingFormLabels(container) {
426    var mappings = {
427      "first-name": { selector: '[name="pickup"]', autocomplete: "section-pickup address-level2" },
428      "last-name": { selector: '[name="dropoff"]', autocomplete: "section-dropoff address-level2" },
429      mobile: { selector: '[name="mobile_number"]', autocomplete: "tel", inputMode: "tel" },
430      email: { selector: '[name="email_address"]', autocomplete: "email", inputMode: "email" }
431    };
432    Object.keys(mappings).forEach(function (originalFor) {
433      var labels = container.querySelectorAll('label[for="' + originalFor + '"]');
434      Array.prototype.forEach.call(labels, function (label, labelIndex) {
435        var form = label.closest("form") || container;
436        if (form.querySelector("#" + originalFor)) return;
437        var config = mappings[originalFor];
438        var control = form.querySelector(config.selector);
439        if (!control) return;
440        var candidate = originalFor;
441        if (document.getElementById(candidate) && document.getElementById(candidate) !== control) {
442          candidate = originalFor + "-booking-" + (labelIndex + 1);
443          var suffix = 1;
444          while (document.getElementById(candidate) && document.getElementById(candidate) !== control) {
445            suffix += 1;
446            candidate = originalFor + "-booking-" + suffix;
447          }
448        }
449        control.id = candidate;
450        label.htmlFor = candidate;
451        if (!control.getAttribute("autocomplete")) control.setAttribute("autocomplete", config.autocomplete);
452        if (config.inputMode && !control.getAttribute("inputmode")) {
453          control.setAttribute("inputmode", config.inputMode);
454        }
455      });
456    });
457  }
458
459  function cleanupScope(scope) {
460    stripSharingSentence(scope);
461    dedupeConsent(scope);
462    repairBookingFormLabels(scope);
463  }
464
465  var hosts = Array.prototype.slice.call(document.querySelectorAll(hostSelector));
466  if (!hosts.length) return;
467  hosts.forEach(cleanupScope);
468  if (!("MutationObserver" in window)) return;
469
470  var observer = new MutationObserver(function (records) {
471    var scopes = [];
472    records.forEach(function (record) {
473      Array.prototype.forEach.call(record.addedNodes, stripSharingSentence);
474      var form = record.target.nodeType === Node.ELEMENT_NODE && record.target.closest ?
475        record.target.closest("form") : null;
476      var scope = form || hosts.find(function (host) { return host.contains(record.target); });
477      if (scope && scopes.indexOf(scope) === -1) scopes.push(scope);
478    });
479    scopes.forEach(function (scope) {
480      dedupeConsent(scope);
481      repairBookingFormLabels(scope);
482    });
483  });
484  hosts.forEach(function (host) {
485    observer.observe(host, { childList: true, subtree: true });
486  });
487  window.setTimeout(function () { observer.disconnect(); }, 15000);
488})();
489
490// One state controller owns floating CTA visibility, on two INDEPENDENT signals:
491//   DESKTOP widget -> the hero. Hidden while the page hero intersects the viewport; revealed
492//                     once it has scrolled fully out. Pages with no hero at all fall back to
493//                     the one-viewport threshold rather than appearing at load.
494//   MOBILE actions + "Back to Top" -> the one-viewport threshold, on every page, always.
495// These used to be coupled: mobile rode the hero observer, so its reveal point silently
496// depended on whether the hero lookup happened to succeed. IntersectionObserver owns
497// visibility when available; geometry-based scroll handling is the compatibility fallback.
498(function () {
499  "use strict";
500
501  var desktopWidget = document.getElementById("desktopCtaWidget");
502  var mobileWidget = document.querySelector(".mobile-cta-widget");
503  var footer = document.querySelector("footer");
504  //: three-tier hero lookup. Order is deliberate and must not be reordered:
505  //   1. [data-page-hero="true"] -- the stable contract the generator stamps on the OUTER
506  //      hero band of newly built NON-NATIONAL home/city pages. (National builds strip the
507  //      VARIANT:hero markers the stamper keys on, so they resolve via #hero at step 2.)
508  //   2. #hero -- legacy nested hero. Kept SECOND so an already-shipped page that receives
509  //      only the new site-ui.js keeps observing exactly the element it observed before.
510  //   3. #intro-layout -- the outer band, present on every hero variant. This is what fixes
511  //      sanmarcoscharterbuscompany.com, whose hero variant
512  //      (hero-mclaren-golf-lower-left-overlay.html) is the one approved variant with no
513  //      nested #hero: the lookup used to return null, the else branch below stripped
514  //      is-hero-hidden at load, and the desktop CTA painted at scrollY 0.
515  // The explicit ="true" match is required so data-page-hero="false" or an empty value can
516  // never silently become an active controller target.
517  var hero = document.querySelector('[data-page-hero="true"]') ||
518    document.getElementById("hero") ||
519    document.getElementById("intro-layout");
520  var backToTopControls = Array.prototype.slice.call(document.querySelectorAll(
521    ".back-to-top, .mobile-cta-top[href='#top']"
522  ));
523  var mobileReady = false;
524
525  if (!desktopWidget && !mobileWidget) return;
526
527  function intersectsViewport(element) {
528    var rect = element.getBoundingClientRect();
529    return rect.bottom > 0 && rect.top < window.innerHeight;
530  }
531
532  function setFooterHidden(isHidden) {
533    if (desktopWidget) desktopWidget.classList.toggle("is-footer-hidden", isHidden);
534    if (mobileWidget) mobileWidget.classList.toggle("is-footer-hidden", isHidden);
535  }
536
537  function syncResponsiveVisibility() {
538    if (desktopWidget) {
539      desktopWidget.classList.toggle("is-visible", window.innerWidth > 1024);
540    }
541    if (mobileWidget) {
542      mobileWidget.classList.toggle("is-visible", window.innerWidth <= 1024 && mobileReady);
543    }
544  }
545
546  function setMobileReady(isReady) {
547    mobileReady = !!isReady;
548    syncResponsiveVisibility();
549  }
550
551  //: the hero observer drives the DESKTOP widget only. Mobile used to ride this
552  // same signal, which meant mobile's reveal point silently depended on whether the hero
553  // lookup happened to succeed. The two contracts are now decoupled: desktop follows the
554  // hero, mobile follows the one-viewport threshold, always.
555  function setHeroIntersecting(isIntersecting) {
556    if (desktopWidget) desktopWidget.classList.toggle("is-hero-hidden", isIntersecting);
557  }
558
559  function setPastPageThreshold(isPastThreshold) {
560    backToTopControls.forEach(function (control) {
561      control.classList.toggle("is-scroll-ready", !!isPastThreshold);
562    });
563    // Mobile is threshold-driven unconditionally now -- no longer gated on `!hero`.
564    setMobileReady(isPastThreshold);
565    // A page with NO hero at all (e.g. /contact-us/) still must not show the desktop CTA at
566    // load; it falls back to the same one-viewport threshold rather than appearing instantly.
567    if (!hero && desktopWidget) {
568      desktopWidget.classList.toggle("is-hero-hidden", !isPastThreshold);
569    }
570  }
571
572  function bindVisibility(element, setter, rootMargin) {
573    if (!element) return;
574    setter(intersectsViewport(element));
575    if ("IntersectionObserver" in window) {
576      var observer = new IntersectionObserver(function (entries) {
577        setter(entries.some(function (entry) { return entry.isIntersecting; }));
578      }, { threshold: 0, rootMargin: rootMargin || "0px" });
579      observer.observe(element);
580      return;
581    }
582
583    function sync() {
584      setter(intersectsViewport(element));
585    }
586    window.addEventListener("scroll", sync, { passive: true });
587    window.addEventListener("resize", sync);
588    window.addEventListener("load", sync, { once: true });
589  }
590
591  function bindPageThreshold() {
592    var marker = document.createElement("span");
593    marker.setAttribute("aria-hidden", "true");
594    marker.className = "page-scroll-threshold-sentinel";
595    marker.style.cssText = "position:absolute;top:100vh;inset-inline-start:0;width:1px;height:1px;pointer-events:none";
596    document.body.appendChild(marker);
597
598    function sync() {
599      setPastPageThreshold(marker.getBoundingClientRect().top <= 0);
600    }
601
602    sync();
603    if ("IntersectionObserver" in window) {
604      var observer = new IntersectionObserver(function (entries) {
605        entries.forEach(function (entry) {
606          setPastPageThreshold(entry.boundingClientRect.top <= 0);
607        });
608      }, { threshold: 0 });
609      observer.observe(marker);
610      return;
611    }
612
613    window.addEventListener("scroll", sync, { passive: true });
614    window.addEventListener("resize", sync);
615    window.addEventListener("load", sync, { once: true });
616  }
617
618  syncResponsiveVisibility();
619  window.addEventListener("resize", syncResponsiveVisibility);
620
621  if (footer) {
622    bindVisibility(footer, setFooterHidden, "0px 0px -1px 0px");
623  }
624
625  bindPageThreshold();
626
627  //: the old `else` branch removed is-hero-hidden the moment the lookup failed,
628  // which is exactly how the desktop CTA came to paint at scrollY 0. Hero-less pages are now
629  // handled by the threshold fallback inside setPastPageThreshold() instead.
630  if (hero) {
631    bindVisibility(hero, setHeroIntersecting);
632  }
633})();

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.