β˜… Group Discount for Schools & Centres

JAMB Scores Aren’t Luck.
They are a Calculation.

Nigeria’s Only AI-Native Mastery Engine. Master the syllabus, own your future.

English β€’ Physics β€’ Chemistry β€’ Biology β€’ Government β€’ Economics β€’ Accounting β€’ Agric Science β€’ Mathematics β€’ Literature β€’ Commerce

The Problem: The Past Question Trap

Every year, candidates solve thousands of random past questions without deconstructing the syllabus first. This leads to "JAMB Fever." Success shouldn't be a gamble. AnchorLearn treats your score as a surgical calculation.

The Solution: The 80% Mastery Rule

On AnchorLearn, you don’t finish a topic; you Master it.

The engine only grants you "Mastered" status when you hit the 80% threshold in our curriculum assessments.

Your Board of Examiners

βš–οΈ Justice Aisha Mohammed (Government)
Senior Jurist teaching the Mechanics of Power.

πŸ”¬ Prof. Adebayo (Physics)
Senior Examiner breaking down complex laws into simple principles.

πŸ“ˆ Prof. O. J. Mahmud (Economics)
Policy Visionary mentoring the next generation of CEOs.

Log In to the Engine

TRIAL CODE:TRIAL

Full Access & Licensing

Reach our support desk instantly via Call or WhatsApp:

09048220128

Choose Stream

JAMB

WAEC

Mastery Map

πŸ† Syllabus Deep Mastery Guide
0-79% Gearing Up
Cyan: Passed 1x (Surface Mastery)
Purple: Passed 2x (Consolidated)
Gold: Passed 3x (Deep Mastery Engine)

πŸ† Final CBT Exam Simulator

Achieve 80% overall subject mastery to unlock the 40-seconds-per-question Exam Engine.

0%

