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.