HIGH FOCUS SESSION

Welcome back, Student.

Pacing yourself is key. You are on track to crush the CFA exam.

Accuracy
0%
Keep practicing!
Qs Answered
0
0 today
Days to Exam
-- Days
Mocks Taken
0 / 3
Recommend taking 1 soon

Resume Session

You left off in Quantitative Methods. You have 45 questions remaining in this topic.

Continue Studying
0%Completed

Daily Review

You have 15 flashcards due for spaced-repetition review today.

Review Cards
'); printWin.document.close(); printWin.focus(); setTimeout(() => { printWin.print(); }, 250); } } }(function() { const portal = document.getElementById('charterly-dashboard'); if (portal && portal.parentNode !== document.body) { document.body.appendChild(portal); } // Force Dark Mode by default for Glass UI const savedTheme = localStorage.getItem('charterly_theme'); if (savedTheme !== 'light') { portal.classList.add('dark-mode'); const toggleIcon = document.querySelector('#cd-theme-toggle i'); if (toggleIcon) { toggleIcon.classList.remove('fa-moon'); toggleIcon.classList.add('fa-sun'); } if (!savedTheme) localStorage.setItem('charterly_theme', 'dark'); } syncFlashcardProgress(); initExamDate(); applyPremiumLocks(); fetchUserName(); updateMotivationalMessage(); loadDailyTip(); initCardGlow(); })();function loadDailyTip() { const tips = [ "Don't study for more than 90 minutes without taking a 15-minute break to maximize retention.", "Ethics is highly weighted (15-20%). Try to do 10 Ethics practice questions every single day.", "When taking a mock exam, mimic real testing conditions: no phone, no music, 4.5 hours with split sessions.", "Focus on your weakest subject with highest exam weight (like FSA or Fixed Income) rather than re-reading your strongest.", "Review all your wrong answers immediately after a mock exam while your thought process is still fresh.", "Formulas not sticking? Write down the most difficult ones on a whiteboard right before you sleep.", "Consistency beats intensity. 2 hours of focused study every day is better than 14 hours on Sunday.", "When calculating present and future values, always clear your calculator's TVM worksheet first.", "If you are stuck on a question for more than 2 minutes, flag it and move on. Time management is critical.", "Sleep is when your brain commits short-term memory to long-term storage. Don't sacrifice sleep for late-night cramming.", "Use the process of elimination. Getting a question down to 2 choices drastically improves your odds.", "Pay close attention to wording like 'least likely', 'except', or 'most accurate' in exam questions.", "Financial Statement Analysis formulas can be overwhelming. Create a dedicated formula sheet and read it every morning.", "Understand the 'why' behind the math. If you just memorize, a slightly reworded question will trip you up.", "Take your mock exams at the exact same time of day as your real exam to train your circadian rhythm.", "Trust your first instinct. Candidates who change their answers are statistically more likely to change them to incorrect ones.", "The CFA exam is a marathon, not a sprint. Pace your study hours evenly to avoid burnout.", "Master your BA II Plus financial calculator inside and out. Knowing the STO/RCL and TVM shortcuts saves minutes.", "Focus intensely on Corporate Issuers and Equity. Together with Ethics, they form a massive chunk of your score.", "You don't need 100% to pass. You just need to be above the Minimum Passing Score (~70%). Don't panic over a few hard questions." ]; const now = new Date(); const start = new Date(now.getFullYear(), 0, 0); const diff = now - start; const oneDay = 1000 * 60 * 60 * 24; const day = Math.floor(diff / oneDay); const tipIndex = day % tips.length; const tipEl = document.getElementById('cd-daily-tip-text'); if (tipEl) tipEl.innerText = tips[tipIndex]; }function switchSidebarSection(sectionId, element) { document.querySelectorAll('.cd-section').forEach(el => el.style.display = 'none'); const target = document.getElementById('cd-section-' + sectionId); if(target) target.style.display = 'block';document.querySelectorAll('.cd-nav-item').forEach(el => el.classList.remove('active')); if(element) element.classList.add('active'); const contentArea = document.querySelector('.cd-content'); if (contentArea) { if (sectionId === 'performance') { contentArea.classList.add('full-width'); } else { contentArea.classList.remove('full-width'); } }if(sectionId === 'performance') { switchPaTab('mocks'); setTimeout(renderPerformanceChart, 150); } setTimeout(initCardGlow, 50); }function switchPaTab(tab) { document.getElementById('tab-qbank').classList.remove('active'); document.getElementById('tab-mocks').classList.remove('active'); document.getElementById('view-qbank').style.display = 'none'; document.getElementById('view-mocks').style.display = 'none';document.getElementById('tab-' + tab).classList.add('active'); document.getElementById('view-' + tab).style.display = 'block';if(tab === 'mocks') { loadMocksData(); } else if (tab === 'qbank') { loadQBankData(); } setTimeout(initCardGlow, 50); }function loadMocksData() { let totalAnswered = 0; let totalCorrect = 0; let totalSeconds = 0; let aggregatedTopics = {};for (let i = 0; i < localStorage.length; i++) { let key = localStorage.key(i); if (key && key.startsWith('charterly_exam_') && !key.includes('sample-mock')) { try { let state = JSON.parse(localStorage.getItem(key)); if (state.status === 'finished') { if (state.totalQuestions) totalAnswered += state.totalQuestions; if (state.totalCorrect) totalCorrect += state.totalCorrect; if (state.timeSpentSeconds) totalSeconds += state.timeSpentSeconds; if (state.topicStats) { for (let topic in state.topicStats) { if (!aggregatedTopics[topic]) aggregatedTopics[topic] = { total: 0, correct: 0 }; aggregatedTopics[topic].total += state.topicStats[topic].total; aggregatedTopics[topic].correct += state.topicStats[topic].correct; } } } } catch(e) { } } }if (totalAnswered === 0) { document.getElementById('mocks-data-container').style.display = 'none'; document.getElementById('mocks-empty').style.display = 'block'; return; }document.getElementById('mocks-data-container').style.display = 'block'; document.getElementById('mocks-empty').style.display = 'none';let avgScore = Math.round((totalCorrect / totalAnswered) * 100); animateValue('val-score', 0, avgScore, 1200, '%'); animateValue('val-answered', 0, totalAnswered, 1200, ''); animateValue('val-time', 0, totalSeconds, 1200, 'time');let avgSec = Math.round(totalSeconds / totalAnswered); animateValue('val-pacing', 0, avgSec, 1200, 'pacing');// Calculate Subject-wise Breakdown let mockTopicsArr = []; for (let topic in aggregatedTopics) { let t = aggregatedTopics[topic]; if (t.total > 0) { mockTopicsArr.push({ name: topic, total: t.total, correct: t.correct, percent: Math.round((t.correct / t.total) * 100) }); } }if (mockTopicsArr.length === 0) { let fallbackTopics = {}; for (let i = 0; i < localStorage.length; i++) { let key = localStorage.key(i); if (key && key.startsWith('charterly_exam_') && !key.includes('sample-mock')) { try { let state = JSON.parse(localStorage.getItem(key)); if (state && (state.status === 'finished' || state.answers || state.userAnswers)) { let answers = state.userAnswers || state.answers || {}; if (Array.isArray(answers)) { answers.forEach(a => { let subj = a.subject || a.topic || 'General'; if (!fallbackTopics[subj]) fallbackTopics[subj] = { total: 0, correct: 0 }; fallbackTopics[subj].total++; if (a.isCorrect || a.correct) fallbackTopics[subj].correct++; }); } else if (typeof answers === 'object') { for (let qid in answers) { let a = answers[qid]; let subj = a.subject || a.topic || 'General'; if (!fallbackTopics[subj]) fallbackTopics[subj] = { total: 0, correct: 0 }; fallbackTopics[subj].total++; if (a.isCorrect || a.correct) fallbackTopics[subj].correct++; } } } } catch(e){} } } for (let topic in fallbackTopics) { let t = fallbackTopics[topic]; if (t.total > 0) { mockTopicsArr.push({ name: topic, total: t.total, correct: t.correct, percent: Math.round((t.correct / t.total) * 100) }); } } }if (mockTopicsArr.length === 0 && totalAnswered > 0) { mockTopicsArr.push({ name: 'Overall Mock Exam Performance', total: totalAnswered, correct: totalCorrect, percent: avgScore }); }mockTopicsArr.sort((a, b) => a.percent - b.percent);const portalForColor = document.getElementById('charterly-dashboard'); const isDarkForColor = portalForColor && portalForColor.classList.contains('dark-mode');let mockHtml = ''; mockTopicsArr.forEach((t, idx) => { let color = isDarkForColor ? '#38bdf8' : '#2563eb'; if (t.percent < 50) color = '#ef4444'; else if (t.percent >= 70) color = '#10b981'; else if (t.percent >= 50) color = '#f59e0b';let weight = CFA_TOPIC_WEIGHTS[t.name] || 'High Yield';let priorityNum = idx + 1; let priorityClass = 'priority-high'; if (priorityNum > 6) priorityClass = 'priority-low'; else if (priorityNum > 3) priorityClass = 'priority-med';mockHtml += `
${t.name} ${weight} Priority #${priorityNum} ${t.percent}% (${t.correct}/${t.total})
`; });const mockSubjList = document.getElementById('mock-subject-list'); if (mockSubjList) mockSubjList.innerHTML = mockHtml;setTimeout(() => { document.querySelectorAll('#mock-subject-list .cd-pa-progress-fill').forEach(el => { el.style.width = el.getAttribute('data-target') + '%'; }); }, 100);// Dynamic Rich Multi-Section Smart Insight let mockFeedback = ''; if (mockTopicsArr.length > 0) { const mWeakest = mockTopicsArr[0]; const mStrongest = mockTopicsArr[mockTopicsArr.length - 1];let overallStatus = ''; if (avgScore >= 75) { overallStatus = `Your overall mock accuracy of ${avgScore}% is exceptional and comfortably clear of the historical ~70% Minimum Passing Score (MPS).`; } else if (avgScore >= 70) { overallStatus = `Your overall mock accuracy of ${avgScore}% is on track, meeting the CFA Institute target threshold. Maintaining momentum in the final stretch is key.`; } else if (avgScore >= 60) { overallStatus = `Your overall mock accuracy of ${avgScore}% shows solid foundations, but is hovering slightly below the recommended 70% buffer.`; } else { overallStatus = `Your overall mock accuracy of ${avgScore}% indicates significant opportunity for rapid score gains by drilling high-weight core concepts.`; }let consistencyText = ''; if (mWeakest.percent < 60) { const weakWeight = CFA_TOPIC_WEIGHTS[mWeakest.name] || 'core'; consistencyText = `