window.vault = { activeMetadata: { topicTitle: "", videoUrl: "", audioUrl: "", infographicUrl: "", slidesUrl: "" }, activeConfig: null, activeModuleIdx: 0, zoomLevel: 1.0, activeCards: [] }; window.vault.fetchVaultData = async function(topicCode, subjectName) { try { let cleanToken = topicCode.trim().toLowerCase(); if (cleanToken.includes("section")) { cleanToken = cleanToken.replace(/section/g, "").trim(); } const strictAlphaToken = cleanToken.replace(/[^a-z0-9]/g, ''); let targetSubject = subjectName.trim().toLowerCase(); if (targetSubject.includes("account")) targetSubject = "accounts"; if (targetSubject.includes("english")) targetSubject = "use of english"; if (targetSubject.includes("agric") || targetSubject.includes("agricultur")) targetSubject = "agric"; if (targetSubject.includes("biol") || targetSubject.includes("biology")) targetSubject = "biology"; const { collection, getDocs } = await import("https://www.gstatic.com/firebasejs/10.7.1/firebase-firestore.js"); const resourcesRef = collection(window.db, "learning_resources"); const querySnapshot = await getDocs(resourcesRef); // 🟒 HARD FORCED RESET BEFORE SCANNING window.vault.activeMetadata = { topicTitle: `Section ${topicCode.replace(/section/gi, "").trim()}`, videoUrl: "", audioUrl: "", infographicUrl: "", slidesUrl: "" }; window.vault.activeCards = []; querySnapshot.forEach(doc => { const data = doc.data(); const dbSubject = data.subject ? data.subject.toString().trim().toLowerCase() : ""; const dbRawCode = data.videoCode ? data.videoCode.toString().trim().toLowerCase() : ""; const dbCleanCode = dbRawCode.replace(/[^a-z0-9]/g, ''); const rType = data.resourceType ? data.resourceType.toLowerCase().trim() : ""; const subjectMatches = (dbSubject === targetSubject || dbSubject.includes(targetSubject) || targetSubject.includes(dbSubject) || (targetSubject === "biology" && (dbSubject.includes("biol") || dbSubject.includes("bio"))) || (targetSubject === "agric" && (dbSubject.includes("agric") || dbSubject.includes("agri")))); if (subjectMatches) { if (rType.includes("video")) { // 🎯 TRACK 1: PERFECT DYNAMIC BOUNDARY VIDEO MATCHING const normalizedDbCode = dbRawCode.toLowerCase().replace(/section/gi, "").replace(/[^a-z0-9]/g, "").trim(); const normalizedButtonCode = cleanToken.toLowerCase().replace(/section/gi, "").replace(/[^a-z0-9]/g, "").trim(); // Strip Roman numerals (e.g., 'i1a' -> '1a') const coreDbCode = normalizedDbCode.replace(/^[ivxlcdm]+/, ''); const coreButtonCode = normalizedButtonCode.replace(/^[ivxlcdm]+/, ''); // Strict validation paths const isExactMatch = (normalizedDbCode === normalizedButtonCode || dbCleanCode === strictAlphaToken); const isRomanBridgeMatch = (coreDbCode === coreButtonCode && coreButtonCode.length > 0); if (isExactMatch || isRomanBridgeMatch) { window.vault.activeMetadata.videoUrl = data.url; } } else { // πŸ“‘ TRACK 2: BALANCED ALPHANUMERIC METADATA FILTER MATRIX const dbCleanNorm = dbRawCode.toLowerCase().replace(/section/g, "").replace(/[^a-z0-9]/g, "").trim(); const targetCleanNorm = cleanToken.toLowerCase().replace(/section/g, "").replace(/[^a-z0-9]/g, "").trim(); // Strip off Roman numeral prefixes to align Mathematics & Government (e.g., 'iii4b' -> '4b') const dbCore = dbCleanNorm.replace(/^[ivxlcdm]+/, ''); const targetCore = targetCleanNorm.replace(/^[ivxlcdm]+/, ''); // Alphanumeric clean boundaries comparison checks const isCoreMatch = (targetCore.length > 0 && dbCore === targetCore) || (dbCleanNorm === targetCleanNorm); const isContained = dbCleanNorm.includes(targetCleanNorm) || targetCleanNorm.includes(dbCleanNorm); // Absolute exact check boundaries for isolation (blocks 1ai from bleeding into 1aii) let safeGuardBlock = true; if (targetCleanNorm === "1ai" && dbCleanNorm === "1aii") safeGuardBlock = false; if (targetCleanNorm === "1ei" && dbCleanNorm === "1eii") safeGuardBlock = false; if (targetCleanNorm.length > 0 && dbCleanNorm.includes(targetCleanNorm + "i") && !targetCleanNorm.endsWith("i")) safeGuardBlock = false; const assetLooseMatch = (isCoreMatch || (isContained && safeGuardBlock)); if (assetLooseMatch) { if (rType.includes("audio")) window.vault.activeMetadata.audioUrl = data.url; else if (rType.includes("slide")) window.vault.activeMetadata.slidesUrl = data.url; else if (rType.includes("graphic") || rType.includes("info")) window.vault.activeMetadata.infographicUrl = data.url; } } } }); try { const flashcardsRef = collection(window.db, "flashcards"); const cardsSnapshot = await getDocs(flashcardsRef); cardsSnapshot.forEach(doc => { const cardData = doc.data(); const cardSubject = cardData.subject ? cardData.subject.toString().trim().toLowerCase() : ""; const cardRawCode = cardData.videoCode ? cardData.videoCode.toString().trim().toLowerCase() : ""; const cardCleanCode = cardRawCode.replace(/[^a-z0-9]/g, ''); const subMatches = (cardSubject === targetSubject || targetSubject.includes(cardSubject) || cardSubject.includes(targetSubject) || (targetSubject === "biology" && (cardSubject.includes("biol") || cardSubject.includes("bio")))); // πŸ“‘ BALANCED FLASHCARD ALPHANUMERIC METADATA FILTER MATRIX const cardCleanNorm = cardRawCode.toLowerCase().replace(/section/g, "").replace(/[^a-z0-9]/g, "").trim(); const targetCleanNorm = cleanToken.toLowerCase().replace(/section/g, "").replace(/[^a-z0-9]/g, "").trim(); const cardCore = cardCleanNorm.replace(/^[ivxlcdm]+/, ''); const targetCore = targetCleanNorm.replace(/^[ivxlcdm]+/, ''); const isCardCoreMatch = (targetCore.length > 0 && cardCore === targetCore) || (cardCleanNorm === targetCleanNorm); const isCardContained = cardCleanNorm.includes(targetCleanNorm) || targetCleanNorm.includes(cardCleanNorm); let cardSafeGuard = true; if (targetCleanNorm === "1ai" && cardCleanNorm === "1aii") cardSafeGuard = false; if (targetCleanNorm === "1ei" && cardCleanNorm === "1eii") cardSafeGuard = false; if (targetCleanNorm.length > 0 && cardCleanNorm.includes(targetCleanNorm + "i") && !targetCleanNorm.endsWith("i")) cardSafeGuard = false; const cardCodeMatches = (isCardCoreMatch || (isCardContained && cardSafeGuard)); if (subMatches && cardCodeMatches && cardData.cards) { window.vault.activeCards = cardData.cards; } }); } catch (cardErr) { console.error("Flashcard Sync Snag:", cardErr); } return true; } catch (e) { console.error("Vault Query Failure Snag:", e); return false; } }; window.vault.renderResourceUI = function(idx) { const meta = window.vault.activeMetadata || {}; window.vault.activeModuleIdx = idx; window.vault.resetZoom(); const vaultScreen = document.getElementById('vault-resource-screen'); if (vaultScreen) vaultScreen.style.setProperty('display', 'block', 'important'); window.app.view('vault-resource-screen'); document.getElementById('v-topic-title').innerText = meta.topicTitle || "Learning Vault"; // ⚑ BULLETPROOF EXPANDABLE MOBILE-FIRST CROSS-VAULT DROPDOWN PROTOCOL const partnerContainer = document.getElementById('v-partner-nav-container'); if (partnerContainer) { partnerContainer.innerHTML = ""; // Clear existing elements Safely const activeModule = window.app.activeModules[idx]; if (activeModule && activeModule.codes) { // Strictly match the topic title provided in fetchVaultData const currentTokenText = activeModule.codes.find(code => meta.topicTitle.toLowerCase().replace(/section/g, "").trim() === code.trim().toLowerCase() ) || activeModule.codes[0]; // 1. CREATE THE MASTER TRIGGER TOGGLE BUTTON (Calibrated Dashboard Tabs Twin Layout) const mainTriggerBtn = document.createElement('button'); mainTriggerBtn.className = "btn-main"; mainTriggerBtn.style.cssText = `width: 100%; height: 100%; display: inline-flex; align-items: center; justify-content: center; gap: 4px; padding: 0 8px; font-size: 0.9rem; font-weight: 800; background: var(--bg); border: 2px solid var(--cyan); color: var(--cyan); border-radius: 12px; cursor: pointer; box-shadow: 0 4px 15px rgba(0, 229, 255, 0.1); transition: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; box-sizing: border-box;`; mainTriggerBtn.innerHTML = `🧭 Vault: ${currentTokenText.trim()} β–Ό`; // 2. CREATE THE ISOLATED DROPDOWN MENU PANEL OVERLAY const dropdownMenu = document.createElement('div'); dropdownMenu.style.cssText = `display: none; position: absolute; top: calc(100% + 8px); left: 0; background: #13193E; border: 2px solid var(--border); border-radius: 20px; box-shadow: 0 15px 40px rgba(0,0,0,0.5); width: 280px; padding: 10px; overflow: hidden;`; // Toggle visibility without causing screen flicker loops mainTriggerBtn.onclick = (e) => { e.preventDefault(); e.stopPropagation(); const isHidden = dropdownMenu.style.display === "none" || dropdownMenu.style.display === ""; dropdownMenu.style.display = isHidden ? "block" : "none"; mainTriggerBtn.style.borderColor = isHidden ? "var(--gold)" : "var(--cyan)"; mainTriggerBtn.style.color = isHidden ? "var(--gold)" : "var(--cyan)"; }; // Close automatically if the user clicks anywhere else on the screen layout document.addEventListener('click', () => { dropdownMenu.style.display = "none"; mainTriggerBtn.style.borderColor = "var(--cyan)"; mainTriggerBtn.style.color = "var(--cyan)"; }, { once: false }); // 3. GENERATE THE TARGET PASS COMPANION ROW BUTTONS activeModule.codes.forEach(code => { const cleanCodeToken = code.trim(); const isCurrentActive = meta.topicTitle.toLowerCase().includes(cleanCodeToken.toLowerCase()); const itemRow = document.createElement('button'); itemRow.style.cssText = `width: 100%; display: block; padding: 18px 20px; font-size: 1.1rem; font-weight: 700; text-align: left; background: transparent; border: none; border-radius: 12px; margin-bottom: 4px; transition: none; box-sizing: border-box;`; if (isCurrentActive) { itemRow.style.color = "var(--gold)"; itemRow.style.background = "rgba(255, 215, 0, 0.08)"; itemRow.innerHTML = `πŸ“ Active Vault: ${cleanCodeToken}`; } else { itemRow.style.color = "var(--text-light)"; itemRow.style.cursor = "pointer"; itemRow.innerHTML = `πŸ”— Jump to Section ${cleanCodeToken}`; itemRow.onmouseover = () => { itemRow.style.background = "rgba(0, 229, 255, 0.08)"; itemRow.style.color = "var(--cyan)"; }; itemRow.onmouseout = () => { itemRow.style.background = "transparent"; itemRow.style.color = "var(--text-light)"; }; itemRow.onclick = async (evt) => { evt.preventDefault(); evt.stopPropagation(); dropdownMenu.style.display = "none"; mainTriggerBtn.style.borderColor = "var(--cyan)"; mainTriggerBtn.innerHTML = `⏳ Synchronizing Section ${cleanCodeToken}...`; await window.app.triggerVault(cleanCodeToken, idx, cleanCodeToken); }; } dropdownMenu.appendChild(itemRow); }); partnerContainer.appendChild(mainTriggerBtn); partnerContainer.appendChild(dropdownMenu); } } const videoContainer = document.querySelector('.video-container'); const videoPlayer = document.getElementById('v-player'); if (videoContainer && videoPlayer) { // 🟒 INSTANT CACHE RESET BEFORE MOUNTING videoPlayer.src = ""; videoContainer.style.setProperty('display', 'none', 'important'); if (meta.videoUrl && meta.videoUrl.trim() !== "") { videoContainer.style.setProperty('display', 'block', 'important'); videoPlayer.src = `https://www.youtube.com/embed/${meta.videoUrl}?rel=0&modestbranding=1`; } } const docViewerBox = document.getElementById('v-embedded-document-viewer-box'); const infoWrapper = document.getElementById('v-info-zoom-wrapper'); const infoIframe = document.getElementById('v-info-iframe'); const slidesIframe = document.getElementById('v-slides-iframe'); const zoomToolbar = document.getElementById('v-zoom-toolbar'); if (docViewerBox) docViewerBox.style.setProperty('display', 'none'); if (zoomToolbar) zoomToolbar.style.setProperty('display', 'none'); if (infoWrapper) infoWrapper.style.setProperty('display', 'none'); if (infoIframe) infoIframe.src = ""; if (slidesIframe) { slidesIframe.src = ""; slidesIframe.style.setProperty('display', 'none'); } const audioPlayer = document.getElementById('v-audio'); const audioFallback = document.getElementById('v-audio-fallback'); if (audioPlayer && audioFallback) { if (meta.audioUrl && meta.audioUrl.trim() !== "") { audioPlayer.src = meta.audioUrl; audioPlayer.style.setProperty('display', 'block', 'important'); audioFallback.style.display = "none"; audioPlayer.load(); } else { audioPlayer.src = ""; audioPlayer.style.setProperty('display', 'none', 'important'); audioFallback.style.display = "block"; } } document.getElementById('v-btn-info').onclick = (e) => { e.preventDefault(); if (meta.infographicUrl && meta.infographicUrl.trim() !== "") { if (infoIframe && slidesIframe && docViewerBox && zoomToolbar && infoWrapper) { window.vault.resetZoom(); slidesIframe.style.setProperty('display', 'none'); const cleanUrl = meta.infographicUrl.split('?')[0].toLowerCase(); if (cleanUrl.endsWith('.png') || cleanUrl.endsWith('.jpg') || cleanUrl.endsWith('.jpeg') || cleanUrl.endsWith('.webp')) { infoIframe.src = "about:blank"; setTimeout(() => { const iframeDoc = infoIframe.contentDocument || infoIframe.contentWindow.document; if (iframeDoc) { iframeDoc.open(); iframeDoc.write(``); iframeDoc.close(); } }, 50); infoWrapper.style.setProperty('display', 'block', 'important'); zoomToolbar.style.setProperty('display', 'flex', 'important'); } else { infoIframe.src = `https://docs.google.com/viewer?url=${encodeURIComponent(meta.infographicUrl)}&embedded=true`; infoWrapper.style.setProperty('display', 'block', 'important'); zoomToolbar.style.setProperty('display', 'none'); } docViewerBox.style.setProperty('display', 'block', 'important'); docViewerBox.scrollIntoView({ behavior: 'smooth' }); } } else { alert("This infographic guide is currently being compiled by your Board of Examiners."); } }; document.getElementById('v-btn-slides').onclick = (e) => { e.preventDefault(); if (meta.slidesUrl && meta.slidesUrl.trim() !== "") { if (slidesIframe && infoWrapper && docViewerBox && zoomToolbar) { zoomToolbar.style.setProperty('display', 'none'); infoWrapper.style.setProperty('display', 'none'); slidesIframe.src = `https://docs.google.com/viewer?url=${encodeURIComponent(meta.slidesUrl)}&embedded=true`; slidesIframe.style.setProperty('display', 'block', 'important'); docViewerBox.style.setProperty('display', 'block', 'important'); docViewerBox.scrollIntoView({ behavior: 'smooth' }); } } else { alert("The high-yield review slide deck for this module is dropping soon!"); } }; const flashArea = document.getElementById('v-flash-area'); if (flashArea) { if (window.vault.activeCards && window.vault.activeCards.length > 0) { let cardIndex = 0; const renderCardState = () => { const currentCard = window.vault.activeCards[cardIndex]; const rawFront = currentCard.front || currentCard.Front || ""; const rawBack = currentCard.back || currentCard.Back || ""; const compiledFront = window.app.math(rawFront); const compiledBack = window.app.math(rawBack); flashArea.innerHTML = `
❓ ${compiledFront}
πŸ’‘ Click Card to Flip
Card ${cardIndex + 1} of ${window.vault.activeCards.length}
`; let isFlipped = false; const flipbox = document.getElementById('f-card-flipbox'); const textContainer = document.getElementById('f-card-text'); flipbox.onclick = () => { isFlipped = !isFlipped; flipbox.style.transform = isFlipped ? "rotateY(180deg)" : "rotateY(0deg)"; setTimeout(() => { if (isFlipped) { flipbox.style.borderColor = "var(--gold)"; textContainer.style.transform = "rotateY(180deg)"; textContainer.innerHTML = `🎯 ANSWER:
${compiledBack}
`; } else { flipbox.style.borderColor = "var(--cyan)"; textContainer.style.transform = "rotateY(0deg)"; textContainer.innerHTML = `❓ ${compiledFront}`; } }, 180); }; document.getElementById('f-prev').onclick = () => { if (cardIndex > 0) { cardIndex--; renderCardState(); } }; document.getElementById('f-next').onclick = () => { if (cardIndex < window.vault.activeCards.length - 1) { cardIndex++; renderCardState(); } }; }; renderCardState(); } else { flashArea.innerHTML = `

⚑ FLASHCARDS IN PRODUCTION

Active recall cards are currently being compiled. Use the Audio summary and Study Slides to anchor understanding!

`; } } }; window.vault.adjustZoom = function(amount) { this.zoomLevel = Math.max(1.0, Math.min(this.zoomLevel + amount, 3.0)); const infoIframe = document.getElementById('v-info-iframe'); if (infoIframe) { const iframeDoc = infoIframe.contentDocument || infoIframe.contentWindow.document; if (iframeDoc) { const img = iframeDoc.getElementById('v-target-img'); if (img) { img.style.transform = `scale(${this.zoomLevel})`; if (this.zoomLevel === 1.0) { img.style.maxWidth = "100%"; } else { img.style.maxWidth = "none"; } } } } }; window.vault.resetZoom = function() { this.zoomLevel = 1.0; const infoIframe = document.getElementById('v-info-iframe'); if (infoIframe) { const iframeDoc = infoIframe.contentDocument || infoIframe.contentWindow.document; if (iframeDoc) { const img = iframeDoc.getElementById('v-target-img'); if (img) { img.style.transform = "scale(1)"; img.style.maxWidth = "100%"; } } } }; window.vault.triggerMasteryTestGate = function() { const audioPlayer = document.getElementById('v-audio'); if (audioPlayer) { audioPlayer.pause(); audioPlayer.src = ""; } const vPlayer = document.getElementById('v-player'); if (vPlayer) vPlayer.src = ""; const vaultScreen = document.getElementById('vault-resource-screen'); if (vaultScreen) { vaultScreen.style.setProperty('display', 'none', 'important'); } window.app.launchModule(window.vault.activeModuleIdx); }; window.vault.exitVaultBackToMap = function() { const audioPlayer = document.getElementById('v-audio'); if (audioPlayer) { audioPlayer.pause(); audioPlayer.src = ""; } const vPlayer = document.getElementById('v-player'); if (vPlayer) vPlayer.src = ""; const vaultScreen = document.getElementById('vault-resource-screen'); if (vaultScreen) { vaultScreen.style.setProperty('display', 'none', 'important'); } window.app.view('topics-screen'); }; window.switchVaultPane = function(paneId, btn) { // πŸ›‘οΈ SECURITY INTERCEPT: Block unauthorized access to interactive Socratic Mentor chat if (paneId === 'mentor' && window.app.mode !== 'platinum' && window.app.mode !== 'admin') { window.app.showModal(`πŸ‘‘ Platinum Tier Required

Unlimited interactive, Socratic chat sessions with Subject Experts are reserved exclusively for top-tier Platinum members.

Please contact our help desk to upgrade your access license package!`); return; } document.querySelectorAll('.v-tab-btn').forEach(b => b.classList.remove('active')); btn.classList.add('active'); document.querySelectorAll('.pane').forEach(p => p.classList.remove('active')); const targetPane = document.getElementById('pane-' + paneId); if (targetPane) { targetPane.classList.add('active'); } }; const SUBS = ['Use of English', 'Mathematics', 'Physics', 'Chemistry', 'Biology', 'Economics', 'Government', 'Literature in English', 'Principles of Accounts', 'Commerce', 'Agricultural Science']; // Global variable container to store true quiz counts per subject dynamically window.app_totals = { counts: {}, subjectsMap: {} }; const precalculateAllSubjectModules = async () => { try { for (const s of SUBS) { const dbPath = s.toLowerCase().replace(/\s/g, ''); const snap = await get(child(ref(rtdb), `/${dbPath}`)); const data = snap.val(); if (data) { const raw = Object.values(data).filter(q => q && typeof q === 'object' && (q.question || q["question text"])); const collator = new Intl.Collator(undefined, { numeric: true }); raw.sort((a, b) => collator.compare(a.section_code || "A", b.section_code || "A")); const blocksByCode = {}; raw.forEach(q => { const code = q.section_code || "A"; if (!blocksByCode[code]) blocksByCode[code] = []; blocksByCode[code].push(q); }); const sortedUniqueCodes = Object.keys(blocksByCode).sort((a, b) => collator.compare(a, b)); const modulesConfigList = []; let currentBatchQs = []; let currentBatchCodes = []; let topicNameTracker = {}; sortedUniqueCodes.forEach(code => { const codeQs = blocksByCode[code]; if (codeQs.length >= 40) { if (currentBatchQs.length > 0) { const bName = currentBatchQs[0].topic_name || "Module"; if (!topicNameTracker[bName]) topicNameTracker[bName] = 0; topicNameTracker[bName]++; modulesConfigList.push({ totalCount: currentBatchQs.length }); currentBatchQs = []; currentBatchCodes = []; } let sliceIdx = 0; const baseName = codeQs[0].topic_name || "Module"; while (sliceIdx < codeQs.length) { const chunk = codeQs.slice(sliceIdx, sliceIdx + 20); if (!topicNameTracker[baseName]) topicNameTracker[baseName] = 0; topicNameTracker[baseName]++; modulesConfigList.push({ totalCount: chunk.length }); sliceIdx += 20; } return; } currentBatchQs = currentBatchQs.concat(codeQs); currentBatchCodes.push(code); if (currentBatchQs.length >= 20) { const bName = currentBatchQs[0].topic_name || "Module"; if (!topicNameTracker[bName]) topicNameTracker[bName] = 0; topicNameTracker[bName]++; modulesConfigList.push({ totalCount: currentBatchQs.length }); currentBatchQs = []; currentBatchCodes = []; } }); if (currentBatchQs.length > 0) { const bName = currentBatchQs[0].topic_name || "Module"; if (!topicNameTracker[bName]) topicNameTracker[bName] = 0; topicNameTracker[bName]++; modulesConfigList.push({ totalCount: currentBatchQs.length }); } window.app_totals.counts[s] = modulesConfigList.length || 1; window.app_totals.subjectsMap[s] = modulesConfigList; } else { window.app_totals.counts[s] = 1; window.app_totals.subjectsMap[s] = []; } } } catch (e) { console.error("Critical Subject Breakdown Count Sync Exception:", e); } }; window.app = { email: null, prog: {}, sub: "", stream: "JAMB", activeModules: [], qIdx: 0, pool: [], ans: [], bId: "", mode: "none", mentorHistory: [], // ADDED: Explicitly define copyCode here inside the window.app object copyCode: () => { navigator.clipboard.writeText("TRIAL").then(() => { const b = document.getElementById('btn-copy-code'); if (b) { b.innerText = "COPIED!"; setTimeout(() => { b.innerText = "COPY"; }, 2000); } }); }, sendMessage: async () => { const input = document.getElementById('mentor-input'); const chatArea = document.getElementById('mentor-chat-area'); const text = input.value.trim(); if (!text) return; // 1. Show student message chatArea.innerHTML += `
${text}
`; input.value = ""; chatArea.scrollTop = chatArea.scrollHeight; try { // 2. Call the Function const response = await askUncleTunde({ message: text, history: window.app.mentorHistory }); console.log("Cloud Function Data:", response.data); if (response.data.status === "LIMIT_REACHED") { window.app.showModal(`🚨 CREDITS USED

${response.data.message}`); return; } // 3. FIX: Extract message with a fallback const rawText = response.data.message || "Uncle Tunde is nodding his head, but no words came out. Check backend deployment!"; const tundeText = window.app.math(rawText); // 4. Show Uncle Tunde's response chatArea.innerHTML += `
Uncle Tunde:
${tundeText}
`; chatArea.scrollTop = chatArea.scrollHeight; // 5. Update history for context window.app.mentorHistory.push({ role: "user", parts: [{ text: text }] }); window.app.mentorHistory.push({ role: "model", parts: [{ text: rawText }] }); } catch (err) { console.error("Auth Failure Log:", err); // This will display the actual error message (e.g., "Invalid Access Code") window.app.showModal(`πŸ“‘ Authentication Failure

${err.message}`); } }, view: (id) => { document.querySelectorAll('.screen').forEach(s => s.classList.remove('active')); document.getElementById(id).classList.add('active'); window.scrollTo(0,0); }, generateQuestionGrid: (totalQuestions) => { const grid = document.getElementById('question-grid'); if (!grid) return; grid.innerHTML = ''; for (let i = 1; i <= totalQuestions; i++) { const btn = document.createElement('button'); btn.id = `q-btn-${i}`; btn.style.cssText = `padding: 10px 5px; font-weight: 800; border-radius: 8px; background: #6c757d; border: 1px solid #444; color: white; cursor: pointer; font-size: 0.95rem; text-align: center; font-family: 'Inter', sans-serif; transition: 0.2s;`; btn.innerText = i; btn.onclick = (e) => { e.preventDefault(); window.app.loadQuestion(i - 1); }; grid.appendChild(btn); } }, shuffleQuestions: (array) => { let currentIndex = array.length, randomIndex; while (currentIndex !== 0) { randomIndex = Math.floor(Math.random() * currentIndex); currentIndex--; [array[currentIndex], array[randomIndex]] = [array[randomIndex], array[array[currentIndex]]]; } return array; }, // πŸš€ CENTRALIZED SIMULATOR MATRIX CONTROLLER startExamSession: (subjectPool, subjectName) => { console.log("Launching simulation engine for subject:", subjectName); alert("SUBJECT POOL LENGTH: " + (subjectPool ? subjectPool.length : "UNDEFINED")); alert("ACTIVE MODULES LENGTH: " + (window.app.activeModules ? window.app.activeModules.length : "UNDEFINED")); const cleanSubName = subjectName.toLowerCase().replace(/[^a-z]/g, ''); const isEnglish = (cleanSubName === 'useofenglish' || cleanSubName === 'english'); let livePool = []; if (window.app.activeModules && window.app.activeModules.length > 0) { window.app.activeModules.forEach(m => { if (m && m.qs && m.qs.length > 0) { m.qs.forEach(q => { let composedQ = {...q}; composedQ.topicName = m.name || "Syllabus Segment Focus"; livePool.push(composedQ); }); } }); } if (livePool.length === 0 && subjectPool && subjectPool.length > 0) { livePool = [...subjectPool]; } if (!livePool || livePool.length === 0) { alert("⚠️ Question repository pool is empty or initializing. Please ensure questions are loaded into your array before launching."); return; } const totalToDraw = isEnglish ? 60 : 40; const shuffledPool = window.app.shuffleQuestions([...livePool]); const selectedQuestions = shuffledPool.slice(0, totalToDraw); const sessionData = { subject: subjectName, questions: selectedQuestions, startTime: Date.now(), durationSeconds: isEnglish ? 2400 : 1600, answers: new Array(selectedQuestions.length).fill(null), visited: new Array(selectedQuestions.length).fill(false) }; window.app.currentExamQuestions = selectedQuestions; window.app.currentExamAnswers = new Array(selectedQuestions.length).fill(null); sessionStorage.setItem('cbt_active_session', JSON.stringify(sessionData)); const titleEl = document.getElementById('exam-title'); if (titleEl) titleEl.innerHTML = `${subjectName.toUpperCase()} CBT Simulator`; window.app.generateQuestionGrid(selectedQuestions.length); // View transition and delayed question load to ensure DOM ready window.app.view('exam-screen'); setTimeout(() => { window.app.loadQuestion(0); }, 100); if (window.app.examInterval) clearInterval(window.app.examInterval); let remainingTime = sessionData.durationSeconds; window.app.examRemainingSeconds = remainingTime; window.app.examInterval = setInterval(() => { remainingTime--; window.app.examRemainingSeconds = remainingTime; const timerEl = document.getElementById('exam-timer'); if (remainingTime <= 0) { clearInterval(window.app.examInterval); if (timerEl) timerEl.innerHTML = "00:00"; alert("⏰ Time is up! Your exam session is being automatically compiled."); window.app.submitExamSession(); } else { const mins = Math.floor(remainingTime / 60).toString().padStart(2, '0'); const secs = (remainingTime % 60).toString().padStart(2, '0'); if (timerEl) timerEl.innerHTML = `${mins}:${secs}`; } }, 1000); }, // 🎯 CORE EXAM RENDER ENGINE: Fetches and displays active simulation metrics loadQuestion: (idx) => { console.log("Loading question index:", idx); // <--- ADD THIS window.app.currentExamIdx = idx; const sessionRaw = sessionStorage.getItem('cbt_active_session'); if (!sessionRaw) return; const sessionData = JSON.parse(sessionRaw); const question = sessionData.questions[idx]; if (!question) return; sessionData.visited[idx] = true; sessionStorage.setItem('cbt_active_session', JSON.stringify(sessionData)); const qTextEl = document.getElementById('question-text'); if (qTextEl) { let questionStem = question.q || question.question || question["question text"] || ''; qTextEl.innerHTML = `Q${idx + 1}. ${questionStem}`; } const optionsContainer = document.getElementById('options-container'); if (optionsContainer) { optionsContainer.innerHTML = ''; let optionsList = []; if (question.options && Array.isArray(question.options)) { optionsList = question.options; } else if (question.options && typeof question.options === 'object') { if (question.options.A !== undefined) optionsList.push(question.options.A); if (question.options.B !== undefined) optionsList.push(question.options.B); if (question.options.C !== undefined) optionsList.push(question.options.C); if (question.options.D !== undefined) optionsList.push(question.options.D); } else { if (question['Option A'] !== undefined) optionsList.push(question['Option A']); else if (question.option_a !== undefined) optionsList.push(question.option_a); if (question['Option B'] !== undefined) optionsList.push(question['Option B']); else if (question.option_b !== undefined) optionsList.push(question.option_b); if (question['Option C'] !== undefined) optionsList.push(question['Option C']); else if (question.option_c !== undefined) optionsList.push(question.option_c); if (question['Option D'] !== undefined) optionsList.push(question['Option D']); else if (question.option_d !== undefined) optionsList.push(question.option_d); } const savedAnswer = sessionData.answers[idx]; optionsList.forEach((opt, oIdx) => { const optLetter = String.fromCharCode(65 + oIdx); const isSelected = savedAnswer === optLetter; const optBtn = document.createElement('button'); optBtn.style.cssText = `width:100%; padding:15px 20px; text-align:left; border-radius:12px; cursor:pointer; transition:0.2s; font-size:1rem; font-family:'Inter',sans-serif; font-weight:600; display:block; box-sizing:border-box;`; optBtn.style.border = isSelected ? '2px solid var(--cyan)' : '1px solid rgba(255,255,255,0.1)'; optBtn.style.background = isSelected ? 'rgba(0, 229, 255, 0.15)' : 'rgba(255,255,255,0.04)'; optBtn.style.color = isSelected ? 'var(--cyan)' : 'white'; optBtn.innerHTML = `${optLetter}. ${opt}`; optBtn.onclick = (e) => { e.preventDefault(); sessionData.answers[idx] = optLetter; sessionStorage.setItem('cbt_active_session', JSON.stringify(sessionData)); window.app.loadQuestion(idx); }; optionsContainer.appendChild(optBtn); }); } } } }, // πŸ“Š CBT PERFORMANCE COMPILER ENGINE submitExamSession: () => { if (window.app.examInterval) clearInterval(window.app.examInterval); const sessionRaw = sessionStorage.getItem('cbt_active_session'); if (!sessionRaw) { window.app.view('dashboard-screen'); return; } const sessionData = JSON.parse(sessionRaw); const totalQuestions = sessionData.questions ? sessionData.questions.length : 0; if (totalQuestions === 0) { window.app.view('dashboard-screen'); return; } let correctCount = 0; let attemptedCount = 0; let reviewHTML = ""; sessionData.questions.forEach((q, idx) => { if (!q) return; const userAns = sessionData.answers[idx] || "NOT ANSWERED"; // 1. Identify the correct letter index (0, 1, 2, or 3) let rawAns = q.a !== undefined ? q.a : (q.answer !== undefined ? q.answer : (q.Index !== undefined ? q.Index : (q.index !== undefined ? q.index : '0'))); let correctIdx = parseInt(rawAns); // 2. Map index to letter const letters = ['A', 'B', 'C', 'D']; let correctLetter = letters[correctIdx] || 'A'; // 3. Get the actual option text const options = q.options || [q['Option A'], q['Option B'], q['Option C'], q['Option D']]; let correctText = options[correctIdx] || "N/A"; // 4. Determine if correct const isCorrect = userAns === correctLetter; if (userAns !== "NOT ANSWERED") attemptedCount++; if (isCorrect) correctCount++; let questionBody = q.q || q.question || q["question text"] || ''; reviewHTML += `
QUESTION ${idx + 1} OF ${totalQuestions} πŸ“š Topic Focus: ${q.topicName || 'General Syllabus Matrix'}

${questionBody}

Your Selection: ${userAns}
Correct Answer: ${correctLetter}. ${correctText}
`; }); const scorePct = Math.round((correctCount / totalQuestions) * 100); const targetDuration = sessionData.durationSeconds; const spentSeconds = targetDuration - (window.app.examRemainingSeconds || 0); const spentMins = Math.floor(spentSeconds / 60); const spentSecs = spentSeconds % 60; let analyticsCoaching = ""; if (scorePct >= 80) { analyticsCoaching = `🟒 EXCELLENT SPEED MATRICULATION! You scored ${scorePct}%. This velocity proves your standard knowledge architecture is fully ready for high-stakes speed thresholds. Keep polishing minor error logs!`; } else if (scorePct >= 50) { analyticsCoaching = `🟑 RETRACE REVIEW OPTIMIZATION REQUIRED. You scored ${scorePct}%. You are processing definitions correctly but losing precision under time constraints. Use the topic tags listed below to re-visit specific flashcard blocks inside the Learning Vault.`; } else { analyticsCoaching = `πŸ”΄ SPEED THRESHOLD CRITICAL WARNING. You scored ${scorePct}%. You spent ${spentMins}m ${spentSecs}s on this session. Conceptual blocks are locking your parameters. Return to your Core Module Mastery Tests until you achieve stable 80% marks before attempting another full simulation engine.`; } let reportPane = document.getElementById('exam-report-screen'); if (!reportPane) { reportPane = document.createElement('div'); reportPane.id = 'exam-report-screen'; reportPane.className = 'screen'; document.getElementById('exam-screen').parentNode.appendChild(reportPane); } // This line forces the feedback page to use the subject from your current session const subjectTitle = sessionData ? sessionData.subject : "EXAM REPORT"; reportPane = document.createElement('div'); reportPane.id = 'exam-report-screen'; reportPane.className = 'screen'; document.getElementById('exam-screen').parentNode.appendChild(reportPane); } reportPane.style.cssText = "padding:20px 15px; background:#0a0a0a; min-height:100vh; color:white; overflow-y:auto; box-sizing:border-box; width:100%;"; reportPane.innerHTML = `

πŸ“Š Simulation Performance Index

Subject Exam Engine: ${sessionData.subject}

Final Score
${scorePct}%
(${correctCount} / ${totalQuestions} Right)
Time Consumed
${spentMins}m ${spentSecs}s
Allocated Limit: ${Math.floor(targetDuration/60)}m 00s
Remediation Count
${totalQuestions - correctCount}
Incorrect/Skipped Inputs
${analyticsCoaching}

πŸ” Item-by-Item Review Matrix

${reviewHTML}
`; window.app.view('exam-report-screen'); }, copyCode: () => { navigator.clipboard.writeText("TRIAL").then(() => { const b = document.getElementById('btn-copy-code'); b.innerText = "COPIED!"; setTimeout(() => { b.innerText = "COPY"; }, 2000); }); }, triggerVault: async (topicCode, idx, title) => { // πŸ›‘οΈ SECURITY INTERCEPT: Block Silver, and restrict Trial tiers to the first topic only if (window.app.mode === 'silver' || (window.app.mode === 'trial' && idx > 0)) { window.app.showModal(`πŸ’Ž Gold Upgrade Required

Syllabus video overviews, audio concepts, review slides, and active recall flashcards are exclusive to Gold and Platinum scholars.

Please upgrade your membership to unlock the full Learning Vault layout!`); return; } let cleanToken = topicCode.trim(); if (cleanToken.toLowerCase().includes("section")) { cleanToken = cleanToken.replace(/section/gi, "").trim(); } const success = await window.vault.fetchVaultData(cleanToken, window.app.sub); if (success) { if (window.vault.activeMetadata) { window.vault.activeMetadata.topicTitle = `Section ${cleanToken}`; } window.vault.renderResourceUI(idx); } else { window.app.showModal(`πŸ“½οΈ Vault Code: ${cleanToken}
The cinematic resources for this specific section are currently in production.

Please proceed to the Mastery Test to check your current proficiency.`); } }, showModal: (msg) => { const m = document.getElementById('mastery-modal'); if(m) { document.getElementById('modal-msg-txt').innerHTML = msg; m.classList.add('active'); } else { alert(msg); } }, closeModal: () => { document.getElementById('mastery-modal').classList.remove('active'); }, login: async () => { const e = document.getElementById('u-mail-field').value.trim().toLowerCase(); const p = document.getElementById('u-pass-field').value.trim().toUpperCase(); if(!e || !p) { window.app.showModal("🚨 Email and Passcode are required."); return; } try { const loginResult = await processLogin(e, p, db, rtdb); window.app.mode = loginResult.mode; window.app.email = e; const userDocRef = doc(db, "users", e); const userSnap = await getDoc(userDocRef); if (userSnap.exists()) { const uData = userSnap.data(); window.app.uName = uData.name || "Scholar"; window.app.prog = uData.progress || {}; window.app.view('stream-screen'); setTimeout(() => { if(typeof precalculateAllSubjectModules === 'function') precalculateAllSubjectModules(); }, 100); } else { window.app.prog = {}; if (window.app.mode === 'trial') { window.app.uName = "Scholar"; window.app.view('stream-screen'); setTimeout(() => { if(typeof precalculateAllSubjectModules === 'function') precalculateAllSubjectModules(); }, 50); } else { const modalOverlay = document.getElementById('mastery-modal'); const modalMessage = document.getElementById('modal-msg-txt'); const modalButton = modalOverlay.querySelector('.btn-main'); modalMessage.innerHTML = `

Welcome to AnchorLearn! πŸŽ“

Please enter your first name to activate your license profile:

`; modalButton.innerText = "ACTIVATE PROFILE πŸš€"; modalOverlay.classList.add('active'); modalButton.onclick = async (evt) => { evt.preventDefault(); const inputName = document.getElementById('v-activation-name-field').value.trim(); const finalName = inputName !== "" ? inputName : "Scholar"; modalButton.disabled = true; await setDoc(userDocRef, { email: window.app.email, name: finalName, progress: {}, createdAt: new Date().toISOString() }); window.app.uName = finalName; modalOverlay.classList.remove('active'); modalButton.disabled = false; modalButton.onclick = () => window.app.closeModal(); window.app.view('stream-screen'); setTimeout(() => { if(typeof precalculateAllSubjectModules === 'function') precalculateAllSubjectModules(); }, 50); }; } } } catch (err) { console.error("Auth Failure Log:", err); window.app.showModal(`πŸ“‘ Authentication Failure

${err.message}`); } }, setStream: (s) => { if (s === 'WAEC') { window.app.showModal("πŸš€ WAEC Stream Coming Soon!

Our Board of Examiners is currently compiling the high-yield syllabus maps and active recall assets for WAEC.

For now, launch the JAMB Mastery Engine to lock into your calculations!"); return; } window.app.stream = s; document.getElementById('stream-title-lbl').innerText = `${s} Mastery Map`; window.app.renderDash(); window.app.view('dashboard-screen'); }, renderDash: () => { const target = document.getElementById('sub-list-render'); target.innerHTML = ""; const mapTitle = document.getElementById('stream-title-lbl'); if (mapTitle && window.app.uName) { mapTitle.innerHTML = `🎯 Welcome, ${window.app.uName} | ${window.app.stream} Map`; } SUBS.forEach(s => { const subProgressObj = window.app.prog[s] || {}; const maxQuizzesForSubject = window.app_totals.counts[s] || 20; let countPassedOnce = 0; let countPassedTwice = 0; let countPassedThrice = 0; Object.keys(subProgressObj).forEach(modKey => { const val = subProgressObj[modKey]; let passesCount = 0; if (Array.isArray(val)) { passesCount = Math.min(val.filter(score => score >= 80).length, 3); } else if (typeof val === 'number' && val >= 80) { passesCount = 1; } if (passesCount === 1) countPassedOnce++; if (passesCount === 2) countPassedTwice++; if (passesCount >= 3) countPassedThrice++; }); const totalUniqueMastered = countPassedOnce + countPassedTwice + countPassedThrice; const totalPct = Math.min(Math.round((totalUniqueMastered / maxQuizzesForSubject) * 100), 100); const goldWidth = Math.round((countPassedThrice / maxQuizzesForSubject) * 100); const purpleWidth = Math.round((countPassedTwice / maxQuizzesForSubject) * 100); const cyanWidth = Math.round((countPassedOnce / maxQuizzesForSubject) * 100); const scoreboardText = `🩡 ${countPassedOnce} Cyan | πŸ’œ ${countPassedTwice} Purple | πŸ’› ${countPassedThrice} Gold β€” (${totalUniqueMastered} / ${maxQuizzesForSubject} Quizzes Mastered)`; target.innerHTML += `

${s}

${totalPct}%
${goldWidth > 0 ? `
` : ''} ${purpleWidth > 0 ? `
` : ''} ${cyanWidth > 0 ? `
` : ''}
${scoreboardText}
`; }); }, loadSyllabus: async (sName) => { try { window.app.sub = sName; document.getElementById('sub-title-render').innerText = sName; const dbPath = sName.toLowerCase().replace(/\s/g, ''); const snap = await get(child(ref(rtdb), `/${dbPath}`)); const data = snap.val(); if(!data) throw new Error(); const raw = Object.values(data).filter(q => q && typeof q === 'object' && (q.question || q["question text"])); const collator = new Intl.Collator(undefined, { numeric: true }); raw.sort((a, b) => collator.compare(a.section_code || "A", b.section_code || "A")); const blocksByCode = {}; raw.forEach(q => { const code = q.section_code || "A"; if (!blocksByCode[code]) blocksByCode[code] = []; blocksByCode[code].push(q); }); const sortedUniqueCodes = Object.keys(blocksByCode).sort((a, b) => collator.compare(a, b)); const modules = []; let currentBatchQs = []; let currentBatchCodes = []; let topicNameTracker = {}; sortedUniqueCodes.forEach(code => { const codeQs = blocksByCode[code]; if (codeQs.length >= 40) { if (currentBatchQs.length > 0) { const baseName = currentBatchQs[0].topic_name || "Module"; if (!topicNameTracker[baseName]) topicNameTracker[baseName] = 0; topicNameTracker[baseName]++; modules.push({ name: `${baseName} [${currentBatchCodes.join(', ')}]`, sec: currentBatchQs[0].section_code || "A", codes: [...currentBatchCodes], qs: [...currentBatchQs] }); currentBatchQs = []; currentBatchCodes = []; } let sliceIdx = 0; const baseName = codeQs[0].topic_name || "Module"; while (sliceIdx < codeQs.length) { const chunk = codeQs.slice(sliceIdx, sliceIdx + 20); if (!topicNameTracker[baseName]) topicNameTracker[baseName] = 0; topicNameTracker[baseName]++; modules.push({ name: `${baseName} [${code}] β€” Part ${topicNameTracker[baseName]}`, sec: chunk[0].section_code || "A", codes: [code], qs: chunk }); sliceIdx += 20; } return; } currentBatchQs = currentBatchQs.concat(codeQs); currentBatchCodes.push(code); if (currentBatchQs.length >= 20) { const baseName = currentBatchQs[0].topic_name || "Module"; if (!topicNameTracker[baseName]) topicNameTracker[baseName] = 0; topicNameTracker[baseName]++; modules.push({ name: `${baseName} [${currentBatchCodes.join(', ')}]`, sec: currentBatchQs[0].section_code || "A", codes: [...currentBatchCodes], qs: [...currentBatchQs] }); currentBatchQs = []; currentBatchCodes = []; } }); if (currentBatchQs.length > 0) { const baseName = currentBatchQs[0].topic_name || "Module"; if (!topicNameTracker[baseName]) topicNameTracker[baseName] = 0; topicNameTracker[baseName]++; modules.push({ name: `${baseName} [${currentBatchCodes.join(', ')}]`, sec: currentBatchQs[0].section_code || "A", codes: [...currentBatchCodes], qs: [...currentBatchQs] }); } window.app.activeModules = modules; const target = document.getElementById('topics-render'); target.innerHTML = ""; const { collection, getDocs } = await import("https://www.gstatic.com/firebasejs/10.7.1/firebase-firestore.js"); const resRef = collection(window.db, "learning_resources"); const resSnap = await getDocs(resRef); const globalResources = []; resSnap.forEach(doc => globalResources.push(doc.data())); let curSec = ""; modules.forEach((mod, mIdx) => { if (mod.sec !== curSec) { curSec = mod.sec; target.innerHTML += `

${curSec.toUpperCase()}

`; } const isM = (window.app.prog[window.app.sub] || {})[`M${mIdx}`] >= 80; if (window.app.mode === 'trial' && mIdx > 0) { target.innerHTML += `
πŸ”’ ${mod.name} (Trial)
`; } else { target.innerHTML += `
${mod.name}
${mod.codes.map(code => ` `).join('')}
`; } }); // Calculate Overall Subject Mastery Progress let totalMasteryPct = 0; let activeModCount = window.app.activeModules ? window.app.activeModules.length : 0; if (activeModCount > 0) { let totalScore = 0; window.app.activeModules.forEach((mod, mIdx) => { const score = (window.app.prog[window.app.sub] || {})[`M${mIdx}`] || 0; totalScore += score; }); totalMasteryPct = Math.round(totalScore / activeModCount); } // Fallback email detection and profile mode status verification let currentUserEmail = ""; if (window.app.auth && window.app.auth.currentUser && window.app.auth.currentUser.email) { currentUserEmail = window.app.auth.currentUser.email; } const isAdminMode = (window.app.mode === 'admin' || currentUserEmail === 'grithallacademy@gmail.com'); // Safe DOM references lookup after layout initialization const launchBtn = document.getElementById('exam-launch-btn'); const statusTxt = document.getElementById('exam-portal-status-text'); if (totalMasteryPct >= 80 || isAdminMode) { if (launchBtn) { launchBtn.disabled = false; launchBtn.style.background = 'linear-gradient(135deg, #ffc107, #ff9800)'; launchBtn.style.color = 'black'; launchBtn.style.cursor = 'pointer'; launchBtn.innerHTML = 'πŸš€ Launch Engine (26m 40s)'; } if (statusTxt) { statusTxt.innerHTML = isAdminMode ? `⚑ Admin Override Activated: Full simulation engine unlocked for client demonstration.` : `🟒 Subject mastery is at ${totalMasteryPct}%! The CBT simulator is fully unlocked.`; } } else { if (launchBtn) { launchBtn.disabled = true; launchBtn.style.background = '#333'; launchBtn.style.color = '#666'; launchBtn.style.cursor = 'not-allowed'; launchBtn.innerHTML = `πŸ”’ Locked (Requires 80% Mastery)`; } if (statusTxt) { statusTxt.innerHTML = `Achieve 80% overall subject mastery to unlock the 40-seconds-per-question Exam Engine. Current Mastery: ${totalMasteryPct}%`; } } window.app.view('topics-screen'); } catch (err) { console.error("Sync Engine Snag:", err); window.app.showModal("Data Sync Error."); } }, launchModule: (idx) => { const mod = window.app.activeModules[idx]; window.app.pool = [...mod.qs].sort(() => 0.5 - Math.random()).slice(0, 10); window.app.ans = new Array(window.app.pool.length).fill(null); window.app.qIdx = 0; window.app.bId = `M${idx}`; window.app.view('quiz-screen'); window.app.renderQ(); }, renderQ: () => { const q = window.app.pool[window.app.qIdx]; document.getElementById('quiz-idx-lbl').innerText = `STEP ${window.app.qIdx+1} / ${window.app.pool.length}`; const qYear = q.year || q.Year || "2024"; document.getElementById('quiz-meta-lbl').innerText = `${(q.topic_name || "Assessment").toUpperCase()} (${qYear})`; const pPane = document.getElementById('passage-pane'); if (q.passage && q.passage.length > 5) { pPane.innerHTML = `

REFERENCE

${window.app.math(q.passage)}`; pPane.style.display = 'block'; } else { pPane.style.display = 'none'; } document.getElementById('quiz-body-txt').innerHTML = window.app.math(q.question || q["question text"]); const target = document.getElementById('quiz-options-grid'); target.innerHTML = ""; ['A','B','C','D'].forEach(l => { const val = q.options ? q.options[l] : q[`option_${l.toLowerCase()}`]; if(!val) return; const div = document.createElement('div'); div.className = `opt-btn ${window.app.ans[window.app.qIdx] === l ? 'active-opt' : ''}`; div.innerHTML = `
${l}
${window.app.math(val)}
`; div.onclick = () => { window.app.ans[window.app.qIdx] = l; window.app.renderQ(); }; target.appendChild(div); }); // 🎯 DYNAMIC EXAM CONTROLLER: NEXT VS SUBMIT TEXT CONVERTER MATRIX const nextButtonElementRef = document.getElementById('quiz-screen').querySelector('.btn-main'); if (nextButtonElementRef) { // If this is the 10th question (Index position 9 out of our 10 shuffled draws) if (window.app.qIdx === 9) { nextButtonElementRef.innerHTML = 'Submit Quiz πŸš€'; nextButtonElementRef.style.background = 'linear-gradient(135deg, #FFD700, #FFA500)'; // Golden Alert Highlight nextButtonElementRef.style.color = '#000'; } else { nextButtonElementRef.innerHTML = 'NEXT'; nextButtonElementRef.style.background = ''; // Resets back to your standard platform look nextButtonElementRef.style.color = ''; } } }, prev: () => { if(window.app.qIdx > 0) { window.app.qIdx--; window.app.renderQ(); } }, next: () => { if(window.app.qIdx < (window.app.pool.length - 1)) { window.app.qIdx++; window.app.renderQ(); } else { // πŸ”„ CLEANUP INTERFACE REGISTRY BEFORE TRANSITIONING TO RESULTS SCREEN const nextButtonElementRef = document.getElementById('quiz-screen').querySelector('.btn-main'); if (nextButtonElementRef) { nextButtonElementRef.innerHTML = 'NEXT'; nextButtonElementRef.style.background = ''; nextButtonElementRef.style.color = ''; } window.app.finish(); } }, math: (t) => { if (!t) return ""; try { let r = t.toString().replace(/\$\$(.*?)\$\$/g, (m, g) => katex.renderToString(g, { displayMode: true, throwOnError: false })); return r.replace(/\$(.*?)\$/g, (m, g) => katex.renderToString(g, { displayMode: false, throwOnError: false })); } catch(e) { return t; } }, finish: async () => { window.app.view('results-screen'); let correct = 0; const target = document.getElementById('diag-render'); target.innerHTML = ""; window.app.pool.forEach((q, i) => { const ok = window.app.ans[i] === q.answer; if(ok) correct++; const vCode = q.section_code || q["section code"] || q["Video Code"] || "A"; const tName = q.topic_name || q["topic name"] || "this topic"; const getFullText = (letter) => { if(!letter) return "No answer selected"; try { const val = q.options ? q.options[letter] : q[`option_${letter.toLowerCase()}`]; return `(${letter}) ${val}`; } catch(e) { return `(${letter})`; } } target.innerHTML += `
${window.app.math(q.question || q["question text"])}
Your Answer: ${window.app.math(getFullText(window.app.ans[i]))}
${!ok ? `
Correct Answer: ${window.app.math(getFullText(q.answer))}
` : ''}
Check "${vCode}" for review on "${tName}"
`; }); const scorePct = Math.round((correct / window.app.pool.length) * 100); document.getElementById('score-pct-render').innerText = `${scorePct}%`; const statusLabel = document.getElementById('mastery-status-lbl'); const studentFirstName = window.app.uName || "Scholar"; if (scorePct >= 80) { statusLabel.innerHTML = `⚑ Mastered, ${studentFirstName}! Syllabus crushed.`; } else { statusLabel.innerHTML = `πŸ“‰ Gaps found, ${studentFirstName}. Lock back into the Vault.`; } if(scorePct >= 80) { if(!window.app.prog[window.app.sub]) window.app.prog[window.app.sub] = {}; let scoresArray = window.app.prog[window.app.sub][window.app.bId] || []; if (!Array.isArray(scoresArray)) { scoresArray = typeof scoresArray === 'number' ? [scoresArray] : []; } scoresArray.push(scorePct); window.app.prog[window.app.sub][window.app.bId] = scoresArray; if(window.app.email && window.app.mode !== 'trial') { try { await updateDoc(doc(db, "users", window.app.email), { [`progress.${window.app.sub}.${window.app.bId}`]: scoresArray }); } catch(e) { console.error("Cloud Progress Write Exception:", e); } } } window.app.renderDash(); } };