PageSourceSearch

https://www.sistrix.com/static/public/assets/js/landingpage.js

js sistrix.com collected 2026-09-24 08:27:35 UTC 43,902 bytes, 1,123 lines download raw bytes

1/* =============================================================
2   landingpage.js – Shared JS for google, amazon, ai
3   All blocks use defensive element checks and are safe to run
4   on any page (no-op if the element doesn't exist).
5   ============================================================= */
6
7// ── SVG Chart: hero siLine/siArea/siDot (google, amazon, ai) ─────────────────
8(function () {
9  var line = document.getElementById('siLine');
10  var area = document.getElementById('siArea');
11  var dot  = document.getElementById('siDot');
12  if (!line) return;
13  var len = line.getTotalLength();
14  function play() {
15    line.style.animation = 'none'; area.style.animation = 'none'; dot.style.animation = 'none';
16    line.style.strokeDasharray = len; line.style.strokeDashoffset = len;
17    area.style.opacity = '0'; dot.style.opacity = '0';
18    requestAnimationFrame(function () {
19      requestAnimationFrame(function () {
20        line.style.animation = 'drawLine 2s ease-out forwards';
21        area.style.animation = 'fadeInFill 0.6s ease-out forwards 1.8s';
22        dot.style.animation  = 'fadeInFill 0.3s ease-out forwards 1.8s';
23      });
24    });
25  }
26  document.addEventListener('DOMContentLoaded', play);
27})();
28
29// ── SVG Chart: CTA sparkline (google only) ───────────────────────────────────
30(function () {
31  var line = document.getElementById('ctaSparkLine');
32  var area = document.getElementById('ctaSparkArea');
33  var dot  = document.getElementById('ctaSparkDot');
34  if (!line) return;
35  var len = line.getTotalLength();
36  line.style.strokeDasharray = len;
37  line.style.strokeDashoffset = len;
38
39  function play() {
40    line.style.animation = 'none'; area.style.animation = 'none'; dot.style.animation = 'none';
41    line.style.strokeDasharray = len; line.style.strokeDashoffset = len;
42    area.style.opacity = '0'; dot.style.opacity = '0';
43    requestAnimationFrame(function () {
44      requestAnimationFrame(function () {
45        line.style.animation = 'drawLine 1.6s ease-out forwards';
46        area.style.animation = 'fadeInFill 0.5s ease-out forwards 1.4s';
47        dot.style.animation  = 'fadeInFill 0.3s ease-out forwards 1.4s';
48      });
49    });
50  }
51
52  var section = document.getElementById('trial');
53  if (section && 'IntersectionObserver' in window) {
54    var played = false;
55    new IntersectionObserver(function (entries) {
56      if (entries[0].isIntersecting && !played) { played = true; play(); }
57    }, { threshold: 0.3 }).observe(section);
58  } else {
59    document.addEventListener('DOMContentLoaded', play);
60  }
61})();
62
63// ── SVG Chart: google feature chart (fsiLine/fsiArea/fsiDots) ────────────────
64(function () {
65  var chart = document.getElementById('feature-chart');
66  var line  = document.getElementById('fsiLine');
67  var area  = document.getElementById('fsiArea');
68  var dots  = document.getElementById('fsiDots');
69  if (!line) return;
70  var len = line.getTotalLength();
71  line.style.strokeDasharray = len; line.style.strokeDashoffset = len;
72
73  function play() {
74    line.style.animation = 'none';
75    if (area) area.style.animation = 'none';
76    if (dots) dots.style.animation = 'none';
77    line.style.strokeDasharray = len; line.style.strokeDashoffset = len;
78    if (area) area.style.opacity = '0';
79    if (dots) dots.style.opacity = '0';
80    requestAnimationFrame(function () {
81      requestAnimationFrame(function () {
82        line.style.animation = 'drawLine 2s ease-out forwards';
83        if (area) area.style.animation = 'fadeInFill 0.6s ease-out forwards 1.8s';
84        if (dots) dots.style.animation = 'fadeInFill 0.5s ease-out forwards 1.8s';
85      });
86    });
87  }
88
89  document.addEventListener('DOMContentLoaded', play);
90  if (chart) chart.addEventListener('mouseenter', play);
91})();
92
93// ── SVG Chart: ai-onpage-chart multi-line (ai only) ──────────────────────────
94(function () {
95  var chart = document.getElementById('ai-onpage-chart');
96  if (!chart) return;
97  var pairs = [1,2,3,4].map(function (n) {
98    return { line: document.getElementById('fsiLine' + n), area: document.getElementById('fsiArea' + n) };
99  }).filter(function (p) { return p.line; });
100
101  pairs.forEach(function (p) {
102    var len = p.line.getTotalLength();
103    p.line._len = len;
104    p.line.style.strokeDasharray = len;
105    p.line.style.strokeDashoffset = len;
106  });
107
108  function play() {
109    pairs.forEach(function (p) {
110      var len = p.line._len;
111      p.line.style.animation = 'none';
112      if (p.area) p.area.style.animation = 'none';
113      p.line.style.strokeDasharray = len;
114      p.line.style.strokeDashoffset = len;
115      if (p.area) p.area.style.opacity = '0';
116    });
117    requestAnimationFrame(function () {
118      requestAnimationFrame(function () {
119        pairs.forEach(function (p) {
120          p.line.style.animation = 'drawLine 2s ease-out forwards';
121          if (p.area) p.area.style.animation = 'fadeInFill 0.6s ease-out forwards 1.8s';
122        });
123      });
124    });
125  }
126
127  document.addEventListener('DOMContentLoaded', play);
128  chart.addEventListener('mouseenter', play);
129})();
130
131// ── SVG Chart: bento-fsi-chart (ai only) ─────────────────────────────────────
132(function () {
133  var chart = document.getElementById('bento-fsi-chart');
134  var line  = document.getElementById('bentoFsiLine');
135  var area  = document.getElementById('bentoFsiArea');
136  if (!line) return;
137  var len = line.getTotalLength();
138  line.style.strokeDasharray  = len;
139  line.style.strokeDashoffset = len;
140
141  function play() {
142    line.style.animation = 'none';
143    if (area) area.style.animation = 'none';
144    line.style.strokeDasharray  = len;
145    line.style.strokeDashoffset = len;
146    if (area) area.style.opacity = '0';
147    requestAnimationFrame(function () {
148      requestAnimationFrame(function () {
149        line.style.animation = 'drawLine 2s ease-out forwards';
150        if (area) area.style.animation = 'fadeInFill 0.6s ease-out forwards 1.8s';
151      });
152    });
153  }
154
155  document.addEventListener('DOMContentLoaded', play);
156  if (chart) chart.addEventListener('mouseenter', play);
157})();
158
159// ── SVG Chart: bento6 multi-line (ai only) ───────────────────────────────────
160(function () {
161  var box  = document.getElementById('bento6-box');
162  var ids  = ['bento6-line-1','bento6-line-2','bento6-line-3','bento6-line-4','bento6-line-5'];
163  var lens = [];
164
165  function play() {
166    var els = ids.map(function (id, i) {
167      var el = document.getElementById(id);
168      if (!el) return null;
169      if (!lens[i]) lens[i] = el.getTotalLength();
170      el.style.transition       = 'none';
171      el.style.strokeDasharray  = lens[i];
172      el.style.strokeDashoffset = lens[i];
173      return el;
174    });
175    requestAnimationFrame(function () {
176      requestAnimationFrame(function () {
177        els.forEach(function (el, i) {
178          if (!el) return;
179          el.style.transition       = 'stroke-dashoffset ' + (1.4 + i * 0.12) + 's ease-out ' + (i * 0.08) + 's';
180          el.style.strokeDashoffset = '0';
181        });
182      });
183    });
184  }
185
186  if (!ids.some(function (id) { return document.getElementById(id); })) return;
187  document.addEventListener('DOMContentLoaded', play);
188  if (box) box.addEventListener('mouseenter', play);
189})();
190
191// ── Amazon BVI counter (amazon only) ─────────────────────────────────────────
192(function () {
193  var chart   = document.getElementById('prompt-chart');
194  var numEl   = document.getElementById('bvi-number');
195  var badgeEl = document.getElementById('bvi-badge');
196  if (!chart || !numEl || !badgeEl) return;
197
198  var TARGET_CENTS = 163893;
199  var DURATION = 900;
200  var animating = false;
201
202  function formatBVI(cents) {
203    var dec      = (cents % 100).toString().padStart(2, '0');
204    var whole    = Math.floor(cents / 100);
205    var wholeStr = whole >= 1000
206      ? Math.floor(whole / 1000) + '.' + (whole % 1000).toString().padStart(3, '0')
207      : whole.toString();
208    return wholeStr + ',' + dec;
209  }
210
211  function play() {
212    if (animating) return;
213    animating = true;
214    badgeEl.style.transition = 'none';
215    badgeEl.style.opacity = '0';
216    var start = performance.now();
217
218    function step(now) {
219      var t    = Math.min((now - start) / DURATION, 1);
220      var ease = 1 - Math.pow(1 - t, 3);
221      numEl.textContent = formatBVI(Math.round(ease * TARGET_CENTS));
222      if (t < 1) {
223        requestAnimationFrame(step);
224      } else {
225        numEl.textContent = '1.638,93';
226        badgeEl.style.transition = 'opacity 0.3s ease-out';
227        badgeEl.style.opacity = '1';
228        animating = false;
229      }
230    }
231    requestAnimationFrame(step);
232  }
233
234  chart.addEventListener('mouseenter', play);
235})();
236
237// ── Hero Parallax (google: hero-text / hero-cards-wrap) ───────────────────────
238(function () {
239  var heroText      = document.getElementById('hero-text');
240  var heroCardsWrap = document.getElementById('hero-cards-wrap');
241  if (!heroText || !heroCardsWrap) return;
242
243  heroCardsWrap.style.willChange = 'transform';
244  heroCardsWrap.style.transform = 'translateZ(0)';
245  heroCardsWrap.style.webkitBackfaceVisibility = 'hidden';
246  heroCardsWrap.style.backfaceVisibility = 'hidden';
247  heroText.style.willChange = 'transform';
248  heroText.style.transform = 'translateZ(0)';
249  heroText.style.webkitBackfaceVisibility = 'hidden';
250  heroText.style.backfaceVisibility = 'hidden';
251
252  var ticking = false;
253  window.addEventListener('scroll', function () {
254    if (ticking) return;
255    ticking = true;
256    requestAnimationFrame(function () {
257      var y = window.scrollY;
258      heroText.style.transform      = 'translateY(' + (y * 0.07) + 'px)';
259      heroCardsWrap.style.transform = 'translateY(' + (y * 0.16) + 'px)';
260      ticking = false;
261    });
262  }, { passive: true });
263})();
264
265// ── Hero Parallax (amazon: amz-hero-text / amz-hero-cards-wrap) ──────────────
266(function () {
267  var heroText      = document.getElementById('amz-hero-text');
268  var heroCardsWrap = document.getElementById('amz-hero-cards-wrap');
269  if (!heroText || !heroCardsWrap) return;
270
271  heroCardsWrap.style.willChange = 'transform';
272  heroCardsWrap.style.transform = 'translateZ(0)';
273  heroCardsWrap.style.webkitBackfaceVisibility = 'hidden';
274  heroCardsWrap.style.backfaceVisibility = 'hidden';
275  heroText.style.willChange = 'transform';
276  heroText.style.transform = 'translateZ(0)';
277  heroText.style.webkitBackfaceVisibility = 'hidden';
278  heroText.style.backfaceVisibility = 'hidden';
279
280  var ticking = false;
281  window.addEventListener('scroll', function () {
282    if (ticking) return;
283    ticking = true;
284    requestAnimationFrame(function () {
285      var y = window.scrollY;
286      heroText.style.transform      = 'translateY(' + (y * 0.07) + 'px)';
287      heroCardsWrap.style.transform = 'translateY(' + (y * 0.16) + 'px)';
288      ticking = false;
289    });
290  }, { passive: true });
291})();
292
293// ── Hero Parallax (ai: ai-hero-text / ai-hero-cards-wrap) ─────────────────────
294(function () {
295  var heroText      = document.getElementById('ai-hero-text');
296  var heroCardsWrap = document.getElementById('ai-hero-cards-wrap');
297  if (!heroText || !heroCardsWrap) return;
298
299  heroText.style.willChange      = 'transform';
300  heroCardsWrap.style.willChange = 'transform';
301
302  var ticking = false;
303  window.addEventListener('scroll', function () {
304    if (ticking) return;
305    ticking = true;
306    requestAnimationFrame(function () {
307      var y = window.scrollY;
308      heroText.style.transform      = 'translateY(' + (y * 0.07) + 'px)';
309      heroCardsWrap.style.transform = 'translateY(' + (y * 0.16) + 'px)';
310      ticking = false;
311    });
312  }, { passive: true });
313})();
314
315// ── Hero card 3D tilt (amazon) ────────────────────────────────────────────────
316(function () {
317  [{ id: 'amz-hero-card-1', baseRotate: 3, baseZ: '10' },
318   { id: 'amz-hero-card-2', baseRotate: -1.5, baseZ: '20' }].forEach(function (cfg) {
319    var card = document.getElementById(cfg.id);
320    if (!card) return;
321    var rafId = null;
322    card.addEventListener('mousemove', function (e) {
323      var rect = card.getBoundingClientRect();
324      if (rafId) return;
325      rafId = requestAnimationFrame(function () {
326        rafId = null;
327        var dx = (e.clientX - (rect.left + rect.width  / 2)) / (rect.width  / 2);
328        var dy = (e.clientY - (rect.top  + rect.height / 2)) / (rect.height / 2);
329        card.style.transition = 'transform 0.08s ease-out';
330        card.style.transform  = 'rotate(' + cfg.baseRotate + 'deg) perspective(700px) rotateX(' + (-dy * 12) + 'deg) rotateY(' + (dx * 12) + 'deg) scale(1.04)';
331        card.style.zIndex     = '40';
332      });
333    });
334    card.addEventListener('mouseleave', function () {
335      card.style.transition = 'transform 0.45s cubic-bezier(0.23,1,0.32,1)';
336      card.style.transform  = 'rotate(' + cfg.baseRotate + 'deg)';
337      card.style.zIndex     = cfg.baseZ;
338    });
339  });
340})();
341
342// ── Subtle tilt: feature/section charts (google) ──────────────────────────────
343(function () {
344  ['feature-chart', 'concurrence-chart', 'ca-card', 'onpage-chart'].forEach(function (id) {
345    var el = document.getElementById(id);
346    if (!el) return;
347    var rafId = null;
348    el.addEventListener('mousemove', function (e) {
349      var r = el.getBoundingClientRect();
350      if (rafId) return;
351      rafId = requestAnimationFrame(function () {
352        rafId = null;
353        var dx = (e.clientX - (r.left + r.width  / 2)) / (r.width  / 2);
354        var dy = (e.clientY - (r.top  + r.height / 2)) / (r.height / 2);
355        el.style.transition = 'transform 0.1s ease-out';
356        el.style.transform  = 'perspective(900px) rotateX(' + (-dy * 5) + 'deg) rotateY(' + (dx * 5) + 'deg) 
356scale(1.02)';
357      });
358    });
359    el.addEventListener('mouseleave', function () {
360      el.style.transition = 'transform 0.5s cubic-bezier(0.23,1,0.32,1)';
361      el.style.transform  = '';
362    });
363  });
364})();
365
366// ── Subtle tilt: feature/section charts (amazon) ─────────────────────────────
367(function () {
368  ['prompt-chart', 'prompt-cloud', 'ai-card-1'].forEach(function (id) {
369    var el = document.getElementById(id);
370    if (!el) return;
371    var rafId = null;
372    el.addEventListener('mousemove', function (e) {
373      var r = el.getBoundingClientRect();
374      if (rafId) return;
375      rafId = requestAnimationFrame(function () {
376        rafId = null;
377        var dx = (e.clientX - (r.left + r.width  / 2)) / (r.width  / 2);
378        var dy = (e.clientY - (r.top  + r.height / 2)) / (r.height / 2);
379        el.style.transition = 'transform 0.1s ease-out';
380        el.style.transform  = 'perspective(900px) rotateX(' + (-dy * 5) + 'deg) rotateY(' + (dx * 5) + 'deg) scale(1.02)';
381      });
382    });
383    el.addEventListener('mouseleave', function () {
384      el.style.transition = 'transform 0.5s cubic-bezier(0.23,1,0.32,1)';
385      el.style.transform  = '';
386    });
387  });
388})();
389
390// ── Subtle tilt: bento-fsi-chart (ai) ────────────────────────────────────────
391(function () {
392  var chart = document.getElementById('bento-fsi-chart');
393  if (!chart) return;
394  var rafId = null;
395  chart.addEventListener('mousemove', function (e) {
396    var r = chart.getBoundingClientRect();
397    if (rafId) return;
398    rafId = requestAnimationFrame(function () {
399      rafId = null;
400      var dx = (e.clientX - (r.left + r.width  / 2)) / (r.width  / 2);
401      var dy = (e.clientY - (r.top  + r.height / 2)) / (r.height / 2);
402      chart.style.transition = 'transform 0.1s ease-out';
403      chart.style.transform  = 'perspective(900px) rotateX(' + (-dy * 5) + 'deg) rotateY(' + (dx * 5) + 'deg) scale(1.02)';
404    });
405  });
406  chart.addEventListener('mouseleave', function () {
407    chart.style.transition = 'transform 0.5s cubic-bezier(0.23,1,0.32,1)';
408    chart.style.transform  = '';
409  });
410})();
411
412// ── Typewriter: google ca-card ────────────────────────────────────────────────
413(function () {
414  var el   = document.getElementById('ca-typewriter');
415  var card = document.getElementById('ca-card');
416  if (!el || !card) return;
417  var fullText = el.dataset.text || '';
418  var cursor = '<span class="font-medium" style="animation:caBlinkCursor 0.8s step-end infinite;border-right:2px solid #888;margin-left:1px;">&nbsp;</span>';
419  var timer = null;
420
421  function startTyping() {
422    if (timer) clearTimeout(timer);
423    var i = 0;
424    function type() {
425      el.innerHTML = fullText.substring(0, i) + cursor;
426      if (i <= fullText.length) { i++; timer = setTimeout(type, 28 + Math.random() * 22); }
427    }
428    timer = setTimeout(type, 300);
429  }
430
431  card.addEventListener('mouseenter', startTyping);
432  document.addEventListener('DOMContentLoaded', startTyping);
433})();
434
435// ── Typewriter: amazon ai-card-1 ───────────────────────────────────────────
436(function () {
437  var el   = document.getElementById('ca-typewriter');
438  var card = document.getElementById('ai-card-1');
439  if (!el || !card) return;
440  var fullText = el.dataset.text || '';
441  var cursor = '<span class="font-medium" style="animation:caBlinkCursor 0.8s step-end infinite;border-right:2px solid #888;margin-left:1px;">&nbsp;</span>';
442  var timer = null;
443
444  function startTyping() {
445    if (timer) clearTimeout(timer);
446    var i = 0;
447    function type() {
448      el.innerHTML = fullText.substring(0, i) + cursor;
449      if (i <= fullText.length) { i++; timer = setTimeout(type, 28 + Math.random() * 22); }
450    }
451    timer = setTimeout(type, 300);
452  }
453
454  card.addEventListener('mouseenter', startTyping);
455  document.addEventListener('DOMContentLoaded', startTyping);
456})();
457
458// ── Typewriter: ai-ca-card (Forces) ──────────────────────────────────────────
459(function () {
460  var el   = document.getElementById('ai-ca-typewriter');
461  var card = document.getElementById('ai-ca-card');
462  if (!el || !card) return;
463  var fullText = el.getAttribute('data-tw-text') || "More Nutrition est une marque moderne et lifestyle-orientée, connue pour ses protéines multi-composants de haute qualité, arômes excellents, faibles en sucre et forte présence sur les réseaux sociaux.";
464  var cursor = '<span class="font-medium" style="animation:caBlinkCursor 0.8s step-end infinite;border-right:2px solid #888;margin-left:1px;">&nbsp;</span>';
465  var timer = null;
466
467  function startTyping() {
468    if (timer) clearTimeout(timer);
469    var i = 0;
470    function type() {
471      el.innerHTML = fullText.substring(0, i) + cursor;
472      if (i <= fullText.length) { i++; timer = setTimeout(type, 28 + Math.random() * 22); }
473    }
474    timer = setTimeout(type, 300);
475  }
476
477  card.addEventListener('mouseenter', startTyping);
478  document.addEventListener('DOMContentLoaded', startTyping);
479})();
480
481// ── Typewriter: ai-ca-card-2 (Faiblesses) ────────────────────────────────────
482(function () {
483  var el   = document.getElementById('ai-ca-typewriter-2');
484  var card = document.getElementById('ai-ca-card-2');
485  if (!el || !card) return;
486  var fullText = el.getAttribute('data-tw-text') || "Les produits More Nutrition sont nettement plus chers (env. 1,50 € par portion). La marque est critiquée pour ses produits ultra-transformés, ses nombreux additifs et ses allégations marketing non conformes.";
487  var cursor = '<span class="font-medium" style="animation:caBlinkCursor 0.8s step-end infinite;border-right:2px solid #888;margin-left:1px;">&nbsp;</span>';
488  var timer = null;
489
490  function startTyping() {
491    if (timer) clearTimeout(timer);
492    var i = 0;
493    function type() {
494      el.innerHTML = fullText.substring(0, i) + cursor;
495      if (i <= fullText.length) { i++; timer = setTimeout(type, 28 + Math.random() * 22); }
496    }
497    timer = setTimeout(type, 500);
498  }
499
500  card.addEventListener('mouseenter', startTyping);
501  document.addEventListener('DOMContentLoaded', startTyping);
502})();
503
504// ── AI card-2 hover z-index (ai only) ────────────────────────────────────────
505(function () {
506  var card1 = document.getElementById('ai-ca-card');
507  var card2 = document.getElementById('ai-ca-card-2');
508  if (!card1 || !card2) return;
509
510  card2.addEventListener('mouseenter', function () {
511    card2.style.zIndex = '30';
512    card2.style.transform = 'translateX(-8px)';
513    card1.style.zIndex = '10';
514  });
515  card2.addEventListener('mouseleave', function () {
516    card2.style.zIndex = '10';
517    card2.style.transform = '';
518    card1.style.zIndex = '20';
519  });
520})();
521
522// ── Section 2: bubble scale-in (google only) ──────────────────────────────────
523(function () {
524  var chart = document.getElementById('concurrence-chart');
525  if (!chart) return;
526  var bubbles = ['b-decathlon', 'b-cdiscount', 'b-fnac', 'b-leboncoin', 'b-facebook', 'b-youtube', 'b-amazon'];
527
528  bubbles.forEach(function (id) {
529    var el = document.getElementById(id);
530    if (el) { el.style.transform = 'scale(0)'; el.style.opacity = '0'; }
531  });
532
533  function playBubbles(delay) {
534    bubbles.forEach(function (id) {
535      var el = document.getElementById(id);
536      if (!el) return;
537      el.style.transition = 'none';
538      el.style.transform  = 'scale(0)';
539      el.style.opacity    = '0';
540    });
541    requestAnimationFrame(function () {
542      requestAnimationFrame(function () {
543        bubbles.forEach(function (id, i) {
544          var el = document.getElementById(id);
545          if (!el) return;
546          setTimeout(function () {
547            el.style.transition = 'transform 0.4s cubic-bezier(0.34,1.56,0.64,1), opacity 0.3s ease-out';
548            el.style.transform  = 'scale(1)';
549            el.style.opacity    = '1';
550          }, delay + i * 110);
551        });
552      });
553    });
554  }
555
556  document.addEventListener('DOMContentLoaded', function () { playBubbles(400); });
557  chart.addEventListener('mouseenter', function () { playBubbles(0); });
558})();
559
560// ── Word cloud sequential fade (ai only) ──────────────────────────────────────
561(function () {
562  var chart = document.getElementById('ai-concurrence-chart');
563  if (!chart) return;
564  var words = Array.from(chart.querySelectorAll('.wc-w'));
565  var playing = false;
566
567  function playWords() {
568    if (playing) return;
569    playing = true;
570    words.forEach(function (w) {
571      w.style.transition = 'none';
572      w.style.opacity    = '0';
573      w.style.transform  = 'translateY(10px) scale(0.92)';
574    });
575    requestAnimationFrame(function () {
576      requestAnimationFrame(function () {
577        words.forEach(function (w, i) {
578          setTimeout(function () {
579            w.style.transition = 'opacity 0.65s cubic-bezier(0.22,1,0.36,1), transform 0.65s cubic-bezier(0.22,1,0.36,1)';
580            w.style.opacity    = '1';
581            w.style.transform  = 'translateY(0) scale(1)';
582            if (i === words.length - 1) {
583              setTimeout(function () { playing = false; }, 700);
584            }
585          }, i * 120);
586        });
587      });
588    });
589  }
590
591  new IntersectionObserver(function (entries) {
592    entries.forEach(function (entry) {
593      if (entry.isIntersecting) { playWords(); }
594    });
595  }, { threshold: 0.25 }).observe(chart);
596  chart.addEventListener('mouseenter', playWords);
597})();
598
599// ── Row slide-in with opacity levels (google: onpage-chart) ──────────────────
600(function () {
601  var rowDefs = [
602    { id: 'row-1', opacity: '0.6'    }, { id: 'row-2', opacity: '0.8' },
603    { id: 'row-3', opacity: '1'    }, { id: 'row-4', opacity: '0.8' },
604    { id: 'row-5', opacity: '0.6' }, { id: 'row-6', opacity: '0.4' },
605    { id: 'row-7', opacity: '0.2'  },
606  ];
607
608  var chart = document.getElementById('onpage-chart');
609  if (!chart) return;
610
611  rowDefs.forEach(function (r) {
612    var el = chart.querySelector('#' + r.id);
613    if (el) { el.style.opacity = '0'; el.style.transform = 'translateX(-10px)'; }
614  });
615
616  function playRows(delay) {
617    rowDefs.forEach(function (r, i) {
618      var el = chart.querySelector('#' + r.id);
619      if (!el) return;
620      el.style.transition = 'none';
621      el.style.opacity    = '0';
622      el.style.transform  = 'translateX(-10px)';
623      requestAnimationFrame(function () {
624        requestAnimationFrame(function () {
625          setTimeout(function () {
626            el.style.transition = 'opacity 0.35s ease-out, transform 0.35s ease-out';
627            el.style.opacity    = r.opacity;
628            el.style.transform  = 'translateX(0)';
629          }, delay + i * 75);
630        });
631      });
632    });
633  }
634
635  document.addEventListener('DOMContentLoaded', function () { playRows(400); });
636  chart.addEventListener('mouseenter', function () { playRows(0); });
637})();
638
639// ── Row slide-in with opacity levels (amazon: prompt-cloud + ai-card-1) ──
640(function () {
641  var rowDefs = [
642    { id: 'row-1', opacity: '0.6' }, { id: 'row-2', opacity: '0.8' },
643    { id: 'row-3', opacity: '1'   }, { id: 'row-4', opacity: '0.8' },
644    { id: 'row-5', opacity: '0.6' }, { id: 'row-6', opacity: '0.4' },
645    { id: 'row-7', opacity: '0.2' },
646  ];
647
648  ['prompt-cloud', 'ai-card-1'].forEach(function (chartId) {
649    var chart = document.getElementById(chartId);
650    if (!chart) return;
651
652    rowDefs.forEach(function (r) {
653      var el = chart.querySelector('#' + r.id);
654      if (el) { el.style.opacity = '0'; el.style.transform = 'translateX(-10px)'; }
655    });
656
657    function playRows(delay) {
658      rowDefs.forEach(function (r, i) {
659        var el = chart.querySelector('#' + r.id);
660        if (!el) return;
661        el.style.transition = 'none';
662        el.style.opacity    = '0';
663        el.style.transform  = 'translateX(-10px)';
664        requestAnimationFrame(function () {
665          requestAnimationFrame(function () {
666            setTimeout(function () {
667              el.style.transition = 'opacity 0.35s ease-out, transform 0.35s ease-out';
668              el.style.opacity    = r.opacity;
669              el.style.transform  = 'translateX(0)';
670            }, delay + i * 75);
671          });
672        });
673      });
674    }
675
676    document.addEventListener('DOMContentLoaded', function () { playRows(400); });
677    chart.addEventListener('mouseenter', function () { playRows(0); });
678  });
679})();
680
681// ── Row slide-in with opacity levels (content-creator: prompt-chart) ────
682(function () {
683  var rowDefs = [
684    { id: 'row-1', opacity: '0.6' }, { id: 'row-2', opacity: '0.8' },
685    { id: 'row-3', opacity: '1'   }, { id: 'row-4', opacity: '0.8' },
686    { id: 'row-5', opacity: '0.6' }, { id: 'row-6', opacity: '0.4' },
687    { id: 'row-7', opacity: '0.2' },
688  ];
689
690  var chart = document.getElementById('prompt-chart');
691  if (!chart) return;
692
693  rowDefs.forEach(function (r) {
694    var el = chart.querySelector('#' + r.id);
695    if (el) { el.style.opacity = '0'; el.style.transform = 'translateX(-10px)'; }
696  });
697
698  function playRows(delay) {
699    rowDefs.forEach(function (r, i) {
700      var el = chart.querySelector('#' + r.id);
701      if (!el) return;
702      el.style.transition = 'none';
703      el.style.opacity    = '0';
704      el.style.transform  = 'translateX(-10px)';
705      requestAnimationFrame(function () {
706        requestAnimationFrame(function () {
707          setTimeout(function () {
708            el.style.transition = 'opacity 0.35s ease-out, transform 0.35s ease-out';
709            el.style.opacity    = r.opacity;
710            el.style.transform  = 'translateX(0)';
711          }, delay + i * 75);
712        });
713      });
714    });
715  }
716
717  document.addEventListener('DOMContentLoaded', function () { playRows(400); });
718  chart.addEventListener('mouseenter', function () { playRows(0); });
719})();
720
721// ── Row slide-in (ai: ai-feature-chart) ──────────────────────────────────────
722(function () {
723  var chart = document.getElementById('ai-feature-chart');
724  if (!chart) return;
725  var rows = ['row-1','row-2','row-3','row-4','row-5','row-6','row-7'];
726
727  rows.forEach(function (id) {
728    var el = document.getElementById(id);
729    if (el) { el.style.opacity = '0'; el.style.transform = 'translateX(-10px)'; }
730  });
731
732  function playRows(delay) {
733    var els = rows.map(function (id) { return document.getElementById(id); });
734    els.forEach(function (el) {
735      if (!el) return;
736      el.style.transition = 'none';
737      el.style.opacity    = '0';
738      el.style.transform  = 'translateX(-10px)';
739    });
740    requestAnimationFrame(function () {
741      requestAnimationFrame(function () {
742        els.forEach(function (el, i) {
743          if (!el) return;
744          setTimeout(function () {
745            el.style.transition = 'opacity 0.35s ease-out, transform 0.35s ease-out';
746            el.style.opacity    = '1';
747            el.style.transform  = 'translateX(0)';
748          }, delay + i * 75);
749        });
750      });
751    });
752  }
753
754  document.addEventListener('DOMContentLoaded', function () { playRows(400); });
755  chart.addEventListener('mouseenter', function () { playRows(0); });
756})();
757
758// ── Counter animation (ai: ai-onpage-chart) ───────────────────────────────────
759(function () {
760  var container = document.getElementById('ai-onpage-chart');
761  if (!container) return;
762
763  var DURATION = 900;
764  var animating = false;
765  var boxes = [
766    { numId: 'ai-num-1', badgeId: 'ai-badge-1', target: 1876, type: 'decimal1',     final: '187,6' },
767    { numId: 'ai-num-2', badgeId: 'ai-badge-2', target: 2341, type: 'int_thousands', final: '2.341' },
768    { numId: 'ai-num-3', badgeId: 'ai-badge-3', target: 823,  type: 'int',           final: '823'   },
769    { numId: 'ai-num-4', badgeId: 'ai-badge-4', target: 562,  type: 'decimal1',      final: '56,2'  },
770  ];
771
772  function fmt(val, type) {
773    if (type === 'decimal1') {
774      return Math.floor(val / 10) + ',' + (val % 10);
775    }
776    if (type === 'int_thousands') {
777      var v = Math.round(val);
778      return v >= 1000
779        ? Math.floor(v / 1000) + '.' + (v % 1000).toString().padStart(3, '0')
780        : v.toString();
781    }
782    return Math.round(val).toString();
783  }
784
785  function play() {
786    if (animating) return;
787    animating = true;
788    boxes.forEach(function (b) {
789      var badge = document.getElementById(b.badgeId);
790      if (badge) { badge.style.transition = 'none'; badge.style.opacity = '0'; }
791    });
792    var start = performance.now();
793    function step(now) {
794      var t    = Math.min((now - start) / DURATION, 1);
795      var ease = 1 - Math.pow(1 - t, 3);
796      boxes.forEach(function (b) {
797        var el = document.getElementById(b.numId);
798        if (el) el.textContent = fmt(Math.round(ease * b.target), b.type);
799      });
800      if (t < 1) {
801        requestAnimationFrame(step);
802      } else {
803        boxes.forEach(function (b) {
804          var el    = document.getElementById(b.numId);
805          var badge = document.getElementById(b.badgeId);
806          if (el) el.textContent = b.final;
807          if (badge) { badge.style.transition = 'opacity 0.3s ease-out'; badge.style.opacity = '1'; }
808        });
809        animating = false;
810      }
811    }
812    requestAnimationFrame(step);
813  }
814
815  container.addEventListener('mouseenter', play);
816})();
817
818// ── Testimonials carousel (google only) ───────────────────────────────────────
819(function () {
820  var outer = document.getElementById('testimonials-outer');
821  var track = document.getElementById('testimonials-track');
822  if (!track || !outer) return;
823  var cards = Array.from(track.querySelectorAll('.t-card'));
824  var dots = Array.from(document.querySelectorAll('#testimonials-dots .t-dot'));
825  var n = cards.length;
826  var active = 1;
827  var cardW = 560;
828  var carouselTimer = null;
829
830  function scheduleNext() {
831    clearTimeout(carouselTimer);
832    carouselTimer = setTimeout(function () { go(active + 1); }, 5000);
833  }
834
835  function calcCardW() {
836    var ow = outer.offsetWidth;
837    cardW = Math.min(560, Math.max(300, ow * 0.68));
838    cards.forEach(function (c) { c.style.width = cardW + 'px'; });
839  }
840
841  function updateCarousel() {
842    var ow = outer.offsetWidth;
843    var offset = (ow - cardW) / 2 - active * cardW;
844    track.style.transform = 'translateX(' + offset + 'px)';
845    cards.forEach(function (c, i) {
846      c.classList.toggle('is-center', i === active);
847      c.classList.toggle('is-side', i !== active);
848    });
849    dots.forEach(function (d, i) { d.classList.toggle('active', i === active); d.setAttribute('aria-selected', i === active ? 'true' : 'false'); });
850  }
851
852  function go(idx) {
853    active = ((idx % n) + n) % n;
854    updateCarousel();
855    scheduleNext();
856  }
857
858  track.addEventListener('transitionend', scheduleNext);
859  cards.forEach(function (c, i) {
860    c.addEventListener('click', function () { if (i !== active) go(i); });
861  });
862  dots.forEach(function (d, i) {
863    d.addEventListener('click', function () { go(i); });
864  });
865  window.addEventListener('resize', function () { calcCardW(); updateCarousel(); });
866
867  function init() { calcCardW(); updateCarousel(); scheduleNext(); }
868  if (document.readyState === 'loading') {
869    document.addEventListener('DOMContentLoaded', init);
870  } else {
871    init();
872  }
873})();
874
875// ── AI button border rotation (ai only) ───────────────────────────────────────
876(function () {
877  var buttons = document.querySelectorAll('.ai-button');
878  if (!buttons.length) return;
879  var animRunning = false;
880  var angle = 0;
881  var lastTime = 0;
882  var duration = 2500;
883
884  function step(timestamp) {
885    if (!animRunning) return;
886    if (!lastTime) lastTime = timestamp;
887    angle = (angle + ((timestamp - lastTime) / duration) * 360) % 360;
888    lastTime = timestamp;
889    buttons.forEach(function (btn) {
890      if (!btn.matches(':hover')) {
891        btn.style.setProperty('--ai-border-angle', angle + 'deg');
892      }
893    });
894    requestAnimationFrame(step);
895  }
896
897  new IntersectionObserver(function (entries) {
898    var visible = entries.some(function (e) { return e.isIntersecting; });
899    if (visible && !animRunning) {
900      animRunning = true;
901      lastTime = 0;
902      requestAnimationFrame(step);
903    } else if (!visible) {
904      animRunning = false;
905    }
906  }, { threshold: 0.1 }).observe(buttons[0]);
907  buttons.forEach(function (btn) {
908    new IntersectionObserver(function (entries) {
909      var visible = entries.some(function (e) { return e.isIntersecting; });
910      if (visible && !animRunning) { animRunning = true; lastTime = 0; requestAnimationFrame(step); }
911      else if (!visible && !Array.from(buttons).some(function (b) { return b.getBoundingClientRect().top < window.innerHeight; })) { animRunning = false; }
912    }, { threshold: 0.1 }).observe(btn);
913  });
914})();
915
916// ── Sticky subnav: google (sticky-subnav / features-sentinel) ─────────────────
917(function () {
918  var subnav   = document.getElementById('sticky-subnav');
919  var sentinel = document.getElementById('features-sentinel');
920  if (!subnav || !sentinel) return;
921
922  function show() {
923    subnav.style.transform = 'translateY(0)';
924    subnav.style.opacity = '1';
925    subnav.style.pointerEvents = 'auto';
926    subnav.removeAttribute('inert');
927  }
928  function hide() {
929    subnav.style.transform = 'translateY(-100%)';
930    subnav.style.opacity = '0';
931    subnav.style.pointerEvents = 'none';
932    subnav.setAttribute('inert', '');
933  }
934  function check() {
935    sentinel.getBoundingClientRect().top <= 120 ? show() : hide();
936  }
937
938  window.addEventListener('scroll', check, { passive: true });
939  check();
940})();
941
942// ── Sticky subnav: amazon (amz-sticky-subnav / amz-features-sentinel) ─────────
943(function () {
944  var subnav   = document.getElementById('amz-sticky-subnav');
945  var sentinel = document.getElementById('amz-features-sentinel');
946  if (!subnav || !sentinel) return;
947
948  function show() {
949    subnav.style.transform = 'translateY(0)';
950    subnav.style.opacity = '1';
951    subnav.style.pointerEvents = 'auto';
952    subnav.removeAttribute('inert');
953  }
954  function hide() {
955    subnav.style.transform = 'translateY(-100%)';
956    subnav.style.opacity = '0';
957    subnav.style.pointerEvents = 'none';
958    subnav.setAttribute('inert', '');
959  }
960  function check() {
961    sentinel.getBoundingClientRect().top <= 120 ? show() : hide();
962  }
963
964  window.addEventListener('scroll', check, { passive: true });
965  check();
966})();
967
968// ── Sticky subnav: ai (ai-sticky-subnav / ai-features-sentinel, uses inert) ───
969(function () {
970  var subnav   = document.getElementById('ai-sticky-subnav');
971  var sentinel = document.getElementById('ai-features-sentinel');
972  if (!subnav || !sentinel) return;
973
974  function show() {
975    subnav.style.transform = 'translateY(0)';
976    subnav.style.opacity = '1';
977    subnav.style.pointerEvents = 'auto';
978    subnav.removeAttribute('inert');
979  }
980  function hide() {
981    subnav.style.transform = 'translateY(-100%)';
982    subnav.style.opacity = '0';
983    subnav.style.pointerEvents = 'none';
984    subnav.setAttribute('inert', '');
985  }
986  function check() {
987    sentinel.getBoundingClientRect().top <= 120 ? show() : hide();
988  }
989
990  window.addEventListener('scroll', check, { passive: true });
991  check();
992})();
993
994// ── Shared page init: sticky-subnav aria-hidden, main-nav move, tilt, testimonials ──
995(function() {
996  // inert is set in HTML and managed via show()/hide() above
997
998  // Move global #main-nav into #heroSection as first child (skip if inline script already did it)
999  var nav = document.getElementById('main-nav');
1000  var hero = document.getElementById('heroSection');
1001  if (nav && hero && hero.firstChild !== nav) {
1002    hero.insertBefore(nav, hero.firstChild);
1003  }
1004
1005  document.addEventListener('DOMContentLoaded', function() {
1006    var track = document.getElementById('testimonials-track');
1007
1008    var cards = Array.from(document.querySelectorAll('.feature-row .glasrand-outer, .feature-row-reverse .glasrand-outer, .feature-row .glasrand-outer-dark, .feature-row-reverse .glasrand-outer-dark')).filter(function(card) {
1009      var main = card.closest('main');
1010      return !main || main.hasAttribute('data-tilt-sections'); // Tilt-Hover im Hero immer, in Sections nur auf den mit data-tilt-sections markierten Hub-Seiten (google/amazon/ai)
1011    });
1012    cards.forEach(function(card) {
1013      var rafId = null;
1014      var lastE = null;
1015      card.addEventListener('mouseenter', function() {
1016        card.style.willChange = 'transform';
1017        card.style.animationPlayState = 'paused';
1018        card.style.transition = 'transform 0.12s ease, box-shadow 0.2s ease';
1019      });
1020      card.addEventListener('mousemove', function(e) {
1021        if (window.innerWidth < 672) return;
1022        lastE = e;
1023        if (rafId) return;
1024        rafId = requestAnimationFrame(function() {
1025          rafId = null;
1026          if (!lastE) return;
1027          var rect = card.getBoundingClientRect();
1028          var cx = rect.left + rect.width / 2;
1029          var cy = rect.top + rect.height / 2;
1030          var dx = (lastE.clientX - cx) / (rect.width / 2);
1031          var dy = (lastE.clientY - cy) / (rect.height / 2);
1032          var maxDeg = 7;
1033          var rotX = (-dy * maxDeg).toFixed(2);
1034          var rotY = (dx * maxDeg).toFixed(2);
1035          card.style.transform = 'perspective(700px) rotateX(' + rotX + 'deg) rotateY(' + rotY + 'deg) scale(1.025)';
1036        });
1037      });
1038      card.addEventListener('mouseleave', function() {
1039        lastE = null;
1040        card.style.transition = 'transform 0.45s ease, box-shadow 0.3s ease';
1041        card.style.transform = 'perspective(700px) rotateX(0deg) rotateY(0deg) scale(1)';
1042        setTimeout(function() {
1043          card.style.willChange = '';
1044          card.style.animationPlayState = '';
1045          card.style.transition = '';
1046          card.style.transform = '';
1047        }, 450);
1048      });
1049    });
1050
1051    // Hero card tilt (google/amazon hero cards)
1052    [{ id: 'cc-hero-card-1', baseRotate: 3, baseZ: '1' },
1053     { id: 'cc-hero-card-2', baseRotate: -1.5, baseZ: '2' }].forEach(function(cfg) {
1054      var hCard = document.getElementById(cfg.id);
1055      if (!hCard) return;
1056      var hRaf = null;
1057      var hLastE = null;
1058      hCard.addEventListener('mousemove', function(e) {
1059        if (window.innerWidth < 672) return;
1060        hLastE = e;
1061        if (hRaf) return;
1062        hRaf = requestAnimationFrame(function() {
1063          hRaf = null;
1064          if (!hLastE) return;
1065          var rect = hCard.getBoundingClientRect();
1066          var dx = (hLastE.clientX - (rect.left + rect.width  / 2)) / (rect.width  / 2);
1067          var dy = (hLastE.clientY - (rect.top  + rect.height / 2)) / (rect.height / 2);
1068          hCard.style.transition = 'transform 0.08s ease-out';
1069          hCard.style.transform  = 'rotate(' + cfg.baseRotate + 'deg) perspective(700px) rotateX(' + (-dy * 12) + 'deg) rotateY(' + (dx * 12) + 'deg) scale(1.04)';
1070          hCard.style.zIndex     = '4';
1071        });
1072      });
1073      hCard.addEventListener('mouseleave', function() {
1074        hLastE = null;
1075        hCard.style.transition = 'transform 0.45s cubic-bezier(0.23,1,0.32,1)';
1076        hCard.style.transform  = 'rotate(' + cfg.baseRotate + 'deg)';
1077        hCard.style.zIndex     = cfg.baseZ;
1078      });
1079    });
1080
1081// ── Hero card 3D tilt (api / technologie-analyse) ────────────────────────────
1082(function () {
1083  [{ id: 'bb-hero-card-1', baseRotate: 3,    baseZ: '1' },
1084   { id: 'bb-hero-card-2', baseRotate: -1.5, baseZ: '2' }].forEach(function(cfg) {
1085    var hCard = document.getElementById(cfg.id);
1086    if (!hCard) return;
1087    var hRaf = null, hLastE = null;
1088    hCard.addEventListener('mousemove', function(e) {
1089      hLastE = e;
1090      if (hRaf) return;
1091      hRaf = requestAnimationFrame(function() {
1092        hRaf = null;
1093        if (!hLastE) return;
1094        var r = hCard.getBoundingClientRect();
1095        var dx = (hLastE.clientX - (r.left + r.width  / 2)) / (r.width  / 2);
1096        var dy = (hLastE.clientY - (r.top  + r.height / 2)) / (r.height / 2);
1097        hCard.style.transition = 'transform 0.08s ease-out';
1098        hCard.style.transform  = 'rotate(' + cfg.baseRotate + 'deg) perspective(700px) rotateX(' + (-dy * 12) + 'deg) rotateY(' + (dx * 12) + 'deg) scale(1.04)';
1099        hCard.style.zIndex     = '4';
1100      });
1101    });
1102    hCard.addEventListener('mouseleave', function() {
1103      hLastE = null;
1104      hCard.style.transition = 'transform 0.45s cubic-bezier(0.23,1,0.32,1)';
1105      hCard.style.transform  = 'rotate(' + cfg.baseRotate + 'deg)';
1106      hCard.style.zIndex     = cfg.baseZ;
1107    });
1108  });
1109})();
1110
1111    // will-change for testimonials only during animation
1112    if (track) {
1113      var observer = new MutationObserver(function() {
1114        track.style.willChange = 'transform';
1115        clearTimeout(track._wcTimer);
1116        track._wcTimer = setTimeout(function() {
1117          track.style.willChange = 'auto';
1118        }, 600);
1119      });
1120      observer.observe(track, { attributes: true, attributeFilter: ['style'] });
1121    }
1122  });
1123})();

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.