PageSourceSearch

https://lead-tree.de/assets/js/main.js?v=20250813t

js lead-tree.de collected 2026-10-04 22:32:12 UTC 29,429 bytes, 500 lines download raw bytes

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 &amp; 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 &amp; 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 + '">&times;</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.