PageSourceSearch

https://www.levantare.co.in/wp-content/themes/levantare_v1/Levantare_2026/Home_2026/home_2026.js

js levantare.co.in collected 2026-10-02 00:11:32 UTC 39,733 bytes, 1,014 lines download raw bytes

1// ── Wait for DOM to be fully loaded ──
2document.addEventListener('DOMContentLoaded', function () {
3
4  // ── Phone list spring animation ──
5  function animPhone() {
6    const items = [...document.querySelectorAll('.a-item')];
7    if (!items.length) return; // Guard: exit if no items found
8
9    items.forEach((el, i) => setTimeout(() => el?.classList?.add('in'), 350 + i * 180));
10    setTimeout(() => {
11      let i = 0;
12      setInterval(() => {
13        const el = items[i % items.length];
14        if (el) { // Guard: check if element exists
15          el.classList.remove('in');
16          setTimeout(() => el.classList.add('in'), 550);
17        }
18        i++;
19      }, 1700);
20    }, 2800); 
21  }
22  animPhone();
23
24  // ── Bar chart animation ──
25  setTimeout(() => {
26    const bars = [['wb1', '84'], ['wb2', '67'], ['wb3', '91'], ['wb4', '55'], ['wb5', '78']];
27    bars.forEach(([id, h], i) => {
28      const el = document.getElementById(id);
29      if (el) setTimeout(() => el.style.height = h + '%', 600 + i * 120);
30    });
31  }, 800);
32
33  // ── Lottie: web frame overlay (rotating gear + pulse) ──
34  (function () {
35    const el = document.getElementById('webLottie');
36    if (!el || !window.lottie) return;
37    const teal = [0, 0.655, 0.616, 1], blue = [0.024, 0.576, 0.89, 1], green = [0.137, 0.749, 0.267, 1];
38    const anim = {
39      v: "5.7.4", fr: 30, ip: 0, op: 120, w: 60, h: 60, nm: "web", ddd: 0, assets: [], layers: [
40        { ddd: 0, ind: 1, ty: 4, nm: "gear", sr: 1, ks: { o: { a: 0, k: 100 }, r: { a: 1, k: [{ t: 0, s: [0], e: [360] }, { t: 120, s: [360] }] }, p: { a: 0, k: [30, 30, 0] }, a: { a: 0, k: [0, 0, 0] }, s: { a: 0, k: [100, 100, 100] } }, ao: 0, shapes: [{ ty: "sr", sy: 2, d: 1, pt: { a: 0, k: 8 }, p: { a: 0, k: [0, 0] }, r: { a: 0, k: 0 }, ir: { a: 0, k: 14 }, is: { a: 0, k: 0 }, or: { a: 0, k: 22 }, os: { a: 0, k: 0 } }, { ty: "fl", c: { a: 0, k: blue }, o: { a: 0, k: 100 }, r: 1 }], ip: 0, op: 120, st: 0, bm: 0 },
41        { ddd: 0, ind: 2, ty: 4, nm: "inner", sr: 1, ks: { o: { a: 0, k: 100 }, r: { a: 0, k: 0 }, p: { a: 0, k: [30, 30, 0] }, a: { a: 0, k: [0, 0, 0] }, s: { a: 0, k: [100, 100, 100] } }, ao: 0, shapes: [{ ty: "el", d: 1, s: { a: 0, k: [16, 16] }, p: { a: 0, k: [0, 0] } }, { ty: "fl", c: { a: 0, k: [1, 1, 1, 1] }, o: { a: 0, k: 100 }, r: 1 }], ip: 0, op: 120, st: 0, bm: 0 },
42        { ddd: 0, ind: 3, ty: 4, nm: "ring", sr: 1, ks: { o: { a: 1, k: [{ t: 0, s: [80], e: [0] }, { t: 40, s: [0] }, { t: 41, s: [80], e: [0] }, { t: 80, s: [0] }, { t: 81, s: [80], e: [0] }, { t: 120, s: [0] }] }, r: { a: 0, k: 0 }, p: { a: 0, k: [30, 30, 0] }, a: { a: 0, k: [0, 0, 0] }, s: { a: 1, k: [{ t: 0, s: [100, 100, 100], e: [200, 200, 100] }, { t: 40, s: [200, 200, 100] }] } }, ao: 0, shapes: [{ ty: "el", d: 1, s: { a: 0, k: [44, 44] }, p: { a: 0, k: [0, 0] } }, { ty: "st", c: { a: 0, k: green }, o: { a: 0, k: 100 }, w: { a: 0, k: 2 }, lc: 2 }], ip: 0, op: 120, st: 0, bm: 0 }
43      ]
44    };
45    lottie.loadAnimation({ container: el, renderer: 'svg', loop: true, autoplay: true, animationData: anim });
46  })();
47
48  // ── Lottie: CTA section (star + orbit) ──
49  (function () {
50    const el = document.getElementById('ctaLottie');
51    if (!el || !window.lottie) return;
52    const blue = [0.024, 0.576, 0.89, 1], green = [0.137, 0.749, 0.267, 1];
53    const anim = {
54      v: "5.7.4", fr: 30, ip: 0, op: 90, w: 76, h: 76, nm: "cta", ddd: 0, assets: [], layers: [
55        { ddd: 0, ind: 1, ty: 4, nm: "orbit", sr: 1, ks: { o: { a: 0, k: 100 }, r: { a: 1, k: [{ t: 0, s: [0], e: [360] }, { t: 90, s: [360] }] }, p: { a: 0, k: [38, 38, 0] }, a: { a: 0, k: [0, 0, 0] }, s: { a: 0, k: [100, 100, 100] } }, ao: 0, shapes: [{ ty: "el", d: 1, s: { a: 0, k: [60, 60] }, p: { a: 0, k: [0, 0] } }, { ty: "st", c: { a: 0, k: blue }, o: { a: 0, k: 100 }, w: { a: 0, k: 4 }, lc: 2 }, { ty: "tm", s: { a: 0, k: 0 }, e: { a: 0, k: 72 }, o: { a: 0, k: 0 } }], ip: 0, op: 90, st: 0, bm: 0 },
56        { ddd: 0, ind: 2, ty: 4, nm: "star", sr: 1, ks: { o: { a: 0, k: 100 }, r: { a: 1, k: [{ t: 0, s: [-18], e: [18] }, { t: 45, s: [18], e: [-18] }, { t: 90, s: [-18] }] }, p: { a: 0, k: [38, 38, 0] }, a: { a: 0, k: [0, 0, 0] }, s: { a: 1, k: [{ t: 0, s: [90, 90, 100], e: [110, 110, 100] }, { t: 45, s: [110, 110, 100], e: [90, 90, 100] }, { t: 90, s: [90, 90, 100] }] } }, ao: 0, shapes: [{ ty: "sr", sy: 1, d: 1, pt: { a: 0, k: 5 }, p: { a: 0, k: [0, 0] }, r: { a: 0, k: 0 }, ir: { a: 0, k: 10 }, is: { a: 0, k: 0 }, or: { a: 0, k: 18 }, os: { a: 0, k: 0 } }, { ty: "fl", c: { a: 0, k: green }, o: { a: 0, k: 100 }, r: 1 }], ip: 0, op: 90, st: 0, bm: 0 },
57        { ddd: 0, ind: 3, ty: 4, nm: "dot", sr: 1, ks: { o: { a: 1, k: [{ t: 0, s: [100], e: [0] }, { t: 45, s: [0] }, { t: 46, s: [100], e: [0] }, { t: 90, s: [0] }] }, r: { a: 1, k: [{ t: 0, s: [0], e: [360] }, { t: 90, s: [360] }] }, p: { a: 0, k: [38, 38, 0] }, a: { a: 0, k: [0, 0, 0] }, s: { a: 1, k: [{ t: 0, s: [80, 80, 100], e: [180, 180, 100] }, { t: 45, s: [180, 180, 100] }] } }, ao: 0, shapes: [{ ty: "el", d: 1, s: { a: 0, k: [60, 60] }, p: { a: 0, k: [0, 0] } }, { ty: "st", c: { a: 0, k: green }, o: { a: 0, k: 100 }, w: { a: 0, k: 2 }, lc: 2 }], ip: 0, op: 90, st: 0, bm: 0 }
58      ]
59    };
60    lottie.loadAnimation({ container: el, renderer: 'svg', loop: true, autoplay: true, animationData: anim });
61  })();
62
63  // ── Generic carousel ──
64  function mkCarousel(trackId, prevId, nextId, dotsId, autoId, cardSel, vis, interval) {
65    const track = document.getElementById(trackId);
66    if (!track) return; // Guard: exit if track doesn't exist
67
68    const prev = document.getElementById(prevId),
69      next = document.getElementById(nextId),
70      dWrap = document.getElementById(dotsId),
71      aBtn = document.getElementById(autoId);
72
73    if (!prev || !next || !dWrap || !aBtn) return; // Guard: check all elements exist
74
75    const cards = [...track.querySelectorAll(cardSel)], total = cards.length, max = total - vis;
76    if (total === 0) return; // Guard: exit if no cards
77
78    let idx = 0, playing = false, timer = null;
79    for (let i = 0; i <= max; i++) {
80      const d = document.createElement('div');
81      d.className = 'cdot' + (i === 0 ? ' active' : '');
82      d.onclick = () => go(i);
83      dWrap.appendChild(d);
84    }
85    function go(n) {
86      idx = Math.max(0, Math.min(n, max));
87      const w = cards[0].offsetWidth + 20;
88      track.style.transform = `translateX(-${idx * w}px)`;
89      [...dWrap.querySelectorAll('.cdot')].forEach((d, i) => d.classList.toggle('active', i === idx));
90    }
91    prev.onclick = () => go(idx - 1);
92    next.onclick = () => go(idx + 1);
93    function startA() { playing = true; aBtn.style.background = 'var(--green)'; const svg = aBtn.querySelector('svg'); if (svg) svg.style.stroke = '#fff'; timer = setInterval(() => go(idx >= max ? 0 : idx + 1), interval); }
94    function stopA() { playing = false; clearInterval(timer); aBtn.style.background = 'var(--green-lt)'; const svg = aBtn.querySelector('svg'); if (svg) svg.style.stroke = 'var(--green)'; }
95    aBtn.onclick = () => playing ? stopA() : startA();
96    startA();
97  }
98
99  window.addEventListener('load', () => {
100    mkCarousel('tTr', 'tPr', 'tNx', 'tDts', 'tAu', '.tc', 3, 4000);
101    mkCarousel('bTr', 'bPr', 'bNx', 'bDts', 'bAu', '.bc', 3, 3500);
102  });
103
104  /* ── Mobile nav ── */
105  function toggleMenu() {
106    const menu = document.getElementById('mobileMenu');
107    if (menu) menu.classList.toggle('open');
108  }
109
110  /* ── Scroll reveal ── */
111  const obs = new IntersectionObserver(entries => {
112    entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add('vis'); obs.unobserve(e.target); } });
113  }, { threshold: 0.08 });
114  document.querySelectorAll('.rv').forEach(el => obs.observe(el));
115
116  /* ═══════════════════════════════════════
117      HERO CAROUSEL ENGINE (REFACTORED)
118  ═══════════════════════════════════════ */
119  const SLIDE_DURATION = 7000;
120  const TOTAL = 4;
121
122  let current = 0;
123  let autoTimer = null;
124  let isPaused = false;
125
126  const slides = document.querySelectorAll('.hero-slide');
127  const dotItems = document.querySelectorAll('.hero-dot-item');
128  const heroContainer = document.getElementById('heroSlides');
129
130  if (slides.length > 0) { // Guard: only run if slides exist
131
132    function animFill(el, targetPct, duration) {
133      if (!el) return;
134      el.style.transition = 'none';
135      el.style.width = '0%';
136      void el.offsetWidth;
137      el.style.transition = 'width ' + duration + 's ease';
138      requestAnimationFrame(() => {
139        setTimeout(() => { el.style.width = targetPct; }, 60);
140      });
141    }
142
143    function triggerSlideAnimations(idx) {
144      if (idx === 0) {
145        // no-op
146      }
147      if (idx === 1 || idx === 2) {
148        const progFill = document.getElementById(idx === 1 ? 's2ProgFill' : 's2ProgFill2');
149        if (progFill) animFill(progFill, '87%', 3.2);
150
151        const slide = document.getElementById(idx === 1 ? 'slide-1' : 'slide-2');
152        if (slide) {
153          slide.querySelectorAll('[class*="ra"]').forEach(el => {
154            el.style.animation = 'none';
155            void el.offsetWidth;
156            el.style.animation = '';
157          });
158        }
159      }
160      if (idx === 3) {
161        const bars = [['lcb1', 84], ['lcb2', 67], ['lcb3', 91], ['lcb4', 55], ['lcb5', 78]];
162        bars.forEach(([id, h], i) => {
163          const el = document.getElementById(id);
164          if (!el) return;
165          el.style.height = '0%';
166          setTimeout(() => { el.style.height = h + '%'; }, 200 + i * 120);
167        });
168        const phItems = document.querySelectorAll('#s3PhList .ph-asset-s3');
169        phItems.forEach(el => { el.classList.remove('in'); });
170        phItems.forEach((el, i) => { setTimeout(() => el.classList.add('in'), 300 + i * 160); });
171      }
172    }
173
174    function goTo(n) {
175      if (!slides[0]) return; // Guard: check slides exist
176
177      const prev = current;
178      current = ((n % TOTAL) + TOTAL) % TOTAL;
179
180      if (prev === current && slides[current] && slides[current].classList.contains('active')) return;
181
182      if (slides[prev]) {
183        slides[prev].classList.remove('active');
184        slides[prev].classList.add('exit');
185        setTimeout(() => { if (slides[prev]) slides[prev].classList.remove('exit'); }, 700);
186      }
187
188      if (slides[current]) slides[current].classList.add('active');
189
190      dotItems.forEach((item, i) => {
191        item?.classList?.toggle('on', i === current);
192      });
193
194      triggerSlideAnimations(current);
195    }
196
197    function startAuto() {
198      clearInterval(autoTimer);
199      autoTimer = setInterval(() => {
200        if (!isPaused) {
201          goTo(current + 1);
202        }
203      }, SLIDE_DURATION);
204    }
205
206    function resetAuto() {
207      clearInterval(autoTimer);
208      startAuto();
209    }
210
211    if (heroContainer) {
212      heroContainer.addEventListener('mouseenter', () => {
213        isPaused = true;
214      });
215
216      heroContainer.addEventListener('mouseleave', () => {
217        isPaused = false;
218      });
219    }
220
221    const hNext = document.getElementById('heroNext');
222    const hPrev = document.getElementById('heroPrev');
223
224    if (hNext) hNext.addEventListener('click', () => { goTo(current + 1); resetAuto(); });
225    if (hPrev) hPrev.addEventListener('click', () => { goTo(current - 1); resetAuto(); });
226
227    dotItems.forEach(item => {
228      item?.addEventListener('click', () => {
229        const targetIdx = parseInt(item.getAttribute('data-i'));
230        if (!isNaN(targetIdx)) {
231          goTo(targetIdx);
232          resetAuto();
233        }
234      });
235    });
236
237    document.addEventListener('keydown', e => {
238      if (e.key === 'ArrowRight') { goTo(current + 1); resetAuto(); }
239      if (e.key === 'ArrowLeft') { goTo(current - 1); resetAuto(); }
240    });
241
242    let touchStartX = 0;
243    const heroSlidesEl = document.getElementById('heroSlides');
244    if (heroSlidesEl) {
245      heroSlidesEl.addEventListener('touchstart', e => { touchStartX = e.touches[0].clientX; }, { passive: true });
246      heroSlidesEl.addEventListener('touchend', e => {
247        const dx = e.changedTouches[0].clientX - touchStartX;
248        if (Math.abs(dx) > 50) { goTo(dx < 0 ? current + 1 : current - 1); resetAuto(); }
249      }, { passive: true });
250    }
251
252    triggerSlideAnimations(0);
253    startAuto();
254
255  } // End of slides.length guard
256
257  const assetData = [
258    { label: 'Building', value:15, color: '#4472c4' },
259    { label: 'Computer and Accessories', value:561, color: '#ed7d31' },
260    { label: 'Electrical Equipments', value:162, color: '#a5a5a5' },
261    { label: 'Furniture and Fittings', value: 1599, color: '#ffc000' },
262    { label: 'Motor Car', value:6, color: '#5b9bd5' },
263    { label: 'Office Equipments', value: 627, color: '#70ad47' },
264    { label: 'Plant & Machinery', value: 1062, color: '#70ad47' }
265  ];
266
267  const deptData = [
268    { l: 'Business Operation', v: 15}, { l: 'Engineering', v: 916}, { l: 'Facility', v: 1627 },
269    { l: 'Finance', v: 85 }, { l: 'General', v: 291 }, { l: 'General', v: 57 },
270    { l: 'IT', v: 867 }, { l: 'Maintenance', v: 16 }, { l: 'Product Management', v: 1 },
271    { l: 'QA', v: 135 }, { l: 'Sales Operation', v: 22 }
272    //  { l: 'Utilities', v: 587 },
273    // { l: 'Security', v: 805 }, { l: 'Telecom', v: 848 }
274  ];
275
276  const totalSum = 17925;
277
278  function populateAuditSection(suffix = '') {
279    const assetTable = document.getElementById('assetTableBody' + suffix);
280    if (assetTable) {
281      assetTable.innerHTML = ''; 
282      assetData.forEach(item => {
283        assetTable.innerHTML += `<tr><td>${item.label}</td><td>
283${item.value.toLocaleString()}</td></tr>`;
284      });
285    }
286
287    const deptTable = document.getElementById('deptTableBody' + suffix);
288    if (deptTable) {
289      deptTable.innerHTML = '';
290      deptData.forEach(item => {
291        deptTable.innerHTML += `<tr><td>${item.l}</td><td>${item.v.toLocaleString()}</td></tr>`;
292      });
293    }
294
295    const assetSum = assetData.reduce((s, x) => s + x.value, 0);
296    const deptSum = deptData.reduce((s, x) => s + x.v, 0);
297
298    const assetTotalCell = document.getElementById('assetTotalCell' + suffix);
299    if (assetTotalCell) assetTotalCell.textContent = assetSum.toLocaleString();
300
301    const deptTotalCell = document.getElementById('deptTotalCell' + suffix);
302    if (deptTotalCell) deptTotalCell.textContent = deptSum.toLocaleString();
303
304    const barTotalEl = document.getElementById('barTotal' + suffix);
305    if (barTotalEl) barTotalEl.textContent = deptSum.toLocaleString();
306
307    const pieLegend = document.getElementById('pieLeg' + suffix);
308    if (pieLegend) {
309      pieLegend.innerHTML = '';
310      assetData.forEach(item => {
311        pieLegend.innerHTML += `
312            <div class="leg-item">
313                <span class="leg-sq" style="background:${item.color}"></span>
314                ${item.label}: ${item.value.toLocaleString()}
315            </div>`;
316      });
317    }
318
319    const pieCtx = document.getElementById('pieC' + suffix);
320    if (pieCtx && typeof Chart !== 'undefined') {
321      new Chart(pieCtx, {
322        type: 'pie',
323        data: {
324          labels: assetData.map(d => d.label),
325          datasets: [{
326            data: assetData.map(d => d.value),
327            backgroundColor: assetData.map(d => d.color),
328            borderWidth: 2,
329            borderColor: '#fff'
330          }]
331        },
332        options: {
333          responsive: true,
334          maintainAspectRatio: false,
335          layout: { padding: 15 },
336          plugins: {
337            legend: { display: false },
338            tooltip: {
339              backgroundColor: 'rgba(255, 255, 255, 0.95)',
340              titleColor: '#1a2a4a',
341              bodyColor: '#444',
342              bodyFont: { size: 9 },
343              borderColor: '#e2e4ea',
344              borderWidth: 1,
345              padding: 6,
346              cornerRadius: 4,
347              caretPadding: 10,
348              displayColors: true,
349              callbacks: {
350                label: c => [
351                  ` ${c.label}`,
352                  ` ${c.raw.toLocaleString()} (${Math.round(c.raw / assetSum * 100)}%)`
353                ]
354              }
355            }
356          }
357        }
358      });
359    }
360
361    const barCtx = document.getElementById('barC' + suffix);
362    if (barCtx && typeof Chart !== 'undefined') {
363      new Chart(barCtx, {
364        type: 'bar',
365        data: {
366          labels: deptData.map(d => d.l),
367          datasets: [{
368            label: 'Total',
369            data: deptData.map(d => d.v),
370            backgroundColor: [
371              '#3b5bdb', '#4dabf7', '#15aabf', '#12b886', '#40c057',
372              '#82c91e', '#fab005', '#fd7e14', '#fa5252', '#be4bdb',
373              '#7950f2', '#4c6ef5', '#228be6', '#15aabf'
374            ],
375            borderRadius: 4,
376            borderSkipped: false,
377            barPercentage: 0.9,
378            categoryPercentage: 0.8
379          }]
380        },
381        options: {
382          responsive: true,
383          maintainAspectRatio: false,
384          layout: { padding: { top: 15, bottom: 10, left: 5, right: 5 } },
385          plugins: {
386            legend: { display: false },
387            tooltip: {
388              backgroundColor: 'rgba(255, 255, 255, 0.95)',
389              titleColor: '#1a2a4a',
390              bodyColor: '#444',
391              bodyFont: { size: 9 },
392              borderColor: '#e2e4ea',
393              borderWidth: 1,
394              padding: 6,
395              cornerRadius: 4,
396              caretPadding: 10,
397              callbacks: { label: c => ` ${c.raw.toLocaleString()} assets` }
398            }
399          },
400          scales: {
401            x: {
402              ticks: {
403                display: false,
404                color: '#666',
405                font: { size: 8 },
406                maxRotation: 0,
407                autoSkip: true,
408                padding: 5
409              },
410              grid: { display: false }
411            },
412            y: {
413              ticks: { color: '#666', font: { size: 8 }, callback: v =>
413 v.toLocaleString() },
414              grid: { color: 'rgba(0,0,0,0.06)' },
415              beginAtZero: true,
416              max: 4200
417            }
418          }
419        },
420        plugins: [{
421          id: 'dataLabels',
422          afterDatasetsDraw(chart) {
423            const { ctx, data } = chart;
424            ctx.save();
425            ctx.font = '500 7.5px Inter';
426            ctx.fillStyle = '#333';
427            ctx.textAlign = 'center';
428            chart.getDatasetMeta(0).data.forEach((bar, i) => {
429              ctx.fillText(data.datasets[0].data[i].toLocaleString(), bar.x, bar.y - 3);
430            });
431            ctx.restore();
432          }
433        }]
434      });
435    }
436  }
437
438  populateAuditSection();
439  populateAuditSection('2');
440
441  // Testimonial — driven by Home_testimonial_2026.json
442  (function () {
443    const track = document.getElementById('testimonialTrack');
444    if (!track) return; // Guard: exit if track doesn't exist
445
446    const prevBtn = document.getElementById('prevTestiBtn');
447    const nextBtn = document.getElementById('nextTestiBtn');
448
449    function buildCard(item) {
450      const card = document.createElement('div');
451      card.className = 'testi-card';
452      card.innerHTML = `
453      <p class="testi-quote">"${item.quote}"</p>
454      <div class="testi-author">
455        <div>
456          <div class="testi-name">${item.name}</div>
457          <div class="testi-role">${item.role}</div>
458          <div class="testi-org">${item.org}</div>
459        </div>
460      </div>
461    `;
462      return card;
463    }
464
465    function initCarousel(testimonials) {
466      track.innerHTML = '';
467      testimonials.forEach(item => track.appendChild(buildCard(item)));
468      const allCards = Array.from(track.children);
469
470      if (allCards.length <= 1) {
471        if (prevBtn) prevBtn.style.display = 'none';
472        if (nextBtn) nextBtn.style.display = 'none';
473        track.style.justifyContent = 'center';
474        track.style.transform = 'none';
475        if (allCards.length === 1) allCards[0].classList.add('active');
476        return;
477      }
478
479      if (prevBtn) prevBtn.style.display = '';
480      if (nextBtn) nextBtn.style.display = '';
481
482      let currentIndex = 0;
483
484      // function updateCoverflow() {
485      //   const containerWidth = track.parentElement.offsetWidth;
486      //   const cardWidth = allCards[0].offsetWidth;
487      //   track.style.transition = 'transform 0.7s cubic-bezier(0.23, 1, 0.32, 1)';
488      //   const centerOffset = containerWidth / 2 - currentIndex * cardWidth - cardWidth / 2;
489      //   track.style.transform = `translateX(${centerOffset}px)`;
490      //   allCards.forEach((card, i) => card.classList.toggle('active', i === currentIndex));
491      // }
492      function updateCoverflow() {
493        const sideSpace = 48; // Permanent left & right space
494
495        const containerWidth = track.parentElement.offsetWidth;
496        const cardWidth = allCards[0].offsetWidth;
497
498        track.style.transition =
499          'transform 0.7s cubic-bezier(0.23, 1, 0.32, 1)';
500
501        const centerOffset =
502          (containerWidth - sideSpace * 2) / 2 -
503          currentIndex * cardWidth -
504          cardWidth / 2;
505
506        track.style.transform = `translateX(${centerOffset}px)`;
507
508        allCards.forEach((card, i) =>
509          card.classList.toggle('active', i === currentIndex)
510        );
511      }
512
513      let autoPlayInterval;
514
515      function startAutoPlay() {
516        autoPlayInterval = setInterval(() => {
517          currentIndex = (currentIndex + 1) % allCards.length;
518          updateCoverflow();
519        }, 3000);
520      }
521
522      function resetAutoPlay() {
523        clearInterval(autoPlayInterval);
524        startAutoPlay();
525      }
526
527      if (nextBtn) {
528        nextBtn.addEventListener('click', () => {
529          currentIndex = (currentIndex + 1) % allCards.length;
530          updateCoverflow();
531          resetAutoPlay();
532        });
533      }
534
535      if (prevBtn) {
536        prevBtn.addEventListener('click', () => {
537          currentIndex = (currentIndex - 1 + allCards.length) % allCards.length;
538          updateCoverflow();
539          resetAutoPlay();
540        });
541      }
542
543      track.addEventListener('mouseenter', () => {
544        clearInterval(autoPlayInterval);
545      });
546
547      track.addEventListener('mouseleave', () => {
548        startAutoPlay();
549      });
550
551      window.addEventListener('resize', () => {
552        track.style.transition = 'none';
553        updateCoverflow();
554      });
555
556      setTimeout(() => {
557        track.style.transition = 'none';
558        updateCoverflow();
559        startAutoPlay();
560      }, 50);
561    }
562
563    if (typeof THEME_URI !== 'undefined') {
564      fetch(THEME_URI + '/Levantare_2026/Home_2026/Home_testimonial_2026.json')
565        .then(res => {
566          if (!res.ok) throw new Error('Could not load Home_testimonial_2026.json');
567          return res.json();
568        })
569        .then(data => initCarousel(data))
570        .catch(err => console.error('Testimonials error:', err));
571    }
572  })();
573
574  // ── Physical Verification Scan Cycling ──
575  (function () {
576    const wrap = document.getElementById('scanTargetWrap');
577    if (!wrap) return;
578
579    const modes = ['mode-qr', 'mode-qr', 'mode-barcode'];
580    const rows = document.querySelectorAll('.far-row');
581    const sfId = document.querySelector('.sf-id');
582    const sfLoc = document.querySelector('.sf-loc');
583
584    if (!rows.length) return; // Guard: exit if no rows
585
586    let currentIndex = 0;
587    //   const assetLoc = 'Default Location'; // Define assetLoc globally
588
589    function cycle() {
590      if (!rows.length) return;
591
592      const row = rows[currentIndex];
593      const assetId = row.getAttribute('data-id') || '';
594      const assetDesc = row.children[2]?.textContent || '';
595
596      ['mode-qr', 'mode-barcode', 'mode-rfid'].forEach(m => wrap.classList.remove(m));
597      const mode = modes[currentIndex % modes.length];
598      wrap.classList.add(mode);
599
600      if (sfId) sfId.style.opacity = '0';
601      if (sfLoc) sfLoc.style.opacity = '0';
602
603      if (currentIndex === 0) {
604        rows.forEach(r => {
605          const b = r.querySelector('.fbadge');
606          if (b) {
607            b.classList.remove('ok');
608            b.classList.add('pend');
609            b.textContent = '';
610          }
611          r.classList.remove('match');
612        });
613      }
614
615      setTimeout(() => {
616        if (sfId) {
617          sfId.textContent = `${assetId} ${assetDesc}`;
618          sfId.style.opacity = '1';
619          sfId.style.transition = 'opacity 0.5s ease';
620        }
621        if (sfLoc) {
622          // sfLoc.textContent = assetLoc;
623          sfLoc.style.opacity = '1';
624          sfLoc.style.transition = 'opacity 0.5s ease';
625        }
626      }, 1400);
627
628      const badge = row.querySelector('.fbadge');
629      if (badge && badge.classList.contains('pend')) {
630        setTimeout(() => {
631          badge.classList.remove('pend');
632          badge.classList.add('ok');
633          badge.textContent = '✓';
634          row.classList.add('match');
635        }, 2800);
636      }
637
638      currentIndex = (currentIndex + 1) % rows.length;
639    }
640
641    cycle();
642    setInterval(cycle, 6000);
643  })();
644
645  // ── Live Assets Tracked Counter ──
646  (function () {
647    const el = document.getElementById('assetsTrackedCount');
648    if (!el) return;
649
650    const REF_COUNT = 1621777;
651    const REF_DATE = new Date('2026-04-10T19:00:00').getTime();
652
653    function getCurrentGlobalCount() {
654      const now = Date.now();
655      const elapsedMs = now - REF_DATE;
656      const elapsedMins = Math.floor(elapsedMs / 60000);
657      return REF_COUNT + Math.max(0, elapsedMins);
658    }
659
660    function updateDisplay() {
661      const count = getCurrentGlobalCount();
662      el.textContent = count.toLocaleString('en-IN');
663    }
664
665    updateDisplay();
666    setInterval(updateDisplay, 60000);
667  })();
668
669
670
671  // ── Years of Experience Auto Counter ──
672  (function () {
673    const el = document.getElementById('yearsExperience');
674    if (!el) return;
675
676    const START_YEAR = 2008; // Company started in 2008
677
678    function updateYears() {
679      const currentYear = new Date().getFullYear();
680      const years = currentYear - START_YEAR;
681
682      el.textContent = years + '+';
683    }
684
685    updateYears();
686  })();
687
688
689  // ── Full-Page Scroll Manager ──
690//   (function () {
691//     if (!document.querySelector('.hero-section') ||
692//       (window.location.pathname !== '/' && window.location.pathname !== '/home_2026/')) return;
693
694//     const sections = [
695//       '#navbar-placeholder',
696//       '.hero-section',
697//       '.section',
698//       '.container',
699//       '.trio-wrapper',
700//       '.testimonials-bg',
701//       '.award-strip',
702//       '#footer-placeholder'
703//     ].map(s => document.querySelector(s)).filter(el => !!el);
704
705//     if (!sections || sections.length === 0) return;
706
707//     let isScrolling = false;
708//     let currentIdx = 0;
709
710//     function syncIndex() {
711//       if (isScrolling) return;
712
713//       if (window.innerHeight + window.scrollY >= document.body.scrollHeight - 10) {
714//         currentIdx = sections.length - 1;
715//         return;
716//       }
717
718//       const y = window.scrollY + (window.innerHeight * 0.4);
719//       sections.forEach((sec, i) => {
720//         const top = sec.getBoundingClientRect().top + window.scrollY;
721//         const bottom = top + sec.offsetHeight;
722//         if (y >= top && y < bottom) {
723//           currentIdx = i;
724//         }
725//       });
726//     }
727
728//     setTimeout(syncIndex, 500);
729//     window.addEventListener('scroll', syncIndex, { passive: true });
730
731//     function goTo(idx) {
732//       if (idx < 0 || idx >= sections.length || isScrolling) return;
733//       isScrolling = true;
734//       currentIdx = idx;
735
736//       const isLast = idx === sections.length - 1;
737//       let targetY = isLast
738//         ? document.body.scrollHeight - window.innerHeight
739//         : sections[idx].getBoundingClientRect().top + window.scrollY;
740
741//       if (idx > 0 && !isLast) {
742//         targetY -= 80;
743//       }
744
745//       window.scrollTo({
746//         top: targetY,
747//         behavior: 'smooth'
748//       });
749
750//       setTimeout(() => { isScrolling = false; }, 1000);
751//     }
752
753//     window.addEventListener('wheel', (e) => {
754//       if (window.innerWidth <= 1024) return;
755//       if (Math.abs(e.deltaY) < 30) return;
756
757//       if (!isScrolling) {
758//         e.preventDefault();
759//         if (e.deltaY > 0) goTo(currentIdx + 1);
760//         else goTo(currentIdx - 1);
761//       } else {
762//         e.preventDefault();
763//       }
764//     }, { passive: false });
765
766//     let ts = 0;
767//     window.addEventListener('touchstart', e => {
768//       ts = e.touches[0].clientY;
769//     }, { passive: true });
770
771//     window.addEventListener('touchmove', e => {
772//       if (window.innerWidth > 1024 && isScrolling) {
773//         e.preventDefault();
774//       }
775//     }, { passive: false });
776
777//     window.addEventListener('touchend', e => {
778//       if (window.innerWidth <= 1024) return;
779//       const te = e.changedTouches[0].clientY;
780//       const diff = ts - te;
781//       if (Math.abs(diff) > 50) {
782//         if (!isScrolling) {
783//           if (diff > 0) goTo(currentIdx + 1);
784//           else goTo(currentIdx - 1);
785//         }
786//       }
787//     }, { passive: true });
788
789//     window.addEventListener('keydown', (e) => {
790//       if (window.innerWidth <= 1024) return;
791
792//       if (['ArrowDown', 'PageDown'].includes(e.key)) {
793//         e.preventDefault();
794//         goTo(currentIdx + 1);
795//       } else if (['ArrowUp', 'PageUp'].includes(e.key)) {
796//         e.preventDefault();
797//         goTo(currentIdx - 1);
798//       }
799//     });
800//   })();
801
802  var targets = [4032, 20, 0, 43, 14, 22, 2901, 1131];
803  var subs = ['₹2,13,41,659', '₹21,000', '₹0', '₹11,000', '₹1', '₹422', '₹2,13,48,160', '₹4,500'];
804  var btargets = [3953, 24, 55];
805
806  function easeOut(t) { return 1 - (1 - t) * (1 - t) * (1 - t); }
807
808  function animNum(el, target, duration, delay) {
809    if (!el) return; // Guard: check element exists
810    setTimeout(function () {
811      var start = null;
812      function step(ts) {
813        if (!start) start = ts;
814        var p = Math.min((ts - start) / duration, 1);
815        el.textContent = Math.round(easeOut(p) * target);
816        if (p < 1) requestAnimationFrame(step);
817        else el.textContent = target;
818      }
819      requestAnimationFrame(step);
820    }, delay);
821  }
822
823  function startAnims() {
824    var cards = document.querySelectorAll('.card');
825    cards.forEach(function (c, i) {
826      setTimeout(function () {
827        c.classList.add('show');
828        animNum(document.getElementById('n' + i), targets[i], 600, 0);
829        setTimeout(function () {
830          var nEl = document.getElementById('n' + i);
831          if (nEl) {
832            const card = nEl.closest('.card');
833            const sub = card ? card.querySelector('.card-sub') : null;
834            if (sub) sub.textContent = subs[i];
835          }
836        }, 400);
837      }, i * 80 + 200);
838    });
839    setTimeout(function () {
840      var strip = document.getElementById('bottomStrip');
841      if (strip) strip.classList.add('show');
842      animNum(document.getElementById('bn0'), btargets[0], 700, 0);
843      animNum(document.getElementById('bn1'), btargets[1], 700, 100);
844      animNum(document.getElementById('bn2'), btargets[2], 700, 200);
845    }, cards.length * 80 + 400);
846  }
847
848  setTimeout(startAnims, 500);
849
850//   var tabs = document.querySelectorAll('.tab');
851//   tabs.forEach(function (t) {
852//     t.addEventListener('click', function () {
853//       tabs.forEach(function (x) { x.classList.remove('active'); });
854//       t.classList.add('active');
855//     });
856//   });
857
858//   var perBtns = document.querySelectorAll('.per-btn');
859//   perBtns.forEach(function (b) {
860//     b.addEventListener('click', function () {
861//       perBtns.forEach(function (x) { x.classList.remove('active'); });
862//       b.classList.add('active');
863//       var cards2 = document.querySelectorAll('.card');
864//       cards2.forEach(function (c) { c.classList.remove('show'); });
865//       var strip = document.getElementById('bottomStrip');
866//       if (strip) strip.classList.remove('show');
867//       setTimeout(startAnims, 200);
868//     });
869//   });
870
871  // ── Asset Management Dashboard UI ──
872  (function () {
873    var targets = [4032, 20, 0, 43, 14, 22, 2901, 1131];
874    var subs = ['₹171Cr', '₹4L','₹0', '₹36L', '₹12L', '₹29Cr', '₹152Cr', '₹18Cr'];
875    var btargets = [3953, 24, 55];
876
877    function easeOut(t) { return 1 - (1 - t) * (1 - t) * (1 - t); }
878
879    function animNum(el, target, duration, delay) {
880      if (!el) return;
881      setTimeout(function () {
882        var start = null;
883        function step(ts) {
884          if (!start) start = ts;
885          var p = Math.min((ts - start) / duration, 1);
886          el.textContent = Math.round(easeOut(p) * target);
887          if (p < 1) requestAnimationFrame(step);
888          else el.textContent = target;
889        }
890        requestAnimationFrame(step);
891      }, delay);
892    }
893
894    function startAnims() {
895      var cards = document.querySelectorAll('.dashboard-card');
896      cards.forEach(function (c, i) {
897        setTimeout(function () {
898          c.classList.add('show');
899          animNum(document.getElementById('n' + i), targets[i], 600, 0);
900          setTimeout(function () {
901            var nEl = document.getElementById('n' + i);
902            if (nEl) {
903              const card = nEl.closest('.dashboard-card');
904              const sub = card ? card.querySelector('.card-sub') : null;
905              if (sub) sub.textContent = subs[i];
906            }
907          }, 400);
908        }, i * 80 + 200);
909      });
910      setTimeout(function () {
911        var strip = document.getElementById('bottomStrip');
912        if (strip) strip.classList.add('show');
913        animNum(document.getElementById('bn0'), btargets[0], 700, 0);
914        animNum(document.getElementById('bn1'), btargets[1], 700, 100);
915        animNum(document.getElementById('bn2'), btargets[2], 700, 200);
916      }, cards.length * 80 + 400);
917    }
918
919    var win = document.getElementById('win');
920    if (!win) return;
921
922    var observer = new IntersectionObserver(function (entries) {
923      if (entries[0].isIntersecting) {
924        setTimeout(startAnims, 500);
925        observer.disconnect();
926      }
927    }, { threshold: 0.1 });
928    observer.observe(win);
929
930    // var tabs = document.querySelectorAll('.dashboard-tab');
931    // tabs.forEach(function (t) {
932    //   t.addEventListener('click', function () {
933    //     tabs.forEach(function (x) { x.classList.remove('active'); });
934    //     t.classList.add('active');
935    //   });
936    // });
937
938    var perBtns = document.querySelectorAll('.per-btn');
939    perBtns.forEach(function (b) {
940      b.addEventListener('click', function () {
941          
942        // Only allow "This year" button
943        if (b.textContent.trim() !== "This year") {
944          return;
945        }
946  
947        perBtns.forEach(function (x) { x.classList.remove('active'); });
948        b.classList.add('active');
949        var cards2 = document.querySelectorAll('.dashboard-card');
950        cards2.forEach(function (c) { c.classList.remove('show'); });
951        var strip = document.getElementById('bottomStrip');
952        if (strip) strip.classList.remove('show');
953        setTimeout(startAnims, 200);
954      });
955    });  
956  })();
957
958  var chartInited = false;
959
960  function ease(t) { return t < .5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2; }
961  function countUp(el, target, fmt, dur) {
962    if (!el) return; // Guard: check element exists
963    var s = performance.now();
964    (function f() {
965      var p = Math.min((performance.now() - s) / dur, 1);
966      el.textContent = fmt(Math.round(ease(p) * target));
967      if (p < 1) requestAnimationFrame(f);
968    })();
969  }
970
971  function initChart() {
972    if (chartInited) return;
973    chartInited = true;
974    countUp(document.getElementById('sv0'), 4032, function (v) { return v.toLocaleString(); }, 800);
975    countUp(document.getElementById('sv1'), 1714984831, function (v) { return '₹' + (v / 10000000).toFixed(2) + ' Cr'; }, 1000);
976    countUp(document.getElementById('sv2'), 1062847431, function (v) { return '₹' + (v / 10000000).toFixed(2) + ' Cr'; }, 1000);
977
978    var cats = [['Matched', 'Verified'], ['Not', 'Available'], ['Details', 'Required'], ['Building'], ['Not', 'Verifiable'], ['Software']];
979    const chartCtx = document.getElementById('vc');
980    if (!chartCtx || typeof Chart === 'undefined') return;
981
982    new Chart(chartCtx, {
983      data: {
984        labels: cats,
985        datasets: [
986          { type: 'bar', label: 'Gross Block', data: [1188905711, 19872985, 22801356, 382513738, 85877572, 15013468], backgroundColor: 'rgba(91,141,238,0.8)', borderColor: '#5b8dee', borderWidth: 1, borderRadius: 4, yAxisID: 'y', minBarLength: 4 },
987          { type: 'bar', label: 'Net Block', data: [762120459, 12965349, 14589433, 218125414, 53940314, 1106462], backgroundColor: 'rgba(111,207,151,0.8)', borderColor: '#6fcf97', borderWidth: 1, borderRadius: 4, yAxisID: 'y', minBarLength: 4 },
988          { type: 'line', label: 'Asset Count', data: [2463, 468, 54, 9, 957, 81], borderColor: '#f5a623', backgroundColor: '#fff', pointBackgroun
988dColor: '#fff', pointBorderColor: '#f5a623', pointBorderWidth: 2.5, pointRadius: 5, borderWidth: 2.5, tension: .35, yAxisID: 'y2' }
989        ]
990      },
991      options: {
992        responsive: true, maintainAspectRatio: false,
993        animation: { duration: 900, easing: 'easeOutQuart' },
994        plugins: { legend: { display: false }, tooltip: { backgroundColor: '#0f2652', titleFont: { size: 10 }, bodyFont: { size: 10 }, padding: 8, cornerRadius: 6, callbacks: { label: function (c) { return ' ' + c.dataset.label + ': ' + (c.datasetIndex < 2 ? '₹' + c.raw.toLocaleString('en-IN') : c.raw.toLocaleString('en-IN')); } } } },
995        scales: {
996          x: { ticks: { font: { size: 9 }, color: '#7a8fad', maxRotation: 0 }, grid: { display: false }, border: { display: false } },
997          y: { position: 'left', ticks: { font: { size: 9 }, color: '#7a8fad', callback: function (v) { return v >= 1e7 ? (v / 1e7).toFixed(1) + ' Cr' : (v >= 1e5 ? (v / 1e5).toFixed(0) + ' L' : v.toLocaleString()); } }, grid: { color: 'rgba(0,0,0,0.05)', drawTicks: false }, border: { display: false } },
998          y2: { position: 'right', ticks: { font: { size: 9 }, color: '#f5a623' }, grid: { display: false }, border: { display: false } }
999        }
1000      }
1001    });
1002  }
1003
1004  var root = document.getElementById('root');
1005  if (root) { 
1006    var dots = document.querySelectorAll('.sdot');
1007    root.addEventListener('scroll', function () {
1008      var active = Math.round(root.scrollTop / 550);
1009      dots.forEach(function (d, i) { d.classList.toggle('active', i === active); });
1010      if (root.scrollTop > 200) initChart();
1011    });
1012  }
1013
1014}); // End of DOMContentLoaded

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.