1// =========================== 2// UGMF â Main JS 3// =========================== 4 5// Mobile nav toggle 6(function() { 7 const toggle = document.getElementById('nav-toggle'); 8 const navList = document.getElementById('nav-list'); 9 if (!toggle || !navList) return; 10 11 function closeNav() { 12 navList.classList.remove('is-open'); 13 toggle.setAttribute('aria-expanded', 'false'); 14 document.documentElement.style.overflow = ''; 15 } 16 17 toggle.addEventListener('click', function() { 18 const isOpen = navList.classList.toggle('is-open'); 19 toggle.setAttribute('aria-expanded', isOpen); 20 document.documentElement.style.overflow = isOpen ? 'hidden' : ''; 21 }); 22 23 document.addEventListener('keydown', function(e) { 24 if (e.key === 'Escape' && navList.classList.contains('is-open')) { 25 closeNav(); 26 } 27 }); 28 29 // Close on link click 30 navList.querySelectorAll('.nav__link').forEach(function(link) { 31 link.addEventListener('click', function() { 32 closeNav(); 33 }); 34 }); 35})(); 36 37// Show ticker: the track holds two identical sets and slides by exactly half 38// its width, so the loop is seamless only if that half is a whole number of 39// pixels. The sets' natural width is fractional (1252.89px at desktop size), 40// and on a Retina screen the reset then snaps the text by a fraction of a 41// pixel, which reads as a jump. Round each set up to a whole pixel. 42(function() { 43 var sets = document.querySelectorAll('.hero__ticker-set'); 44 if (sets.length < 2) return; 45 function fit() { 46 sets.forEach(function(set) { set.style.width = ''; }); 47 var width = Math.ceil(sets[0].getBoundingClientRect().width); 48 sets.forEach(function(set) { set.style.width = width + 'px'; }); 49 } 50 fit(); 51 if (document.fonts && document.fonts.ready) document.fonts.ready.then(fit); 52 var resizeTimer; 53 window.addEventListener('resize', function() { 54 clearTimeout(resizeTimer); 55 resizeTimer = setTimeout(fit, 150); 56 }); 57})(); 58 59// Scroll reveal 60(function() { 61 var reveals = document.querySelectorAll('.reveal'); 62 if (!reveals.length) return; 63 64 if ('IntersectionObserver' in window) { 65 var observer = new IntersectionObserver(function(entries) { 66 entries.forEach(function(entry) { 67 if (entry.isIntersecting) { 68 entry.target.classList.add('is-visible'); 69 observer.unobserve(entry.target); 70 } 71 }); 72 }, { threshold: 0.1, rootMargin: '0px 0px -40px 0px' }); 73 74 reveals.forEach(function(el) { observer.observe(el); }); 75 } else { 76 reveals.forEach(function(el) { el.classList.add('is-visible'); }); 77 } 78 79 // Once a card has faded in, drop its stagger delay so hovers respond at once. 80 document.addEventListener('transitionend', function(e) { 81 var el = e.target; 82 if (e.propertyName === 'opacity' && el.classList && el.classList.contains('reveal') && el.classList.contains('is-visible')) { 83 el.classList.add('is-revealed'); 84 } 85 }); 86})();
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.