Priority Action: Your lowest scoring area is ${mWeakest.name} (${mWeakest.percent}%), which carries a ${weakWeight} exam weight. Shifting 30% of your daily study time to review this topic will yield the largest net score boost.

`; } else { consistencyText = `

Consistency: Your scores are well-balanced across subjects. Continue periodic formula drills in ${mWeakest.name} (${mWeakest.percent}%) to keep concepts fresh.

`; }let pacingText = ''; if (avgSec <= 85) { pacingText = `

Great Pacing: You are averaging ${avgSec}s per question (benchmark is 90s), leaving you with an estimated 15+ minute buffer on exam day to review flagged questions.

`; } else if (avgSec <= 105) { pacingText = `

Pacing Alert: You are averaging ${avgSec}s per question. Aim to bring calculation pacing under 90s by drilling BA II Plus calculator shortcuts.

`; } else { pacingText = `

Time Discipline: Averaging ${avgSec}s per question puts you at risk of time pressure. Practice strict 2-minute time-boxing per question.

`; }let actionBullets = ``;mockFeedback = `

${overallStatus}

${consistencyText} ${pacingText} ${actionBullets} `; } else { mockFeedback = '

Complete a mock exam to generate your personalized AI diagnostics and high-yield focus plan.

'; }const mockAiText = document.getElementById('mock-ai-feedback-text'); if (mockAiText) mockAiText.innerHTML = mockFeedback;renderPerformanceChart(); }function fetchUserName() { try { let firstName = window.CHARTERLY_USER_FIRST_NAME;if (firstName && firstName.toLowerCase() !== 'charterly') { const welcomeEl = document.getElementById('cd-dynamic-welcome-title'); const vibeEl = document.getElementById('cd-dynamic-vibe-tag'); const greetingData = getUltraCoolGreeting(firstName);if (welcomeEl) welcomeEl.innerText = greetingData.title; if (vibeEl) { vibeEl.innerHTML = ` ${greetingData.vibe.text}`; vibeEl.style.color = greetingData.vibe.color; vibeEl.style.borderColor = greetingData.vibe.color + '44'; vibeEl.style.background = greetingData.vibe.color + '1a'; }const headerEl = document.getElementById('cd-dynamic-header-name'); if (headerEl) headerEl.innerText = firstName; }if (window.CHARTERLY_ACCESS_EXPIRATION) { const expiryEl = document.getElementById('cd-dynamic-expiry'); if (expiryEl) { expiryEl.innerHTML = ` Premium Access valid until ${window.CHARTERLY_ACCESS_EXPIRATION}`; expiryEl.style.display = 'block'; } } } catch(e) { console.log("Could not auto-detect name."); } }function getUltraCoolGreeting(firstName) { const now = new Date(); const hour = now.getHours(); const day = now.getDay();let vibe = { icon: 'fa-bolt', text: 'HIGH FOCUS SESSION', color: '#38bdf8' }; let greetings = [];if (day === 1 && hour < 12) { vibe = { icon: 'fa-rocket', text: 'WEEKLY LAUNCH', color: '#10b981' }; greetings = [ `Setting the pace for the week, ${firstName}.`, `Monday momentum unlocked, ${firstName}.`, `Fresh week, fresh focus, ${firstName}.` ]; } else if (day === 6 || day === 0) { vibe = { icon: 'fa-trophy', text: 'WEEKEND CHAMPION', color: '#f59e0b' }; greetings = [ `This is what separates candidates, ${firstName}.`, `Weekend grind in progress, ${firstName}.`, `Outworking the competition today, ${firstName}.` ]; } else if (hour >= 5 && hour < 12) { vibe = { icon: 'fa-sun', text: 'MORNING MASTERY', color: '#38bdf8' }; greetings = [ `Greatness starts early, ${firstName}.`, `Compounding knowledge today, ${firstName}.`, `Fresh mind, sharp focus, ${firstName}.` ]; } else if (hour >= 12 && hour < 17) { vibe = { icon: 'fa-chart-line', text: 'PEAK MOMENTUM', color: '#3b82f6' }; greetings = [ `Precision over luck on exam day, ${firstName}.`, `Channeling deep focus, ${firstName}.`, `Every question answered counts, ${firstName}.` ]; } else if (hour >= 17 && hour < 22) { vibe = { icon: 'fa-fire', text: 'EVENING INTENSITY', color: '#8b5cf6' }; greetings = [ `The charter is earned in sessions like this, ${firstName}.`, `Sharp focus tonight, ${firstName}.`, `Building your edge, ${firstName}.` ]; } else { vibe = { icon: 'fa-moon', text: 'MIDNIGHT GRIND', color: '#ec4899' }; greetings = [ `Greatness is built in the quiet hours, ${firstName}.`, `Burning the midnight oil, ${firstName}.`, `Silent work, loud results on exam day, ${firstName}.` ]; }const title = greetings[Math.floor(Math.random() * greetings.length)]; return { vibe, title }; }function applyPremiumLocks() { const isPremium = window.CHARTERLY_PREMIUM_UNLOCKED === true; if (!isPremium) { const premiumCards = document.querySelectorAll('.cd-stat-card[data-premium-only="true"], .cd-action-card[data-premium-only="true"]'); premiumCards.forEach(el => { el.classList.add('cd-premium-locked'); const oldOverlay = el.querySelector('.cd-lock-overlay'); if(oldOverlay) oldOverlay.remove(); const overlay = document.createElement('div'); overlay.className = 'cd-lock-overlay'; overlay.innerHTML = `
Unlock Premium `; el.appendChild(overlay); });const premiumNavs = document.querySelectorAll('.cd-nav-item[data-premium-only="true"]'); premiumNavs.forEach(nav => { nav.href = '/course-selection'; if(!nav.querySelector('.fa-lock')) { nav.innerHTML += ''; nav.style.display = 'flex'; } }); } else { document.querySelectorAll('.cd-premium-locked').forEach(el => { el.classList.remove('cd-premium-locked'); const overlay = el.querySelector('.cd-lock-overlay'); if(overlay) overlay.remove(); }); document.querySelectorAll('.cd-nav-item[data-premium-only="true"]').forEach(nav => { nav.href = nav.getAttribute('data-original-href') || '#'; nav.onclick = null; const lockIcon = nav.querySelector('.fa-lock'); if(lockIcon) lockIcon.remove(); }); } }// Dynamic Learning Velocity & Trajectory Predictive Engine for Mock Exams function renderPerformanceChart() { const canvas = document.getElementById('performanceChart'); if (!canvas) return;if (window.performanceChartInstance) { window.performanceChartInstance.destroy(); window.performanceChartInstance = null; }const getMockScore = (mockNumber) => { let totalQ = 0, totalC = 0; for(let i=1; i<=2; i++) { const key = `charterly_exam_mock-${mockNumber}-session-${i}`; try { const state = JSON.parse(localStorage.getItem(key)); if(state && state.status === 'finished') { if(state.totalQuestions) totalQ += state.totalQuestions; if(state.totalCorrect) totalC += state.totalCorrect; } } catch(e) {} } return totalQ > 0 ? Math.round((totalC/totalQ)*100) : null; };const m1 = getMockScore(1); const m2 = getMockScore(2); const m3 = getMockScore(3); let completedScores = []; if (m1 !== null) completedScores.push({ index: 0, score: m1 }); if (m2 !== null) completedScores.push({ index: 1, score: m2 }); if (m3 !== null) completedScores.push({ index: 2, score: m3 });let lastScore = 55; let lastIndex = 0; let velocity = 6;if (completedScores.length > 0) { const lastItem = completedScores[completedScores.length - 1]; lastScore = lastItem.score; lastIndex = lastItem.index;if (completedScores.length >= 2) { velocity = (completedScores[completedScores.length - 1].score - completedScores[0].score) / (completedScores.length - 1); velocity = Math.max(2, Math.min(15, velocity)); } }const remainingSteps = 3 - lastIndex; let expectedExamScore = Math.min(94, Math.max(35, Math.round(lastScore + (velocity * remainingSteps * 0.85)))); let optimisticExamScore = Math.min(98, Math.max(expectedExamScore + 6, Math.round(lastScore + (velocity * 1.5 * remainingSteps)))); let conservativeExamScore = Math.min(expectedExamScore - 4, Math.max(30, Math.round(lastScore + (velocity * 0.25 * remainingSteps) - 3)));let actualData = [m1, m2, m3, null]; let expectedData = [null, null, null, null]; expectedData[lastIndex] = lastScore; expectedData[3] = expectedExamScore;let optData = [null, null, null, null]; optData[lastIndex] = lastScore; optData[3] = optimisticExamScore;let consData = [null, null, null, null]; consData[lastIndex] = lastScore; consData[3] = conservativeExamScore;let mpsData = [70, 70, 70, 70];const portal = document.getElementById('charterly-dashboard'); const isDark = portal && portal.classList.contains('dark-mode'); const textColor = isDark ? 'rgba(255,255,255,0.7)' : '#64748b'; const gridColor = isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.05)'; const brandColor = isDark ? '#38bdf8' : '#2563eb'; const optColor = isDark ? '#34d399' : '#10b981'; const expectedColor = isDark ? '#fbbf24' : '#f59e0b'; const consColor = isDark ? '#f87171' : '#ef4444'; const mpsColor = isDark ? 'rgba(251, 191, 36, 0.7)' : 'rgba(217, 119, 6, 0.7)';window.performanceChartInstance = new Chart(canvas, { type: 'line', data: { labels: ['Mock 1', 'Mock 2', 'Mock 3', 'Exam Day 🎯'], datasets: [ { label: 'Actual Scores', data: actualData, borderColor: brandColor, backgroundColor: brandColor, borderWidth: 3.5, pointBackgroundColor: brandColor, pointBorderColor: isDark ? '#0f172a' : '#fff', pointBorderWidth: 2.5, pointRadius: 7, pointHoverRadius: 9, tension: 0.25, spanGaps: true, zIndex: 10 }, { label: 'Optimistic (+Spaced Repetition)', data: optData, borderColor: optColor, borderDash: [6, 4], borderWidth: 2.5, pointBackgroundColor: optColor, pointRadius: (ctx) => ctx.dataIndex === 3 ? 7 : 0, fill: '+1', backgroundColor: isDark ? 'rgba(52, 211, 153, 0.08)' : 'rgba(16, 185, 129, 0.06)', tension: 0.3, spanGaps: true }, { label: 'Expected Trajectory', data: expectedData, borderColor: expectedColor, borderDash: [5, 5], borderWidth: 2.5, pointBackgroundColor: expectedColor, pointRadius: (ctx) => ctx.dataIndex === 3 ? 7 : 0, fill: false, tension: 0.3, spanGaps: true }, { label: 'Conservative (Stagnation)', data: consData, borderColor: consColor, borderDash: [4, 4], borderWidth: 2, pointBackgroundColor: consColor, pointRadius: (ctx) => ctx.dataIndex === 3 ? 6 : 0, fill: false, tension: 0.3, spanGaps: true }, { label: 'CFA MPS Threshold (70%)', data: mpsData, borderColor: mpsColor, borderDash: [8, 6], borderWidth: 1.5, pointRadius: 0, fill: false } ] }, options: { responsive: true, maintainAspectRatio: false, interaction: { mode: 'index', intersect: false }, plugins: { legend: { display: false }, tooltip: { backgroundColor: isDark ? 'rgba(15, 23, 42, 0.95)' : 'rgba(255,255,255,0.95)', titleColor: isDark ? '#fff' : '#0f172a', bodyColor: isDark ? '#cbd5e1' : '#475569', borderColor: isDark ? 'rgba(255,255,255,0.15)' : '#e2e8f0', borderWidth: 1, padding: 14, displayColors: true, boxPadding: 4, callbacks: { label: (context) => { if (context.raw === null) return null; if (context.dataset.label.includes('MPS')) return '⭐ MPS Passing Bar: 70%'; const diff = context.raw - 70; const diffStr = diff >= 0 ? ` (+${diff}% vs MPS)` : ` (${diff}% vs MPS)`; return `${context.dataset.label}: ${context.raw}%${diffStr}`; } } } }, scales: { y: { min: 0, max: 100, grid: { color: gridColor }, ticks: { color: textColor, stepSize: 20, callback: (val) => val + '%' } }, x: { grid: { display: false }, ticks: { color: textColor, font: { weight: 'bold', size: 12 } } } } } }); }// Questions Attempted Over Time (QBank Activity Compact Fitted Chart) function renderQbActivityChart(horizon = '14') { const canvas = document.getElementById('qbActivityChart'); if (!canvas) return;if (window.qbActivityChartInstance) { window.qbActivityChartInstance.destroy(); window.qbActivityChartInstance = null; }let state = {}; try { state = JSON.parse(localStorage.getItem('charterly_qb_v2') || '{}'); } catch(e){}let daysCount = parseInt(horizon) || 14; const now = new Date(); const dayLabels = []; const dateKeys = []; const dailyCounts = {};for (let i = daysCount - 1; i >= 0; i--) { const d = new Date(); d.setDate(now.getDate() - i); const dateStr = d.toISOString().slice(0, 10); const displayStr = d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); dateKeys.push(dateStr); dayLabels.push(displayStr); dailyCounts[dateStr] = 0; }let totalTracked = 0; for (let k in state) { if (!k.startsWith(window.QB_YEAR + '|')) continue; if (state[k].selected !== null && state[k].selected !== undefined) { totalTracked++; if (state[k].ts) { const itemDate = new Date(Number(state[k].ts)).toISOString().slice(0, 10); if (dailyCounts[itemDate] !== undefined) { dailyCounts[itemDate]++; } } } }let activityData = dateKeys.map(k => dailyCounts[k]); const maxActive = Math.max(...activityData); if (maxActive === 0 && totalTracked > 0) { activityData[dateKeys.length - 1] = totalTracked; }const portal = document.getElementById('charterly-dashboard'); const isDark = portal && portal.classList.contains('dark-mode'); const textColor = isDark ? 'rgba(255,255,255,0.7)' : '#64748b'; const gridColor = isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.05)'; const brandColor = isDark ? '#38bdf8' : '#2563eb'; const areaBg = isDark ? 'rgba(56, 189, 248, 0.15)' : 'rgba(37, 99, 235, 0.08)';window.qbActivityChartInstance = new Chart(canvas, { type: 'line', data: { labels: dayLabels, datasets: [{ label: 'Questions Attempted', data: activityData, borderColor: brandColor, backgroundColor: areaBg, fill: true, borderWidth: 2.5, pointBackgroundColor: brandColor, pointBorderColor: isDark ? '#0f172a' : '#fff', pointBorderWidth: 1.5, pointRadius: daysCount > 14 ? 3 : 4.5, pointHoverRadius: 7, tension: 0.35 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, tooltip: { backgroundColor: isDark ? 'rgba(15, 23, 42, 0.95)' : 'rgba(255,255,255,0.95)', titleColor: isDark ? '#fff' : '#0f172a', bodyColor: isDark ? '#cbd5e1' : '#475569', borderColor: isDark ? 'rgba(255,255,255,0.15)' : '#e2e8f0', borderWidth: 1, padding: 10, callbacks: { label: (ctx) => `🎯 ${ctx.raw} questions solved` } } }, scales: { y: { beginAtZero: true, grid: { color: gridColor }, ticks: { color: textColor, precision: 0, font: { size: 10 } } }, x: { grid: { display: false }, ticks: { color: textColor, font: { weight: '600', size: 10 }, maxTicksLimit: daysCount > 14 ? 7 : daysCount } } } } }); }function syncFlashcardProgress() { try { const stateStr = localStorage.getItem('cfa_fc_state_v4'); let masteredCount = 0; let reviewCount = 0; const totalCards = 555; if (stateStr) { const stateObj = JSON.parse(stateStr); masteredCount = Object.keys(stateObj).filter(k => stateObj[k] === 'mastered').length; reviewCount = Object.keys(stateObj).filter(k => stateObj[k] === 'review').length; } const remaining = totalCards - masteredCount; const actionCard = document.querySelectorAll('.cd-action-card.secondary')[0]; if (actionCard) { const titleEl = actionCard.querySelector('.cd-ac-title'); const descEl = actionCard.querySelector('.cd-ac-desc'); const btnEl = actionCard.querySelector('.cd-btn'); if (remaining === 0) { titleEl.innerText = "Deck Mastered!"; descEl.innerText = "You have completely mastered all 555 flashcards. Great job!"; btnEl.innerText = "Review Again"; } else { descEl.innerText = `You have ${remaining} flashcards left to master. Keep pushing!`; if (masteredCount > 0 || reviewCount > 0) { btnEl.innerText = "Continue Review"; } } } } catch(e) { console.error("Failed to sync flashcard state", e); } // Sync Mocks Taken let mocksCompleted = 0; if (localStorage.getItem('charterly_exam_mock-1-session-1') && localStorage.getItem('charterly_exam_mock-1-session-2')) mocksCompleted++; if (localStorage.getItem('charterly_exam_mock-2-session-1') && localStorage.getItem('charterly_exam_mock-2-session-2')) mocksCompleted++; if (localStorage.getItem('charterly_exam_mock-3-session-1') && localStorage.getItem('charterly_exam_mock-3-session-2')) mocksCompleted++; const mocksValEl = document.getElementById('cd-mocks-val'); if (mocksValEl) { mocksValEl.innerText = `${mocksCompleted} / 3`; } }function updateExamDate() { const input = document.getElementById('cd-exam-date'); if(input.value) { localStorage.setItem('cfa_exam_date', input.value); calculateDaysRemaining(input.value); } }function initExamDate() { const savedDate = localStorage.getItem('cfa_exam_date'); if(savedDate) { document.getElementById('cd-exam-date').value = savedDate; calculateDaysRemaining(savedDate); } }function calculateDaysRemaining(dateString) { const examDate = new Date(dateString); examDate.setHours(23, 59, 59, 999); const today = new Date(); const diffTime = examDate.getTime() - today.getTime(); const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); const displayEl = document.getElementById('cd-days-remaining'); if(diffDays < 0) { displayEl.innerText = "0 Days"; displayEl.style.color = "#ef4444"; } else { displayEl.innerText = diffDays + " Days"; displayEl.style.color = ""; } updateMotivationalMessage(); }function updateMotivationalMessage() { const subEl = document.querySelector('.cd-welcome-sub'); if (!subEl) return; const accuracyEl = document.querySelectorAll('.cd-stat-value')[0]; const accuracy = accuracyEl ? parseInt(accuracyEl.innerText.replace('%', '')) : 0; const qsEl = document.querySelectorAll('.cd-stat-value')[1]; const qsAnswered = qsEl ? parseInt(qsEl.innerText) : 0; let daysRemaining = -1; const dateStr = localStorage.getItem('cfa_exam_date'); if (dateStr) { const examDate = new Date(dateStr); examDate.setHours(23, 59, 59, 999); const diffTime = examDate.getTime() - new Date().getTime(); daysRemaining = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); } let timeMessage = ""; if (daysRemaining > 0 && daysRemaining <= 30) { timeMessage = `You are exactly ${daysRemaining} days away. It's time for the final sprint! `; } else if (daysRemaining > 30) { timeMessage = `Pacing yourself is key with ${daysRemaining} days left. `; } else if (daysRemaining === 0) { timeMessage = `Today is the day! Good luck on the exam. `; } else { timeMessage = `Keep up the momentum! `; } let statMessage = ""; if (accuracy >= 70 && qsAnswered > 0) { statMessage = `Your ${accuracy}% accuracy across ${qsAnswered} questions is fantastic for Level I.`; } else if (accuracy > 0 && accuracy < 70) { statMessage = `Focus on bringing that ${accuracy}% accuracy up to 70%. You got this.`; } else { statMessage = `You are on track to crush the CFA exam.`; } subEl.style.opacity = '0'; setTimeout(() => { subEl.innerText = timeMessage + statMessage; subEl.style.transition = 'opacity 0.4s ease-in-out'; subEl.style.opacity = '1'; }, 150); }function toggleDashboardTheme() { const portal = document.getElementById('charterly-dashboard'); const toggleIcon = document.querySelector('#cd-theme-toggle i'); portal.classList.toggle('dark-mode'); const isDark = portal.classList.contains('dark-mode'); if (isDark) { toggleIcon.classList.remove('fa-moon'); toggleIcon.classList.add('fa-sun'); } else { toggleIcon.classList.remove('fa-sun'); toggleIcon.classList.add('fa-moon'); } localStorage.setItem('charterly_theme', isDark ? 'dark' : 'light'); if (document.getElementById('cd-section-performance').style.display !== 'none') { renderPerformanceChart(); const select = document.getElementById('qb-horizon-select'); const horizon = select ? select.value : '14'; renderQbActivityChart(horizon); } }window.addEventListener('storage', (e) => { if (e.key === 'charterly_theme') { const portal = document.getElementById('charterly-dashboard'); const toggleIcon = document.querySelector('#cd-theme-toggle i'); if (e.newValue === 'dark') { portal.classList.add('dark-mode'); if(toggleIcon) { toggleIcon.classList.remove('fa-moon'); toggleIcon.classList.add('fa-sun'); } } else { portal.classList.remove('dark-mode'); if(toggleIcon) { toggleIcon.classList.remove('fa-sun'); toggleIcon.classList.add('fa-moon'); } } } });// Dedicated QBank Data Loader with Dynamic Countdown/Count-up Animation & Precise Resume Routing function loadQBankData() { var state = {}; try { state = JSON.parse(localStorage.getItem('charterly_qb_v2') || '{}'); } catch(e){} var totalDone = 0; var totalCorrect = 0; var totalToday = 0; var totalSeconds = 0; var nowTs = Date.now(); var oneDay = 24 * 60 * 60 * 1000; var latestTs = 0; var latestSubj = '';var subjectStats = {}; var grandTotalQBank = 0; if (window.QB_SUBJECTS) { window.QB_SUBJECTS.forEach(function(s) { subjectStats[s.name] = { total: s.total_questions, done: 0, correct: 0, slug: s.slug }; grandTotalQBank += s.total_questions; }); }for (var k in state) { if (!k.startsWith(window.QB_YEAR + '|')) continue; var parts = k.split('|'); if (parts.length < 4) continue; var subj = parts[1]; var item = state[k]; if (item.selected !== null && item.selected !== undefined) { totalDone++; if (item.correct) totalCorrect++; if (item.timeSpent) { totalSeconds += Number(item.timeSpent); } if (item.ts) { var itemTs = Number(item.ts); if (nowTs - itemTs < oneDay) { totalToday++; } if (itemTs > latestTs) { latestTs = itemTs; latestSubj = subj; } } } if (subjectStats[subj] && item.selected !== null && item.selected !== undefined) { subjectStats[subj].done++; if (item.correct) subjectStats[subj].correct++; } } if (totalSeconds === 0 && totalDone > 0) { totalSeconds = totalDone * 78; } var acc = totalDone > 0 ? Math.round((totalCorrect / totalDone) * 100) : 0; var accEl = document.getElementById('cd-dyn-accuracy'); if (accEl) animateValue('cd-dyn-accuracy', 0, acc, 1000, '%'); var ansEl = document.getElementById('cd-dyn-answered'); if (ansEl) animateValue('cd-dyn-answered', 0, totalDone, 1000, ''); var todayEl = document.getElementById('cd-dyn-today'); if (todayEl) todayEl.textContent = totalToday; animateValue('val-qb-score', 0, acc, 1200, '%'); animateValue('val-qb-answered', 0, totalDone, 1200, ''); animateValue('val-qb-time', 0, totalSeconds, 1200, 'time'); var qbPacing = totalDone > 0 ? Math.round(totalSeconds / totalDone) : 0; animateValue('val-qb-pacing', 0, qbPacing, 1200, 'pacing');// Identify exact subject to resume based on active history var bestSubj = latestSubj; if (!bestSubj) { bestSubj = localStorage.getItem('charterly_qb_last_subject') || ''; } if (!bestSubj) { for (var s in subjectStats) { var st = subjectStats[s]; if (st.done > 0 && st.done < st.total) { bestSubj = s; break; } } } if (!bestSubj && window.QB_SUBJECTS && window.QB_SUBJECTS.length > 0) { bestSubj = window.QB_SUBJECTS[0].name; } if (bestSubj) { var matchedSubjObj = null; if (window.QB_SUBJECTS) { matchedSubjObj = window.QB_SUBJECTS.find(function(s) { return s.name.toLowerCase() === bestSubj.toLowerCase() || s.name.replace(/&/g, '&').toLowerCase() === bestSubj.replace(/&/g, '&').toLowerCase(); }); } var displaySubjName = matchedSubjObj ? matchedSubjObj.name.replace(/&/g, '&') : bestSubj.replace(/&/g, '&'); var subjSlug = matchedSubjObj ? matchedSubjObj.slug : bestSubj.toLowerCase().replace(/[^a-z0-9]+/g, '-'); var st = subjectStats[bestSubj] || (matchedSubjObj ? subjectStats[matchedSubjObj.name] : null); var remaining = st ? Math.max(0, st.total - st.done) : 0; var subjDone = st ? st.done : 0; var subjTotal = st ? st.total : 0; var subjPct = subjTotal > 0 ? Math.round((subjDone / subjTotal) * 100) : 0; var tEl = document.getElementById('cd-dyn-resume-topic'); if (tEl) tEl.textContent = displaySubjName; var rEl = document.getElementById('cd-dyn-resume-remaining'); if (rEl) rEl.textContent = remaining; var ringText = document.getElementById('cd-dyn-resume-ring-text'); if (ringText) ringText.textContent = subjPct + '%'; var ringFill = document.getElementById('cd-dyn-resume-ring-fill'); if (ringFill) { var dashoffset = 314 - (314 * subjPct / 100); ringFill.style.animation = 'none'; ringFill.style.strokeDashoffset = dashoffset; } var targetUrl = '/questionbank/?topic=' + encodeURIComponent(subjSlug); var resumeBtn = document.getElementById('cd-dyn-resume-btn'); if (resumeBtn) { resumeBtn.href = targetUrl; resumeBtn.onclick = function() { try { localStorage.setItem('charterly_qb_last_subject', displaySubjName); } catch(e){} }; } var navQb = document.getElementById('cd-nav-qbank'); if (navQb) { navQb.href = targetUrl; navQb.onclick = function() { try { localStorage.setItem('charterly_qb_last_subject', displaySubjName); } catch(e){} }; } } // Generate QBank Performance Analytics bars with Priority Box var topicsArr = []; for (var s in subjectStats) { var st = subjectStats[s]; if(st.total > 0) { st.name = s; st.accuracy = st.done > 0 ? Math.round((st.correct / st.done) * 100) : 0; st.percent = Math.round((st.done / st.total) * 100); topicsArr.push(st); } } topicsArr.sort((a, b) => a.percent - b.percent);var html = ''; topicsArr.forEach((t, idx) => { let color = '#2563eb'; const portalEl = document.getElementById('charterly-dashboard'); if (portalEl && portalEl.classList.contains('dark-mode')) color = '#38bdf8'; if (t.percent < 25) color = '#ef4444'; else if (t.percent > 70) color = '#10b981';let weight = CFA_TOPIC_WEIGHTS[t.name] || 'High Yield';let priorityNum = idx + 1; let priorityClass = 'priority-high'; if (priorityNum > 6) priorityClass = 'priority-low'; else if (priorityNum > 3) priorityClass = 'priority-med';html += `
${t.name} ${weight} Priority #${priorityNum} ${t.percent}% (${t.done}/${t.total} Qs)
`; }); var subjList = document.getElementById('subject-list'); if(subjList) subjList.innerHTML = html;setTimeout(() => { document.querySelectorAll('#subject-list .cd-pa-progress-fill').forEach(el => { el.style.width = el.getAttribute('data-target') + '%'; }); }, 100);// Rich Smart Insight for QBank var feedback = ''; if (topicsArr.length > 0) { var weakest = topicsArr[0]; var strongest = topicsArr[topicsArr.length - 1]; var overallCompPct = grandTotalQBank > 0 ? Math.round((totalDone / grandTotalQBank) * 100) : 0;var compSummary = ''; if (overallCompPct >= 50) { compSummary = `You have completed ${overallCompPct}% (${totalDone}/${grandTotalQBank}) of the entire Question Bank with an overall accuracy of ${acc}%.`; } else { compSummary = `You have answered ${totalDone} questions across the QBank with an accuracy of ${acc}%.`; }var weakWeight = CFA_TOPIC_WEIGHTS[weakest.name] || 'core'; feedback = `

${compSummary}

High Momentum: Strongest progression is in ${strongest.name} (${strongest.percent}% completed).

Priority Target: ${weakest.name} (${weakWeight} exam weight) is currently at ${weakest.percent}% completion. Completing 2 readings in this subject this week will significantly boost your mock readiness.

`; var aiText = document.getElementById('ai-feedback-text'); if(aiText) aiText.innerHTML = feedback; }const select = document.getElementById('qb-horizon-select'); const horizon = select ? select.value : '14'; renderQbActivityChart(horizon); setTimeout(initCardGlow, 50); }// Initial Data Sync on Page Ready document.addEventListener("DOMContentLoaded", function() { loadQBankData(); initCardGlow(); });