PageSourceSearch

https://salv.com/assets/site.js?v1.1.98

js salv.com collected 2026-09-29 23:25:10 UTC 51,502 bytes, 1,188 lines download raw bytes

1document.addEventListener("DOMContentLoaded", function (event) {
2    // Stuff goes here
3
4    //*** HAMBURGER MENU ***
5
6    document.querySelector("#js-nav-menu").addEventListener("click", function (event) {
7
8        const navBody = document.querySelector(".js-nav-body");
9        navBody.classList.toggle("open");
10
11        let hamburgerButton = document.querySelector("#js-nav-menu");
12        // toggle aria-expanded attribute value for accessibility
13        hamburgerButton.setAttribute("aria-expanded", hamburgerButton.getAttribute('aria-expanded') === 'true' ? 'false' : 'true');
14
15        // for when their is a banner above the nav bar: add class so that mobile nav close button can be correctly positioned
16        let hamburgerButtonWrapper = document.querySelector(".hamburger-button-wrapper");
17        hamburgerButtonWrapper.classList.toggle("nav-open");
18
19
20        event.preventDefault();
21    });
22
23    // Open collapsed submenu
24    const navButtons = document.querySelectorAll(".nav-button");
25    const navButtonsArray = [...navButtons];
26
27    navButtonsArray.forEach(button => button.addEventListener("click", mobileNavigate));
28
29    function mobileNavigate(e) {
30        // for pages that have submenu
31        if (e.target?.nextElementSibling?.classList.contains("submenu--with-expand") && window.innerWidth < 768) {
32            const openSubMenu = document.querySelector(".submenu--expanded");
33            // Autocollapse submenus
34            if (openSubMenu) {
35                openSubMenu.classList.remove("submenu--expanded");
36                openSubMenu.previousElementSibling.firstChild.classList.remove("nav-product-button--tangerine", "nav-company-button--pear", "nav-newsroom-button--capri");
37                openSubMenu.previousElementSibling.classList.remove("button-submenu--expanded");
38            }
39
40            const target = e.target.firstChild;
41            if (openSubMenu == e.target.nextElementSibling) return;
42
43            e.target.nextElementSibling.classList.add("submenu--expanded");
44            e.target.classList.add("button-submenu--expanded");
45
46            if (target == document.querySelector("#nav-product-button")) {
47                target.classList.add("nav-product-button--tangerine");
48            } else if (target == document.querySelector("#nav-company-button")) {
49                target.classList.add("nav-company-button--pear");
50            } else if (target == document.querySelector("#nav-newsroom-button")) {
51                target.classList.add("nav-newsroom-button--capri");
52            }
53        }
54    }
55
56    // remove link from mobile nav buttons that have submenu
57    function removeLinkOnMobile() {
58        if (window.innerWidth < 768) {
59            document.querySelector("#nav-product-button").style.pointerEvents = "none";
60            document.querySelector("#nav-company-button").style.pointerEvents = "none";
61            document.querySelector("#nav-newsroom-button").style.pointerEvents = "none";
62
63            document.querySelector("#nav-mission-button").parentElement.addEventListener('click', function (e) {
64                window.open(e.currentTarget.querySelector("A").href, "_self")
65            });
66
67            document.querySelector("#nav-product-button").addEventListener('click', function (e) {
68                e.preventDefault();
69            });
70            document.querySelector("#nav-company-button").addEventListener('click', function (e) {
71                e.preventDefault();
72
73            });
74            document.querySelector("#nav-newsroom-button").addEventListener('click', function (e) {
75                e.preventDefault();
76            });
77        }
78    }
79
80    //  check for window being resized
81    window.addEventListener("resize", function () {
82        removeLinkOnMobile();
83    });
84
85    // call function
86    removeLinkOnMobile();
87
88
89    //*** MOUSE HOVERING  LINKS ***
90    var items = document.querySelectorAll(".js-nav-body > div > a");
91
92    for (var i = 0; i < items.length; i++) {
93
94        items[i].addEventListener("mouseover", function (event) {
95
96            //Hides previously open submenus
97
98            var otherSubMenus = document.querySelectorAll(".submenu");
99            for (var i = 0; i < otherSubMenus.length; i++) {
100
101                if (otherSubMenus != nextSibling) {
102                    otherSubMenus[i].classList.remove("submenu--shown")
103                    otherSubMenus[i].classList.remove("submenu--visible");
104                }
105            }
106
107            //Adds .submenu--shown to next sibling with class .submenu., then adds .submenu--visible for animation purposes, then triggers submenu 
107container
108
109            var nextSibling = event.target.closest("div").nextElementSibling;
110            var subMenuContainer = document.querySelector('#submenu-container');
111
112            if (nextSibling != null && nextSibling.classList.contains("submenu")) {
113                nextSibling.classList.add("submenu--shown");
114
115                // This used to be wrapped in setTimeout, but it triggered some kind of rendering problem in Safari. https://github.com/DATAMlNER/www/issues/38
116                nextSibling.classList.add("submenu--visible");
117
118                subMenuContainer.classList.remove("submenu-container-closed");
119                subMenuContainer.classList.add("submenu-container-open");
120            } else {
121                subMenuContainer.classList.add("submenu-container-closed");
122                subMenuContainer.classList.remove("submenu-container-open");
123            }
124
125            //Changing line color of buttons
126
127            // const integrationButton = document.querySelector("#nav-integrations-button");
128            const missionButton = document.querySelector("#nav-mission-button");
129            const productButton = document.querySelector("#nav-product-button");
130            const companyButton = document.querySelector("#nav-company-button");
131            const contactButton = document.querySelector("#nav-contact-button");
132            const newsroomButton = document.querySelector("#nav-newsroom-button");
133
134            if (event.target == missionButton) {
135                document.querySelector("#submenu-container").style.borderBottom = "solid 3px var(--brand-rose-plus-color)";
136            }
137
138                // if (event.target == integrationButton) {
139                //     document.querySelector("#submenu-container").style.borderBottom ="solid 3px var(--brand-rose-plus-color)";
140            // }
141
142            else if (event.target == productButton) {
143                if (location.pathname == "/product/" || location.pathname == "/product/screening-auto-resolver/") {
144                    document.querySelector("#submenu-container").style.borderBottom = "solid 3px var(--brand-tangerine-plus-color)";
145
146                } else if (location.pathname == "/product/screening/") {
147                    document.querySelector("#submenu-container").style.borderBottom = "solid 3px var(--brand-tangerine-plus-color)";
148
149                } else if (location.pathname == "/product/transaction-monitoring/") {
150                    document.querySelector("#submenu-container").style.borderBottom = "solid 3px var(--brand-pear-plus-color)";
151
152                } else if (location.pathname == "/product/kyc-risk-scoring/") {
153                    document.querySelector("#submenu-container").style.borderBottom = "solid 3px var(--brand-capri-plus-color)";
154
155                } else if (location.pathname == "/product/salv-bridge/" || location.pathname == "/salv-bridge-estonia/") {
156                    document.querySelector("#submenu-container").style.borderBottom = "solid 3px var(--brand-rose-plus-color)";
157
158                } else {
159                    document.querySelector("#submenu-container").style.borderBottom = "solid 3px var(--brand-tangerine-plus-color)";
160                }
161            } else if (event.target == companyButton) {
162                document.querySelector("#submenu-container").style.borderBottom = "solid 3px var(--brand-pear-plus-color)";
163            } else if (event.target == newsroomButton) {
164                document.querySelector("#submenu-container").style.borderBottom = "solid 3px var(--brand-capri-plus-color)";
165            } else if (event.target == contactButton) {
166
167                document.querySelector("#submenu-container").style.borderBottom = "solid 3px var(--brand-slate-color)";
168            } else {
169                document.querySelector("#submenu-container").style.borderBottom = "";
170            }
171
172        });
173    }
174
175    //*** PRODUCT SUBNAV HOVER COLOURS ***
176
177    const productSubnav = document.querySelector("#product-subnav");
178    const productPageLink = document.querySelector(".nav-button--active");
179
180    if (location.pathname == "/product/" || location.pathname == "/product/screening-auto-resolver/") {
181        productSubnav.classList.add("platform-active");
182        productPageLink.classList.add("platform-active");
183
184    } else if (location.pathname == "/product/screening/") {
185        productSubnav.classList.add("screening-active");
186        productPageLink.classList.add("screening-active");
187
188    } else if (location.pathname == "/product/transaction-monitoring/") {
189        productSubnav.classList.add("monitoring-active");
190        productPageLink.classList.add("monitoring-active");
191
192    } else if (location.pathname == "/product/kyc-risk-scoring/") {
193        productSubnav.classList.add("kyc-active");
194        productPageLink.classList.add("kyc-active");
195
196    } else if (location.pathname.includes("/product/salv-bridge/")) {
197        productSubnav.classList.add("bridge-active");
198        productPageLink.classList.add("bridge-active");
199    } else if (location.pathname == "/salv-bridge-estonia/") {
200        productSubnav.classList.add("bridge-active");
201        productPageLink.firstChild.style.color = "var(--brand-rose-color)";
202    } else {
203        productSubnav.classList.add("tangerine-active");
204    }
205
206    //*** MOUSE LEAVING NAVBAR ***
207
208    document.querySelector('.nav-wrapper').addEventListener("mouseleave", function (event) {
209
210        // Closes submenu container
211        var subMenuContainer = document.querySelector('#submenu-container');
212
213        subMenuContainer.classList.add("submenu-container-closed");
214        subMenuContainer.classList.remove("submenu-container-open");
215
216        // Checks if any of the submenus should be left open and visible and implements
217        var otherSubMenus = document.querySelectorAll(".submenu");
218
219        function submenuVisible(i) {
220
221            i.classList.add("submenu--shown");
222
223            setTimeout(function () {
224                i.classList.add("submenu--visible");
225            }, 110);
226        }
227
228        for (var i = 0; i < otherSubMenus.length; i++) {
229
230            if (otherSubMenus[i].classList.contains("submenu--active--page")) {
231
232                submenuVisible(otherSubMenus[i]);
233
234                subMenuContainer.classList.remove("submenu-container-closed");
235                subMenuContainer.classList.add("submenu-container-open");
236            } else {
237                otherSubMenus[i].classList.remove("submenu--shown");
238                otherSubMenus[i].classList.remove("submenu--visible");
239
240            }
241        }
242        // Changes line color back to default
243        document.querySelector("#submenu-container").style.borderBottom = "";
244    });
245
246    //*** BOOK A DEMO MODAL ***
247
248    var modal = document.getElementById("demoModal");
249
250    var btn = document.querySelectorAll(".contactbutton");
251
252    var closeBtns = document.getElementsByClassName("close")
253
254    // When the user clicks on the button, open the modal
255    for (i = 0; i < btn.length; i++) {
256        btn[i].onclick = function () {
257            modal.style.display = "block";
258        }
259    }
260
261    // When the user clicks on <span> (x), close the modal
262    Array.from(closeBtns).forEach(
263        closeBtn => {
264            closeBtn.onclick = function () {
265                modal.style.display = "none";
266            }
267        }
268    )
269
270    // When the user clicks anywhere outside of the modal, close it
271    window.onclick = function (event) {
272        if (event.target == modal) {
273            modal.style.display = "none";
274        }
275    }
276
277
278    //*** TOGGLE Q&A ACCORDION ***
279
280    // select all questions
281    const questionContainers = document.querySelectorAll(".js-toggle");
282
283    // loop throught questions and add event listeners for clicking event
284    // to execute opening and closing individual questions
285    for (let i = 0; i < questionContainers.length; i++) {
286        questionContainers[i].addEventListener("click", openCurrentAccordion);
287    }
288
289    // open clicked question and auto collapse the previous open question
290    function openCurrentAccordion() {
291        for (let i = 0; i < questionContainers.length; i++) {
292            // get qa-box and answer elements
293            const qaBox = questionContainers[i].parentElement.parentElement;
294            const answer = questionContainers[i].parentElement.nextElementSibling;
295
296            // auto collapse previous question, set hight style of open question
297            if (this === questionContainers[i] && !qaBox.classList.contains('open')) {
298                qaBox.classList.add('open');
299                answer.style.height = 'auto';
300
301                // set aria-expanded true on opened question
302                questionContainers[i].setAttribute("aria-expanded", "true");
303
304                const newHeight = answer.clientHeight + 'px';
305
306                answer.style.height = '0px';
307
308                setTimeout(function () {
309                    answer.style.height = newHeight;
310                }, 0);
311
312            } else {
313                // remove styling from closed question
314                qaBox.classList.remove('open');
315                answer.style.height = '0px';
316                // set aria-expanded false on closed questions
317                questionContainers[i].setAttribute("aria-expanded", "false");
318            }
319        }
320    }
321
322
323    //*** HOME AND PRODUCT PAGE TABS ***
324
325
326    var tabToggle = document.querySelectorAll('.tab-toggle');
327
328    for (i = 0; i < tabToggle.length; i++) {
329
330
331        tabToggle[i].addEventListener('click', function (event) {
332
333            event.preventDefault();
334
335            //change button appearance
336
337            for (i = 0; i < tabToggle.length; i++) {
338                tabToggle[i].classList.remove("active");
339            }
340            this.classList.add("active");
341
342            //swap content
343
344            var fintechContent = document.querySelectorAll('.fintech');
345            var enterpriseContent = document.querySelectorAll('.enterprise');
346
347            for (i = 0; i < fintechContent.length; i++) {
348
349                if (document.getElementById('fintech-tab').classList.contains('active')) {
350                    fintechContent[i].style.display = "inherit";
351                } else {
352                    fintechContent[i].style.display = "none";
353                }
354
355                if (document.getElementById('enterprise-tab').classList.contains('active')) {
356                    enterpriseContent[i].style.display = "inherit";
357                } else {
358                    enterpriseContent[i].style.display = "none";
359                }
360            }
361        });
362
363    }
364
365    //*** MAILCHIMP FORM - REMOVE INPUT WHEN SUBMITTED ***
366    // const inputWrapper = document.querySelector(".input-wrapper");
367    // const successMessage = document.querySelector("#mce-success-response");
368    // const errorMessages = document.querySelectorAll(".response-error");
369    // const errorIcon = document.querySelector(".input__error-icon");
370    // const successIcon = document.querySelector(".input__success-icon");
371    // const signUpForm = document.querySelector("#mc-embedded-subscribe-form");
372    // const footerSubtitle = document.querySelector(".nav-footer--subtitle");
373    // const formConsentText = document.querySelector(".nav-footer--newsletter-consent");
374
375    // // hide all error messages on form submission - so that only one error message is shown at a time (error messages are made visible by mailchimp)
376    // signUpForm.addEventListener("submit", () => {
377    //     [...errorMessages].filter(error =>
377 !error.classList.contains('mce_inline_error')).forEach(error => error.style.display = "none");
378    // });
379
380    // // Options for the observer (which mutations to observe)
381    // const config = { attributes: true };
382
383    // // Callback function to execute when mutations are observed
384    // const successCallback = (mutationList, observer) => {
385    //     for (const mutation of mutationList) {
386    //         // when thereä's an attribute change on the success message
387    //         if (mutation.type === "attributes" && mutation.target.style.display !== "none") {
388    //             inputWrapper.classList.add("submitted");
389    //             footerSubtitle.classList.add("submitted");
390    //             formConsentText.classList.add("submitted");
391    //             successIcon.classList.add("show");
392    //             errorIcon.classList.remove("show");
393    //         }
394    //         observer.disconnect();
395    //     }
396    // };
397
398    // const errorCallback = (mutationList, observer) => {
399    //     for (const mutation of mutationList) {
400    //         if (mutation.type === "attributes" && mutation.target.style.display !== "none") {
401    //             errorIcon.classList.add("show");
402    //         }
403    //         // if all the error messages are not shown, remove the error icon
404    //         // only remove the error icon when non of the error messages are shown
405    //         else if (mutation.type === "attributes" && [...errorMessages].filter(error => error.style.display === "none").length == errorMessages.length ) {
406    //             errorIcon.classList.remove("show");
407    //         }
408    //     }
409    // }
410
411    // // Create an observer instance linked to the callback function
412    // const successObserver = new MutationObserver(successCallback);
413    // const errorObserver = new MutationObserver(errorCallback);
414
415    // // Start observing the target node for configured mutations
416    // successObserver.observe(successMessage, config);
417    // errorObserver.observe(errorMessages[0], config);
418    // errorObserver.observe(errorMessages[1], config);
419
420
421    //*** TESTIMONIALS CAROUSEL ***
422
423    let carouselPosition = 0;
424    const testimonials = document.querySelectorAll(".testimonial-item");
425    const testimonialsContainer = document.querySelector(".testimonials-carousel");
426    const totalTestimonials = testimonials.length;
427    const previousButton = document.getElementById("testimonials-carousel__button--previous");
428    const nextButton = document.getElementById("testimonials-carousel__button--next");
429    const dots = document.querySelectorAll(".testimonials-carousel__dots-wrapper__dot");
430    const dotsArray = [...dots];
431
432    // add event listeners to nav buttons
433    previousButton?.addEventListener("click", function () {
434        moveToPreviousTestimonial();
435    });
436
437    nextButton?.addEventListener("click", function () {
438        moveToNextTestimonial();
439    });
440
441
442    // style line color
443    const testimonialDecorationLine = document.querySelector(".home__testimonials");
444
445    function setLineColor() {
446        if (carouselPosition == 0) {
447            testimonialDecorationLine.classList.remove("line--rose", "line--tangerine", "line--pear", "line--capri");
448            testimonialDecorationLine.classList.add("line--rose");
449        } else if (carouselPosition == 1) {
450            testimonialDecorationLine.classList.remove("line--rose", "line--tangerine", "line--pear", "line--capri");
451            testimonialDecorationLine.classList.add("line--tangerine");
452        } else if (carouselPosition == 2) {
453            testimonialDecorationLine.classList.remove("line--rose", "line--tangerine", "line--pear", "line--capri");
454            testimonialDecorationLine.classList.add("line--pear");
455        } else if (carouselPosition == 3) {
456            testimonialDecorationLine.classList.remove("line--rose", "line--tangerine", "line--pear", "line--capri");
457            testimonialDecorationLine.classList.add("line--capri");
458        }
459    }
460
461    if (testimonialDecorationLine) setLineColor();
462
463    // update carousel position
464    function updateCarouselPosition() {
465        for (let testimonial of testimonials) {
466            testimonial.classList.remove("testimonial--visible");
467            testimonial.setAttribute('style', 'transform:translateX(-' + 150 * carouselPosition + '%)');
468        }
469        testimonials[carouselPosition].classList.add("testimonial--visible");
470        // indicate visible testimonial on dots
471        dotsArray.forEach(dot => dot.classList.remove("testimonials-carousel__dots-wrapper__dot--active"));
472        dotsArray[carouselPosition].classList.add("testimonials-carousel__dots-wrapper__dot--active");
473    }
474
475    // move carousel
476    function moveToPreviousTestimonial() {
477        if (carouselPosition == 0) {
478            carouselPosition = totalTestimonials - 1;
479        } else {
480            carouselPosition--;
481        }
482
483        updateCarouselPosition();
484        setLineColor();
485    }
486
487    function moveToNextTestimonial() {
488        if (carouselPosition == totalTestimonials - 1) {
489            carouselPosition = 0;
490        } else {
491            carouselPosition++;
492        }
493
494        updateCarouselPosition();
495        setLineColor();
496    }
497
498    // automatically move carousel
499    let autoscroll;
500
501
502    function autoPlayOnDesktop() {
503        if (location.pathname == "/") {
504            if (window.innerWidth > 768) {
505                autoscroll = setInterval(function () {
506                    moveToNextTestimonial()
507                }, 9000);
508
509                // stop autoplay on hover
510                testimonialsContainer.onmouseover = function () {
511                    autoscroll = clearInterval(autoscroll);
512                };
513
514                testimonialsContainer.onmouseout = function () {
515                    clearInterval(autoscroll);
516                    autoscroll = setInterval(function () {
517                        moveToNextTestimonial()
518                    }, 9000);
519                };
520            }
521        }
522
523    }
524
525    // call function
526    // autoPlayOnDesktop();
527
528
529    // hide dots on product pages
530    if (location.pathname != "/") {
531        document.querySelector(".testimonials-carousel__dots-wrapper")?.classList.add("hide");
532    }
533
534    // set which testimonial to show on each product page
535    function displayFixedCarousel() {
536        if (location.pathname == "/product/screening/") {
537            carouselPosition = 1;
538            testimonialDecorationLine.classList.remove("line--rose", "line--tangerine", "line--pear", "line--capri");
539            testimonialDecorationLine.classList.add("line--tangerine");
540            for (let testimonial of testimonials) {
541                testimonial.classList.remove("testimonial--visible");
542                testimonial.setAttribute('style', 'transform:translateX(-' + 150 * carouselPosition + '%)');
543            }
544            testimonials[carouselPosition].classList.add("testimonial--visible");
545            document.querySelector(".testimonials-carousel__dots-wrapper").style.display == "none";
546        } else if (location.pathname == "/product/transaction-monitoring/") {
547            carouselPosition = 2;
548            testimonialDecorationLine.classList.remove("line--rose", "line--tangerine", "line--pear", "line--capri");
549            testimonialDecorationLine.classList.add("line--pear");
550            for (let testimonial of testimonials) {
551                testimonial.classList.remove("testimonial--visible");
552                testimonial.setAttribute('style', 'transform:translateX(-' + 150 * carouselPosition + '%)');
553            }
554            testimonials[carouselPosition].classList.add("testimonial--visible");
555        } else if (location.pathname == "/product/kyc-risk-scoring/") {
556            carouselPosition = 3;
557            testimonialDecorationLine.classList.remove("line--rose", "line--tangerine", "line--pear", "line--capri");
558            testimonialDecorationLine.classList.add("line--capri");
559            for (let testimonial of testimonials) {
560                testimonial.classList.remove("testimonial--visible");
561                testimonial.setAttribute('style', 'transform:translateX(-' + 150 * carouselPosition + '%)');
562            }
563            testimonials[carouselPosition].classList.add("testimonial--visible");
564        } else if (location.pathname == "/product/salv-bridge/") {
565            carouselPosition = 0;
566            testimonialDecorationLine.classList.remove("line--rose", "line--tangerine", "line--pear", "line--capri");
567            testimonialDecorationLine.classList.add("line--rose");
568            for (let testimonial of testimonials) {
569                testimonial.classList.remove("testimonial--visible");
570                testimonial.setAttribute('style', 'transform:translateX(-' + 150 * carouselPosition + '%)');
571            }
572            testimonials[carouselPosition].classList.add("testimonial--visible");
573        }
574    }
575
576    //run function to display carousel as static on product pages
577    if (testimonials && testimonialDecorationLine) displayFixedCarousel();
578
579    // make carousel draggable on mobile
580    let touchPosition;
581
582    function handleTouchStart(e) {
583        const touchDown = e.touches[0].clientX;
584        touchPosition = touchDown;
585    }
586
587    const handleTouchMove = (e) => {
588        const touchDown = touchPosition;
589
590        if (touchDown === null) {
591            return;
592        }
593        ;
594
595        const currentTouch = e.touches[0].clientX;
596        const diff = touchDown - currentTouch;
597
598        if (diff > 10) {
599            moveToNextTestimonial();
600        }
601        ;
602
603        if (diff < -10) {
604            moveToPreviousTestimonial();
605        }
606        ;
607
608        touchPosition = null;
609    }
610
611    // only allow swiping on landing page
612    // if (location.pathname == "/") {
613    //     testimonialsContainer.addEventListener('touchstart', handleTouchStart);
614    //     testimonialsContainer.addEventListener('touchmove', handleTouchMove);
615    // }
616
617    // //*** BLOG - TABLE OF CONTENTS ***
618    const sidePanel = document.querySelector(".post__side-panel");
619    const tocWrapper = document.querySelector(".toc-wrapper");
620    const firstParagraph = document.querySelector("#post__wrapper > div > p");
621    const postShareContainer = document.querySelector(".post__share-container");
622    const postShareContainerInnerWrapper = document.querySelector(".post__share-container__inner-wrapper");
623
624    // if the blog post starts with an image, add margin-top ot the toc wrapper to keep alignment
625    if (firstParagraph?.getElementsByTagName('img').length > 0) {
626        sidePanel.style.setProperty("margin-top", `32px`);
627    }
628
629    // if there's table of contents, (there were h2s to generate from), add Contents title
630    const tocTitleWrapper = document.querySelector(".toc-title-wrapper");
631    const tableOfContents = document.querySelector("#table-of-contents");
632
633    if (tableOfContents) {
634        const tocTitle = document.createElement("p");
635        var tocTitleText = document.createTextNode("Table of Contents");
636        tocTitle.appendChild(tocTitleText);
637        tocTitle.classList.add("toc-title");
638        tocTitleWrapper.appendChild(tocTitle).focus();
639        tocTitleWrapper.classList.add("in");
640
641    } else {
642        // remove table of contents if it's empty
643        tocWrapper?.remove();
644        sidePanel?.style.setProperty("margin-top", `0px`);
645        postShareContainer?.classList.add("no-border");
646        if (firstParagraph?.getElementsByTagName('img').length <= 0) {
647            postShareContainerInnerWrapper?.classList.add("no-margin");
648        }
649    }
650
651    // Get poosition of last line
652    function getHeightOfContent(containerElement) {
653        const linesInBlogs = [...document.querySelectorAll(".blog #postContent hr")];
654        if (linesInBlogs.length > 0) {
655            return linesInBlogs[linesInBlogs.length - 1].offsetTop - containerElement.offsetTop;
656        }
657
658        // Handle case where no lines are found
659        return null;
660    }
661
662    // Highlight active section
663    const tocHeaders = document.querySelector("#post__wrapper > div")?.querySelectorAll("h2, .h3-in-toc");
664
665    // Add H3 elements with class "h3-in-toc" to toc
666    if (window.location.pathname.toString().includes("/blog")) {
667        const h3sInBlogPost = [...document.querySelectorAll(".h3-in-toc")].reverse();
668
669        h3sInBlogPost.forEach((h3) => {
670            const li = document.createElement("li");
671            li.classList.add("toc-h3");
672            const linkInLi = document.createElement("a");
673            let linkText = document.createTextNode(h3.textContent);
674            linkInLi.appendChild(linkText);
675            linkInLi.href = "#" + h3.id;
676            li.appendChild(linkInLi);
677
678            // Calculate the closest preceding H2 for each H3
679            const closestH2ID = findClosestH2ID(h3);
680            const H2inToc = document.querySelector(`#table-of-contents a[href='#${closestH2ID}']`).parentElement;
681
682            // Insert the li element after the appropriate H2 element
683            H2inToc.after(li);
684        });
685
686        // Find the closest H2 ID
687        function findClosestH2ID(h3) {
688            let previous = h3.previousElementSibling;
689            while (previous) {
690                if (previous.tagName === "H2") {
691                    return previous.id;
692                }
693                previous = previous.previousElementSibling;
694            }
695            return null; // If no preceding H2 is found
696        }
697    }
698
699    // When all elements are added to the blog post
700    const postSideBar = document.querySelector(".blog #postSideBar");
701    const postContent = document.querySelector(".blog #postContent");
702    let postContentHeight = 0;
703
704    if (postContent) {
705        const postContentObserverCallback = () => {
706            const newPostContentHeight = postContent.getBoundingClientRect().height;
707
708            let postSideBarStyle = sidePanel.currentStyle || window.getComputedStyle(sidePanel);
709            let postSideBarMarginBottom = +postSideBarStyle.marginBottom.slice(0, -2)
710
711            if (newPostContentHeight !== postContentHeight) {
712                const heightOfContent = getHeightOfContent(postContent);
713                if (!heightOfContent) return;
714
715                postSideBar.style.height = heightOfContent + postShareContainer.getBoundingClientRect().height + postSideBarMarginBottom;
716                postContentHeight = newPostContentHeight;
717            }
718        };
719
720        const postContentObserver = new MutationObserver(postContentObserverCallback);
721        postContentObserver.observe(postContent, {attributes: false, childList: true, subtree: true});
722        postContentObserverCallback()
723    }
724
725
726    window.onscroll = () => {
727        if (!tocHeaders) return;
728
729        const scrollPos = document.body.scrollTop;
730        const navWrapperBounding = document.querySelector(".nav-wrapper").getBoundingClientRect();
731        const scrollOffset = navWrapperBounding.bottom - navWrapperBounding.top + 150;
732
733        // highlight currently visible header in table of contents
734        // remove highlight from all elements
735        document.querySelector('.highlighted')?.classList.remove('highlighted');
736
737        const headerInView = [...tocHeaders].reverse().find((header) => header.offsetTop <= (scrollPos + scrollOffset));
738        if (!headerInView) return;
739
740        const highlightedTocLink = document.querySelector(`.toc a[href="#${headerInView.id}"]`);
741        highlightedTocLink.classList.add('highlighted');
742    }
743
744
745    //*** HOME PRODUCT CARDS - make whole div clickable ***
746    const screeningCard = document.querySelector("#screening-solutions-card");
747    const monitoringCard = document.querySelector("#monitoring-solutions-card");
748    const kycCard = document.querySelector("#kyc-solutions-card");
749    const intelligenceSharingCard = document.querySelector("#intelligence-sharing-solutions-card");
750
751    const solutionsCards = document.querySelectorAll(".solutions-card");
752    const solutionsCardsArray = [...solutionsCards];
753
754    // On wide screen: make whole card clickable
755    if ((window.location.pathname == "/" || window.location.pathname == "/product/")) { //Make whole divs clickable
756        screeningCard.addEventListener("click", openProductPage);
757        monitoringCard.addEventListener("click", openProductPage);
758        kycCard.addEventListener("click", openProductPage);
759        intelligenceSharingCard.addEventListener("click", openProductPage);
760    }
761
762    // expand card on click and collapse previously open card
763    // function openCard(event) {
764    //     for (let i = 0; i < solutionsCardsArray.length; i++) {
765    //         if (this === solutionsCardsArray[i]) {
766    //             solutionsCardsArray[i].classList.toggle("solutions-card--collapsed");
767    //             solutionsCardsArray[i].classList.toggle("solutions-card--expanded");
768    //         } else if (this === solutionsCardsArray[i].querySelector("A")) {
769    //             event.stopPropagation();
770    //         } else {
771    //             solutionsCardsArray[i].classList.add("solutions-card--collapsed");
772    //             solutionsCardsArray[i].classList.remove("solutions-card--expanded");
773    //         }
774    //     }
775    // }
776
777    // redirect to target page
778    function openProductPage(e) {
779        window.open(e.currentTarget.querySelector("A").href, "_self")
780    }
781
782
783    //*** PRODUCT CARDS on product pages ***
784    const smallSolutionsCards = [...document.querySelectorAll(".solutions-card--small")];
785    smallSolutionsCards.forEach(card => card.addEventListener("click", openProductPage));
786
787
788    //*** PRODUCT BENEFITS AND FEATURES READ MORE BUTTON TOGGLE ***
789    //*** Bridge page: INDICATE EXPANDED USE CASE CARD ***
790
791    const readMoreButtons = document.querySelectorAll(".product__benefits-and-features__read-more-button");
792    const readMoreButtonsArray = [...readMoreButtons];
793
794    function openReadMoreButtonAccordion(button) {
795        if (button) {
796            button.parentElement.parentElement.previousElementSibling.classList.toggle("read-more--visible"); // hidden text in accordion
797            button.parentElement.parentElement.parentElement.classList.toggle("open"); // use case card
798            button.parentElement.parentElement.parentElement.parentElement.parentElement.classList.toggle(`open-${button.parentElement.parentElement.parentElement.dataset.order}`); // use case wrapper to indicate which one is open
799            button.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.classList.toggle(`open-${button.parentElement.parentElement.parentElement.dataset.order}`); // whole use case section to indicate which one is open
800            button.setAttribute("aria-expanded", button.getAttribute('aria-expanded') === 'true' ? 'false' : 'true'); // read more button
801            if (button.parentElement.parentElement.previousElementSibling.classList.contains("read-more--visible")) {
802                button.innerHTML = "Show less" + '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M360.5 217.5l-152 143.1C203.9 365.8 197.9 368 192 368s-11.88-2.188-16.5-6.562L23.5 217.5C13.87 208.3 13.47 193.1 22.56 183.5C31.69 173.8 46.94 173.5 56.5 182.6L192 310.9l135.5-128.4c9.562-9.094 24.75-8.75 33.94 .9375C370.5 193.1 370.1 208.3 360.5 217.5z"/></svg>';
803            } else {
804                button.innerHTML = "Read more" + '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M360.5 217.5l-152 143.1C203.9 365.8 197.9 368 192 368s-11.88-2.188-16.5-6.562L23.5 217.5C13.87 208.3 13.47 193.1 22.56 183.5C31.69 173.8 46.94 173.5 56.5 182.6L192 310.9l135.5-128.4c9.562-9.094 24.75-8.75 33.94 .9375C370.5 193.1 370.1 208.3 360.5 217.5z"/></svg>';
805            }
806            ;
807
808        }
809    }
810
811    readMoreButtonsArray.forEach(button => button?.addEventListener("click", () => openReadMoreButtonAccordion(button)));
812
813
814    //*** ABOVE NAVIAGTION BAR BANNER - CLOSE ***
815    const bannerCloseButton = document.querySelector("#top-banner__close-button");
816    const topBanner = document.querySelector(".top-banner-wrapper");
817    const hamburgerButtonWrapper = document.querySelector(".hamburger-button-wrapper");
818    const homeHeroLarger = document.querySelector("#home-hero");
819
820    // Get session storage (it's null at this point)
821    let topBannerStatus = sessionStorage.getItem("top-banner");
822
823    // If there's no saves session storage or the the abnner ahsn't been dismissed -> show the banner
824    if (sessionStorage.getItem("top-banner") === null || topBannerStatus !== "dismissed") {
825        topBanner?.classList.remove("hide");
826        // for hero layout adjustments
827        hamburgerButtonWrapper?.classList.add("with-top-banner");
828        homeHeroLarger?.classList.add("with-top-banner");
829
830        // for animation
831        setTimeout(() => {
832            topBanner?.classList.add("opaque");
833        }, 0)
834    }
835
836    // dismiss banner
837    bannerCloseButton?.addEventListener("click", () => {
838        topBanner.classList.add("hide");
839        hamburgerButtonWrapper.classList.remove("with-top-banner");
840        homeHeroLarger?.classList.remove("with-top-banner");
841        sessionStorage.setItem("top-banner", "dismissed");
842    })
843
844
845    //*** LOGOS CAROUSEL: dynamically set variable through javascript for the carousel width, set aniamtion though JS so that it works on Safari as well ***
846    const slider = document.querySelector(".logos-carousel");
847    const sliderTrack = document.querySelector(".logos-carousel__slider-track");
848    slider?.style.setProperty("--slidesWidthSum", `${slider.scrollWidth / -2}px`);
849    if (sliderTrack) {
850        sliderTrack.style.animation = "scrollLogos var(--scrollSpeed) linear infinite";
851    }
852
853    //*** IMAGES CAROUSEL: dynamically set variable through javascript for the carousel width, set animation though JS so that it works on Safari as well ***
854    const imagesSlider = document.querySelector(".home__key-benefits__carousel");
855    const imagesSliderTrack = document.querySelector(".home__key-benefits__carousel__slider-track");
856
857    if (window.innerWidth < 768) {
858        imagesSlider?.style.setProperty("--imagesWidthSum", `${imagesSlider.scrollWidth / -2}px`);
859        if (imagesSliderTrack) {
860            imagesSliderTrack.style.animation = "scrollImages var(--scrollSpeed) linear infinite";
861        }
862    }
863
864
865    //*** CLIENTS TESTIMONIALS HORIZONTAL ACCORDION ***
866    const testimonalCardsContainer = document.querySelector(".clients-testimonials--horizontal__inner");
867    const testimonialCards = [...document.querySelectorAll(".clients-testimonials--horizontal__container")];
868    let testimonialPosition = 0; // 0-3
869    const testimonialCardDots = [...document.querySelectorAll(".clients-testimonials--horizontal__dot")];
870
871    // mobile - add expanded styling
872    if (window.innerWidth < 768) {
873        testimonialCards.forEach(card => card.classList.add("expanded"));
874    }
875
876    // desktop - remove shaded styling
877    if (window.innerWidth > 768) {
878        testimonialCards.forEach(card => card.classList.remove("shaded"));
879    }
880
881    testimonialCards?.forEach((testimonialCard) => {
882        testimonialCard.addEventListener('click', (e) => {
883            if (window.innerWidth > 768) {
884                testimonialCards.find((card) => card.classList.contains("expanded"))?.classList.remove("expanded");
885            }
886
887            testimonialCards?.filter(card =>
887 card.classList.contains("current")).forEach(testimonial => testimonial.classList.remove("current"));
888
889            e.currentTarget.classList.add("expanded");
890
891            // add a class that indicates which card is expanded (used to style arrow direction)
892            testimonalCardsContainer.classList.replace(
893                testimonalCardsContainer.className.match(/expanded-.*?-testimonial/)[0],
894                `expanded-${[...e.currentTarget.className.matchAll(/\w*-testimonial/g)][1]}`
895            );
896
897            // mobile - when clicked on an adjacent card - move to that card, and add shaded styling
898            if (window.innerWidth < 768) {
899                testimonalCardsContainer.style.transform = `translateX(-${[...testimonalCardsContainer.children].indexOf(e.currentTarget) * 25}%)`;
900
901                testimonialCards?.filter(card => card.classList.contains("shaded")).forEach(testimonial => testimonial.classList.remove("shaded"));
902                e.currentTarget.nextElementSibling?.classList.add("shaded");
903                e.currentTarget.previousElementSibling?.classList.add("shaded");
904
905                testimonialPosition = [...testimonalCardsContainer.children].indexOf(testimonialCards.find((card) => card === e.currentTarget));
906
907                updateTestimonialDots();
908            }
909        });
910    });
911
912    // MOBILE DRAGGABILITY
913    const totalTestimonialCards = testimonialCards.length;
914
915    // swipe testimonial cards to next one
916    function swipeToNextTestimonialCard() {
917        if (testimonialPosition == totalTestimonialCards - 1) {
918            return;
919        } else {
920            testimonalCardsContainer.style.transform = `translateX(-${(testimonialPosition + 1) * 25}%)`;
921
922            testimonialCards?.filter(card => card.classList.contains("shaded")).forEach(testimonial => testimonial.classList.remove("shaded"));
923            testimonialCards[testimonialPosition + 1]?.previousElementSibling?.classList.add("shaded");
924            testimonialCards[testimonialPosition + 1]?.nextElementSibling?.classList.add("shaded");
925
926            testimonialPosition++;
927        }
928    }
929
930    // swipe testimonial cards to previous one
931    function swipeToPreviousTestimonialCard() {
932        if (testimonialPosition == 0) {
933            return;
934        } else {
935            testimonalCardsContainer.style.transform = `translateX(-${(testimonialPosition - 1) * 25}%)`;
936
937            testimonialCards?.filter(card => card.classList.contains("shaded")).forEach(testimonial => testimonial.classList.remove("shaded"));
938            testimonialCards[testimonialPosition - 1]?.previousElementSibling?.classList.add("shaded");
939            testimonialCards[testimonialPosition - 1]?.nextElementSibling?.classList.add("shaded");
940
941            testimonialPosition--;
942        }
943    }
944
945    // make carousel draggable on mobile
946    let touchTestimonialPosition;
947
948    function handleTestimonialTouchStart(e) {
949        const touchDownOnTestimonialsContainer = e.touches[0].clientX;
950        touchTestimonialPosition = touchDownOnTestimonialsContainer;
951    }
952
953    const handleTestimonialTouchMove = (e) => {
954        const touchDownOnTestimonialsContainer = touchTestimonialPosition;
955
956        if (touchDownOnTestimonialsContainer === null) {
957            return;
958        }
959        ;
960
961        const currentTestimonialTouch = e.touches[0].clientX;
962        const difference = touchDownOnTestimonialsContainer - currentTestimonialTouch;
963
964        if (difference > 10) {
965            swipeToNextTestimonialCard();
966        }
967        ;
968
969        if (difference < -10) {
970            swipeToPreviousTestimonialCard();
971        }
972        ;
973
974        updateTestimonialDots();
975
976        touchTestimonialPosition = null;
977    }
978
979    function updateTestimonialDots() {
980        testimonialCardDots.forEach(dot => dot.classList.remove("clients-testimonials--horizontal__active-dot"));
981        testimonialCardDots[testimonialPosition].classList.add("clients-testimonials--horizontal__active-dot");
982    }
983
984    testimonalCardsContainer?.addEventListener('touchstart', handleTestimonialTouchStart);
985    testimonalCardsContainer?.addEventListener('touchmove', handleTestimonialTouchMove);
986
987
988    //*** Landing page: COLOUR KEY MESSAGE TITLES ON HOVER ***
989    const colouredSpans = document.querySelectorAll('[data-coloured-text]');
990
991    colouredSpans?.forEach(span => {
992        span.addEventListener("mouseover", () => {
993            span.parentElement.classList.add(span.dataset.colouredText);
994        });
995    })
996
997    colouredSpans?.forEach(span => {
998        span.addEventListener("mouseleave", () => {
999            span.parentElement.classList.remove(span.dataset.colouredText);
1000        });
1001    })
1002
1003
1004    //*** VIEW IMAGE IN LARGE IN BLOG POST MODAL ***
1005
1006    const imageModal = document.getElementById("imageModal");
1007
1008    const imagesToEnlarge = [...document.querySelectorAll(".image-to-enlarge img")];
1009
1010    const imageInModal = document.querySelector(".image-in-modal");
1011
1012    const imageModalCloseButton = document.querySelector(".image-modal-close-button");
1013
1014    // When the user clicks on an image that has "image-to-enlarge" class, open the modal and in it the image
1015    if (window.innerWidth > 768) {
1016        imagesToEnlarge.forEach(image => image.addEventListener("click", () => {
1017            imageModal.classList.add("open");
1018            imageInModal.src = image.getAttribute("src");
1019        }));
1020    }
1021
1022    // When the user clicks on <span> (x), close the modal
1023    imageModalCloseButton?.addEventListener("click", () => imageModal.classList.remove("open"));
1024
1025    // When the user clicks anywhere outside of the modal, close it
1026    imageModal?.addEventListener("click", () => imageModal.classList.remove("open"))
1027
1028
1029    //*** BLOG - ADD GRADIENT ON BOTH SIDES OF THE FILER CONTAINER ***
1030    const filtersContainer = document.querySelector(".blog__tags-filter-container");
1031    const firstFilter = document.querySelector("#first-tag");
1032
1033    // Add gradietn to beginning of tag filters container when scrolled away - intersection observer
1034    const filtersCallback = (entries, observer) => {
1035        entries.forEach((entry) => {
1036            if (entry.isIntersecting) {
1037                filtersContainer.classList.remove("scrolled");
1038            } else {
1039                filtersContainer.classList.add("scrolled");
1040            }
1041        });
1042    };
1043
1044    const filtersOptions = {
1045        root: null,
1046        rootMargin: '0px',
1047        threshold: 0.5
1048    };
1049
1050    const scrollObserver = new IntersectionObserver(filtersCallback, filtersOptions);
1051    if (firstFilter) {
1052        scrollObserver.observe(firstFilter);
1053    }
1054
1055    // Scroll active tag into view after page load
1056    const activeTag = document.querySelector(".tag-filter.active");
1057
1058    if (window.innerWidth < 768 && activeTag) {
1059        activeTag.scrollIntoView({behavior: 'smooth', block: 'nearest', inline: 'center'});
1060    }
1061
1062
1063    //*** OPEN BRIDGE USE CASE ACCORDION WHEN LINKED TO IT FROM EXTERNAL SOURCE ***
1064    if (window.location.hash != '') {
1065        const hash = window.location.hash;
1066
1067        // Q&A accordions on product pages
1068        if (hash.includes("aml-bridge-question") || hash.includes("screening-question") || hash.includes("monitoring-question") || hash.includes("kyc-question")) {
1069            const questionToOpen = document.querySelector(`.qa-box ${hash}`);
1070            questionToOpen.parentElement.parentElement.classList.toggle("open");
1071            questionToOpen.parentElement.nextElementSibling.style.height = "auto";
1072            questionToOpen.setAttribute("aria-expanded", "true");
1073
1074            const newerHeight = questionToOpen.parentElement.nextElementSibling.clientHeight + 'px';
1075
1076            questionToOpen.parentElement.nextElementSibling.style.height = '0px';
1077
1078            setTimeout(function () {
1079                questionToOpen.parentElement.nextElementSibling.style.height = newerHeight;
1080            }, 0);
1081
1082        } else { // Bridge page use case cards
1083            const bridgeUseCaseToOpenButton = document.querySelector(`${hash} .product__benefits-and-features__read-more-button`);
1084            openReadMoreButtonAccordion(bridgeUseCaseToOpenButton);
1085            // add outline aniamtion
1086            bridgeUseCaseToOpenButton?.parentElement.parentElement.parentElement.classList.add("animate-border");
1087        }
1088    }
1089
1090
1091    //*** BRIDGE UI CAROUSEL ***
1092    const swipeCarousel = document.querySelector(".swipe-carousel");
1093    const swipeCarouselContainers = [...document.querySelectorAll(".swipe--content")];
1094    const swipeCarouselColumns = document.querySelector(".swipe-carousel__columns");
1095    const swipeCarouselDots = [...document.querySelectorAll(".swipe-carousel__navigation__dot")];
1096
1097    let swipeCarouselPosition = 0; //0-1, two options
1098    const totalSwipeCarouselContainers = swipeCarouselContainers.length;
1099
1100    // swipe Bridge UI container to next one
1101    function swipeToNextSwipeCarousel() {
1102        if (swipeCarouselPosition == totalSwipeCarouselContainers - 1) {
1103            return;
1104        } else {
1105            swipeCarouselColumns.style.transform = `translateX(-${(swi
1105peCarouselPosition + 1) * (100 / totalSwipeCarouselContainers)}%)`;
1106            swipeCarouselPosition++;
1107        }
1108
1109    }
1110
1111    // swipe Bridge UI container to previous one
1112    function swipeToPreviousSwipeCarousel() {
1113        if (swipeCarouselPosition == 0) {
1114            return;
1115        } else {
1116            swipeCarouselColumns.style.transform = `translateX(-${(swipeCarouselPosition - 1) * (100 / totalSwipeCarouselContainers)}%)`;
1117            swipeCarouselPosition--;
1118        }
1119    }
1120
1121    // update dots indicating which Bridge UI column is active/visible
1122    function updateSwipeCarouselDots() {
1123        swipeCarouselDots.forEach(dot => dot.classList.remove("active"));
1124        swipeCarouselDots[swipeCarouselPosition].classList.add("active");
1125    }
1126
1127    // make Bridge UI carousel draggable on mobile
1128    let touchSwipeCarouselPosition;
1129
1130    function handleSwipeCarouselTouchStart(e) {
1131        const touchDownOnSwipeCarouselContainer = e.touches[0].clientX;
1132        touchSwipeCarouselPosition = touchDownOnSwipeCarouselContainer;
1133    }
1134
1135    const handleSwipeCarouselTouchMove = (e) => {
1136        const touchDownOnSwipeCarouselContainer = touchSwipeCarouselPosition;
1137
1138        if (touchDownOnSwipeCarouselContainer === null) {
1139            return;
1140        }
1141        ;
1142
1143        const currentSwipeCarouselTouch = e.touches[0].clientX;
1144        const differences = touchDownOnSwipeCarouselContainer - currentSwipeCarouselTouch;
1145
1146        if (differences > 10) {
1147            swipeToNextSwipeCarousel();
1148        }
1149        ;
1150
1151        if (differences < -10) {
1152            swipeToPreviousSwipeCarousel();
1153        }
1154        ;
1155
1156        updateSwipeCarouselDots();
1157
1158        touchSwipeCarouselPosition = null;
1159
1160        swipeCarouselColumns.classList.replace(
1161            swipeCarouselColumns.className.match(/active-swipe-.*/)[0],
1162            `active-swipe-${swipeCarouselPosition}`
1163        );
1164    }
1165
1166    if (window.innerWidth < 768) {
1167        swipeCarousel?.addEventListener('touchstart', handleSwipeCarouselTouchStart);
1168        swipeCarousel?.addEventListener('touchmove', handleSwipeCarouselTouchMove);
1169    }
1170
1171
1172    // Redirect to link in clicked element
1173
1174    function navigateTolink(e) {
1175        window.open(e.currentTarget.querySelector("A").href, "_self");
1176    }
1177
1178    // Careers page job cards are rendered from the Personio feed after this handler
1179    // runs, so their whole-card click is bound by delegation in
1180    // _includes/personio-jobs.html instead.
1181
1182    // Blog page - Make full blog card clickable
1183
1184    const blogPostCard = [...document.querySelectorAll(".post-list")];
1185
1186    blogPostCard?.forEach(post => post.addEventListener("click", navigateTolink));
1187
1188});

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.