{"id":9667,"date":"2026-05-28T17:17:45","date_gmt":"2026-05-28T10:17:45","guid":{"rendered":"https:\/\/man2probolinggo.sch.id\/?page_id=9667"},"modified":"2026-05-28T17:17:45","modified_gmt":"2026-05-28T10:17:45","slug":"portal-ujian-inklusif-man-2-probolinggo","status":"publish","type":"page","link":"https:\/\/man2probolinggo.sch.id\/index.php\/portal-ujian-inklusif-man-2-probolinggo\/","title":{"rendered":"Portal Ujian Inklusif &#8211; MAN 2 Probolinggo"},"content":{"rendered":"\r\n<!-- WRAPPER FULL WINDOW -->\r\n<div id=\"lms-fullscreen-wrapper\" style=\"position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 2147483647; background-color: #f1f5f9; overflow-y: auto; overflow-x: hidden; font-family: 'Inter', 'Segoe UI', sans-serif; margin: 0; padding: 0;\">\r\n\r\n    <!-- Library Eksternal -->\r\n    <script src=\"https:\/\/cdn.tailwindcss.com\"><\/script>\r\n    <link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.4.0\/css\/all.min.css\">\r\n    <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/sweetalert2@11\"><\/script>\r\n    \r\n    <script>\r\n        \/\/ Mematikan preflight agar tidak merusak elemen WP lain (meski tertutup)\r\n        tailwind.config = { corePlugins: { preflight: false } }\r\n    <\/script>\r\n\r\n    <style>\r\n        \/* CSS INTERNAL APLIKASI *\/\r\n        :root { --lms-font-scale: 16px; }\r\n        #lms-fullscreen-wrapper { font-size: var(--lms-font-scale); color: #1e293b; line-height: 1.5; display: flex; flex-direction: column; }\r\n        #lms-fullscreen-wrapper .hidden { display: none !important; }\r\n        #lms-fullscreen-wrapper *:focus-visible { outline: 4px solid #3b82f6 !important; outline-offset: 2px !important; }\r\n        \r\n        #lms-fullscreen-wrapper .glass-panel { background: rgba(255, 255, 255, 0.98); border: 1px solid #e2e8f0; box-shadow: 0 10px 25px rgba(0,0,0,0.03); border-radius: 16px; transition: all 0.3s;}\r\n        #lms-fullscreen-wrapper .btn { transition: all 0.2s; cursor: pointer; border: none; }\r\n        #lms-fullscreen-wrapper .btn:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(0,0,0,0.08); }\r\n        #lms-fullscreen-wrapper .btn:disabled { opacity: 0.6; cursor: not-allowed; transform: none; box-shadow: none;}\r\n        \r\n        \/* Grid Navigasi *\/\r\n        #lms-fullscreen-wrapper .nav-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(45px, 1fr)); gap: 10px; }\r\n        #lms-fullscreen-wrapper .nav-btn { width: 100%; aspect-ratio: 1; border-radius: 10px; font-weight: 600; display: flex; align-items: center; justify-content: center; cursor: pointer; border: 2px solid #cbd5e1; background: white; color: #475569; padding:0;}\r\n        #lms-fullscreen-wrapper .nav-btn.answered { background-color: #10b981; color: white; border-color: #059669; }\r\n        #lms-fullscreen-wrapper .nav-btn.active { border-color: #2563eb; background-color: #eff6ff; color: #1e3a8a; box-shadow: 0 0 0 3px rgba(59,130,246,0.3);}\r\n        #lms-fullscreen-wrapper .nav-btn.answered.active { background-color: #10b981; border-color: #1e3a8a; box-shadow: 0 0 0 3px rgba(16,185,129,0.4);}\r\n        \r\n        \/* Opsi Jawaban *\/\r\n        #lms-fullscreen-wrapper .opsi-label { display: block; padding: 1rem; border: 2px solid #e2e8f0; border-radius: 12px; cursor: pointer; transition: all 0.2s; margin-bottom: 0.75rem; background: white; font-size: 1.125rem;}\r\n        #lms-fullscreen-wrapper .opsi-label:hover { border-color: #bfdbfe; background: #f8fafc; }\r\n        #lms-fullscreen-wrapper .opsi-input { position: absolute; opacity: 0; width: 0; height: 0; }\r\n        #lms-fullscreen-wrapper .opsi-input:checked + .opsi-text-wrapper { font-weight: bold; color: #1d4ed8; }\r\n        #lms-fullscreen-wrapper .opsi-input:checked ~ .opsi-text-wrapper .indicator { background-color: #2563eb; border-color: #2563eb; color: white;}\r\n        #lms-fullscreen-wrapper .indicator { width: 28px; height: 28px; border-radius: 6px; border: 2px solid #cbd5e1; display: flex; align-items: center; justify-content: center; margin-right: 15px; flex-shrink:0;}\r\n        #lms-fullscreen-wrapper .indicator i { opacity: 0; font-size: 14px;}\r\n        #lms-fullscreen-wrapper .opsi-input:checked ~ .opsi-text-wrapper .indicator i { opacity: 1; }\r\n        \r\n        #lms-fullscreen-wrapper .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0; }\r\n        \r\n        \/* Tabel & Form *\/\r\n        #lms-fullscreen-wrapper .table-wrap { overflow-x: auto; width: 100%; }\r\n        #lms-fullscreen-wrapper table { width: 100%; border-collapse: collapse; text-align: left; }\r\n        #lms-fullscreen-wrapper th { background: #f8fafc; padding: 12px 16px; font-size: 0.875rem; text-transform: uppercase; color: #64748b; font-weight: 700; border-bottom: 2px solid #e2e8f0;}\r\n        #lms-fullscreen-wrapper td { padding: 14px 16px; border-bottom: 1px solid #f1f5f9; color: #334155;}\r\n        \r\n        #lms-fullscreen-wrapper .modal-overlay { position: fixed; inset: 0; background: rgba(15, 23, 42, 0.7); z-index: 9999999; display: flex; align-items: center; justify-content: center; padding: 15px; backdrop-filter: blur(4px);}\r\n        #lms-fullscreen-wrapper .modal-card { background: white; border-radius: 20px; width: 100%; max-width: 800px; max-height: 90vh; overflow-y: auto; box-shadow: 0 25px 50px -12px rgba(0,0,0,0.25); position: relative;}\r\n        #lms-fullscreen-wrapper .input-field { width: 100%; padding: 12px 16px; border: 2px solid #e2e8f0; border-radius: 10px; font-size: 1rem; outline: none; background: #fff;}\r\n        #lms-fullscreen-wrapper .input-field:focus { border-color: #3b82f6; }\r\n\r\n        \/* TEMA KONTRAS TINGGI *\/\r\n        #lms-fullscreen-wrapper.high-contrast { background-color: #000 !important; color: #fff !important; }\r\n        #lms-fullscreen-wrapper.high-contrast header { background-color: #111 !important; border-bottom: 2px solid #ff0 !important; color: #ff0 !important; }\r\n        #lms-fullscreen-wrapper.high-contrast .glass-panel, #lms-fullscreen-wrapper.high-contrast .modal-card { background: #000 !important; border: 2px solid #ff0 !important; color: #fff !important; }\r\n        #lms-fullscreen-wrapper.high-contrast .text-gray-800, #lms-fullscreen-wrapper.high-contrast .text-gray-700, #lms-fullscreen-wrapper.high-contrast .text-gray-600, #lms-fullscreen-wrapper.high-contrast .text-blue-800 { color: #fff !important; }\r\n        #lms-fullscreen-wrapper.high-contrast .btn { background-color: #000 !important; color: #ff0 !important; border: 2px solid #ff0 !important; }\r\n        #lms-fullscreen-wrapper.high-contrast .btn:hover { background-color: #ff0 !important; color: #000 !important; }\r\n        #lms-fullscreen-wrapper.high-contrast .opsi-label { background: #000; border: 2px solid #fff; color:#fff;}\r\n        #lms-fullscreen-wrapper.high-contrast .opsi-input:checked + .opsi-text-wrapper { color: #ff0; }\r\n        #lms-fullscreen-wrapper.high-contrast .opsi-input:checked ~ .opsi-text-wrapper .indicator { background-color: #ff0; border-color: #ff0; color: #000;}\r\n        #lms-fullscreen-wrapper.high-contrast .nav-btn { background: #000; border-color: #fff; color: #fff;}\r\n        #lms-fullscreen-wrapper.high-contrast .nav-btn.answered { background: #ff0; color: #000; border-color: #ff0;}\r\n        #lms-fullscreen-wrapper.high-contrast .nav-btn.active { border-color: #ff0; outline: 3px solid #ff0; }\r\n        #lms-fullscreen-wrapper.high-contrast .bg-blue-100, #lms-fullscreen-wrapper.high-contrast .bg-gray-50, #lms-fullscreen-wrapper.high-contrast .bg-yellow-50 { background: transparent !important; border: 1px solid #ff0 !important;}\r\n\r\n        @media print { \r\n            #lms-fullscreen-wrapper { position: static !important; overflow: visible !important; height: auto !important; background: white !important;}\r\n            #lms-fullscreen-wrapper header, #lms-fullscreen-wrapper footer, #lms-fullscreen-wrapper #a11y-menu, #lms-fullscreen-wrapper .no-print { display: none !important; } \r\n            #lms-fullscreen-wrapper .glass-panel { box-shadow: none; border: none; } \r\n        }\r\n    <\/style>\r\n\r\n    <!-- Pembaca Layar Utama -->\r\n    <div aria-live=\"polite\" aria-atomic=\"true\" id=\"sr-announcer\" class=\"sr-only\"><\/div>\r\n\r\n    <header class=\"bg-blue-800 text-white p-4 shadow-md sticky top-0 z-40\" style=\"display:flex; justify-content:space-between; align-items:center;\">\r\n        <h1 class=\"text-xl font-bold flex items-center gap-2\" tabindex=\"0\" style=\"margin:0;\"><i class=\"fa-solid fa-graduation-cap\"><\/i> <span class=\"hidden sm:inline\">Portal Ujian Inklusif<\/span><\/h1>\r\n        <div id=\"user-profile\" class=\"hidden flex items-center gap-3\">\r\n            <div class=\"bg-blue-700 px-3 py-1.5 rounded-lg border border-blue-600 flex items-center gap-2\"><i class=\"fa-solid fa-user-circle\"><\/i> <span id=\"user-name-display\" class=\"font-medium text-sm\"><\/span><\/div>\r\n            <button onclick=\"prosesLogout()\" class=\"bg-red-500 hover:bg-red-600 text-white px-3 py-1.5 rounded-lg text-sm font-bold btn flex items-center gap-1\"><i class=\"fa-solid fa-power-off\"><\/i> <span class=\"hidden sm:inline\">Keluar<\/span><\/button>\r\n        <\/div>\r\n    <\/header>\r\n\r\n    <main id=\"app-container\" class=\"flex-grow container mx-auto p-4 md:p-6 max-w-7xl relative\"><\/main>\r\n\r\n    <footer class=\"bg-slate-900 text-slate-400 py-6 text-center text-sm mt-auto\">\r\n        <p tabindex=\"0\" style=\"margin:0;\">&copy; 2026 Portal LMS Inklusif &bull; Standar Aksesibilitas Terjamin.<\/p>\r\n    <\/footer>\r\n\r\n    <!-- FAB MENU AKSESIBILITAS -->\r\n    <div id=\"a11y-menu\" class=\"hidden fixed bottom-4 right-4 z-50 flex flex-col gap-2 bg-white\/90 p-2 rounded-xl shadow-lg border border-gray-200 backdrop-blur-sm no-print\">\r\n        <p class=\"text-[10px] font-bold text-center text-gray-500 uppercase border-b pb-1 mb-1\" style=\"margin:0;\">Aksesibilitas<\/p>\r\n        <button onclick=\"a11yZoom(1)\" class=\"w-10 h-10 bg-gray-100 rounded-lg font-bold btn\">A+<\/button>\r\n        <button onclick=\"a11yZoom(-1)\" class=\"w-10 h-10 bg-gray-100 rounded-lg font-bold btn\">A-<\/button>\r\n        <button onclick=\"a11yContrast()\" class=\"w-10 h-10 bg-gray-800 text-yellow-400 rounded-lg btn\"><i class=\"fa-solid fa-circle-half-stroke\"><\/i><\/button>\r\n        <button onclick=\"a11yStopTTS()\" class=\"w-10 h-10 bg-red-100 text-red-600 rounded-lg btn\"><i class=\"fa-solid fa-volume-xmark\"><\/i><\/button>\r\n    <\/div>\r\n\r\n    <!-- MODAL CONTAINER -->\r\n    <div id=\"modal-container\" class=\"hidden\"><\/div>\r\n\r\n    <script type=\"text\/javascript\">\r\n        \/* <![CDATA[ *\/\r\n        \/\/ ==========================================\r\n        \/\/ 1. DATABASE LOKAL MOCKUP\r\n        \/\/ ==========================================\r\n        var isGAS = typeof google !== 'undefined' && google.script && google.script.run;\r\n        \r\n        var MockDB = {\r\n            admin: [ {id_admin: \"ADM001\", username: \"admin\", password: \"admin123\", nama: \"Administrator Sistem\"} ],\r\n            siswa: [\r\n                {id_siswa: \"SIS001\", nomor_peserta: \"12345\", nama_siswa: \"Budi Santoso (Tunanetra)\", kelas: \"XII A\", status: \"aktif\"},\r\n                {id_siswa: \"SIS002\", nomor_peserta: \"67890\", nama_siswa: \"Siti Aminah\", kelas: \"XII B\", status: \"aktif\"}\r\n            ],\r\n            ujian: [ {id_ujian: \"UJ001\", token: \"TOKEN123\", mapel: \"Bahasa Indonesia\", deskripsi: \"Ujian Akhir Semester\", waktu_menit: 60, acak_soal: true, acak_opsi: true, status: \"aktif\", created_at: new Date().toISOString()} ],\r\n            soal: [\r\n                {id_soal: \"SOAL001\", id_ujian: \"UJ001\", tipe_soal: \"PG\", stimulus: \"\", pertanyaan: \"Manakah yang merupakan ibu kota Indonesia?\", opsi_jawaban: '[\"Jakarta\",\"Bandung\",\"Surabaya\",\"Semarang\",\"Medan\"]', kunci_jawaban: JSON.stringify({kunci: \"Jakarta\", bobot: 10})},\r\n                {id_soal: \"SOAL002\", id_ujian: \"UJ001\", tipe_soal: \"BS\", stimulus: \"Hewan Mamalia\", pertanyaan: \"Tentukan Benar\/Salah:\", opsi_jawaban: '[\"Kucing bertelur\",\"Paus menyusui anaknya\"]', kunci_jawaban: JSON.stringify({\"Kucing bertelur\": {kunci: \"Salah\", bobot: 5}, \"Paus menyusui anaknya\": {kunci: \"Benar\", bobot: 5}})},\r\n                {id_soal: \"SOAL003\", id_ujian: \"UJ001\", tipe_soal: \"PGK\", stimulus: \"Hardware Komputer\", pertanyaan: \"Pilih yang merupakan hardware:\", opsi_jawaban: '[\"Monitor\",\"Windows\",\"Keyboard\",\"Word\"]', kunci_jawaban: JSON.stringify({\"Monitor\": {is_benar: true, bobot: 5}, \"Windows\": {is_benar: false, bobot: 2}, \"Keyboard\": {is_benar: true, bobot: 5}, \"Word\": {is_benar: false, bobot: 2}})}\r\n            ], hasil: []\r\n        };\r\n\r\n        var AppState = { view: 'login', user: null, ujianAktif: null, soalUjian: [], jawabanSiswa: {}, currentSoalIndex: 0, waktuSisa: 0, timerInterval: null, evaluasiSelesai: null, admin: { currentMenu: 'dashboard', data: { admin: [], siswa: [], ujian: [], soal: [], hasil: [] } }, a11y: { fontSize: 16, isHighContrast: false } };\r\n\r\n        function shuffleArray(array) { let curId = array.length; while (0 !== curId) { let randId = Math.floor(Math.random() * curId); curId -= 1; let tmp = array[curId]; array[curId] = array[randId]; array[randId] = tmp; } return array; }\r\n\r\n        var MockAPI = {\r\n            loginSiswa: (nomor) => new Promise((res, rej) => setTimeout(() => { let s = MockDB.siswa.find(x => x.nomor_peserta === nomor && x.status === \"aktif\"); if(s) res({success: true, data: {...s, role: \"siswa\"}}); else rej(new Error(\"Nomor tidak valid.\")); }, 500)),\r\n            loginAdmin: (u, p) => new Promise((res, rej) => setTimeout(() => { let adm = MockDB.admin.find(x => x.username === u && x.password === p); if(adm) res({success: true, data: {nama: adm.nama, role: \"admin\"}}); else rej(new Error(\"Kredensial Salah\")); }, 500)),\r\n            validasiToken: (t, nop) => new Promise((res, rej) => setTimeout(() => {\r\n                let u = MockDB.ujian.find(x => x.token === t && x.status === \"aktif\"); if(!u) return rej(new Error(\"Token salah atau ujian tidak aktif.\"));\r\n                if(MockDB.hasil.find(h => h.id_ujian === u.id_ujian && h.nomor_peserta === nop)) return rej(new Error(\"Anda sudah mengerjakan ujian ini.\"));\r\n                let soalArr = MockDB.soal.filter(x => x.id_ujian === u.id_ujian).map(x => ({ id_soal: x.id_soal, tipe_soal: x.tipe_soal, stimulus: x.stimulus, pertanyaan: x.pertanyaan, opsi_jawaban: JSON.parse(x.opsi_jawaban) }));\r\n                if(u.acak_soal) shuffleArray(soalArr); if(u.acak_opsi) { soalArr.forEach(s => { if(Array.isArray(s.opsi_jawaban)) shuffleArray(s.opsi_jawaban); }); }\r\n                res({success: true, data: {ujian: u, soal: soalArr}});\r\n            }, 500)),\r\n            submitJawaban: (payload) => new Promise(res => setTimeout(() => {\r\n                let kunci = MockDB.soal.filter(x => x.id_ujian === payload.id_ujian); let totalBobotSeluruh = 0; let skorDiperolehSeluruh = 0; let detail = {};\r\n                kunci.forEach(s => {\r\n                    let jwb = payload.jawaban_siswa[s.id_soal]; let k = JSON.parse(s.kunci_jawaban); let isBenar = false;\r\n                    if(s.tipe_soal === \"PG\") { let b = Number(k.bobot) || 0; totalBobotSeluruh += b; if(jwb === k.kunci) { isBenar = true; skorDiperolehSeluruh += b; } }\r\n                    else if(s.tipe_soal === \"PGK\") { let benarSemua = true; for(let opt in k) { let b = Number(k[opt].bobot) || 0; totalBobotSeluruh += b; let siswaCentang = Array.isArray(jwb) && jwb.includes(opt); if((k[opt].is_benar && siswaCentang) || (!k[opt].is_benar && !siswaCentang)) skorDiperolehSeluruh += b; else benarSemua = false; } isBenar = benarSemua; }\r\n                    else if(s.tipe_soal === \"BS\") { let benarSemua = true; for(let stm in k) { let b = Number(k[stm].bobot) || 0; totalBobotSeluruh += b; let jwbSiswaStm = (typeof jwb === 'object' && jwb !== null) ? jwb[stm] : null; if(jwbSiswaStm === k[stm].kunci) skorDiperolehSeluruh += b; else benarSemua = false; } isBenar = benarSemua; }\r\n                    detail[s.id_soal] = {tipe: s.tipe_soal, stimulus: s.stimulus, pertanyaan: s.pertanyaan, jawaban_siswa: jwb, is_benar: isBenar, kunci: k};\r\n                });\r\n                let skorAkhir = totalBobotSeluruh > 0 ? Math.round((skorDiperolehSeluruh \/ totalBobotSeluruh) * 100) : 0;\r\n                MockDB.hasil.push({id_hasil: \"RES\"+Date.now(), nomor_peserta: payload.nomor_peserta, nama_siswa: payload.nama_siswa, kelas: payload.kelas, id_ujian: payload.id_ujian, mapel: payload.mapel, skor: skorAkhir, detail_jawaban: JSON.stringify(detail), waktu_selesai: new Date().toISOString()});\r\n                res({success: true, skor: skorAkhir, evaluasi: detail});\r\n            }, 800)),\r\n            getAdminData: () => new Promise(res => setTimeout(() => res({success: true, data: MockDB}), 300)),\r\n            simpanAdmin: (p) => new Promise(res => { MockDB.admin.push({id_admin:\"ADM\"+Date.now(), ...p}); res({success:true}); }),\r\n            hapusAdmin: (id) => new Promise(res => { MockDB.admin = MockDB.admin.filter(x=>x.id_admin!==id); res({success:true}); }),\r\n            simpanSiswa: (p) => new Promise(res => { MockDB.siswa.push({id_siswa:\"SIS\"+Date.now(), ...p}); res({success:true}); }),\r\n            hapusSiswa: (id) => new Promise(res => { MockDB.siswa = MockDB.siswa.filter(x=>x.id_siswa!==id); res({success:true}); }),\r\n            simpanUjian: (p) => new Promise(res => { MockDB.ujian.push({id_ujian:\"UJ\"+Date.now(), created_at:new Date().toISOString(), ...p}); res({success:true}); }),\r\n            hapusUjian: (id) => new Promise(res => { MockDB.ujian = MockDB.ujian.filter(x=>x.id_ujian!==id); res({success:true}); }),\r\n            resetHasilUjian: (id) => new Promise(res => { MockDB.hasil = MockDB.hasil.filter(x=>x.id_ujian!==id); res({success:true}); }),\r\n            simpanSoalManual: (p) => new Promise(res => { MockDB.soal.push({id_soal:\"SOAL\"+Date.now(), id_ujian:p.id_ujian, tipe_soal:p.tipe_soal, stimulus:p.stimulus, pertanyaan:p.pertanyaan, opsi_jawaban:JSON.stringify(p.opsi), kunci_jawaban:JSON.stringify(p.kunci)}); res({success:true}); }),\r\n            hapusSoal: (id) => new Promise(res => { MockDB.soal = MockDB.soal.filter(x=>x.id_soal!==id); res({success:true}); })\r\n        };\r\n\r\n        function callBackend(func, ...args) {\r\n            return new Promise((resolve, reject) => {\r\n                if (isGAS) google.script.run.withSuccessHandler(resolve).withFailureHandler(reject)[func](...args);\r\n                else { if(MockAPI[func]) MockAPI[func](...args).then(resolve).catch(reject); else reject(new Error(\"Mock function not found\")); }\r\n            });\r\n        }\r\n\r\n        \/\/ ==========================================\r\n        \/\/ 3. UI HELPERS & AKSESIBILITAS\r\n        \/\/ ==========================================\r\n        function getContainer() { return document.getElementById('app-container'); }\r\n        function announce(msg) { document.getElementById('sr-announcer').textContent = msg; }\r\n        function showLoading(msg = \"Memproses...\") { Swal.fire({ title: msg, allowOutsideClick: false, didOpen: () => Swal.showLoading() }); }\r\n        function closeLoading() { Swal.close(); }\r\n        function showToast(msg, icon='success') { Swal.fire({ toast: true, position: 'top-end', icon: icon, title: msg, showConfirmButton: false, timer: 3000 }); }\r\n        function showError(msg) { Swal.fire({ icon: 'error', title: 'Kesalahan', text: msg }); }\r\n\r\n        window.a11yZoom = function(dir) {\r\n            AppState.a11y.fontSize += (dir * 2);\r\n            if(AppState.a11y.fontSize < 12) AppState.a11y.fontSize = 12;\r\n            if(AppState.a11y.fontSize > 24) AppState.a11y.fontSize = 24;\r\n            document.getElementById('lms-fullscreen-wrapper').style.setProperty('--lms-font-scale', AppState.a11y.fontSize + 'px');\r\n            announce(`Teks diubah menjadi ${AppState.a11y.fontSize} piksel`);\r\n        }\r\n        window.a11yContrast = function() {\r\n            AppState.a11y.isHighContrast = !AppState.a11y.isHighContrast;\r\n            const wrap = document.getElementById('lms-fullscreen-wrapper');\r\n            if(AppState.a11y.isHighContrast) wrap.classList.add('high-contrast'); else wrap.classList.remove('high-contrast');\r\n            announce(`Kontras tinggi ${AppState.a11y.isHighContrast ? 'aktif' : 'nonaktif'}`);\r\n        }\r\n        window.a11ySpeak = function(teks) {\r\n            if ('speechSynthesis' in window) { window.speechSynthesis.cancel(); let u = new SpeechSynthesisUtterance(teks); u.lang = 'id-ID'; window.speechSynthesis.speak(u); }\r\n        }\r\n        window.a11yStopTTS = function() { if ('speechSynthesis' in window) window.speechSynthesis.cancel(); }\r\n\r\n        \/\/ ==========================================\r\n        \/\/ 4. AUTENTIKASI\r\n        \/\/ ==========================================\r\n        window.renderLogin = function() {\r\n            document.getElementById('user-profile').classList.add('hidden');\r\n            document.getElementById('a11y-menu').classList.add('hidden');\r\n            getContainer().innerHTML = `\r\n                <div class=\"max-w-md mx-auto glass-panel p-8 mt-12 bg-white\" style=\"border-radius:15px; padding:30px;\">\r\n                    <div class=\"text-center mb-6\"><div style=\"width:60px; height:60px; background:#dbeafe; color:#2563eb; border-radius:50%; display:inline-flex; align-items:center; justify-content:center; font-size:24px; margin-bottom:15px;\"><i class=\"fa-solid fa-door-open\"><\/i><\/div><h2 class=\"text-2xl font-bold\" tabindex=\"0\">Masuk Portal<\/h2><\/div>\r\n                    <div class=\"flex bg-gray-100 p-1 rounded-xl mb-6\" style=\"display:flex; gap:5px; background:#f1f5f9; padding:5px; border-radius:12px;\">\r\n                        <button id=\"tab-siswa\" onclick=\"switchTab('siswa')\" class=\"flex-1 py-2 text-sm font-bold bg-white text-blue-700 btn\" style=\"width:50%; padding:10px; border-radius:8px;\">Siswa<\/button>\r\n                        <button id=\"tab-admin\" onclick=\"switchTab('admin')\" class=\"flex-1 py-2 text-sm font-bold text-gray-500 btn\" style=\"width:50%; padding:10px; border-radius:8px;\">Guru\/Admin<\/button>\r\n                    <\/div>\r\n                    <form id=\"form-siswa\" onsubmit=\"handleLoginSiswa(event)\">\r\n                        <div style=\"margin-bottom:20px;\"><label class=\"block font-bold mb-2\">Nomor Peserta<\/label><input type=\"text\" id=\"nomor_peserta\" required class=\"input-field text-center font-bold text-xl tracking-widest\" placeholder=\"Contoh: 12345\"><\/div>\r\n                        <button type=\"submit\" class=\"w-full text-white font-bold py-3 rounded-lg btn\" style=\"background:#2563eb; padding:15px; width:100%; border-radius:10px; font-size:16px;\">Masuk Ujian<\/button>\r\n                    <\/form>\r\n                    <form id=\"form-admin\" class=\"hidden\" onsubmit=\"handleLoginAdmin(event)\">\r\n                        <div style=\"margin-bottom:15px;\"><label class=\"block font-bold mb-2\">Username<\/label><input type=\"text\" id=\"username\" class=\"input-field\" required><\/div>\r\n                        <div style=\"margin-bottom:20px;\"><label class=\"block font-bold mb-2\">Password<\/label><input type=\"password\" id=\"password\" class=\"input-field\" required><\/div>\r\n                        <button type=\"submit\" class=\"w-full text-white font-bold py-3 rounded-lg btn\" style=\"background:#1e293b; padding:15px; width:100%; border-radius:10px; font-size:16px;\">Masuk Dasbor<\/button>\r\n                    <\/form>\r\n                <\/div>`;\r\n            announce(\"Halaman login.\");\r\n        }\r\n\r\n        window.switchTab = function(t) {\r\n            let s = t === 'siswa';\r\n            document.getElementById('form-siswa').classList.toggle('hidden', !s); document.getElementById('form-admin').classList.toggle('hidden', s);\r\n            document.getElementById('tab-siswa').style.cssText = s ? 'width:50%; padding:10px; border-radius:8px; background:white; color:#1d4ed8; font-weight:bold;' : 'width:50%; padding:10px; border-radius:8px; background:transparent; color:#64748b; font-weight:bold;';\r\n            document.getElementById('tab-admin').style.cssText = !s ? 'width:50%; padding:10px; border-radius:8px; background:white; color:#1d4ed8; font-weight:bold;' : 'width:50%; padding:10px; border-radius:8px; background:transparent; color:#64748b; font-weight:bold;';\r\n        }\r\n\r\n        window.handleLoginSiswa = function(e) { e.preventDefault(); showLoading(); callBackend('loginSiswa', document.getElementById('nomor_peserta').value).then(r => { closeLoading(); AppState.user = r.data; renderUjianToken(); }).catch(err => { closeLoading(); showError(err.message); }); }\r\n        window.handleLoginAdmin = function(e) { e.preventDefault(); showLoading(); callBackend('loginAdmin', document.getElementById('username').value, document.getElementById('password').value).then(r => { closeLoading(); AppState.user = r.data; initAdminDashboard(); }).catch(err => { closeLoading(); showError(err.message); }); }\r\n\r\n        window.prosesLogout = function() {\r\n            Swal.fire({ title: 'Keluar?', text: 'Anda yakin ingin keluar?', icon: 'warning', showCancelButton: true, confirmButtonText: 'Ya, Keluar', confirmButtonColor: '#d33' })\r\n            .then(r => { if(r.isConfirmed) { if(AppState.timerInterval) clearInterval(AppState.timerInterval); window.speechSynthesis.cancel(); AppState.user = null; AppState.ujianAktif = null; document.getElementById('user-profile').classList.add('hidden'); document.getElementById('a11y-menu').classList.add('hidden'); renderLogin(); } });\r\n        }\r\n\r\n        \/\/ ==========================================\r\n        \/\/ 5. VIEW SISWA UJIAN\r\n        \/\/ ==========================================\r\n        window.renderUjianToken = function() {\r\n            document.getElementById('user-profile').classList.remove('hidden'); document.getElementById('user-name-display').textContent = AppState.user.nama_siswa; document.getElementById('a11y-menu').classList.remove('hidden');\r\n            getContainer().innerHTML = `\r\n                <div class=\"max-w-xl mx-auto mt-8\">\r\n                    <div class=\"glass-panel p-6 mb-6\" style=\"background:#eff6ff; border:1px solid #bfdbfe; border-radius:15px; padding:20px; display:flex; gap:15px; align-items:center;\">\r\n                        <div style=\"width:50px; height:50px; background:#bfdbfe; color:#1d4ed8; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:20px;\"><i class=\"fa-solid fa-user-check\"><\/i><\/div>\r\n                        <div><h2 class=\"font-bold text-lg\" tabindex=\"0\">${AppState.user.nama_siswa}<\/h2><p style=\"color:#475569; font-size:14px;\">No. ${AppState.user.nomor_peserta} | Kelas ${AppState.user.kelas}<\/p><\/div>\r\n                    <\/div>\r\n                    <div class=\"glass-panel p-8 text-center\" style=\"background:white; padding:30px; border-radius:15px;\">\r\n                        <h2 class=\"text-2xl font-bold mb-2\">Token Ujian<\/h2>\r\n                        <form onsubmit=\"handleCekToken(event)\" class=\"mt-6\">\r\n                            <input type=\"text\" id=\"token_ujian\" required class=\"input-field text-center text-2xl uppercase mb-4\" style=\"padding:15px; font-weight:bold; letter-spacing:3px; background:#f8fafc;\" placeholder=\"MASUKKAN TOKEN\">\r\n                            <button type=\"submit\" class=\"w-full text-white font-bold py-4 rounded-lg btn\" style=\"width:100%; padding:15px; background:#10b981; color:white; border-radius:10px; font-size:18px;\">Konfirmasi Token<\/button>\r\n                        <\/form>\r\n                    <\/div>\r\n                <\/div>`;\r\n        }\r\n\r\n        window.handleCekToken = function(e) { e.preventDefault(); showLoading(\"Mencari...\"); callBackend('validasiToken', document.getElementById('token_ujian').value.trim().toUpperCase(), AppState.user.nomor_peserta).then(r => { closeLoading(); AppState.ujianAktif = r.data.ujian; AppState.soalUjian = r.data.soal; renderKonfirmasiUjian(); }).catch(err => { closeLoading(); showError(err.message); }); }\r\n        \r\n        window.renderKonfirmasiUjian = function() {\r\n            const u = AppState.ujianAktif;\r\n            getContainer().innerHTML = `\r\n                <div class=\"max-w-2xl mx-auto glass-panel p-8 mt-8\" style=\"background:white; padding:40px; border-radius:15px;\">\r\n                    <h2 class=\"text-3xl font-bold text-center mb-6\" tabindex=\"0\">${u.mapel}<\/h2>\r\n                    <div class=\"bg-gray-50 p-6 rounded-xl border mb-8\" style=\"background:#f8fafc; padding:20px; border-radius:10px; border:1px solid #e2e8f0; font-size:16px;\">\r\n                        <div style=\"display:flex; justify-content:space-between; border-bottom:1px solid #cbd5e1; padding-bottom:10px; margin-bottom:10px;\"><span style=\"color:#64748b;\">Waktu Mengerjakan<\/span><span style=\"font-weight:bold;\">${u.waktu_menit} Menit<\/span><\/div>\r\n                        <div style=\"display:flex; justify-content:space-between; border-bottom:1px solid #cbd5e1; padding-bottom:10px; margin-bottom:10px;\"><span style=\"color:#64748b;\">Jumlah Soal<\/span><span style=\"font-weight:bold;\">${AppState.soalUjian.length} Butir<\/span><\/div>\r\n                        <div><span style=\"color:#64748b; display:block; margin-bottom:5px;\">Keterangan:<\/span><p style=\"font-weight:500;\">${u.deskripsi}<\/p><\/div>\r\n                    <\/div>\r\n                    <div style=\"display:flex; gap:15px;\">\r\n                        <button onclick=\"renderUjianToken()\" class=\"btn\" style=\"flex:1; padding:15px; border-radius:10px; background:#f1f5f9; font-weight:bold; color:#475569;\">Batal<\/button>\r\n                        <button onclick=\"mulaiUjian()\" class=\"btn\" style=\"flex:2; padding:15px; border-radius:10px; background:#2563eb; font-weight:bold; color:white; font-size:18px;\">Mulai Ujian<\/button>\r\n                    <\/div>\r\n                <\/div>`;\r\n            announce(`Ujian ${u.mapel}. Waktu ${u.waktu_menit} menit.`);\r\n        }\r\n\r\n        window.mulaiUjian = function() {\r\n            AppState.view = 'berlangsung'; AppState.waktuSisa = AppState.ujianAktif.waktu_menit * 60;\r\n            AppState.timerInterval = setInterval(() => { AppState.waktuSisa--; updateTimerVisual(); if(AppState.waktuSisa <= 0) autoSubmit(); }, 1000);\r\n            renderLayarUjian();\r\n        }\r\n\r\n        function updateTimerVisual() {\r\n            const el = document.getElementById('timer-display'); if(!el) return;\r\n            let h = Math.floor(AppState.waktuSisa \/ 3600); let m = Math.floor((AppState.waktuSisa % 3600) \/ 60); let s = AppState.waktuSisa % 60;\r\n            el.textContent = `${h>0?h+':':''}${m.toString().padStart(2,'0')}:${s.toString().padStart(2,'0')}`;\r\n        }\r\n        window.cekWaktuManual = function() { const m = Math.floor(AppState.waktuSisa \/ 60); a11ySpeak(`Waktu tersisa ${m} menit.`); }\r\n        window.bacakanSoalIni = function() {\r\n            const soal = AppState.soalUjian[AppState.currentSoalIndex]; let teks = `Soal nomor ${AppState.currentSoalIndex + 1}. `;\r\n            if(soal.stimulus) teks += `Informasi: ${soal.stimulus}. `; teks += `Pertanyaan: ${soal.pertanyaan}. `;\r\n            if(soal.tipe_soal === 'PG' || soal.tipe_soal === 'PGK') { soal.opsi_jawaban.forEach((o, i) => teks += `Opsi ${i+1}: ${o}. `); } else if(soal.tipe_soal === 'BS') { teks += \"Tentukan benar atau salah. \"; soal.opsi_jawaban.forEach(o => teks += `Pernyataan: ${o}. `); }\r\n            a11ySpeak(teks);\r\n        }\r\n\r\n        window.renderLayarUjian = function() {\r\n            const soal = AppState.soalUjian[AppState.currentSoalIndex]; const num = AppState.currentSoalIndex + 1; const total = AppState.soalUjian.length;\r\n            announce(`Halaman Soal ${num}.`);\r\n            let opsiHTML = '';\r\n            \r\n            if(soal.tipe_soal === 'PG') {\r\n                const jwb = AppState.jawabanSiswa[soal.id_soal] || null;\r\n                opsiHTML = `<fieldset><legend class=\"sr-only\">Pilih jawaban<\/legend><div style=\"margin-top:20px; display:flex; flex-direction:column; gap:10px;\">` + soal.opsi_jawaban.map((o, i) => `\r\n                <label class=\"opsi-label\" style=\"display:block; padding:15px; border:2px solid #e2e8f0; border-radius:10px; cursor:pointer; background:white;\">\r\n                    <input type=\"radio\" name=\"pg\" value=\"${o}\" class=\"opsi-input\" ${jwb===o?'checked':''} onchange=\"simpanJwb('${soal.id_soal}', this.value)\">\r\n                    <div class=\"opsi-text-wrapper\" style=\"display:flex; align-items:center;\"><span class=\"indicator\" style=\"width:24px; height:24px; border:2px solid #cbd5e1; border-radius:50%; margin-right:15px; display:inline-flex; align-items:center; justify-content:center;\"><i class=\"fa-solid fa-circle\" style=\"font-size:10px;\"><\/i><\/span><span style=\"font-size:18px;\">${o}<\/span><\/div>\r\n                <\/label>`).join('') + `<\/div><\/fieldset>`;\r\n            } else if(soal.tipe_soal === 'PGK') {\r\n                const jwbArr = AppState.jawabanSiswa[soal.id_soal] || [];\r\n                opsiHTML = `<fieldset><div style=\"margin-top:20px; display:flex; flex-direction:column; gap:10px;\">` + soal.opsi_jawaban.map((o, i) => `\r\n                <label class=\"opsi-label\" style=\"display:block; padding:15px; border:2px solid #e2e8f0; border-radius:10px; cursor:pointer; background:white;\">\r\n                    <input type=\"checkbox\" value=\"${o}\" class=\"opsi-input\" ${jwbArr.includes(o)?'checked':''} onchange=\"simpanJwbArr('${soal.id_soal}', this.value, this.checked)\">\r\n                    <div class=\"opsi-text-wrapper\" style=\"display:flex; align-items:center;\"><span class=\"indicator\" style=\"width:24px; height:24px; border:2px solid #cbd5e1; border-radius:4px; margin-right:15px; display:inline-flex; align-items:center; justify-content:center;\"><i class=\"fa-solid fa-check\" style=\"font-size:12px;\"><\/i><\/span><span style=\"font-size:18px;\">${o}<\/span><\/div>\r\n                <\/label>`).join('') + `<\/div><\/fieldset>`;\r\n            } else if(soal.tipe_soal === 'BS') {\r\n                const jwbObj = AppState.jawabanSiswa[soal.id_soal] || {};\r\n                opsiHTML = `<fieldset><div style=\"margin-top:20px; border:1px solid #e2e8f0; border-radius:10px; overflow:hidden; background:white;\"><table style=\"width:100%; border-collapse:collapse;\"><thead style=\"background:#f8fafc;\"><tr><th style=\"padding:15px; text-align:left;\">Pernyataan<\/th><th style=\"padding:15px; text-align:center;\">Benar<\/th><th style=\"padding:15px; text-align:center;\">Salah<\/th><\/tr><\/thead><tbody>` + soal.opsi_jawaban.map((o, i) => `\r\n                <tr style=\"border-bottom:1px solid #f1f5f9;\"><td style=\"padding:15px; font-size:18px;\">${o}<\/td>\r\n                <td style=\"padding:15px; text-align:center;\"><input type=\"radio\" aria-label=\"Benar: ${o}\" name=\"bs_${i}\" value=\"Benar\" style=\"transform:scale(1.5);\" ${jwbObj[o]==='Benar'?'checked':''} onchange=\"simpanJwbObj('${soal.id_soal}','${o}','Benar')\"><\/td>\r\n                <td style=\"padding:15px; text-align:center;\"><input type=\"radio\" aria-label=\"Salah: ${o}\" name=\"bs_${i}\" value=\"Salah\" style=\"transform:scale(1.5);\" ${jwbObj[o]==='Salah'?'checked':''} onchange=\"simpanJwbObj('${soal.id_soal}','${o}','Salah')\"><\/td><\/tr>\r\n                `).join('') + `<\/tbody><\/table><\/div><\/fieldset>`;\r\n            }\r\n\r\n            const btnNext = num === total ? `<button onclick=\"konfirmasiSelesai()\" class=\"btn\" style=\"background:#dc2626; color:white; padding:15px 30px; border-radius:10px; font-weight:bold;\">Kirim Jawaban<\/button>` : `<button onclick=\"navigasiSoal(1)\" class=\"btn\" style=\"background:#2563eb; color:white; padding:15px 30px; border-radius:10px; font-weight:bold;\">Selanjutnya<\/button>`;\r\n            \r\n            getContainer().innerHTML = `\r\n                <div style=\"display:flex; flex-wrap:wrap; gap:25px;\">\r\n                    <div style=\"flex:3; min-width:300px; display:flex; flex-direction:column;\">\r\n                        <div class=\"glass-panel\" style=\"background:white; padding:30px; border-radius:15px; margin-bottom:20px; flex-grow:1;\">\r\n                            <div style=\"display:flex; justify-content:space-between; align-items:center; border-bottom:1px solid #e2e8f0; padding-bottom:15px; margin-bottom:25px;\">\r\n                                <span style=\"background:#dbeafe; color:#1e40af; padding:8px 20px; border-radius:10px; font-weight:bold;\">Soal ${num} \/ ${total}<\/span>\r\n                                <div style=\"display:flex; gap:10px;\">\r\n                                    <button onclick=\"bacakanSoalIni()\" class=\"btn\" style=\"background:#fef3c7; color:#854d0e; font-weight:bold; padding:8px 15px; border-radius:8px;\"><i class=\"fa-solid fa-volume-high\"><\/i> Baca<\/button>\r\n                                    <button onclick=\"cekWaktuManual()\" class=\"btn\" style=\"background:#1e293b; color:white; font-weight:bold; padding:8px 15px; border-radius:8px; font-family:monospace;\"><i class=\"fa-regular fa-clock\"><\/i> <span id=\"timer-display\">--:--<\/span><\/button>\r\n                                <\/div>\r\n                            <\/div>\r\n                            ${soal.stimulus ? `<div style=\"background:#f8fafc; padding:20px; border-radius:10px; margin-bottom:20px; font-size:18px; line-height:1.6;\" tabindex=\"0\">${soal.stimulus.replace(\/\\n\/g, '<br>')}<\/div>` : ''}\r\n                            <p style=\"font-size:20px; font-weight:500; margin-bottom:20px; line-height:1.5;\" tabindex=\"0\">${soal.pertanyaan}<\/p>\r\n                            ${opsiHTML}\r\n                        <\/div>\r\n                        <div style=\"display:flex; justify-content:space-between; background:white; padding:20px; border-radius:15px; border:1px solid #e2e8f0;\">\r\n                            <button onclick=\"navigasiSoal(-1)\" ${num===1?'disabled':''} class=\"btn\" style=\"background:#f1f5f9; color:#475569; font-weight:bold; padding:15px 30px; border-radius:10px;\">Sebelumnya<\/button>\r\n                            ${btnNext}\r\n                        <\/div>\r\n                    <\/div>\r\n                    <div style=\"flex:1; min-width:250px;\" class=\"no-print\">\r\n                        <div class=\"glass-panel\" style=\"background:white; padding:25px; border-radius:15px; position:sticky; top:100px;\">\r\n                            <h3 style=\"font-weight:bold; text-align:center; border-bottom:1px solid #e2e8f0; padding-bottom:15px; margin-bottom:20px;\">Peta Soal<\/h3>\r\n                            <div class=\"nav-grid\">\r\n                                ${AppState.soalUjian.map((_, i) => {\r\n                                    let sId = AppState.soalUjian[i].id_soal; let j = AppState.jawabanSiswa[sId];\r\n                                    let isAns = j && (typeof j==='string' || (Array.isArray(j)&&j.length>0) || (typeof j==='object'&&Object.keys(j).length>0));\r\n                                    let act = i===AppState.currentSoalIndex;\r\n                                    let style = `width:100%; aspect-ratio:1; border-radius:8px; font-weight:bold; cursor:pointer; border:2px solid ${isAns?'#059669':'#cbd5e1'}; background:${isAns?'#10b981':'white'}; color:${isAns?'white':'#475569'}; ${act?'box-shadow:0 0 0 3px rgba(59,130,246,0.5);':''}`;\r\n                                    return `<button id=\"navbtn_${i}\" onclick=\"lompatSoal(${i})\" style=\"${style}\">${i+1}<\/button>`;\r\n                                }).join('')}\r\n                            <\/div>\r\n                        <\/div>\r\n                    <\/div>\r\n                <\/div>`;\r\n            updateTimerVisualOnly();\r\n            let st = document.createElement('style'); st.innerHTML = `input.opsi-input:checked ~ .opsi-text-wrapper .indicator { background-color: #2563eb !important; border-color: #2563eb !important; } input.opsi-input:checked ~ .opsi-text-wrapper .indicator i { opacity: 1 !important; color:white !important;}`; document.head.appendChild(st);\r\n        }\r\n\r\n        window.simpanJwb = function(id,v) { AppState.jawabanSiswa[id]=v; updateNav(); }\r\n        window.simpanJwbArr = function(id,v,c) { if(!AppState.jawabanSiswa[id]) AppState.jawabanSiswa[id]=[]; if(c) AppState.jawabanSiswa[id].push(v); else AppState.jawabanSiswa[id]=AppState.jawabanSiswa[id].filter(x=>x!==v); updateNav(); }\r\n        window.simpanJwbObj = function(id,p,v) { if(!AppState.jawabanSiswa[id]) AppState.jawabanSiswa[id]={}; AppState.jawabanSiswa[id][p]=v; updateNav(); }\r\n        function updateNav() { const btn = document.getElementById('navbtn_'+AppState.currentSoalIndex); if(btn) { btn.style.background=\"#10b981\"; btn.style.color=\"white\"; btn.style.borderColor=\"#059669\"; } }\r\n        window.navigasiSoal = (d) => { AppState.currentSoalIndex+=d; renderLayarUjian(); }\r\n        window.lompatSoal = (i) => { AppState.currentSoalIndex=i; renderLayarUjian(); }\r\n\r\n        window.konfirmasiSelesai = function() {\r\n            let jwbCount = 0; AppState.soalUjian.forEach(s => { let j = AppState.jawabanSiswa[s.id_soal]; if(j && (typeof j==='string' || (Array.isArray(j)&&j.length>0) || (typeof j==='object'&&Object.keys(j).length>0))) jwbCount++; });\r\n            const sisa = AppState.soalUjian.length - jwbCount; const txt = sisa > 0 ? `Masih ada ${sisa} soal KOSONG. Yakin kumpul?` : \"Kirim jawaban sekarang?\";\r\n            Swal.fire({title: 'Konfirmasi', text: txt, icon: 'warning', showCancelButton: true, confirmButtonText: 'Kirim Final'}).then(r => { if(r.isConfirmed) autoSubmit(); });\r\n        }\r\n        function autoSubmit() {\r\n            clearInterval(AppState.timerInterval); showLoading(\"Memproses Skor...\");\r\n            callBackend('submitJawaban', {nomor_peserta: AppState.user.nomor_peserta, nama_siswa: AppState.user.nama_siswa, kelas: AppState.user.kelas, id_ujian: AppState.ujianAktif.id_ujian, mapel: AppState.ujianAktif.mapel, jawaban_siswa: AppState.jawabanSiswa}).then(r => { closeLoading(); AppState.evaluasiSelesai = r; renderSelesai(r); }).catch(e => { closeLoading(); showError(e.message); });\r\n        }\r\n        window.renderSelesai = function(res) {\r\n            document.getElementById('a11y-menu').classList.add('hidden');\r\n            getContainer().innerHTML = `<div class=\"max-w-2xl mx-auto mt-10 glass-panel p-10 text-center\" style=\"background:white; padding:40px; border-radius:20px; border-top:8px solid #10b981;\"><h2 class=\"text-3xl font-bold mb-2\">Selesai<\/h2><p class=\"mb-8\">Terima kasih ${AppState.user.nama_siswa}.<\/p><div style=\"background:#f8fafc; padding:30px; border-radius:15px; margin-bottom:30px;\"><p class=\"font-bold\">Nilai Akhir<\/p><p style=\"font-size:5rem; font-weight:900; color:#1d4ed8; margin:0;\" tabindex=\"0\">${res.skor}<\/p><\/div><div style=\"display:flex; gap:10px;\"><button onclick=\"renderReview()\" class=\"btn\" style=\"flex:1; padding:15px; background:#e2e8f0; border-radius:10px; font-weight:bold;\">Cek Jawaban<\/button><button onclick=\"prosesLogout()\" class=\"btn\" style=\"flex:1; padding:15px; background:#1e293b; color:white; border-radius:10px; font-weight:bold;\">Keluar<\/button><\/div><\/div>`;\r\n        }\r\n        window.renderReview = function() {\r\n            const ev = AppState.evaluasiSelesai.evaluasi;\r\n            let html = `<div class=\"max-w-4xl mx-auto mt-6\"><div style=\"display:flex; justify-content:space-between; margin-bottom:20px;\"><h2 class=\"text-2xl font-bold\">Review<\/h2><button onclick=\"renderSelesai(AppState.evaluasiSelesai)\" class=\"btn\" style=\"padding:10px 20px; background:#e2e8f0; border-radius:8px;\">Kembali<\/button><\/div><div>`;\r\n            Object.keys(ev).forEach((id, i) => {\r\n                const item = ev[id]; const bdg = item.is_benar ? `<span style=\"color:#15803d; font-weight:bold;\"><i class=\"fa-solid fa-check\"><\/i> Benar<\/span>` : `<span style=\"color:#b91c1c; font-weight:bold;\"><i class=\"fa-solid fa-xmark\"><\/i> Salah\/Parsial<\/span>`;\r\n                html += `<div class=\"glass-panel\" tabindex=\"0\" style=\"background:white; padding:20px; border-radius:15px; margin-bottom:15px; border-left:8px solid ${item.is_benar?'#22c55e':'#ef4444'};\"><div style=\"display:flex; justify-content:space-between; margin-bottom:10px;\"><b>Soal ${i+1}<\/b> ${bdg}<\/div><p style=\"margin-bottom:15px;\">${item.pertanyaan}<\/p><div style=\"background:#f8fafc; padding:15px; border-radius:8px; border:1px solid #e2e8f0;\">Jawaban Anda: <b style=\"color:${item.is_benar?'#15803d':'#b91c1c'}\">${typeof item.jawaban_siswa==='object'?JSON.stringify(item.jawaban_siswa):item.jawaban_siswa || '-'}<\/b><\/div><\/div>`;\r\n            });\r\n            getContainer().innerHTML = html + `<\/div><\/div>`;\r\n        }\r\n\r\n        \/\/ ==========================================\r\n        \/\/ 6. ADMIN PANEL DASHBOARD\r\n        \/\/ ==========================================\r\n        window.initAdminDashboard = function() { refreshAdminData('dashboard'); }\r\n        window.refreshAdminData = function(view) { callBackend('getAdminData').then(r => { AppState.admin.data = r.data; AppState.admin.currentMenu = view || AppState.admin.currentMenu; renderAdminView(); }); }\r\n        \r\n        window.renderAdminView = function() {\r\n            let menu = AppState.admin.currentMenu; let html = '';\r\n            if(menu === 'dashboard') html = buildAdminDashboard();\r\n            else if(menu === 'admin') html = buildAdminKelolaAdmin();\r\n            else if(menu === 'peserta') html = buildAdminPeserta();\r\n            else if(menu === 'ujian') html = buildAdminUjian();\r\n            else if(menu === 'soal') html = buildAdminSoal();\r\n            else if(menu === 'hasil') html = buildAdminHasil();\r\n\r\n            const mBtn = (id, icon, text) => `<button onclick=\"AppState.admin.currentMenu='${id}'; renderAdminView()\" class=\"btn\" style=\"text-align:left; padding:12px 20px; border-radius:10px; font-weight:bold; margin-bottom:5px; background:${menu===id?'#eff6ff':'transparent'}; color:${menu===id?'#1d4ed8':'#475569'};\"><i class=\"${icon}\" style=\"width:25px;\"><\/i> ${text}<\/button>`;\r\n\r\n            getContainer().innerHTML = `\r\n                <div style=\"display:flex; flex-wrap:wrap; gap:25px; min-height:80vh;\">\r\n                    <div class=\"no-print\" style=\"flex:1; min-width:240px; max-width:280px;\">\r\n                        <div class=\"glass-panel\" style=\"background:white; padding:20px; border-radius:15px; position:sticky; top:100px;\">\r\n                            <h3 style=\"font-size:12px; font-weight:bold; color:#94a3b8; text-transform:uppercase; margin-bottom:15px; padding-left:10px;\">Menu Utama<\/h3>\r\n                            <div style=\"display:flex; flex-direction:column;\">\r\n                                ${mBtn('dashboard', 'fa-solid fa-chart-pie', 'Dasbor Ringkasan')}\r\n                                ${mBtn('admin', 'fa-solid fa-user-shield', 'Kelola Admin')}\r\n                                ${mBtn('peserta', 'fa-solid fa-users', 'Data Peserta')}\r\n                                ${mBtn('ujian', 'fa-solid fa-calendar-check', 'Jadwal Ujian')}\r\n                                ${mBtn('soal', 'fa-solid fa-book', 'Bank Soal')}\r\n                                ${mBtn('hasil', 'fa-solid fa-ranking-star', 'Rekap Hasil')}\r\n                            <\/div>\r\n                        <\/div>\r\n                    <\/div>\r\n                    <div style=\"flex:3; min-width:300px;\">${html}<\/div>\r\n                <\/div>`;\r\n        }\r\n\r\n        function buildAdminDashboard() {\r\n            const d = AppState.admin.data; const avg = d.hasil.length ? Math.round(d.hasil.reduce((s,h)=>s+Number(h.skor),0)\/d.hasil.length) : 0;\r\n            return `\r\n                <h2 style=\"font-size:24px; font-weight:bold; margin-bottom:20px;\">Ringkasan Sistem<\/h2>\r\n                <div style=\"display:grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap:20px;\">\r\n                    <div style=\"background:white; padding:25px; border-radius:15px; border-top:5px solid #3b82f6; box-shadow:0 4px 6px rgba(0,0,0,0.05);\"><p style=\"color:#64748b; font-size:14px;\">Peserta Terdaftar<\/p><p style=\"font-size:32px; font-weight:bold; margin:0;\">${d.siswa.length}<\/p><\/div>\r\n                    <div style=\"background:white; padding:25px; border-radius:15px; border-top:5px solid #10b981; box-shadow:0 4px 6px rgba(0,0,0,0.05);\"><p style=\"color:#64748b; font-size:14px;\">Ujian Diselesaikan<\/p><p style=\"font-size:32px; font-weight:bold; margin:0;\">${d.hasil.length}<\/p><\/div>\r\n                    <div style=\"background:white; padding:25px; border-radius:15px; border-top:5px solid #eab308; box-shadow:0 4px 6px rgba(0,0,0,0.05);\"><p style=\"color:#64748b; font-size:14px;\">Rata-rata Skor<\/p><p style=\"font-size:32px; font-weight:bold; margin:0;\">${avg}<\/p><\/div>\r\n                    <div style=\"background:white; padding:25px; border-radius:15px; border-top:5px solid #a855f7; box-shadow:0 4px 6px rgba(0,0,0,0.05);\"><p style=\"color:#64748b; font-size:14px;\">Total Bank Soal<\/p><p style=\"font-size:32px; font-weight:bold; margin:0;\">${d.soal.length}<\/p><\/div>\r\n                <\/div>`;\r\n        }\r\n\r\n        \/\/ KELOLA ADMIN\r\n        function buildAdminKelolaAdmin() {\r\n            const d = AppState.admin.data.admin;\r\n            return `<div class=\"glass-panel\" style=\"background:white; border-radius:15px; overflow:hidden;\"><div style=\"padding:20px; border-bottom:1px solid #eee; display:flex; justify-content:space-between; align-items:center;\"><h3 style=\"font-weight:bold; font-size:18px; margin:0;\">Manajemen Admin<\/h3><button onclick=\"bukaModalAdmin()\" class=\"btn\" style=\"background:#2563eb; color:white; padding:10px 20px; border-radius:10px; font-weight:bold;\">+ Tambah Admin<\/button><\/div><div class=\"table-wrap\"><table><thead><tr><th>Nama Lengkap<\/th><th>Username<\/th><th>Aksi<\/th><\/tr><\/thead><tbody>${d.map(a => `<tr><td style=\"font-weight:bold;\">${a.nama}<\/td><td><span style=\"background:#f1f5f9; padding:5px 10px; border-radius:6px; font-family:monospace;\">${a.username}<\/span><\/td><td><button onclick=\"aksiHapus('hapusAdmin', '${a.id_admin}', 'Admin ini')\" class=\"btn\" style=\"color:red; background:transparent;\"><i class=\"fa-solid fa-trash\"><\/i><\/button><\/td><\/tr>`).join('')}<\/tbody><\/table><\/div><\/div>`;\r\n        }\r\n        window.bukaModalAdmin = function() { bukaModal(`<h3 style=\"font-size:20px; font-weight:bold; margin-bottom:20px;\">Tambah Admin<\/h3><form onsubmit=\"submitAdmin(event)\"><div style=\"margin-bottom:15px;\"><label style=\"font-weight:bold; display:block; margin-bottom:5px;\">Nama Lengkap<\/label><input type=\"text\" id=\"m_a_nama\" required class=\"input-field\"><\/div><div style=\"margin-bottom:15px;\"><label style=\"font-weight:bold; display:block; margin-bottom:5px;\">Username<\/label><input type=\"text\" id=\"m_a_user\" required class=\"input-field\"><\/div><div style=\"margin-bottom:25px;\"><label style=\"font-weight:bold; display:block; margin-bottom:5px;\">Password<\/label><input type=\"password\" id=\"m_a_pass\" required class=\"input-field\"><\/div><div style=\"text-align:right;\"><button type=\"submit\" class=\"btn\" style=\"background:#2563eb; color:white; padding:12px 25px; border-radius:10px; font-weight:bold;\">Simpan<\/button><\/div><\/form>`); }\r\n        window.submitAdmin = function(e) { e.preventDefault(); showLoading(); callBackend('simpanAdmin', {nama: document.getElementById('m_a_nama').value, username: document.getElementById('m_a_user').value, password: document.getElementById('m_a_pass').value}).then(()=>{ tutupModal(); refreshAdminData(); }); }\r\n\r\n        \/\/ KELOLA PESERTA\r\n        function buildAdminPeserta() {\r\n            const d = AppState.admin.data.siswa;\r\n            return `<div class=\"glass-panel\" style=\"background:white; border-radius:15px; overflow:hidden;\"><div style=\"padding:20px; border-bottom:1px solid #eee; display:flex; justify-content:space-between; align-items:center;\"><h3 style=\"font-weight:bold; font-size:18px; margin:0;\">Kelola Peserta<\/h3><button onclick=\"bukaModalSiswa()\" class=\"btn\" style=\"background:#2563eb; color:white; padding:10px 20px; border-radius:10px; font-weight:bold;\">+ Tambah Siswa<\/button><\/div><div class=\"table-wrap\"><table><thead><tr><th>No. Peserta<\/th><th>Nama<\/th><th>Kelas<\/th><th>Aksi<\/th><\/tr><\/thead><tbody>${d.map(s => `<tr><td style=\"font-family:monospace;\">${s.nomor_peserta}<\/td><td style=\"font-weight:bold;\">${s.nama_siswa}<\/td><td>${s.kelas}<\/td><td><button onclick=\"aksiHapus('hapusSiswa', '${s.id_siswa}', 'Siswa ini')\" class=\"btn\" style=\"color:red; background:transparent;\"><i class=\"fa-solid fa-trash\"><\/i><\/button><\/td><\/tr>`).join('')}<\/tbody><\/table><\/div><\/div>`;\r\n        }\r\n        window.bukaModalSiswa = function() { bukaModal(`<h3 style=\"font-size:20px; font-weight:bold; margin-bottom:20px;\">Tambah Siswa<\/h3><form onsubmit=\"submitSiswa(event)\"><div style=\"margin-bottom:15px;\"><label style=\"font-weight:bold; display:block; margin-bottom:5px;\">Nomor Peserta<\/label><input type=\"text\" id=\"m_s_nop\" required class=\"input-field\"><\/div><div style=\"margin-bottom:15px;\"><label style=\"font-weight:bold; display:block; margin-bottom:5px;\">Nama Lengkap<\/label><input type=\"text\" id=\"m_s_nama\" required class=\"input-field\"><\/div><div style=\"margin-bottom:25px;\"><label style=\"font-weight:bold; display:block; margin-bottom:5px;\">Kelas<\/label><input type=\"text\" id=\"m_s_kelas\" required class=\"input-field\"><\/div><div style=\"text-align:right;\"><button type=\"submit\" class=\"btn\" style=\"background:#2563eb; color:white; padding:12px 25px; border-radius:10px; font-weight:bold;\">Simpan<\/button><\/div><\/form>`); }\r\n        window.submitSiswa = function(e) { e.preventDefault(); showLoading(); callBackend('simpanSiswa', {nomor_peserta: document.getElementById('m_s_nop').value, nama_siswa: document.getElementById('m_s_nama').value, kelas: document.getElementById('m_s_kelas').value, status: 'aktif'}).then(()=>{ tutupModal(); refreshAdminData(); }); }\r\n\r\n        \/\/ KELOLA UJIAN\r\n        function buildAdminUjian() {\r\n            const d = AppState.admin.data.ujian;\r\n            return `<div class=\"glass-panel\" style=\"background:white; border-radius:15px; overflow:hidden;\"><div style=\"padding:20px; border-bottom:1px solid #eee; display:flex; justify-content:space-between; align-items:center;\"><h3 style=\"font-weight:bold; font-size:18px; margin:0;\">Jadwal Ujian<\/h3><button onclick=\"bukaModalUjian()\" class=\"btn\" style=\"background:#2563eb; color:white; padding:10px 20px; border-radius:10px; font-weight:bold;\">+ Buat Ujian<\/button><\/div><div class=\"table-wrap\"><table><thead><tr><th>Mapel<\/th><th>Token<\/th><th>Sistem<\/th><th>Aksi<\/th><\/tr><\/thead><tbody>${d.map(u => `<tr><td style=\"font-weight:bold;\">${u.mapel}<\/td><td><span style=\"background:#f1f5f9; padding:5px 10px; border-radius:6px; font-family:monospace;\">${u.token}<\/span><\/td><td style=\"font-size:13px;\">${u.waktu_menit} Mnt<br>${u.acak_soal?'Acak Soal':'Urut'}, ${u.acak_opsi?'Acak Opsi':'Urut'}<\/td><td><button onclick=\"aksiHapus('resetHasilUjian', '${u.id_ujian}', 'Semua hasil')\" class=\"btn\" style=\"color:#eab308; background:transparent; margin-right:10px;\" title=\"Reset Hasil\"><i class=\"fa-solid fa-rotate-left\"><\/i><\/button><button onclick=\"aksiHapus('hapusUjian', '${u.id_ujian}', 'Ujian ini')\" class=\"btn\" style=\"color:red; background:transparent;\"><i class=\"fa-solid fa-trash\"><\/i><\/button><\/td><\/tr>`).join('')}<\/tbody><\/table><\/div><\/div>`;\r\n        }\r\n        window.bukaModalUjian = function() { bukaModal(`<h3 style=\"font-size:20px; font-weight:bold; margin-bottom:20px;\">Buat Ujian Baru<\/h3><form onsubmit=\"submitUjian(event)\"><div style=\"margin-bottom:15px;\"><label style=\"font-weight:bold; display:block; margin-bottom:5px;\">Mata Pelajaran<\/label><input type=\"text\" id=\"m_u_mapel\" required class=\"input-field\"><\/div><div style=\"display:flex; gap:15px; margin-bottom:15px;\"><div style=\"flex:1;\"><label style=\"font-weight:bold; display:block; margin-bottom:5px;\">Token (Huruf\/Angka)<\/label><input type=\"text\" id=\"m_u_token\" required class=\"input-field\" style=\"text-transform:uppercase; font-family:monospace;\"><\/div><div style=\"flex:1;\"><label style=\"font-weight:bold; display:block; margin-bottom:5px;\">Waktu (Menit)<\/label><input type=\"number\" id=\"m_u_waktu\" required class=\"input-field\" min=\"5\"><\/div><\/div><div style=\"background:#f8fafc; padding:15px; border-radius:10px; border:1px solid #e2e8f0; display:flex; gap:20px; margin-bottom:15px;\"><label style=\"cursor:pointer; font-weight:bold; display:flex; align-items:center; gap:8px;\"><input type=\"checkbox\" id=\"m_u_acak_s\" checked style=\"transform:scale(1.2);\"> Acak Soal<\/label><label style=\"cursor:pointer; font-weight:bold; display:flex; align-items:center; gap:8px;\"><input type=\"checkbox\" id=\"m_u_acak_o\" checked style=\"transform:scale(1.2);\"> Acak Opsi<\/label><\/div><div style=\"margin-bottom:25px;\"><label style=\"font-weight:bold; display:block; margin-bottom:5px;\">Keterangan<\/label><textarea id=\"m_u_desk\" class=\"input-field\" style=\"height:80px;\"><\/textarea><\/div><div style=\"text-align:right;\"><button type=\"submit\" class=\"btn\" style=\"background:#2563eb; color:white; padding:12px 25px; border-radius:10px; font-weight:bold;\">Simpan Jadwal<\/button><\/div><\/form>`); }\r\n        window.submitUjian = function(e) { e.preventDefault(); showLoading(); callBackend('simpanUjian', {mapel: document.getElementById('m_u_mapel').value, token: document.getElementById('m_u_token').value.toUpperCase(), waktu_menit: document.getElementById('m_u_waktu').value, acak_soal: document.getElementById('m_u_acak_s').checked, acak_opsi: document.getElementById('m_u_acak_o').checked, deskripsi: document.getElementById('m_u_desk').value, status: 'aktif'}).then(()=>{ tutupModal(); refreshAdminData(); }); }\r\n\r\n        \/\/ KELOLA SOAL GUI (DYNAMIC FORM & BOBOT)\r\n        function buildAdminSoal() {\r\n            const d = AppState.admin.data.soal; const uj = AppState.admin.data.ujian;\r\n            let filt = `<select style=\"padding:8px; border-radius:8px; border:1px solid #cbd5e1; outline:none;\"><option>Total ${d.length} Soal Tersimpan<\/option><\/select>`;\r\n            return `<div class=\"glass-panel\" style=\"background:white; border-radius:15px; overflow:hidden;\"><div style=\"padding:20px; border-bottom:1px solid #eee; display:flex; justify-content:space-between; align-items:center;\"><div style=\"display:flex; gap:15px; align-items:center;\"><h3 style=\"font-weight:bold; font-size:18px; margin:0;\">Bank Soal<\/h3>${filt}<\/div><button onclick=\"bukaModalSoalGUI()\" class=\"btn\" style=\"background:#2563eb; color:white; padding:10px 20px; border-radius:10px; font-weight:bold;\">+ Form Tambah Soal<\/button><\/div><div class=\"table-wrap\"><table><thead><tr style=\"background:#f8fafc;\"><th style=\"padding:15px;\">Mapel ID<\/th><th style=\"padding:15px;\">Tipe<\/th><th style=\"padding:15px;\">Pertanyaan<\/th><th style=\"padding:15px;\">Aksi<\/th><\/tr><\/thead><tbody>${d.map(s => `<tr style=\"border-bottom:1px solid #eee;\"><td style=\"padding:15px; font-size:12px; font-family:monospace;\">${s.id_ujian}<\/td><td style=\"padding:15px; font-weight:bold;\">${s.tipe_soal}<\/td><td style=\"padding:15px;\">${s.pertanyaan}<\/td><td style=\"padding:15px;\"><button onclick=\"aksiHapus('hapusSoal', '${s.id_soal}', 'Soal ini')\" class=\"btn\" style=\"color:red; background:transparent;\"><i class=\"fa-solid fa-trash\"><\/i><\/button><\/td><\/tr>`).join('') || '<tr><td colspan=\"4\" style=\"text-align:center; padding:30px; color:#94a3b8;\">Data kosong<\/td><\/tr>'}<\/tbody><\/table><\/div><\/div>`;\r\n        }\r\n        window.bukaModalSoalGUI = function() {\r\n            const uj = AppState.admin.data.ujian; if(uj.length === 0) return Swal.fire(\"Buat jadwal ujian dulu!\");\r\n            bukaModal(`\r\n                <div style=\"display:flex; justify-content:space-between; align-items:center; border-bottom:1px solid #eee; padding-bottom:15px; margin-bottom:20px;\">\r\n                    <h3 style=\"font-size:20px; font-weight:bold; margin:0;\">Form Input Soal Baru<\/h3><span id=\"info-nomor-soal\" style=\"background:#dbeafe; color:#1e40af; padding:5px 15px; border-radius:20px; font-size:12px; font-weight:bold;\">Menganalisis...<\/span>\r\n                <\/div>\r\n                <form onsubmit=\"submitSoalGUI(event)\">\r\n                    <div style=\"display:flex; gap:15px; margin-bottom:15px;\">\r\n                        <div style=\"flex:1;\"><label style=\"display:block; font-weight:bold; margin-bottom:5px; font-size:14px;\">Mata Uji Tujuan<\/label><select id=\"f_ujian\" required class=\"input-field\" onchange=\"updateNomorSoal()\">${uj.map(u => `<option value=\"${u.id_ujian}\">${u.mapel} (${u.token})<\/option>`).join('')}<\/select><\/div>\r\n                        <div style=\"flex:1;\"><label style=\"display:block; font-weight:bold; margin-bottom:5px; font-size:14px;\">Jenis Soal<\/label><select id=\"f_tipe\" required class=\"input-field\" onchange=\"renderDynamicForm()\"><option value=\"PG\">Pilihan Ganda (5 Opsi)<\/option><option value=\"PGK\">Pilihan Ganda Kompleks (PGK)<\/option><option value=\"BS\">Benar \/ Salah (BS)<\/option><\/select><\/div>\r\n                    <\/div>\r\n                    <div style=\"margin-bottom:15px;\"><label style=\"display:block; font-weight:bold; margin-bottom:5px; font-size:14px;\">Stimulus \/ Narasi <span style=\"color:#94a3b8; font-weight:normal;\">(Opsional, Wajib untuk BS)<\/span><\/label><textarea id=\"f_stimulus\" class=\"input-field\" style=\"height:60px;\"><\/textarea><\/div>\r\n                    <div style=\"margin-bottom:20px;\"><label style=\"display:block; font-weight:bold; margin-bottom:5px; font-size:14px;\">Pertanyaan Utama<\/label><textarea id=\"f_pertanyaan\" required class=\"input-field\" style=\"height:60px;\"><\/textarea><\/div>\r\n                    \r\n                    <div id=\"dynamic-opsi-area\" style=\"background:#f8fafc; border:1px solid #e2e8f0; border-radius:15px; padding:20px; margin-bottom:20px;\"><\/div>\r\n                    \r\n                    <div style=\"text-align:right; border-top:1px solid #eee; padding-top:20px;\">\r\n                        <button type=\"button\" onclick=\"tutupModal()\" class=\"btn\" style=\"background:#f1f5f9; color:#475569; padding:12px 25px; border-radius:10px; font-weight:bold; margin-right:10px;\">Batal<\/button>\r\n                        <button type=\"submit\" class=\"btn\" style=\"background:#2563eb; color:white; padding:12px 25px; border-radius:10px; font-weight:bold;\"><i class=\"fa-solid fa-save\"><\/i> Simpan Soal<\/button>\r\n                    <\/div>\r\n                <\/form>`);\r\n            renderDynamicForm(); updateNomorSoal();\r\n        }\r\n        window.updateNomorSoal = function() {\r\n            const idUj = document.getElementById('f_ujian').value; const soalArr = AppState.admin.data.soal.filter(s => s.id_ujian === idUj);\r\n            document.getElementById('info-nomor-soal').textContent = `Soal ke-${soalArr.length + 1} di paket ini`;\r\n        }\r\n        window.renderDynamicForm = function() {\r\n            const tipe = document.getElementById('f_tipe').value; const area = document.getElementById('dynamic-opsi-area'); const stim = document.getElementById('f_stimulus');\r\n            if(tipe === 'BS') stim.setAttribute('required', 'true'); else stim.removeAttribute('required');\r\n\r\n            if(tipe === 'PG') {\r\n                area.innerHTML = `<div style=\"display:flex; justify-content:space-between; align-items:center; margin-bottom:15px;\"><label style=\"font-weight:bold; font-size:15px;\">Opsi Jawaban Pilihan Ganda<\/label><div style=\"font-weight:bold; color:#1d4ed8; font-size:14px; display:flex; align-items:center; gap:10px;\">Bobot Nilai Soal: <input type=\"number\" id=\"f_bobot_pg\" value=\"10\" min=\"1\" required class=\"input-field\" style=\"width:70px; padding:5px; text-align:center;\"><\/div><\/div><div style=\"display:flex; flex-direction:column; gap:10px; margin-bottom:20px;\"><div style=\"display:flex; align-items:center; gap:10px;\"><b style=\"width:20px;\">A.<\/b><input type=\"text\" class=\"input-field f-opsi-pg\" style=\"padding:10px;\" required><\/div><div style=\"display:flex; align-items:center; gap:10px;\"><b style=\"width:20px;\">B.<\/b><input type=\"text\" class=\"input-field f-opsi-pg\" style=\"padding:10px;\" required><\/div><div style=\"display:flex; align-items:center; gap:10px;\"><b style=\"width:20px;\">C.<\/b><input type=\"text\" class=\"input-field f-opsi-pg\" style=\"padding:10px;\" required><\/div><div style=\"display:flex; align-items:center; gap:10px;\"><b style=\"width:20px;\">D.<\/b><input type=\"text\" class=\"input-field f-opsi-pg\" style=\"padding:10px;\" required><\/div><div style=\"display:flex; align-items:center; gap:10px;\"><b style=\"width:20px;\">E.<\/b><input type=\"text\" class=\"input-field f-opsi-pg\" style=\"padding:10px;\" required><\/div><\/div><label style=\"font-weight:bold; display:block; margin-bottom:5px; font-size:14px;\">Kunci Jawaban Benar<\/label><select id=\"f_kunci_pg\" class=\"input-field\" style=\"padding:10px; cursor:pointer;\"><option value=\"0\">Opsi A<\/option><option value=\"1\">Opsi B<\/option><option value=\"2\">Opsi C<\/option><option value=\"3\">Opsi D<\/option><option value=\"4\">Opsi E<\/option><\/select>`;\r\n            } else if (tipe === 'PGK') {\r\n                area.innerHTML = `<div style=\"display:flex; justify-content:space-between; align-items:center; margin-bottom:10px;\"><label style=\"font-weight:bold; font-size:15px;\">Opsi PG Kompleks<\/label><button type=\"button\" onclick=\"tambahBarisPGK()\" class=\"btn\" style=\"background:#dbeafe; color:#1d4ed8; padding:8px 15px; border-radius:8px; font-size:13px; font-weight:bold;\">+ Tambah Opsi<\/button><\/div><p style=\"font-size:13px; color:#64748b; margin-bottom:15px;\">Poin didapat jika status centang sesuai dengan kunci. Tentukan kunci & bobot per opsi.<\/p><div id=\"pgk-list\" style=\"display:flex; flex-direction:column; gap:10px;\"><\/div>`;\r\n                tambahBarisPGK(); tambahBarisPGK(); tambahBarisPGK();\r\n            } else if (tipe === 'BS') {\r\n                area.innerHTML = `<div style=\"display:flex; justify-content:space-between; align-items:center; margin-bottom:10px;\"><label style=\"font-weight:bold; font-size:15px;\">Pernyataan Benar \/ Salah<\/label><button type=\"button\" onclick=\"tambahBarisBS()\" class=\"btn\" style=\"background:#dbeafe; color:#1d4ed8; padding:8px 15px; border-radius:8px; font-size:13px; font-weight:bold;\">+ Tambah Pernyataan<\/button><\/div><p style=\"font-size:13px; color:#64748b; margin-bottom:15px;\">Tentukan kunci jawaban dan bobot nilai per pernyataan.<\/p><div id=\"bs-list\" style=\"display:flex; flex-direction:column; gap:10px;\"><\/div>`;\r\n                tambahBarisBS(); tambahBarisBS();\r\n            }\r\n        }\r\n        window.tambahBarisPGK = function() {\r\n            const div = document.createElement('div'); div.style.cssText = \"display:flex; align-items:center; gap:10px; background:white; padding:10px; border:1px solid #cbd5e1; border-radius:10px;\";\r\n            div.innerHTML = `<input type=\"text\" class=\"input-field f-opsi-pgk\" style=\"flex-grow:1; padding:8px 12px; border:none; border-right:1px solid #eee;\" placeholder=\"Teks opsi jawaban...\" required><div style=\"display:flex; flex-direction:column; align-items:center; padding:0 10px; border-right:1px solid #eee;\"><span style=\"font-size:11px; font-weight:bold; color:#64748b; margin-bottom:2px;\">Kunci:<\/span><label style=\"cursor:pointer; font-size:13px; font-weight:bold; display:flex; align-items:center; gap:5px;\"><input type=\"checkbox\" class=\"f-kunci-pgk\" style=\"transform:scale(1.2);\"> Dicentang<\/label><\/div><div style=\"display:flex; flex-direction:column; align-items:center; padding:0 10px;\"><span style=\"font-size:11px; font-weight:bold; color:#64748b; margin-bottom:2px;\">Bobot<\/span><input type=\"number\" class=\"input-field f-bobot-pgk\" style=\"width:60px; padding:5px; text-align:center; border:1px solid #ccc;\" value=\"5\" min=\"1\" required><\/div><button type=\"button\" onclick=\"this.parentElement.remove()\" class=\"btn\" style=\"color:red; background:transparent; padding:8px;\"><i class=\"fa-solid fa-trash\"><\/i><\/button>`;\r\n            document.getElementById('pgk-list').appendChild(div);\r\n        }\r\n        window.tambahBarisBS = function() {\r\n            const div = document.createElement('div'); div.style.cssText = \"display:flex; align-items:center; gap:10px; background:white; padding:10px; border:1px solid #cbd5e1; border-radius:10px;\";\r\n            div.innerHTML = `<input type=\"text\" class=\"input-field f-opsi-bs\" style=\"flex-grow:1; padding:8px 12px; border:none; border-right:1px solid #eee;\" placeholder=\"Teks pernyataan...\" required><div style=\"display:flex; flex-direction:column; align-items:center; padding:0 10px; border-right:1px solid #eee;\"><span style=\"font-size:11px; font-weight:bold; color:#64748b; margin-bottom:2px;\">Kunci:<\/span><select class=\"input-field f-kunci-bs\" style=\"padding:5px; font-weight:bold; border:1px solid #ccc; cursor:pointer;\" required><option value=\"Benar\">Benar<\/option><option value=\"Salah\">Salah<\/option><\/select><\/div><div style=\"display:flex; flex-direction:column; align-items:center; padding:0 10px;\"><span style=\"font-size:11px; font-weight:bold; color:#64748b; margin-bottom:2px;\">Bobot<\/span><input type=\"number\" class=\"input-field f-bobot-bs\" style=\"width:60px; padding:5px; text-align:center; border:1px solid #ccc;\" value=\"5\" min=\"1\" required><\/div><button type=\"button\" onclick=\"this.parentElement.remove()\" class=\"btn\" style=\"color:red; background:transparent; padding:8px;\"><i class=\"fa-solid fa-trash\"><\/i><\/button>`;\r\n            document.getElementById('bs-list').appendChild(div);\r\n        }\r\n\r\n        window.submitSoalGUI = function(e) { \r\n            e.preventDefault(); const tipe = document.getElementById('f_tipe').value; let opsiFinal = []; let kunciFinal = null;\r\n            try {\r\n                if (tipe === 'PG') {\r\n                    const inputs = document.querySelectorAll('.f-opsi-pg'); inputs.forEach(inp => opsiFinal.push(inp.value.trim()));\r\n                    const kunciIdx = document.getElementById('f_kunci_pg').value; const bbt = document.getElementById('f_bobot_pg').value;\r\n                    kunciFinal = { kunci: opsiFinal[kunciIdx], bobot: Number(bbt) };\r\n                } else if (tipe === 'PGK') {\r\n                    const rows = document.getElementById('pgk-list').children; if(rows.length === 0) throw new Error(\"Minimal 1 opsi.\");\r\n                    let objKunci = {};\r\n                    Array.from(rows).forEach(row => {\r\n                        let teks = row.querySelector('.f-opsi-pgk').value.trim(); let isBenar = row.querySelector('.f-kunci-pgk').checked; let bobot = row.querySelector('.f-bobot-pgk').value;\r\n                        opsiFinal.push(teks); objKunci[teks] = { is_benar: isBenar, bobot: Number(bobot) };\r\n                    }); kunciFinal = objKunci;\r\n                } else if (tipe === 'BS') {\r\n                    const rows = document.getElementById('bs-list').children; if(rows.length === 0) throw new Error(\"Minimal 1 pernyataan.\");\r\n                    let objKunci = {};\r\n                    Array.from(rows).forEach(row => {\r\n                        let teks = row.querySelector('.f-opsi-bs').value.trim(); let stat = row.querySelector('.f-kunci-bs').value; let bobot = row.querySelector('.f-bobot-bs').value;\r\n                        opsiFinal.push(teks); objKunci[teks] = { kunci: stat, bobot: Number(bobot) };\r\n                    }); kunciFinal = objKunci;\r\n                }\r\n                showLoading(\"Menyimpan...\"); callBackend('simpanSoalManual', { id_ujian: document.getElementById('f_ujian').value, tipe_soal: tipe, stimulus: document.getElementById('f_stimulus').value.trim(), pertanyaan: document.getElementById('f_pertanyaan').value.trim(), opsi: opsiFinal, kunci: kunciFinal }).then(()=>{ tutupModal(); showToast(\"Soal Disimpan\"); refreshAdminData(); });\r\n            } catch(err) { Swal.fire(\"Gagal\", err.message, \"error\"); }\r\n        }\r\n\r\n        \/\/ REKAP HASIL & WA\r\n        function buildAdminHasil() {\r\n            const h = AppState.admin.data.hasil;\r\n            return `<div class=\"glass-panel\" style=\"background:white; border-radius:15px; overflow:hidden;\"><div style=\"padding:20px; border-bottom:1px solid #eee; display:flex; justify-content:space-between; align-items:center;\"><h3 style=\"font-weight:bold; font-size:18px; margin:0;\">Rekap Hasil Ujian<\/h3><div style=\"display:flex; gap:10px;\"><button onclick=\"window.print()\" class=\"btn\" style=\"background:#f1f5f9; padding:10px 15px; border-radius:8px; font-weight:bold;\"><i class=\"fa-solid fa-print\"><\/i> Print PDF<\/button><button onclick=\"exportCSVLengkap()\" class=\"btn\" style=\"background:#10b981; color:white; padding:10px 15px; border-radius:8px; font-weight:bold;\"><i class=\"fa-solid fa-download\"><\/i> CSV Data<\/button><\/div><\/div><div class=\"table-wrap\"><table><thead><tr style=\"background:#f8fafc;\"><th style=\"padding:15px;\">Waktu<\/th><th style=\"padding:15px;\">Siswa<\/th><th style=\"padding:15px;\">Mapel<\/th><th style=\"padding:15px;\">Skor<\/th><th style=\"padding:15px;\">Aksi<\/th><\/tr><\/thead><tbody>${h.map(r => `<tr style=\"border-bottom:1px solid #eee;\"><td style=\"padding:15px; font-size:12px;\">${new Date(r.waktu_selesai).toLocaleString('id-ID')}<\/td><td style=\"padding:15px; font-weight:500;\">${r.nama_siswa}<\/td><td style=\"padding:15px;\">${r.mapel}<\/td><td style=\"padding:15px;\"><span style=\"background:#eff6ff; color:#1d4ed8; padding:5px 10px; border-radius:6px; font-weight:bold;\">${r.skor}<\/span><\/td><td style=\"padding:15px;\"><button onclick=\"kirimWA('${r.nama_siswa}', '${r.kelas}', '${r.mapel}', '${r.skor}')\" class=\"btn\" style=\"color:#10b981; font-size:22px; background:transparent;\" title=\"Kirim WA\"><i class=\"fa-brands fa-whatsapp\"><\/i><\/button><\/td><\/tr>`).join('') || '<tr><td colspan=\"5\" style=\"text-align:center; padding:30px; color:#94a3b8;\">Belum ada hasil ujian.<\/td><\/tr>'}<\/tbody><\/table><\/div><\/div>`;\r\n        }\r\n\r\n        window.kirimWA = function(nama, kelas, mapel, skor) {\r\n            Swal.fire({ title: 'Kirim WhatsApp', input: 'text', inputLabel: 'Nomor WA Guru (Contoh: 62812...)', inputValue: '628', showCancelButton: true, confirmButtonText: 'Kirim Pesan', confirmButtonColor: '#10b981', inputValidator: (value) => { if (!value) return 'Nomor tidak boleh kosong!'; }\r\n            }).then((result) => { if(result.isConfirmed) { const text = encodeURIComponent(`*Laporan Ujian Inklusif LMS*\\n\\nNama: ${nama}\\nKelas: ${kelas}\\nMata Uji: ${mapel}\\n*Skor Akhir: ${skor}*\\n\\nTerima kasih.`); window.open(`https:\/\/wa.me\/${result.value}?text=${text}`, '_blank'); } });\r\n        }\r\n        window.exportCSVLengkap = function() {\r\n            const h = AppState.admin.data.hasil; if(!h.length) return Swal.fire(\"Data kosong\");\r\n            let csv = \"Waktu Selesai,Nomor Peserta,Nama Siswa,Kelas,Mata Pelajaran,Skor Akhir\\n\" + h.map(r => `\"${r.waktu_selesai}\",\"${r.nomor_peserta}\",\"${r.nama_siswa}\",\"${r.kelas}\",\"${r.mapel}\",${r.skor}`).join(\"\\n\");\r\n            const link = document.createElement(\"a\"); link.href = URL.createObjectURL(new Blob([csv], { type: 'text\/csv' })); link.download = \"Hasil_Ujian.csv\"; link.click();\r\n        }\r\n\r\n        window.aksiHapus = function(funcName, id, label) {\r\n            Swal.fire({ title: 'Hapus Data?', text: `Yakin menghapus ${label}?`, icon: 'warning', showCancelButton: true, confirmButtonColor: '#d33', confirmButtonText: 'Ya, Hapus!' }).then((result) => { if(result.isConfirmed) { showLoading(); callBackend(funcName, id).then(()=>{closeLoading(); refreshAdminData();}); } });\r\n        }\r\n\r\n        window.bukaModal = function(html) { const m = document.getElementById('modal-container'); m.innerHTML = `<div class=\"modal-overlay\" onclick=\"if(event.target===this) tutupModal()\"><div class=\"modal-card\" style=\"background:white; border-radius:20px; padding:30px; position:relative; width:100%; max-width:800px;\"><button onclick=\"tutupModal()\" class=\"btn\" style=\"position:absolute; top:20px; right:20px; background:transparent; font-size:24px; color:#94a3b8;\"><i class=\"fa-solid fa-xmark\"><\/i><\/button>${html}<\/div><\/div>`; m.classList.remove('hidden'); }\r\n        window.tutupModal = function() { document.getElementById('modal-container').classList.add('hidden'); document.getElementById('modal-container').innerHTML = ''; }\r\n\r\n        function initApp() { renderLogin(); }\r\n        if (document.readyState === 'loading') document.addEventListener(\"DOMContentLoaded\", initApp); else initApp();\r\n        \/* ]]> *\/\r\n    <\/script>\r\n<\/div>\r\n\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":5,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-9667","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/man2probolinggo.sch.id\/index.php\/wp-json\/wp\/v2\/pages\/9667","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/man2probolinggo.sch.id\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/man2probolinggo.sch.id\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/man2probolinggo.sch.id\/index.php\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/man2probolinggo.sch.id\/index.php\/wp-json\/wp\/v2\/comments?post=9667"}],"version-history":[{"count":3,"href":"https:\/\/man2probolinggo.sch.id\/index.php\/wp-json\/wp\/v2\/pages\/9667\/revisions"}],"predecessor-version":[{"id":9670,"href":"https:\/\/man2probolinggo.sch.id\/index.php\/wp-json\/wp\/v2\/pages\/9667\/revisions\/9670"}],"wp:attachment":[{"href":"https:\/\/man2probolinggo.sch.id\/index.php\/wp-json\/wp\/v2\/media?parent=9667"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}