1(function () { 2 "use strict"; 3 4 // ââ Reveal-on-scroll ââ 5 const obs = new IntersectionObserver((entries) => { 6 entries.forEach(e => { 7 if (e.isIntersecting) { 8 e.target.classList.add('in'); 9 // Once revealed, we don't need to observe it anymore 10 obs.unobserve(e.target); 11 } 12 }); 13 }, { threshold: 0.15 }); 14 15 function initReveal() { 16 document.querySelectorAll('.reveal').forEach(el => { 17 // If already in view (e.g. on page reload), add .in immediately 18 const rect = el.getBoundingClientRect(); 19 if (rect.top < window.innerHeight && rect.bottom > 0) { 20 el.classList.add('in'); 21 } else { 22 obs.observe(el); 23 } 24 }); 25 } 26 27 // Run on load and after a short delay to catch late-renders 28 initReveal(); 29 setTimeout(initReveal, 400); 30 31 // (Hero carousel removed - replaced by video player) 32 33 // Count-up stat animation 34 ; (function () { 35 function formatNumber(value, format, suffix) { 36 let str; 37 if (format === 'decimal') { 38 str = value.toFixed(1); 39 } else if (format === 'comma') { 40 str = Math.round(value).toLocaleString('en-IN'); 41 } else if (format === 'percent') { 42 str = Math.round(value) + '%'; 43 } else { 44 str = Math.round(value).toString(); 45 } 46 return suffix ? str + suffix : str; 47 } 48 49 function setFinalValue(el) { 50 const target = parseFloat(el.dataset.countTo); 51 const format = el.dataset.countFormat || 'plain'; 52 const suffix = el.dataset.countSuffix || ''; 53 el.textContent = formatNumber(target, format, suffix); 54 } 55 56 function animateCount(el) { 57 if (el.dataset.countDone === '1') return; 58 el.dataset.countDone = '1'; 59 const target = parseFloat(el.dataset.countTo); 60 const format = el.dataset.countFormat || 'plain'; 61 const suffix = el.dataset.countSuffix || ''; 62 const duration = 2600; 63 const start = performance.now(); 64 65 function tick(now) { 66 const progress = Math.min((now - start) / duration, 1); 67 const eased = 1 - Math.pow(1 - progress, 3); 68 el.textContent = formatNumber(target * eased, format, suffix); 69 if (progress < 1) { 70 requestAnimationFrame(tick); 71 } else { 72 setFinalValue(el); 73 } 74 } 75 76 requestAnimationFrame(tick); 77 } 78 79 const counters = document.querySelectorAll('[data-count-to]'); 80 if (!counters.length) return; 81 82 if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) { 83 counters.forEach(setFinalValue); 84 return; 85 } 86 87 if (!('IntersectionObserver' in window)) { 88 counters.forEach(animateCount); 89 return; 90 } 91 92 const counterObserver = new IntersectionObserver((entries) => { 93 entries.forEach(entry => { 94 if (entry.isIntersecting) { 95 animateCount(entry.target); 96 counterObserver.unobserve(entry.target); 97 } 98 }); 99 }, { threshold: 0.4 }); 100 101 counters.forEach(el => counterObserver.observe(el)); 102 })(); 103 104 105 // ââ FAQ toggle ââ 106 window.toggleFaq = function(btn) { 107 const item = btn.parentElement; 108 const wasOpen = item.classList.contains('open'); 109 document.querySelectorAll('.faq-item').forEach(i => i.classList.remove('open')); 110 if (!wasOpen) item.classList.add('open'); 111 }; 112 113 // ââ Seats bar fill ââ 114 setTimeout(() => { 115 const bar = document.getElementById('seatsFill'); 116 if (bar) bar.style.width = '46%'; // 37/80 â 46% 117 }, 600); 118 119 // ââ Sticky CTA show on scroll ââ 120 // Sticky CTA disabled for now. 121 let stickyClosed = true; 122 window.addEventListener('scroll', () => { 123 const sc = document.getElementById('stickyCta'); 124 if (!sc || stickyClosed) return; 125 if (window.scrollY > 800 && window.scrollY < (document.body.scrollHeight - 1200)) { 126 sc.classList.add('show'); 127 } else { 128 sc.classList.remove('show'); 129 } 130 }); 131 132 // ââ Live enrollment toast ââ 133 const toastData = [ 134 { init: 'PR', name: 'Priya R. · Delhi', msg: 'just enrolled in CAT 2026 Complete', time: '2 min ago' }, 135 { init: 'AK', name: 'Arjun K. · Pune', msg: 'started the Free Diagnostic Mock', time: '5 min ago' }, 136 { init: 'MS', name: 'Meera S. · Bangalore', msg: 'registered for Sunday Strategy Webinar', time: '7 min ago' }, 137 { init: 'RV', name: 'Rohit V. · Mumbai', msg: 'reserved a seat in May 4 batch', time: '11 min ago' }, 138 { init: 'DP', name: 'Divya P. · Hyderabad', msg: 'downloaded the Free 6-Month Planner', time: '13 min ago' }, 139 { init: 'SK', name: 'Saurabh K. · Chennai', msg: 'booked a free counselling call', time: '16 min ago' }, 140 ]; 141 let tIdx = 0; 142 function showToast() { 143 const t = toastData[tIdx % toastData.length]; 144 const el = document.getElementById('liveToast'); 145 if (!el) return; 146 const av = document.getElementById('toastAvatar'); 147 const nm = document.getElementById('toastName'); 148 const ms = document.getElementById('toastMsg'); 149 const tm = document.getElementById('toastTime'); 150 if (av) av.textContent = t.init; 151 if (nm) nm.textContent = t.name; 152 if (ms) ms.textContent = t.msg; 153 if (tm) tm.textContent = t.time; 154 el.style.display = 'flex';
155 el.style.animation = 'none'; el.offsetHeight; el.style.animation = 'slideUp 0.4s ease'; 156 tIdx++; 157 setTimeout(() => { el.style.opacity = '0'; setTimeout(() => { el.style.display = 'none'; el.style.opacity = '1'; }, 400); }, 5500); 158 } 159 // Live enrollment toast disabled for now. 160 // setTimeout(() => { showToast(); setInterval(showToast, 16000); }, 6000); 161 162 // âââ PDF DOWNLOAD MODAL âââ 163 const PDF_LIST = [ 164 { name: 'Formula Book', sub: 'Quant + DI shortcuts · 60 pages' }, 165 { name: 'Must Do CAT Questions', sub: '500 high-frequency CAT questions' }, 166 { name: 'CAT Handbook', sub: 'Complete syllabus + strategy guide' }, 167 { name: 'XAT Handbook', sub: 'Decision-making + GK prep guide' }, 168 { name: '50 Must Do LR Caselets', sub: 'Logical reasoning practice set' }, 169 { name: 'Top Bschool Interview Dockets', sub: 'IIM A/B/C/L interview transcripts' }, 170 { name: 'CMAT Handbook', sub: 'CMAT pattern + practice set' }, 171 { name: 'CET Handbook', sub: 'MAH-CET 2026 complete guide' }, 172 { name: 'NMAT Handbook', sub: 'NMIMS admission strategy' }, 173 { name: 'SNAP Handbook', sub: 'Symbiosis exam pattern + tips' }, 174 { name: 'Finance Dossier', sub: 'B-school finance terms decoded' } 175 ]; 176 177 let selectedPdf = null; 178 let signMode = 'signin'; // 'signin' or 'signup' 179 180 function pdfInitial(name) { 181 return name.split(' ').filter(w => w.length > 1).slice(0, 2).map(w => w[0]).join('').toUpperCase(); 182 } 183 184 function renderPdfGrid() { 185 const grid = document.getElementById('pdfGrid'); 186 if (!grid) return; 187 grid.innerHTML = PDF_LIST.map((p, i) => ` 188 <button class="pdf-item" type="button" onclick="selectPdf(${i})"> 189 <span class="pdf-icon">PDF</span> 190 <span class="pdf-info"> 191 <span class="pdf-name">${p.name}</span> 192 <span class="pdf-sub">${p.sub}</span> 193 </span> 194 <span class="pdf-item-arrow" aria-hidden="true">âº</span> 195 </button> 196 `).join(''); 197 } 198 renderPdfGrid(); 199 200 // ââ Scorecard Modal ââ 201 const STUDENT_SCORECARDS = [ 202 { 203 initials: 'PC', name: 'Protham Saha Chowdhury', exam: 'CAT 2024-25', 204 pct: '99.91', 205 sections: [['VARC', 'â', '98.17'], ['DILR', 'â', '100.00'], ['Quant', 'â', '95.31']], 206 overall: ['â', '99.91'], 207 quote: "CATKing's mocks were calibrated perfectly to actual exam difficulty â the wave-based launches kept me consistently challenged and improving week after week.", 208 converts: 'Top IIMs & B-Schools', addl: 'CAT 99.91%ile' 209 }, 210 { 211 initials: 'NA', name: 'Naman', exam: 'CAT 2024-25', 212 pct: '98.98', 213 sections: [['VARC', 'â', '92.59'], ['DILR', 'â', '96.13'], ['Quant', 'â', '99.70']], 214 overall: ['â', '98.98'], 215 quote: "Sumit Sir's VARC modules cracked the toughest section for me. The doubt-clearing sessions and recorded videos made all the difference in my prep.", 216 converts: 'Top IIMs & B-Schools', addl: 'CAT 98.98%ile' 217 }, 218 { 219 initials: 'AS', name: 'Aashna Suneja', exam: 'CAT 2024-25', 220 pct: '98.48', 221 sections: [['VARC', 'â', '92.59'], ['DILR', 'â', '98.78'], ['Quant', 'â', '96.91']], 222 overall: ['â', '98.48'], 223 quote: "The level of mentorship at CATKing is unmatched. From doubt-clearing to interview prep, every stage was handled with precision and personal attention.", 224 converts: 'Top IIMs & B-Schools', addl: 'CAT 98.48%ile' 225 }, 226 { 227 initials: 'RK', name: 'Rohan Khanna', exam: 'CAT 2024-25', 228 pct: '99.37', 229 sections: [['VARC', 'â', '99.85'], ['DILR', 'â', '95.87'], ['Quant', 'â', '93.36']], 230 overall: ['â', '99.37'], 231 quote: "CATKing's structured curriculum and analytics dashboard helped me identify weak areas early. The mocks felt exactly like the real CAT â no surprises on D-day.", 232 converts: 'Top IIMs & B-Schools', addl: 'CAT 99.37%ile' 233 }, 234 { 235 initials: 'AS', name: 'Anssh Sharma', exam: 'CAT 2024-25', 236 pct: '99.53', 237 sections: [['VARC', 'â', '93.92'], ['DILR', 'â', '99.89'], ['Quant', 'â', '97.64']], 238 overall: ['â', '99.53'], 239 quote: "Daily live classes from IIM-A/B/C alumni faculty plus the self-study library gave me everything I needed. The 1-on-1 mentor calls were game-changers.", 240 converts: 'Top IIMs & B-Schools', addl: 'CAT 99.53%ile' 241 }, 242 { 243 initials: 'DK', name: 'Divyansh Kumar', exam: 'CAT 2024-25', 244 pct: '99.16', 245 sections: [['VARC', 'â', '95.04'], ['DILR', 'â', '99.06'], ['Quant', 'â', '94.45']], 246 overall: ['â', '99.16'], 247 quote: "The 130+ LOD1 & LOD2 practice e-books built my fundamentals brick by brick. By mock 8, I was scoring 95+ consistently.", 248 converts: 'Top IIMs & B-Schools', addl: 'CAT 99.16%ile' 249 }, 250 { 251 initials: 'PB', name: 'Prakhar Baheti', exam: 'CAT 2024-25', 252 pct: '99.02', 253 sections: [['VARC', 'â', '93.92'], ['DILR', 'â', '99.86'], ['Quant', 'â', '92.22']], 254 overall: ['â', '99.02'], 255 quote: "What sets CATKing apart is the small-batch attention. Daily targets and weekend strategy sessions kept me accountable through the entire 6-month grind.", 256 converts: 'Top IIMs & B-Schools', addl: 'CAT 99.02%ile' 257 }, 258 { 259 initials: 'DA', name: 'Divyam Agarwal', exam: 'CAT 2024-25', 260 pct: '99.00', 261 sections: [['VARC', 'â', '91.64'], ['DILR', 'â', '99.70'], ['Quant', 'â', '96.91']], 262 overall: ['â', '99.00'], 263 quote: "From struggling with %tiles to walking into my dream IIM â CATKing's faculty and mock series made the journey not just possible but enjoyable.", 264 converts: 'Top IIMs & B-Schools', addl: 'CAT 99.00%ile' 265 }, 266 { 267 initials: 'AS', name: 'Amrit Singh', exam: 'CAT 2024-25', 268 pct: '98.92', 269 sections: [['VARC', 'â', '97.66'], ['DILR', 'â', '91.74'], ['Quant', 'â', '95.12']], 270 overall: ['â', '98.92'], 271 quote: "The MICA-alumni mock interviews and SOP reviews polished my profile in
271ways I didn't think were possible. Truly end-to-end prep.", 272 converts: 'Top IIMs & B-Schools', addl: 'CAT 98.92%ile' 273 }, 274 { 275 initials: 'SS', name: 'Shivam Singh', exam: 'CAT 2024-25', 276 pct: '98.72', 277 sections: [['VARC', 'â', '97.66'], ['DILR', 'â', '91.74'], ['Quant', 'â', '99.77']], 278 overall: ['â', '98.72'], 279 quote: "Speed-focused strategy is what I needed for CAT, and CATKing delivered. The sectional drills helped me cross 99 in DILR after months of being stuck at 90.", 280 converts: 'Top IIMs & B-Schools', addl: 'CAT 98.72%ile' 281 }, 282 { 283 initials: 'PK', name: 'Parushi Singh Kunwar', exam: 'CAT 2024-25', 284 pct: '98.57', 285 sections: [['VARC', 'â', '97.37'], ['DILR', 'â', '99.34'], ['Quant', 'â', '82.87']], 286 overall: ['â', '98.57'], 287 quote: "CATKing's mocks were calibrated perfectly to actual exam difficulty â the wave-based launches kept me consistently challenged and improving week after week.", 288 converts: 'Top IIMs & B-Schools', addl: 'CAT 98.57%ile' 289 }, 290 { 291 initials: 'AK', name: 'Anish Kumar', exam: 'CAT 2024-25', 292 pct: '98.42', 293 sections: [['VARC', 'â', '89.91'], ['DILR', 'â', '97.98'], ['Quant', 'â', '98.93']], 294 overall: ['â', '98.42'], 295 quote: "Sumit Sir's VARC modules cracked the toughest section for me. The doubt-clearing sessions and recorded videos made all the difference in my prep.", 296 converts: 'Top IIMs & B-Schools', addl: 'CAT 98.42%ile' 297 }, 298 { 299 initials: 'PK', name: 'Pranav Kumar', exam: 'CAT 2024-25', 300 pct: '98.40', 301 sections: [['VARC', 'â', '89.91'], ['DILR', 'â', '99.57'], ['Quant', 'â', '95.04']], 302 overall: ['â', '98.40'], 303 quote: "The level of mentorship at CATKing is unmatched. From doubt-clearing to interview prep, every stage was handled with precision and personal attention.", 304 converts: 'Top IIMs & B-Schools', addl: 'CAT 98.40%ile' 305 }, 306 { 307 initials: 'NP', name: 'Nikhil Patil', exam: 'CAT 2024-25', 308 pct: '98.32', 309 sections: [['VARC', 'â', '87.85'], ['DILR', 'â', '97.96'], ['Quant', 'â', '99.12']], 310 overall: ['â', '98.32'], 311 quote: "CATKing's structured curriculum and analytics dashboard helped me identify weak areas early. The mocks felt exactly like the real CAT â no surprises on D-day.", 312 converts: 'Top IIMs & B-Schools', addl: 'CAT 98.32%ile' 313 }, 314 { 315 initials: 'PS', name: 'Pulkit Saini', exam: 'CAT 2024-25', 316 pct: '98.00', 317 sections: [['VARC', 'â', '92.86'], ['DILR', 'â', '99.06'], ['Quant', 'â', '93.36']], 318 overall: ['â', '98.00'], 319 quote: "Daily live classes from IIM-A/B/C alumni faculty plus the self-study library gave me everything I needed. The 1-on-1 mentor calls were game-changers.", 320 converts: 'Top IIMs & B-Schools', addl: 'CAT 98.00%ile' 321 }, 322 { 323 initials: 'KP', name: 'Karthik Puppala', exam: 'CAT 2024-25', 324 pct: '97.97', 325 sections: [['VARC', 'â', '85.47'], ['DILR', 'â', '98.43'], ['Quant', 'â', '98.66']], 326 overall: ['â', '97.97'], 327 quote: "The 130+ LOD1 & LOD2 practice e-books built my fundamentals brick by brick. By mock 8, I was scoring 95+ consistently.", 328 converts: 'Top IIMs & B-Schools', addl: 'CAT 97.97%ile' 329 }, 330 { 331 initials: 'DK', name: 'Dheeraj Kumar', exam: 'CAT 2024-25', 332 pct: '96.80', 333 sections: [['VARC', 'â', '95.47'], ['DILR', 'â', '89.96'], ['Quant', 'â', '95.75']], 334 overall: ['â', '96.80'], 335 quote: "What sets CATKing apart is the small-batch attention. Daily targets and weekend strategy sessions kept me accountable through the entire 6-month grind.", 336 converts: 'Top IIMs & B-Schools', addl: 'CAT 96.80%ile' 337 }, 338 { 339 initials: 'HB', name: 'Himanshu Bahekar', exam: 'CAT 2024-25', 340 pct: '96.50', 341 sections: [['VARC', 'â', '94.16'], ['DILR', 'â', '92.87'], ['Quant', 'â', '94.70']], 342 overall: ['â', '96.50'], 343 quote: "From struggling with %tiles to walking into my dream IIM â CATKing's faculty and mock series made the journey not just possible but enjoyable.", 344 converts: 'Top IIMs & B-Schools', addl: 'CAT 96.50%ile' 345 }, 346 { 347 initials: 'ST', name: 'Sahil Taneja', exam: 'CAT 2024-25', 348 pct: '96.26', 349 sections: [['VARC', 'â', '93.13'], ['DILR', 'â', '86.81'], ['Quant', 'â', '97.64']], 350 overall: ['â', '96.26'], 351 quote: "The MICA-alumni mock interviews and SOP reviews polished my profile in
351ways I didn't think were possible. Truly end-to-end prep.", 352 converts: 'Top IIMs & B-Schools', addl: 'CAT 96.26%ile' 353 }, 354 { 355 initials: 'PS', name: 'Prakhar Kumar Srivastava', exam: 'CAT 2024-25', 356 pct: '95.78', 357 sections: [['VARC', 'â', '95.09'], ['DILR', 'â', '97.27'], ['Quant', 'â', '60.78']], 358 overall: ['â', '95.78'], 359 quote: "Speed-focused strategy is what I needed for CAT, and CATKing delivered. The sectional drills helped me cross 99 in DILR after months of being stuck at 90.", 360 converts: 'Top IIMs & B-Schools', addl: 'CAT 95.78%ile' 361 }, 362 { 363 initials: 'SU', name: 'Sunaina', exam: 'CAT 2024-25', 364 pct: '95.70', 365 sections: [['VARC', 'â', '85.99'], ['DILR', 'â', '97.49'], ['Quant', 'â', '93.63']], 366 overall: ['â', '95.70'], 367 quote: "CATKing's mocks were calibrated perfectly to actual exam difficulty â the wave-based launches kept me consistently challenged and improving week after week.", 368 converts: 'Top IIMs & B-Schools', addl: 'CAT 95.70%ile' 369 }, 370 { 371 initials: 'UR', name: 'Udit Pragadeesh R', exam: 'CAT 2024-25', 372 pct: '95.54', 373 sections: [['VARC', 'â', '92.59'], ['DILR', 'â', '96.13'], ['Quant', 'â', '87.97']], 374 overall: ['â', '95.54'], 375 quote: "Sumit Sir's VARC modules cracked the toughest section for me. The doubt-clearing sessions and recorded videos made all the difference in my prep.", 376 converts: 'Top IIMs & B-Schools', addl: 'CAT 95.54%ile' 377 }, 378 { 379 initials: 'UA', name: 'Ujjawal Awasthi', exam: 'CAT 2024-25', 380 pct: '95.50', 381 sections: [['VARC', 'â', '86.53'], ['DILR', 'â', '45.38'], ['Quant', 'â', '96.11']], 382 overall: ['â', '95.50'], 383 quote: "The level of mentorship at CATKing is unmatched. From doubt-clearing to interview prep, every stage was handled with precision and personal attention.", 384 converts: 'Top IIMs & B-Schools', addl: 'CAT 95.50%ile' 385 }, 386 { 387 initials: 'DS', name: 'Divyanshu Kumar Singh', exam: 'CAT 2024-25', 388 pct: '94.86', 389 sections: [['VARC', 'â', '95.47'], ['DILR', 'â', '90.91'], ['Quant', 'â', '82.87']], 390 overall: ['â', '94.86'], 391 quote: "CATKing's structured curriculum and analytics dashboard helped me identify weak areas early. The mocks felt exactly like the real CAT â no surprises on D-day.", 392 converts: 'Top IIMs & B-Schools', addl: 'CAT 94.86%ile' 393 }, 394 { 395 initials: 'VS', name: 'Vaibhav Raj Sahni', exam: 'CAT 2024-25', 396 pct: '99.52', 397 sections: [['VARC', 'â', '95.09'], ['DILR', 'â', '99.21'], ['Quant', 'â', '99.24']], 398 overall: ['â', '99.52'], 399 quote: "Daily live classes from IIM-A/B/C alumni faculty plus the self-study library gave me everything I needed. The 1-on-1 mentor calls were game-changers.", 400 converts: 'Top IIMs & B-Schools', addl: 'CAT 99.52%ile' 401 }, 402 { 403 initials: 'LA', name: 'Lokesh Agarwal', exam: 'CAT 2024-25', 404 pct: '99.44', 405 sections: [['VARC', 'â', '99.85'], ['DILR', 'â', '99.13'], ['Quant', 'â', '95.75']], 406 overall: ['â', '99.44'], 407 quote: "The 130+ LOD1 & LOD2 practice e-books built my fundamentals brick by brick. By mock 8, I was scoring 95+ consistently.", 408 converts: 'Top IIMs & B-Schools', addl: 'CAT 99.44%ile' 409 }, 410 { 411 initials: 'UB', name: 'Uttarak Bansal', exam: 'CAT 2024-25', 412 pct: '93.04', 413 sections: [['VARC', 'â', '90.64'], ['DILR', 'â', '85.68'], ['Quant', 'â', '92.86']], 414 overall: ['â', '93.04'], 415 quote: "What sets CATKing apart is the small-batch attention. Daily targets and weekend strategy sessions kept me accountable through the entire 6-month grind.", 416 converts: 'Top IIMs & B-Schools', addl: 'CAT 93.04%ile' 417 }, 418 ]; 419 420 let apiScorecards = []; 421 422 function escapeHtml(value) { 423 return String(value || '') 424 .replace(/&/g, '&') 425 .replace(/</g, '<') 426 .replace(/>/g, '>') 427 .replace(/"/g, '"') 428 .replace(/'/g, '''); 429 } 430 431 function safeImageUrl(value) { 432 const url = String(value || '').trim().replace(/\\/g, '/'); 433 if (!url) return ''; 434 if (/^(https?:)?\/\//i.test(url) || url.startsWith('/') || /^data:image\//i.test(url)) return url; 435 return ''; 436 } 437 438 function stripTags(value) { 439 const holder = document.createElement('div'); 440 holder.innerHTML = String(value || ''); 441 return holder.textContent || holder.innerText || ''; 442 } 443 444 function initialsFromName(name) { 445 const parts = String(name || 'Student').trim().split(/\s+/).filter(
445Boolean); 446 return ((parts[0] && parts[0][0]) || 'S') + ((parts[1] && parts[1][0]) || ''); 447 } 448 449 function normalizePercentile(value) { 450 return String(value || '').replace(/%?ile/ig, '').replace(/%/g, '').trim(); 451 } 452 453 function getApiAvatar(item) { 454 return safeImageUrl(item.user_profile_link || (item.user && item.user.avatar_url)); 455 } 456 457 function toApiScorecard(item) { 458 const name = item.user_name || (item.user && item.user.name) || 'Student'; 459 const percentile = normalizePercentile(item.user_percentile || item.percentile || item.score); 460 return { 461 initials: initialsFromName(name).toUpperCase(), 462 name, 463 label: item.user_label || 'CATKing CAT Topper', 464 exam: item.exam_label || item.exam || 'CAT 2025', 465 pct: percentile || '99+', 466 quote: stripTags(item.scorecard_description || item.description || ''), 467 avatar: getApiAvatar(item), 468 cardImage: safeImageUrl(item.scorecard_card_image || item.card_image), 469 fullImage: safeImageUrl(item.scorecard_full_image || item.full_image || item.scorecard_card_image || item.card_image) 470 }; 471 } 472 473 function renderApiStoryCard(item, index, isDuplicate) { 474 const s = toApiScorecard(item); 475 const avatar = s.avatar 476 ? `<img src="${escapeHtml(s.avatar)}" alt="${escapeHtml(s.name)}" class="story-avatar-img" loading="lazy" onerror="this.remove()">` 477 : escapeHtml(s.initials); 478 const preview = s.cardImage 479 ? `<img src="${escapeHtml(s.cardImage)}" alt="${escapeHtml(s.name)} scorecard" class="story-scorecard-img" loading="lazy">` 480 : `<div class="story-sections-header"><div>VARC</div><div>DILR</div><div>QA</div><div>Total</div></div><div class="story-sec-grid values"><div><span>-</span><span>-</span></div><div><span>-</span><span>-</span></div><div><span>-</span><span>-</span></div><div><span>-</span><span>${escapeHtml(s.pct)}</span></div></div>`; 481 const quote = s.quote || 'Verified CATKing scorecard and success story.'; 482 483 return ` 484 <article class="story-card"${isDuplicate ? ' aria-hidden="true"' : ''}> 485 <div class="story-head"> 486 <div class="story-avatar">${avatar}</div> 487 <div class="story-head-info"> 488 <div class="story-name">${escapeHtml(s.name)}</div> 489 <div class="story-sub">${escapeHtml(s.label)}</div> 490 </div> 491 </div> 492 <div class="story-score-banner"> 493 <div class="story-score-pct">${escapeHtml(s.pct)}<sup>%ile</sup></div> 494 <div class="story-score-lbl">CAT Percentile Score</div> 495 </div> 496 <div class="story-sections api-scorecard-preview">${preview}</div> 497 <div class="story-quote">${escapeHtml(quote)}</div> 498 <button class="story-btn" type="button" onclick="openScorecard(${index})">View scorecard</button> 499 </article>`; 500 } 501 502 function setScorecardImageMode(showImage) { 503 const wrap = document.getElementById('scoreImageWrap'); 504 const table = document.querySelector('#scoreSections')?.closest('table'); 505 const quote = document.getElementById('scoreQuote'); 506 const meta = document.querySelector('.scorecard-meta-row'); 507 if (wrap) wrap.hidden = !showImage; 508 if (table) table.style.display = showImage ? 'none' : ''; 509 if (quote) quote.style.display = showImage ? 'none' : ''; 510 if (meta) meta.style.display = showImage ? 'none' : ''; 511 } 512 513 function openApiScorecard(idx) { 514 const item = apiScorecards[idx]; 515 if (!item) return false; 516 const s = toApiScorecard(item); 517 const exam = document.getElementById('scoreExam'); 518 const av = document.getElementById('scoreAvatar'); 519 const title = document.getElementById('scorecardModalTitle'); 520 const pct = document.getElementById('scorePct'); 521 const image = document.getElementById('scoreFullImage'); 522 const modal = document.getElementById('scorecardModal'); 523 524 if (exam) exam.textContent = 'Score Card · ' + s.exam; 525 if (av) { 526 av.innerHTML = s.avatar 527 ? `<img src="${escapeHtml(s.avatar)}" alt="${escapeHtml(s.name)}">` 528 : escapeHtml(s.initials); 529 } 530 if (title) title.textContent = s.name; 531 if (pct) pct.innerHTML = escapeHtml(s.pct) + '<sup>%ile</sup>'; 532 if (image) image.src = s.fullImage || s.cardImage || ''; 533 setScorecardImageMode(Boolean(s.fullImage || s.cardImage)); 534 if (modal) { 535 modal.classList.add('show'); 536 document.body.style.overflow = 'hidden'; 537 } 538 return true; 539 } 540
541 window.openScorecard = function (idx) { 542 if (openApiScorecard(idx)) return; 543 const s = STUDENT_SCORECARDS[idx]; 544 if (!s) return; 545 const exam = document.getElementById('scoreExam'); 546 const av = document.getElementById('scoreAvatar'); 547 const title = document.getElementById('scorecardModalTitle'); 548 const pct = document.getElementById('scorePct'); 549 const tbody = document.getElementById('scoreSections'); 550 const quote = document.getElementById('scoreQuote'); 551 const converts = document.getElementById('scoreConverts'); 552 const addl = document.getElementById('scoreAddl'); 553 const modal = document.getElementById('scorecardModal'); 554 555 if (exam) exam.textContent = 'Score Card · ' + s.exam; 556 if (av) av.textContent = s.initials; 557 if (title) title.textContent = s.name; 558 if (pct) pct.innerHTML = s.pct + '<sup>%ile</sup>'; 559 if (tbody) { 560 let rows = s.sections.map(r => `<tr><td>${r[0]}</td><td>${r[1]}</td><td>${r[2]}</td></tr>`).join(''); 561 rows += `<tr class="total"><td>Overall</td><td>${s.overall[0]}</td><td>${s.overall[1]}<sup>%ile</sup></td></tr>`; 562 tbody.innerHTML = rows; 563 } 564 if (quote) quote.textContent = '"' + s.quote + '"'; 565 if (converts) converts.textContent = s.converts; 566 if (addl) addl.textContent = s.addl; 567 setScorecardImageMode(false); 568 if (modal) { 569 modal.classList.add('show'); 570 document.body.style.overflow = 'hidden'; 571 } 572 }; 573 574 window.closeScorecardModal = function () { 575 const modal = document.getElementById('scorecardModal'); 576 if (modal) modal.classList.remove('show'); 577 const image = document.getElementById('scoreFullImage'); 578 if (image) image.src = ''; 579 document.body.style.overflow = ''; 580 }; 581 582 function fetchScorecardPage(page, collected) { 583 return fetch('/api/scorecards-proxy?base_course_id=56&page=' + page) 584 .then(res => { 585 if (!res.ok) throw new Error('Scorecards request failed'); 586 return res.json(); 587 }) 588 .then(response => { 589 const pageData = response && response.data; 590 const rows = pageData && Array.isArray(pageData.data) ? pageData.data : []; 591 rows.forEach(row => collected.push(row)); 592 if (pageData && pageData.current_page < pageData.last_page) { 593 return fetchScorecardPage(pageData.current_page + 1, collected); 594 } 595 return collected; 596 }); 597 } 598 599 function loadApiScorecards() { 600 const track = document.getElementById('storiesTrack'); 601 if (!track) return; 602 603 fetchScorecardPage(1, []) 604 .then(items => { 605 const usable = items.filter(item => { 606 const s = toApiScorecard(item); 607 return s.name && (s.fullImage || s.cardImage); 608 }); 609 if (!usable.length) return; 610 611 apiScorecards = usable; 612 const cards = usable.map((item, index) => renderApiStoryCard(item, index, false)).join(''); 613 const duplicateCards = usable.map((item, index) => renderApiStoryCard(item, index, true)).join(''); 614 track.innerHTML = cards + duplicateCards; 615 track.style.setProperty('--visible-cards', usable.length); 616 initReveal(); 617 }) 618 .catch(err => { 619 console.warn('Scorecard API unavailable; using static scorecards.', err); 620 }); 621 } 622 623 loadApiScorecards(); 624 625 // Click backdrop to close + Esc 626 const sm = document.getElementById('scorecardModal'); 627 if (sm) { 628 sm.addEventListener('click', function (e) { 629 if (e.target === this) window.closeScorecardModal(); 630 }); 631 } 632 document.addEventListener('keydown', function (e) { 633 if (e.key === 'Escape' && sm && sm.classList.contains('show')) { 634 window.closeScorecardModal(); 635 } 636 }); 637 638 // ââ Enquiry Modal Auto-Show (with 5s delay) ââ 639 ; (function initEnquiryModal() { 640 const modal = document.getElementById('enquiryModal'); 641 window.openEnquiryModal = function () {}; 642 window.closeEnquiryModal = function () { 643 document.body.style.overflow = ''; 644 }; 645 window.submitEnquiry = function (e) { 646 if (e) e.preventDefault(); 647 }; 648 if (modal) modal.remove(); 649 return; 650 651 const form = document.getElementById('enquiryForm'); 652 const success = document.getElementById('enquirySuccess'); 653 let hasShown = false;
654 let dismissed = false; 655 656 try { 657 dismissed = sessionStorage.getItem('enquiryDismissed') === '1'; 658 } catch (e) { 659 console.warn('sessionStorage not available'); 660 } 661 662 window.openEnquiryModal = function () { 663 if (hasShown || dismissed) return; 664 modal.classList.add('show'); 665 hasShown = true; 666 document.body.style.overflow = 'hidden'; 667 if (form) form.reset(); 668 if (form) form.classList.remove('hidden'); 669 if (success) success.classList.remove('show'); 670 }; 671 672 window.closeEnquiryModal = function () { 673 modal.classList.remove('show'); 674 document.body.style.overflow = ''; 675 try { 676 sessionStorage.setItem('enquiryDismissed', '1'); 677 } catch (e) { } 678 }; 679 680 window.submitEnquiry = function (e) { 681 if (e) e.preventDefault(); 682 if (form) form.classList.add('hidden'); 683 if (success) success.classList.add('show'); 684 try { 685 sessionStorage.setItem('enquiryDismissed', '1'); 686 } catch (e) { } 687 setTimeout(window.closeEnquiryModal, 2800); 688 }; 689 690 // Auto-popup disabled for now. 691 // setTimeout(() => { 692 // if (typeof window.openEnquiryModal === 'function') { 693 // window.openEnquiryModal(); 694 // } 695 // }, 5000); 696 697 // Backdrop click close 698 modal.addEventListener('click', (e) => { 699 if (e.target === modal) window.closeEnquiryModal(); 700 }); 701 702 // ESC key close 703 document.addEventListener('keydown', (e) => { 704 if (e.key === 'Escape' && modal.classList.contains('show')) { 705 window.closeEnquiryModal(); 706 } 707 }); 708 })(); 709 710 // ââ Stories marquee â arrow controls ââ 711 ; (function () { 712 const track = document.getElementById('storiesTrack'); 713 const prev = document.getElementById('storiesPrev'); 714 const next = document.getElementById('storiesNext'); 715 if (!track || !prev || !next) return; 716 717 const CARD_STEP = 336; // 320px card + 16px gap 718 let resumeTimer = null; 719 720 function advance(direction) { 721 track.classList.add('is-paused'); 722 const computed = getComputedStyle(track).transform; 723 let currentX = 0; 724 if (computed && computed !== 'none') { 725 const match = computed.match(/matrix.*\((.+)\)/); 726 if (match) { 727 const parts = match[1].split(',').map(Number); 728 currentX = parts[parts.length - 2] || 0; 729 } 730 } 731 const newX = currentX + (direction * CARD_STEP * -1);
732 track.style.animation = 'none'; 733 track.style.transform = 'translateX(' + newX + 'px)'; 734 clearTimeout(resumeTimer); 735 resumeTimer = setTimeout(() => { 736 track.style.animation = ''; 737 track.style.transform = ''; 738 track.classList.remove('is-paused'); 739 }, 4000); 740 } 741 742 prev.addEventListener('click', () => advance(-1)); 743 next.addEventListener('click', () => advance(1)); 744 })(); 745 746 // ââ Secrets marquee â arrow controls + YouTube modal ââ 747 ; (function () { 748 const track = document.getElementById('secretsTrack'); 749 const prev = document.getElementById('secretsPrev'); 750 const next = document.getElementById('secretsNext'); 751 if (!track || !prev || !next) return; 752 753 const CARD_STEP = 244; // 230px card + 14px gap 754 let resumeTimer = null; 755 756 function advance(direction) { 757 track.classList.add('is-paused'); 758 const computed = getComputedStyle(track).transform; 759 let currentX = 0; 760 if (computed && computed !== 'none') { 761 const match = computed.match(/matrix.*\((.+)\)/); 762 if (match) { 763 const parts = match[1].split(',').map(Number); 764 currentX = parts[parts.length - 2] || 0; 765 } 766 } 767 const newX = currentX + (direction * CARD_STEP * -1);
768 track.style.animation = 'none'; 769 track.style.transform = 'translateX(' + newX + 'px)'; 770 clearTimeout(resumeTimer); 771 resumeTimer = setTimeout(() => { 772 track.style.animation = ''; 773 track.style.transform = ''; 774 track.classList.remove('is-paused'); 775 }, 4000); 776 } 777 778 prev.addEventListener('click', () => advance(-1)); 779 next.addEventListener('click', () => advance(1)); 780 781 const wrap = track.closest('.secrets-marquee-wrap'); 782 if (wrap) { 783 wrap.addEventListener('keydown', (e) => { 784 if (e.target.classList && e.target.classList.contains('secret-card')) return; 785 if (e.key === 'ArrowLeft') { advance(-1); e.preventDefault(); } 786 else if (e.key === 'ArrowRight') { advance(1); e.preventDefault(); } 787 }); 788 } 789 790 // Click-to-play YouTube modal 791 function openYtModal(videoId) { 792 if (!videoId) return; 793 const frame = document.getElementById('ytFrame'); 794 const modal = document.getElementById('ytModal'); 795 if (frame) frame.src = 'https://www.youtube.com/embed/' + encodeURIComponent(videoId) + '?autoplay=1&rel=0'; 796 if (modal) modal.classList.add('show'); 797 document.body.style.overflow = 'hidden'; 798 } 799 window.closeYtModal = function () { 800 const frame = document.getElementById('ytFrame'); 801 const modal = document.getElementById('ytModal'); 802 if (frame) frame.src = ''; // stops the playback 803 if (modal) modal.classList.remove('show'); 804 document.body.style.overflow = ''; 805 } 806 807 // Wire up every card (including duplicates) 808 document.querySelectorAll('.secret-card[data-yt]').forEach(card => { 809 card.addEventListener('click', () => openYtModal(card.dataset.yt)); 810 card.addEventListener('keydown', (e) => { 811 if (e.key === 'Enter' || e.key === ' ') { 812 e.preventDefault(); 813 openYtModal(card.dataset.yt); 814 } 815 }); 816 }); 817 818 // Click backdrop or Esc to close 819 const ytm = document.getElementById('ytModal'); 820 if (ytm) { 821 ytm.addEventListener('click', (e) => { 822 if (e.target === ytm) window.closeYtModal(); 823 }); 824 } 825 document.addEventListener('keydown', (e) => { 826 if (e.key === 'Escape' && ytm && ytm.classList.contains('show')) { 827 window.closeYtModal(); 828 } 829 }); 830 })(); 831 832 // ââ Faculty marquee â arrow controls ââ 833 ; (function () { 834 const track = document.getElementById('facultyTrack'); 835 const prev = document.getElementById('facultyPrev'); 836 const next = document.getElementById('facultyNext'); 837 if (!track || !prev || !next) return; 838 839 // 230px card + 14px gap = 244px step; advance by 1 card per click 840 const CARD_STEP = 244; 841 let manualOffset = 0; 842 let resumeTimer = null; 843 844 function advance(direction) { 845 // Pause animation, take over with transform 846 track.classList.add('is-paused'); 847 // Get the current rendered translateX from the running animation 848 const computed = getComputedStyle(track).transform; 849 let currentX = 0; 850 if (computed && computed !== 'none') { 851 const m = computed.match(/matrix.*\((.+)\)/); 852 if (m) { 853 const parts = m[1].split(',').map(Number); 854 // For 2D matrix: [a, b, c, d, tx, ty] 855 currentX = parts[parts.length - 2] || 0; 856 } 857 } 858 manualOffset = currentX + (direction * CARD_STEP * -1);
859 track.style.animation = 'none'; 860 track.style.transform = 'translateX(' + manualOffset + 'px)'; 861 862 // After a delay, hand back to the auto-scroll animation 863 clearTimeout(resumeTimer); 864 resumeTimer = setTimeout(() => { 865 track.style.animation = ''; 866 track.style.transform = ''; 867 track.classList.remove('is-paused'); 868 }, 4000); 869 } 870 871 prev.addEventListener('click', () => advance(-1)); 872 next.addEventListener('click', () => advance(1)); 873 874 // Keyboard: arrow keys when wrap is focused 875 const wrap = track.closest('.faculty-marquee-wrap'); 876 if (wrap) { 877 wrap.addEventListener('keydown', (e) => { 878 if (e.key === 'ArrowLeft') { advance(-1); e.preventDefault(); } 879 else if (e.key === 'ArrowRight') { advance(1); e.preventDefault(); } 880 }); 881 } 882 })(); 883 884 // ââ Mega menu â vertical category cascade ââ 885 ; (function () { 886 const cats = document.querySelectorAll('.mega-cat'); 887 const panels = document.querySelectorAll('.mega-sub-panel'); 888 if (!cats.length || !panels.length) return; 889 890 function activate(catKey) { 891 cats.forEach(c => c.classList.toggle('active', c.dataset.cat === catKey)); 892 panels.forEach(p => p.classList.toggle('active', p.dataset.panel === catKey)); 893 } 894 895 cats.forEach(c => { 896 c.addEventListener('mouseenter', () => activate(c.dataset.cat)); 897 c.addEventListener('focus', () => activate(c.dataset.cat)); 898 c.addEventListener('click', (e) => { e.preventDefault(); activate(c.dataset.cat); }); 899 }); 900 })(); 901 902 // ââ Courses section â tab switcher ââ 903 ; (function () { 904 const tabs = document.querySelectorAll('.courses-tab'); 905 const panels = document.querySelectorAll('.courses-panel'); 906 if (!tabs.length || !panels.length) return; 907 908 function activate(catKey) {
909 tabs.forEach(t => { 910 const isActive = t.dataset.cat === catKey; 911 t.classList.toggle('active', isActive); 912 t.setAttribute('aria-selected', isActive ? 'true' : 'false'); 913 }); 914 panels.forEach(p => p.classList.toggle('active', p.dataset.panel === catKey)); 915 } 916 917 tabs.forEach(t => { 918 t.addEventListener('click', (e) => { e.preventDefault(); activate(t.dataset.cat); }); 919 }); 920 })(); 921 922 // ââ Courses section â View all / Show less toggle ââ 923 ; (function () { 924 const links = document.querySelectorAll('.courses-viewall-link'); 925 if (!links.length) return; 926 927 const labels = { 928 cat: { collapsed: 'View all CAT courses â', expanded: 'â Show less' }, 929 noncat: { collapsed: 'View all Non-CAT courses â', expanded: 'â Show less' }, 930 mocks: { collapsed: 'View all Mocks â', expanded: 'â Show less' } 931 }; 932 933 links.forEach(link => { 934 link.addEventListener('click', (e) => { 935 e.preventDefault(); 936 const target = link.dataset.target; 937 if (!target) return; 938 const panel = document.querySelector('.courses-panel[data-panel="' + target + '"]'); 939 if (!panel) return; 940 const expanded = panel.classList.toggle('expanded'); 941 const lbl = labels[target]; 942 if (lbl) link.textContent = expanded ? lbl.expanded : lbl.collapsed; 943 944 if (!expanded) { 945 const section = panel.closest('.courses-section') || document.getElementById('courses'); 946 if (section) { 947 requestAnimationFrame(() => { 948 section.scrollIntoView({ behavior: 'smooth', block: 'start' }); 949 }); 950 } 951 } 952 }); 953 }); 954 })(); 955 956 // ââ Exam Spotlight: click-to-load embedded YouTube player ââ 957 // Each .exam-spotlight-video card holds a thumbnail facade. On click, swap 958 // for a live <iframe> with autoplay. Lightweight pattern â no upfront 959 // iframe cost, no Google tracking until the user actually plays. 960 ; (function () { 961 function loadEmbeddedPlayer(card) { 962 if (card.classList.contains('is-playing')) return; 963 const videoId = card.dataset.yt; 964 if (!videoId) return; 965 const iframe = document.createElement('iframe'); 966 iframe.className = 'exam-spotlight-iframe'; 967 iframe.src = 'https://www.youtube-nocookie.com/embed/' + videoId 968 + '?autoplay=1&rel=0&modestbranding=1&playsinline=1'; 969 iframe.title = card.getAttribute('aria-label') || 'YouTube video player'; 970 iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture'; 971 iframe.referrerPolicy = 'strict-origin-when-cross-origin'; 972 iframe.allowFullscreen = true; 973 card.appendChild(iframe); 974 card.classList.add('is-playing'); 975 } 976 977 document.querySelectorAll('.exam-spotlight-video').forEach(card => { 978 card.addEventListener('click', (e) => { 979 // Don't trigger play if user clicked the YouTube redirect button 980 if (e.target.closest('.exam-spotlight-yt-link')) return; 981 loadEmbeddedPlayer(card); 982 }); 983 card.addEventListener('keydown', (e) => { 984 if (e.key === 'Enter' || e.key === ' ') { 985 if (e.target.closest('.exam-spotlight-yt-link')) return; 986 e.preventDefault(); 987 loadEmbeddedPlayer(card); 988 } 989 }); 990 }); 991 })(); 992 993 window.openPdfModal = function () { 994 const pm = document.getElementById('pdfModal'); 995 if (pm) pm.classList.add('show'); 996 document.body.style.overflow = 'hidden'; 997 showPdfList(); 998 }; 999 window.closePdfModal = function () { 1000 const pm = document.getElementById('pdfModal'); 1001 if (pm) pm.classList.remove('show'); 1002 document.body.style.overflow = ''; 1003 // reset to list state for next open 1004 setTimeout(() => { 1005 showPdfList(); 1006 const fm = document.getElementById('formMain'); if (fm) fm.style.display = ''; 1007 const fs = document.getElementById('formSuccess'); if (fs) fs.classList.remove('show'); 1008 const mi = document.getElementById('mobileInput'); if (mi) mi.value = ''; 1009 const nameI = document.getElementById('nameInput'); if (nameI) nameI.value = ''; 1010 const emailI = document.getElementById('emailInput'); if (emailI) emailI.value = ''; 1011 }, 200); 1012 }; 1013 // ââ Login / SignUp Modal ââ 1014 window.openLoginModal = function () { 1015 const lm = document.getElementById('loginModal'); 1016 if (lm) lm.classList.add('show'); 1017 document.body.style.overflow = 'hidden'; 1018 setTimeout(() => { 1019 const i = document.getElementById('loginInput'); 1020 if (i) i.focus(); 1021 }, 100); 1022 }; 1023 window.closeLoginModal = function () { 1024 const lm = document.getElementById('loginModal'); 1025 if (lm) lm.classList.remove('show'); 1026 document.body.style.overflow = ''; 1027 }; 1028 // Close on backdrop click 1029 /*; (function () { 1030 const lm = document.getElementById('loginModal'); 1031 if (!lm) return; 1032 lm.addEventListener('click', (e) => { 1033 if (e.target === lm) window.closeLoginModal(); 1034 }); 1035 document.addEventListener('keydown', (e) => { 1036 if (e.key === 'Escape' && lm.classList.contains('show')) window.closeLoginModal(); 1037 }); 1038 })();*/ 1039 // Close on backdrop click 1040 const pdfm = document.getElementById('pdfModal'); 1041 if (pdfm) { 1042 pdfm.addEventListener('click', (e) => { 1043 if (e.target.id === 'pdfModal') window.closePdfModal(); 1044 }); 1045 } 1046 // Close on escape 1047 document.addEventListener('keydown', (e) => { 1048 if (e.key === 'Escape' && pdfm && pdfm.classList.contains('show')) window.closePdfModal(); 1049 }); 1050 1051 window.selectPdf = function (idx) { 1052 selectedPdf = PDF_LIST[idx]; 1053 const pill = document.getElementById('selectedPdfPill'); 1054 if (pill) pill.textContent = selectedPdf.name; 1055 showPdfForm(); 1056 }; 1057 1058 function showPdfList() { 1059 const hl = document.getElementById('pdfHeaderList'); 1060 const hf = document.getElementById('pdfHeaderForm'); 1061 const lv = document.getElementById('pdfListView'); 1062 const fv = document.getElementById('pdfFormView'); 1063 const fs = document.getElementById('formSuccess'); 1064 const fm = document.getElementById('formMain'); 1065 1066 if (hl) hl.style.display = ''; 1067 if (hf) hf.style.display = 'none'; 1068 if (lv) lv.classList.remove('hidden'); 1069 if (fv) fv.classList.remove('active'); 1070 if (fs) fs.classList.remove('show'); 1071 if (fm) fm.style.display = ''; 1072 } 1073 1074 function showPdfForm() { 1075 const hl = document.getElementById('pdfHeaderList'); 1076 const hf = document.getElementById('pdfHeaderForm'); 1077 const lv = document.getElementById('pdfListView'); 1078 const fv = document.getElementById('pdfFormView'); 1079 1080 if (hl) hl.style.display = 'none'; 1081 if (hf) hf.style.display = ''; 1082 if (lv) lv.classList.add('hidden'); 1083 if (fv) fv.classList.add('active'); 1084 const mi = document.getElementById('mobileInput'); 1085 if (mi) setTimeout(() => mi.focus(), 100); 1086 } 1087 1088 window.toggleSignMode = function () { 1089 signMode = signMode === 'signin' ? 'signup' : 'signin'; 1090 const nameI = document.getElementById('nameInput'); 1091 const emailI = document.getElementById('emailInput'); 1092 const toggleBtn = document.getElementById('toggleBtn'); 1093 const formToggle = document.getElementById('formToggle'); 1094 const formTitle = document.getElementById('formTitle'); 1095 const submitBtn = document.getElementById('formSubmitBtn');
1096 if (signMode === 'signup') { 1097 if (nameI) nameI.style.display = ''; 1098 if (emailI) emailI.style.display = ''; 1099 if (toggleBtn) toggleBtn.textContent = 'Sign In'; 1100 if (formToggle) formToggle.childNodes[0].nodeValue = 'Already a member? '; 1101 if (formTitle) formTitle.textContent = 'Create your free account'; 1102 if (submitBtn) submitBtn.textContent = 'Sign Up & Download'; 1103 } else { 1104 if (nameI) nameI.style.display = 'none'; 1105 if (emailI) emailI.style.display = 'none'; 1106 if (toggleBtn) toggleBtn.textContent = 'Sign Up'; 1107 if (formToggle) formToggle.childNodes[0].nodeValue = 'New to CATKing? '; 1108 if (formTitle) formTitle.textContent = 'Sign in to download'; 1109 if (submitBtn) submitBtn.textContent = 'Download PDF'; 1110 } 1111 }; 1112 1113 window.submitPdfForm = function () { 1114 const mi = document.getElementById('mobileInput'); 1115 if (!mi) return; 1116 const mobile = mi.value.trim(); 1117 if (!/^\d{10}$/.test(mobile)) { 1118 mi.style.borderColor = 'var(--red)'; 1119 mi.focus(); 1120 setTimeout(() => { mi.style.borderColor = ''; }, 1800); 1121 return; 1122 } 1123 if (signMode === 'signup') { 1124 const ni = document.getElementById('nameInput'); 1125 const ei = document.getElementById('emailInput'); 1126 if (!ni || !ei) return; 1127 const name = ni.value.trim(); 1128 const email = ei.value.trim(); 1129 if (!name || !/^\S+@\S+\.\S+$/.test(email)) { 1130 const target = !name ? ni : ei; 1131 target.style.borderColor = 'var(--red)'; 1132 target.focus(); 1133 setTimeout(() => { target.style.borderColor = ''; }, 1800); 1134 return; 1135 } 1136 } 1137 completeDownload(); 1138 }; 1139 1140 window.continueWithGoogle = function () { 1141 // Simulated Google flow â same success state 1142 completeDownload(true); 1143 }; 1144 1145 function completeDownload(viaGoogle) { 1146 const fm = document.getElementById('formMain'); 1147 const fs = document.getElementById('formSuccess'); 1148 const msg = document.getElementById('successMsg'); 1149 if (fm) fm.style.display = 'none'; 1150 if (fs) fs.classList.add('show'); 1151 if (msg) msg.textContent = selectedPdf 1152 ? `"${selectedPdf.name}" is downloading${viaGoogle ? ' (signed in via Google)' : ''}. Check your phone for the link.` 1153 : "Your PDF is downloading and you've been logged in."; 1154 // Trigger a real download (text placeholder so demo feels complete) 1155 if (selectedPdf) { 1156 const blob = new Blob([ 1157 `CATKing â ${selectedPdf.name}\n\n` + 1158 `${selectedPdf.sub}\n\n` + 1159 `Thanks for downloading! This is a placeholder file â your real PDF will be sent to your registered mobile/email.\n\n` + 1160 `â CATKing Team` 1161 ], { type: 'text/plain' }); 1162 const url = URL.createObjectURL(blob); 1163 const a = document.createElement('a'); 1164 a.href = url; 1165 a.download = selectedPdf.name.replace(/\s+/g, '_') + '.txt'; 1166 document.body.appendChild(a); a.click(); a.remove(); 1167 setTimeout(() => URL.revokeObjectURL(url), 2000); 1168 } 1169 // Auto-close after a bit 1170 setTimeout(() => { window.closePdfModal(); }, 2800); 1171 } 1172 1173 // Numeric only for mobile field 1174 const mi = document.getElementById('mobileInput'); 1175 if (mi) { 1176 mi.addEventListener('input', (e) => { 1177 e.target.value = e.target.value.replace(/\D/g, '').slice(0, 10); 1178 }); 1179 } 1180 1181 // ââ FAQ Toggling logic ââ 1182 window.toggleFaq = function (btn) { 1183 const item = btn.closest('.faq-item'); 1184 if (!item) return; 1185 const wasOpen = item.classList.contains('open'); 1186 1187 // Close other items 1188 document.querySelectorAll('.faq-item').forEach(el => el.classList.remove('open')); 1189 1190 if (!wasOpen) { 1191 item.classList.add('open'); 1192 } 1193 }; 1194 1195})();
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.