1/* Leadtree rebuild â shared chrome (header/footer) injection + interactions */ 2(function () { 3 // Keep preview/staging domains (*.netlify.app) out of search indexes. 4 // The production domain (lead-tree.de) stays fully indexable. 5 if (/(^|\.)netlify\.app$/i.test(location.hostname)) { 6 var m = document.createElement('meta'); 7 m.name = 'robots'; m.content = 'noindex, nofollow'; 8 (document.head || document.documentElement).appendChild(m); 9 } 10 var STR = { 11 de: { 12 social: 'Leadgenerierung', 13 stories: 'Erfolgsgeschichten', contact: 'Kontakt', 14 langCurrent: 'Deutsch', langOther: 'English (UK)', 15 nlHead: 'Abonniere unseren Newsletter', emailPh: 'Deine E-mail', subscribe: 'Jetzt Abonnieren', 16 faq: 'FAQ', imp: 'Impressum', data: 'Datenschutzerklärung', agb: 'AGB', blog: 'Blog', menu: 'Menü', 17 cookieTitle: 'Datenschutz-Einstellungen', 18 cookieText: 'Wir nutzen Cookies und Analyse-Tools (Google Analytics, Microsoft Clarity), um unsere Website zu verbessern. Diese werden nur mit deiner Zustimmung geladen.', 19 cookieMore: 'Mehr in unserer', 20 cookieAccept: 'Akzeptieren', cookieDecline: 'Ablehnen', cookieSettings: 'Cookie-Einstellungen' 21 }, 22 en: { 23 social: 'Lead generation', 24 stories: 'Success Stories', contact: 'Contact', 25 langCurrent: 'English (UK)', langOther: 'Deutsch', 26 nlHead: 'Subscribe to our newsletter', emailPh: 'Your e-mail', subscribe: 'Subscribe now', 27 faq: 'FAQ', imp: 'Imprint', data: 'Privacy Policy', agb: 'Terms', blog: 'Blog', menu: 'Menu', 28 cookieTitle: 'Privacy settings', 29 cookieText: 'We use cookies and analytics tools (Google Analytics, Microsoft Clarity) to improve our website. These are only loaded with your consent.', 30 cookieMore: 'More in our', 31 cookieAccept: 'Accept', cookieDecline: 'Decline', cookieSettings: 'Cookie settings' 32 } 33 }; 34 35 // --- Analytics IDs (loaded ONLY after the visitor consents) --- 36 var GA_ID = 'G-SCF8ET3PZT'; 37 var CLARITY_ID = 'y3qhxbbo6r'; // Microsoft Clarity project id 38 // page hrefs relative to the nav folder (root for DE, /en for EN) 39 var PAGES = { 40 de: { home: 'index.html', social: 'durch-social-selling-leads-generieren.html', 41 stories: 'erfolgsgeschichten.html', contact: 'kontakt.html', 42 faq: 'faq.html', imp: 'imprint.html', data: 'datenschutzerklarung.html', agb: 'agb.html', blog: 'blog.html' }, 43 en: { home: 'index.html', social: 'generate-leads-through-social-selling.html', 44 stories: 'success-stories.html', contact: 'contact.html', 45 faq: 'faq.html', imp: 'imprint.html', data: 'data-protection-declaration.html', agb: 'agb.html', blog: 'blog.html' } 46 }; 47 48 function buildChrome() { 49 var body = document.body; 50 var lang = body.getAttribute('data-lang') || 'de'; 51 var variant = body.getAttribute('data-header') || 'dark'; // dark | light 52 var assetBase = body.getAttribute('data-base') || ''; // '' for root, '../' for /en 53 var alt = body.getAttribute('data-alt') || (lang === 'de' ? 'en/index.html' : '../index.html'); 54 var t = STR[lang], p = PAGES[lang]; 55 var logo = variant === 'light' ? 'DE_Logo.webp' : 'DE_Logo_White-1.webp'; 56 57 var headerMount = document.querySelector('[data-chrome="header"]'); 58 if (headerMount) { 59 headerMount.outerHTML = 60 '<header class="site-header ' + (variant === 'light' ? 'site-header--light' : '') + '" id="siteHeader">' + 61 '<div class="container">' + 62 '<a class="site-logo" href="' + p.home + '"><img src="' + assetBase + 'assets/img/' + logo + '" alt="Leadtree"></a>' + 63 '<nav class="main-nav" aria-label="' + t.menu + '"><ul>' + 64 '<li><a href="' + p.social + '">' + t.social + '</a></li>' + 65 '<li><a href="' + p.stories + '">' + t.stories + '</a></li>' + 66 '<li class="nav-contact-mobile"><a href="' + p.contact + '">' + t.contact + '</a></li>' + 67 '</ul></nav>' + 68 '<div class="header-actions">' + 69 '<a class="btn btn--sm" href="' + p.contact + '">' + t.contact + '</a>' + 70 '<div class="lang-switch">' + 71 '<button type="button"><img src="' + assetBase + 'assets/img/' + (lang === 'de' ? 'de_flag.webp' : 'en_flag.webp') + '" alt="" onerror="this.style.display=\'none\'">' + t.langCurrent + ' <span class="caret">â¾</span></button>' + 72 '<ul class="lang-menu">' + 73 '<li><a href="' + (lang === 'de' ? p.home : alt) + '"><img src="' + assetBase + 'assets/img/de_flag.webp" alt="">' + STR.de.langCurrent + '</a></li>' + 74 '<li><a href="' + (lang === 'de' ? alt : p.home) + '"><img src="' + assetBase + 'assets/img/en_flag.webp" alt="">' + STR.en.langCurrent + '</a></li>' + 75 '</ul>' + 76 '</div>' + 77 '<button class="burger" aria-label="' + t.menu + '"><span></span><span></span><span></span></button>' + 78 '</div>' + 79 '</div>' + 80 '</header>'; 81 } 82 83 var footerMount = document.querySelector('[data-chrome="footer"]'); 84 if (footerMount) { 85 footerMount.outerHTML = 86 '<footer class="site-footer"><div class="container"><div class="footer-top">' + 87 '<div class="footer-brand">' + 88 '<div class="footer-logo"><img src="' + assetBase + 'assets/img/Logo_Footer-2.webp" alt="Leadtree GmbH"></div>' + 89 '<ul class="footer-links">' + 90 '<li><a href="' + p.faq + '">' + t.faq + '</a></li>' + 91 '<li><a href="' + p.imp + '">' + t.imp + '</a></li>' + 92 '<li><a href="' + p.data + '">' + t.data + '</a></li>' + 93 '<li><a href="' + p.agb + '">' + t.agb + '</a></li>' + 94 '<li><a href="' + p.blog + '">' + t.blog + '</a></li>' + 95 '<li>
95<a href="#" data-cookie-settings>' + t.cookieSettings + '</a></li>' + 96 '</ul>' + 97 '</div>' + 98 '<div class="newsletter"><h4>' + t.nlHead + '</h4>' + 99 '<iframe class="nl-embed" src="https://embeds.beehiiv.com/d53a3a24-300b-49f2-af90-85b3594c0a4c?slim=true" data-test-id="beehiiv-embed" title="' + t.nlHead + '" width="100%" height="52" frameborder="0" scrolling="no" style="margin:0;border:0;background-color:transparent;"></iframe>' + 100 '</div>' + 101 '</div></div></footer>'; 102 } 103 } 104 105 function initInteractions() { 106 /* Mobile menu */ 107 var header = document.querySelector('.site-header'); 108 var burger = document.querySelector('.burger'); 109 if (burger && header) burger.addEventListener('click', function () { header.classList.toggle('open'); }); 110 111 /* Testimonials slider â 2 per view (1 on mobile), dots, autoplay, loop, swipe */ 112 document.querySelectorAll('[data-tcarousel]').forEach(function (car) { 113 var track = car.querySelector('.tc-track'); 114 var dotsWrap = car.querySelector('.tc-dots'); 115 var slides = track ? track.children : []; 116 if (!track || !slides.length) return; 117 var count = slides.length, current = 0, pv = 0, timer = null; 118 function perView() { return window.innerWidth < 768 ? 1 : 2; } 119 function positions() { return Math.max(1, count - pv + 1); } 120 function buildDots() { 121 if (!dotsWrap) return; 122 dotsWrap.innerHTML = ''; 123 for (var i = 0; i < positions(); i++) { 124 var b = document.createElement('button'); 125 b.type = 'button'; b.setAttribute('aria-label', 'Slide ' + (i + 1)); 126 (function (idx) { b.addEventListener('click', function () { goTo(idx); start(); }); })(i); 127 dotsWrap.appendChild(b); 128 } 129 } 130 function goTo(idx) { 131 var n = positions(); 132 current = ((idx % n) + n) % n; 133 track.style.transform = 'translateX(' + (-current * 100 / pv) + '%)'; 134 if (dotsWrap) [].forEach.call(dotsWrap.children, function (d, i) { d.classList.toggle('active', i === current); }); 135 } 136 function layout() { var p = perView(); if (p !== pv) { pv = p; buildDots(); } goTo(current); } 137 function stop() { if (timer) { clearInterval(timer); timer = null; } } 138 function start() { stop(); timer = setInterval(function () { goTo(current + 1); }, 5000); } 139 /* prev / next arrows, as on the original slider */ 140 ['prev', 'next'].forEach(function (dir) { 141 var a = document.createElement('button'); 142 a.type = 'button'; 143 a.className = 'tc-arrow tc-arrow--' + dir; 144 a.setAttribute('aria-label', dir === 'prev' ? 'Previous' : 'Next'); 145 a.innerHTML = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="' + (dir === 'prev' ? 'M14.5 6 8.5 12l6 6' : 'M9.5 6l6 6-6 6') + '"/></svg>'; 146 a.addEventListener('click', function () { goTo(current + (dir === 'prev' ? -1 : 1)); start(); }); 147 car.appendChild(a); 148 }); 149 layout(); start(); 150 window.addEventListener('resize', layout); 151 car.addEventListener('mouseenter', stop); 152 car.addEventListener('mouseleave', start); 153 var x0 = null; 154 track.addEventListener('touchstart', function (e) { x0 = e.touches[0].clientX; stop(); }, { passive: true }); 155 track.addEventListener('touchend', function (e) { 156 if (x0 === null) return; 157 var dx = e.changedTouches[0].clientX - x0; x0 = null; 158 if (Math.abs(dx) > 40) goTo(current + (dx < 0 ? 1 : -1)); 159 start(); 160 }); 161 }); 162 163 /* Success-story tabs */ 164 document.querySelectorAll('[data-tabs]').forEach(function (wrap) { 165 var tabs = wrap.querySelectorAll('.story-tab'), panels = wrap.querySelectorAll('.story-p
165anel'); 166 tabs.forEach(function (tab, i) { tab.addEventListener('click', function () { 167 tabs.forEach(function (t) { t.classList.remove('active'); }); 168 panels.forEach(function (pn) { pn.classList.remove('active'); }); 169 tab.classList.add('active'); if (panels[i]) panels[i].classList.add('active'); }); }); 170 }); 171 172 /* FAQ accordion */ 173 document.querySelectorAll('.faq-item .faq-q').forEach(function (q) { 174 q.addEventListener('click', function () { q.parentElement.classList.toggle('open'); }); 175 }); 176 177 /* Count-up */ 178 var counters = document.querySelectorAll('[data-count]'); 179 if (counters.length && 'IntersectionObserver' in window) { 180 var io = new IntersectionObserver(function (entries) { 181 entries.forEach(function (e) { if (!e.isIntersecting) return; var el = e.target; io.unobserve(el); 182 var target = parseInt(el.getAttribute('data-count'), 10) || 0, dur = 1600, start = null; 183 function step(ts) { if (!start) start = ts; var pr = Math.min((ts - start) / dur, 1); 184 var eased = 1 - Math.pow(1 - pr, 3); el.textContent = Math.floor(eased * target).toLocaleString('de-DE'); 185 if (pr < 1) requestAnimationFrame(step); else el.textContent = target.toLocaleString('de-DE'); } 186 requestAnimationFrame(step); }); 187 }, { threshold: 0.4 }); 188 counters.forEach(function (c) { io.observe(c); }); 189 } 190 } 191 192 /* ---- "LinkedIn-Potenzialanalyse" popup â custom, on-brand ---- */ 193 function initPopup() { 194 if (document.querySelector('.lt-pop-overlay')) return; 195 var body = document.body; 196 var lang = body.getAttribute('data-lang') || 'de'; 197 var contactHref = lang === 'de' ? 'kontakt.html' : 'contact.html'; 198 var thanksHref = lang === 'de' ? 'danke-potenzialanalyse.html' : 'thank-you-potential-analysis.html'; 199 200 function chips(name, type, items) { 201 return '<div class="lt-chips">' + items.map(function (v) { 202 return '<label class="lt-chip"><input type="' + type + '" name="' + name + '" value="' + v + '"><span>' + v + '</span></label>'; 203 }).join('') + '</div>'; 204 } 205 var POP = { 206 de: { 207 bubbleAria: 'Kostenlose LinkedIn-Potenzialanalyse öffnen', closeAria: 'SchlieÃen', 208 eyebrow: 'Kostenlos & unverbindlich', title: 'Deine kostenlose LinkedIn-Potenzialanalyse', 209 intro: 'Wir analysieren für dich als Founder oder CEO kostenlos, ob und wie LinkedIn für deinen spezifischen Fall der richtige Kanal sein kann.', 210 cta: 'â In einem kurzen persönlichen Gespräch', or: 'â Oder hier per Fragebogen:', 211 qIndustry: 'In welcher Branche bist du tätig?', 212 qCustomers: 'In welchen Branchen befinden sich deine Kunden?', 213 hintCustomers: 'Bitte gib uns hier alle relevanten Informationen â wenn du branchenunabhängig arbeitest, stell uns bitte alles Weitere zur Verfügung, was hilft, die für dich relevanten Unternehmen einzugrenzen.', 214 qSize: 'In welchen UnternehmensgröÃen (Mitarbeiteranzahl) befinden sich deine Zielkunden?', 215 hintSize: 'Bitte wähle alle relevanten GröÃenordnungen aus.', 216 qAudience: 'Beschreibe nun deine Zielgruppe auf Personenebene so genau wie möglich und gegliedert nach Priorität.', 217 hintAudience: 'Beispiel: Führungskräfte im Marketing, Finanzabteilung, Indirekter Einkauf, â¦', 218 qRegion: 'Welche Region ist für dich relevant?', 219 qGoal: 'Was möchtest du erreichen?', 220 hintGoal: 'Wir möchten natürlich alles, aber wähle bitte beginnend mit deiner höchsten Priorität aus. ð', 221 qOffer: 'Was bietest du an â in einem Satz?', 222 hintOffer: 'Beispiel: âWir beraten Mittelständler bei der ERP-Einführung und übernehmen den Betrieb.â', 223 qName: 'Wie heiÃt du?', 224 qEmail: 'An welche E-Mail-Adresse dürfen wir deine Analyse senden?', 225 hintEmail: 'Du bekommst unsere Einschätzung innerhalb von 24 Stunden.', 226 qWebsite: 'Wie lautet die Website deines Unternehmens?', 227 phWebsite: 'www.deinefirma.de', 228 qLinkedin: 'Dein LinkedIn-Profil', 229 hintLinkedin: 'Optional â damit wir dein Profil direkt in die Analyse einbeziehen können.', 230 submit: 'Deine kostenlose Analyse anfordern', 231 submitting: 'Wird gesendet â¦', 232 sizes: ['1-10', '11-50', '51-200', '201-500', '501-1.000', '1.001-5.000', '5.001-10.000', '10.001+'], 233 regions: ['Deutschland', 'DACH-Region', 'Europaweit', 'Weltweit'], 234 goals: ['Sichtbarkeit für mein Unternehmen aufbauen', 'Vertrauen in meiner Zielgruppe aufbauen', 'Fokus auf Vertrieb: Aktiv Kunden gewinnen'] 235 }, 236 en: { 237 bubbleAria: 'Open free LinkedIn potential analysis', closeAria: 'Close', 238 eyebrow: 'Free & non-binding', title: 'Your free LinkedIn potential analysis',
239 intro: 'As a founder or CEO, we analyse for free whether and how LinkedIn can be the right channel for your specific case.', 240 cta: 'â In a short personal conversation', or: 'â Or right here via questionnaire:', 241 qIndustry: 'Which industry do you operate in?', 242 qCustomers: 'Which industries are your customers in?', 243 hintCustomers: 'Please give us all the relevant information here â if you work across industries, please provide anything else that helps us narrow down the companies relevant to you.', 244 qSize: 'What company sizes (number of employees) are your target customers?', 245 hintSize: 'Please select all relevant size ranges.', 246 qAudience: 'Now describe your target audience at the individual level as precisely as possible, ordered by priority.', 247 hintAudience: 'Example: marketing executives, finance department, indirect procurement, â¦', 248 qRegion: 'Which region is relevant to you?', 249 qGoal: 'What would you like to achieve?', 250 hintGoal: 'We want it all, of course â but please choose starting with your highest priority. ð', 251 qOffer: 'What do you offer â in one sentence?', 252 hintOffer: 'Example: "We help mid-sized companies select and run their ERP system."', 253 qName: 'What is your name?', 254 qEmail: 'Which email address may we send your analysis to?', 255 hintEmail: 'You will receive our assessment within 24 hours.', 256 qWebsite: 'What is your company website?', 257 phWebsite: 'www.yourcompany.com', 258 qLinkedin: 'Your LinkedIn profile', 259 hintLinkedin: 'Optional â so we can include your profile directly in the analysis.', 260 submit: 'Request your free analysis', 261 submitting: 'Sending â¦', 262 sizes: ['1-10', '11-50', '51-200', '201-500', '501-1,000', '1,001-5,000', '5,001-10,000', '10,001+'], 263 regions: ['Germany', 'DACH region', 'Europe-wide', 'Worldwide'], 264 goals: ['Build visibility for my company', 'Build trust in my target audience', 'Focus on sales: actively win customers'] 265 } 266 }; 267 var s = POP[lang] || POP.de; 268 269 var html = 270 '<button class="lt-pop-bubble" type="button" aria-label="' + s.bubbleAria + '"><span>ð</span></button>' + 271 '<div class="lt-pop-overlay" role="dialog" aria-modal="true" aria-labelledby="ltPopTitle">' + 272 '<div class="lt-pop">' + 273 '<button class="lt-pop-close" type="button" aria-label="' + s.closeAria + '">×</button>' + 274 '<div class="lt-pop-head">' + 275 '<div class="lt-pop-eyebrow">' + s.eyebrow + '</div>' + 276 '<h2 id="ltPopTitle">' + s.title + '</h2>' + 277 '</div>' + 278 '<div class="lt-pop-body">' + 279 '<p class="lt-pop-intro">' + s.intro + '</p>' + 280 '<a class="lt-pop-cta" href="' + contactHref + '">' + s.cta + '</a>' + 281 '<p class="lt-pop-or">' + s.or + '</p>' + 282 '<div class="lt-divider"></div>' + 283 '<form class="lt-pop-form" novalidate>' + 284 '<div class="lt-field"><label>' + s.qIndustry + ' <span class="req">*</span></label>' + chips('branche', 'checkbox', ['B2B', 'B2C']) + '</div>' + 285 '<div class="lt-field"><label for="ltAngebot">' + s.qOffer + ' <span class="req">*</span></label>' + 286 '<p class="lt-hint">' + s.hintOffer + '</p>' + 287 '<input id="ltAngebot" name="angebot" type="text" required></div>' + 288 '<div class="lt-field"><label for="ltKunden">' + s.qCustomers + ' <span class="req">*</span></label>' + 289 '<p class="lt-hint">' + s.hintCustomers + '</p>' + 290 '<textarea id="ltKunden" name="kundenbranchen" required></textarea></div>' + 291 '<div class="lt-field"><label>' + s.qSize + ' <span class="req">*</span></label>' + 292 '<p class="lt-hint">' + s.hintSize + '</p>' + chips('groesse', 'checkbox', s.sizes) + '</div>' + 293 '<div class="lt-field"><label for="ltZielgruppe">' + s.qAudience + ' <span class="req">*</span></label>' + 294 '<p class="lt-hint">' + s.hintAudience + '</p>' + 295 '<textarea id="ltZielgruppe" name="zielgruppe" required></textarea></div>' + 296 '<div class="lt-field"><label>' + s.qRegion + ' <span class="req">*</span></label>' + chips('region', 'radio', s.regions) + '</div>' + 297 '<div class="lt-field"><label>' + s.qGoal + ' <span class="req">*</span></label>' + 298 '<p class="lt-hint">' + s.hintGoal + '</p>' + chips('ziel', 'checkbox', s.goals) + '</div>' + 299 '<div class="lt-field"><label for="ltName">' + s.qName + ' <span class="req">*</span></label><input id="ltName" name="name" type="text" required></div>' + 300 '<div class="lt-field"><label for="ltEmail">' + s.qEmail + ' <span class="req">*</span></label>' + 301 '<p class="lt-hint">' + s.hintEmail + '</p>' + 302 '<input id="ltEmail" name="email" type="email" required></div>' + 303 '<div class="lt-field"><label for="ltWebsite">
303' + s.qWebsite + ' <span class="req">*</span></label>' + 304 '<input id="ltWebsite" name="website" type="text" inputmode="url" autocomplete="url" placeholder="' + s.phWebsite + '" required></div>' + 305 '<div class="lt-field"><label for="ltLinkedin">' + s.qLinkedin + '</label>' + 306 '<p class="lt-hint">' + s.hintLinkedin + '</p>' + 307 '<input id="ltLinkedin" name="linkedin" type="text" inputmode="url" autocomplete="off" placeholder="linkedin.com/in/â¦"></div>' + 308 '<button class="btn lt-pop-submit" type="submit"><span class="lt-pop-submit-label">' + s.submit + '</span> <span class="arrow">âº</span></button>' + 309 '</form>' + 310 '</div>' + 311 '</div>' + 312 '</div>'; 313 314 var wrap = document.createElement('div'); 315 wrap.innerHTML = html; 316 while (wrap.firstChild) body.appendChild(wrap.firstChild); 317 318 var overlay = document.querySelector('.lt-pop-overlay'); 319 var bubble = document.querySelector('.lt-pop-bubble'); 320 function open() { overlay.classList.add('open'); document.documentElement.style.overflow = 'hidden'; } 321 function close() { overlay.classList.remove('open'); document.documentElement.style.overflow = ''; try { sessionStorage.setItem('lt_pop_seen', '1'); } catch (e) {} } 322 bubble.addEventListener('click', open); 323 overlay.querySelector('.lt-pop-close').addEventListener('click', close); 324 overlay.addEventListener('click', function (e) { if (e.target === overlay) close(); }); 325 document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && overlay.classList.contains('open')) close(); }); 326 // Company e-mail â suggest the website (the analysis needs the domain). 327 var emailEl = overlay.querySelector('#ltEmail'), webEl = overlay.querySelector('#ltWebsite'); 328 var FREE_MAIL = /^(gmail|googlemail|yahoo|hotmail|outlook|live|gmx|web|icloud|me|mac|aol|proton|protonmail|t-online|freenet|mail|yandex)\./; 329 if (emailEl && webEl) emailEl.addEventListener('blur', function () { 330 var d = (emailEl.value.split('@')[1] || '').trim().toLowerCase(); 331 if (d && !webEl.value && !FREE_MAIL.test(d)) webEl.value = d; 332 }); 333 overlay.querySelector('.lt-pop-form').addEventListener('submit', function (e) { 334 e.preventDefault(); 335 // The form is novalidate (custom styling) â check the fields the analysis can't do without. 336 var missing = null; 337 [].forEach.call(e.target.querySelectorAll('input[required], textarea[required]'), function (el) { 338 var ok = el.value.trim().length > 0 && (el.type !== 'email' || /.+@.+\..+/.test(el.value)); 339 var field = el.closest('.lt-field'); 340 if (field) field.classList.toggle('invalid', !ok); 341 if (!ok && !missing) missing = el; 342 }); 343 if (missing) { missing.focus(); try { missing.scrollIntoView({ block: 'center', behavior: 'smooth' }); } catch (er) {} return; } 344 // Immediately show a submitting state (the CRM call can take a moment). 345 var submitBtn = e.target.querySelector('.lt-pop-submit'); 346 if (submitBtn) { 347 if (submitBtn.classList.contains('is-loading')) return; // guard double submit 348 submitBtn.classList.add('is-loading'); 349 submitBtn.setAttribute('aria-busy', 'true'); 350 submitBtn.disabled = true; 351 var lbl = submitBtn.querySelector('.lt-pop-submit-label'); 352 if (lbl) lbl.textContent = s.submitting; 353 } 354 try { sessionStorage.setItem('lt_pop_seen', '1'); } catch (er) {} 355 // Collect the questionnaire and hand it to the Attio serverless function. 356 // (The API key lives only in that function's env â never in the browser.) 357 var payload = { lang: lang }; 358 try { 359 new FormData(e.target).forEach(function (v, k) { 360 if (payload[k] === undefined) payload[k] = v; 361 else if (Array.isArray(payload[k])) payload[k].push(v); 362 else payload[k] = [payload[k], v]; 363 }); 364 } catch (er) {} 365 var go = function () { window.location.href = thanksHref; }; 366 var done = false, finish = function () { if (done) return; done = true; go(); };
367 try { 368 var ctrl = new AbortController(); 369 var timer = setTimeout(function () { ctrl.abort(); }, 7000); 370 fetch('/.netlify/functions/attio-lead', { 371 method: 'POST', 372 headers: { 'Content-Type': 'application/json' }, 373 body: JSON.stringify(payload), 374 keepalive: true, 375 signal: ctrl.signal 376 }).catch(function () {}).finally(function () { clearTimeout(timer); finish(); }); 377 // Safety net: never let a slow/failed CRM call block the visitor. 378 setTimeout(finish, 7500); 379 } catch (er) { go(); } 380 }); 381 // auto-open after 10s, once per session 382 var seen; try { seen = sessionStorage.getItem('lt_pop_seen'); } catch (e) { seen = null; } 383 if (!seen) setTimeout(function () { 384 // don't stack on top of the cookie consent banner 385 if (document.querySelector('.lt-cookie.show')) return; 386 if (!overlay.classList.contains('open')) open(); 387 }, 10000); 388 } 389 390 /* ---- Scroll entrance animations (Elementor "fadeIn" equivalent) ---- */ 391 function initReveal() { 392 var sel = '.hero-copy,.hero-media,.testimonials,.section-intro,.story-reason,.solution,' + 393 '.background-grid,.klima-grid,.story-tabs,.story-card,.cases-grid>.case-card,.blog-grid>.blog-card,' + 394 '.pricing-card,.sd-intro-grid,.cs-cover,.video-embed,.cs-stats,.cs-body,.cs-cta,.faq-item,.booking-embed'; 395 var targets = [].slice.call(document.querySelectorAll(sel)); 396 if (!targets.length) return; 397 document.documentElement.classList.add('anim-on'); 398 targets.forEach(function (el) { 399 el.classList.add('reveal'); 400 var p = el.parentElement; 401 if (p && (p.classList.contains('cases-grid') || p.classList.contains('blog-grid') || p.classList.contains('story-reasons'))) { 402 var i = [].indexOf.call(p.children, el); 403 el.style.animationDelay = (Math.min(i, 6) * 90) + 'ms'; 404 } 405 }); 406 function revealInView() { 407 var h = window.innerHeight || document.documentElement.clientHeight; 408 for (var i = 0; i < targets.length; i++) { 409 var el = targets[i]; 410 if (el.classList.contains('in')) continue; 411 var r = el.getBoundingClientRect(); 412 if (r.top < h * 0.9 && r.bottom > 0) el.classList.add('in'); 413 } 414 } 415 revealInView(); 416 window.addEventListener('scroll', revealInView, { passive: true }); 417 window.addEventListener('resize', revealInView); 418 // failsafe: never leave anything hidden 419 setTimeout(function () { targets.forEach(function (el) { el.classList.add('in'); }); }, 2500); 420 } 421 422 /* ---- Cookie consent + analytics (GDPR opt-in: nothing loads until "Accept") ---- */ 423 var CONSENT_KEY = 'lt_consent'; 424 function ltGetConsent() { try { return localStorage.getItem(CONSENT_KEY); } catch (e) { return null; } } 425 function ltSetConsent(v) { try { localStorage.setItem(CONSENT_KEY, v); } catch (e) {} } 426 427 function loadAnalytics() { 428 if (window.__ltAnalytics) return; 429 window.__ltAnalytics = true; 430 // Google Analytics 4 431 if (GA_ID) { 432 var s = document.createElement('script'); s.async = true; 433 s.src = 'https://www.googletagmanager.com/gtag/js?id=' + GA_ID; 434 document.head.appendChild(s); 435 window.dataLayer = window.dataLayer || []; 436 window.gtag = function () { dataLayer.push(arguments); }; 437 window.gtag('js', new Date()); 438 window.gtag('config', GA_ID); 439 } 440 // Microsoft Clarity 441 if (CLARITY_ID) { 442 (function (c, l, a, r, i, t, y) { 443 c[a] = c[a] || function () { (c[a].q = c[a].q || []).push(arguments); }; 444 t = l.createElement(r); t.async = 1; t.src = 'https://www.clarity.ms/tag/' + i; 445 y = l.getElementsByTagName(r)[0]; y.parentNode.insertBefore(t, y); 446 })(window, document, 'clarity', 'script', CLARITY_ID); 447 } 448 } 449 450 function initConsent() { 451 var lang = document.body.getAttribute('data-lang') || 'de'; 452 var t = STR[lang]; 453 var privacyHref = PAGES[lang].data; 454 455 function showBanner() { 456 var existing = document.querySelector('.lt-cookie'); 457 if (existing) { existing.classList.add('show'); return; } 458 var el = document.createElement('div'); 459 el.className = 'lt-cookie'; 460 el.setAttribute('role', 'dialog');
461 el.setAttribute('aria-label', t.cookieTitle); 462 el.innerHTML = 463 '<div class="lt-cookie-inner">' + 464 '<div class="lt-cookie-text"><strong>' + t.cookieTitle + '</strong> ' + t.cookieText + 465 ' ' + t.cookieMore + ' <a href="' + privacyHref + '">' + t.data + '</a>.</div>' + 466 '<div class="lt-cookie-btns">' + 467 '<button type="button" class="lt-cookie-decline">' + t.cookieDecline + '</button>' + 468 '<button type="button" class="btn btn--sm lt-cookie-accept">' + t.cookieAccept + '</button>' + 469 '</div>' + 470 '</div>'; 471 document.body.appendChild(el); 472 document.documentElement.classList.add('lt-cookie-on'); 473 setTimeout(function () { el.classList.add('show'); }, 30); 474 function dismiss() { el.classList.remove('show'); document.documentElement.classList.remove('lt-cookie-on'); } 475 el.querySelector('.lt-cookie-accept').addEventListener('click', function () { 476 ltSetConsent('granted'); loadAnalytics(); dismiss(); 477 }); 478 el.querySelector('.lt-cookie-decline').addEventListener('click', function () { 479 ltSetConsent('denied'); dismiss(); 480 }); 481 } 482 483 // footer "Cookie settings" link reopens the banner 484 var link = document.querySelector('[data-cookie-settings]'); 485 if (link) link.addEventListener('click', function (e) { e.preventDefault(); showBanner(); }); 486 487 var consent = ltGetConsent(); 488 if (consent === 'granted') { loadAnalytics(); return; } 489 if (consent === 'denied') { return; } 490 showBanner(); 491 } 492 493 document.addEventListener('DOMContentLoaded', function () { 494 buildChrome(); 495 initInteractions(); 496 initReveal(); 497 initPopup(); 498 initConsent(); 499 }); 500})();
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.