<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>SIMULADO RDN MURILO</title>
<meta name="theme-color" content="#2E7D32" />
<meta name="description" content="Simulados e prática para o Exame RDN — 900 questões, modos Real, Domínios e Personalizado." />
<link rel="manifest" href="/manifest.json" />
<link rel="apple-touch-icon" href="/icons/icon-192.png" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
<style>
:root {
--primary: #2E7D32;
--bg: #FAFAFA;
--text: #1B1B1B;
--muted: #6b7280;
--card: #ffffff;
--border: #e5e7eb;
}
* { box-sizing: border-box; }
body {
margin: 0; background: var(--bg); color: var(--text);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Inter, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
}
a { color: var(--primary); text-decoration: none; }
header {
position: sticky; top: 0; z-index: 50;
background: var(--card); border-bottom: 1px solid var(--border);
}
.container { max-width: 1100px; margin: 0 auto; padding: 16px; }
.brand { display: flex; align-items: center; gap: 10px; }
.brand .dot {
width: 28px; height: 28px; border-radius: 8px; background: var(--primary);
box-shadow: 0 2px 6px rgba(46,125,50,0.35);
}
.title { font-weight: 700; letter-spacing: 0.2px; }
.subtitle { color: var(--muted); font-size: 14px; }
.grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: 16px; }
.card {
background: var(--card); border: 1px solid var(--border); border-radius: 12px; padding: 16px;
box-shadow: 0 1px 2px rgba(0,0,0,0.04);
}
.btn {
border: 1px solid var(--primary); color: #fff; background: var(--primary);
padding: 10px 14px; border-radius: 10px; cursor: pointer; font-weight: 600;
box-shadow: 0 2px 8px rgba(46,125,50,0.25);
}
.btn.secondary {
background: transparent; color: var(--primary); border-color: var(--primary);
box-shadow: none;
}
.row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.badge { background: #e8f5e9; color: #1b5e20; padding: 4px 10px; border-radius: 999px; font-weight: 600; font-size: 12px; }
.muted { color: var(--muted); }
.footer { color: var(--muted); text-align: center; padding: 20px 0; }
.hidden { display: none !important; }
.col-12 { grid-column: span 12; }
.col-6 { grid-column: span 6; }
.col-4 { grid-column: span 4; }
@media (max-width: 900px) {
.col-6, .col-4 { grid-column: span 12; }
}
.q-stem { font-size: 18px; line-height: 1.5; margin-bottom: 10px; }
.opt { display: flex; gap: 10px; align-items: flex-start; padding: 10px; border: 1px solid var(--border); border-radius: 10px; cursor: pointer; }
.opt + .opt { margin-top: 8px; }
.opt input { margin-top: 4px; }
.pill { font-size: 12px; padding: 4px 8px; border-radius: 999px; border: 1px solid var(--border); color: var(--muted); }
.progress { height: 8px; background: #e5f3e6; border-radius: 999px; overflow: hidden; }
.progress > div { height: 100%; background: var(--primary); width: 0%; transition: width 0.3s; }
.rationale { background: #f6fdf7; border: 1px dashed #b7dfbb; padding: 10px; border-radius: 8px; margin-top: 10px; }
.table { width: 100%; border-collapse: collapse; }
.table th, .table td { padding: 8px 10px; border-bottom: 1px solid var(--border); text-align: left; }
.table th { color: var(--muted); font-weight: 600; }
.chip { background: #eef6ff; color: #0b5cab; padding: 4px 8px; border-radius: 999px; font-size: 12px; }
.hl { color: var(--primary); font-weight: 700; }
</style>
</head>
<body>
<header>
<div class="container row" style="justify-content: space-between">
<div class="brand">
<div class="dot"></div>
<div>
<div class="title">SIMULADO RDN MURILO</div>
<div class="subtitle">Prática por Domínio • Modo Real • Resultados detalhados</div>
</div>
</div>
<button id="btnInstall" class="btn secondary hidden">Instalar App</button>
</div>
</header>
<main class="container" style="padding: 16px 16px 60px">
<div class="grid">
<section class="card col-12" id="welcome">
<div class="row" style="justify-content: space-between; align-items: flex-start">
<div>
<h2 style="margin: 0 0 6px 0">Bem-vindo(a)!</h2>
<p class="muted" style="margin: 0 0 12px 0">
Treine com 900 questões originais alinhadas ao exame RDN. Escolha o modo e comece já.
</p>
<div class="row">
<button class="btn" id="startReal">Modo Real (125 questões)</button>
<button class="btn secondary" id="startDomain">Praticar por Domínio</button>
<button class="btn secondary" id="startCustom">Prática Personalizada</button>
</div>
</div>
<div class="card" style="min-width: 260px">
<div class="row" style="justify-content: space-between">
<div class="badge">PWA</div>
<span class="muted">Instale na tela inicial</span>
</div>
<div style="height: 8px"></div>
<div class="progress"><div id="overallProgress"></div></div>
<div class="muted" style="margin-top: 8px; font-size: 12px">Progresso geral</div>
</div>
</div>
</section>
<section class="card col-12 hidden" id="config">
<h3 style="margin-top:0">Configurar Sessão</h3>
<div class="row">
<label>
Modo:
<select id="mode">
<option value="real">Real (125)</option>
<option value="domain">Domínio</option>
<option value="custom">Personalizado</option>
</select>
</label>
<label id="domainSelWrap" class="hidden">
Domínio:
<select id="domainSel">
<option value="1">Domínio 1</option>
<option value="2">Domínio 2</option>
<option value="3">Domínio 3</option>
<option value="4">Domínio 4</option>
</select>
</label>
<label id="countWrap" class="hidden">
Nº de questões:
<input id="count" type="number" min="5" max="200" value="30" />
</label>
<button class="btn" id="btnBegin">Começar</button>
</div>
</section>
<section class="card col-12 hidden" id="exam">
<div class="row" style="justify-content: space-between">
<div class="row" style="gap:6px">
<span class="pill" id="sessionMode">Real</span>
<span class="pill" id="sessionExtra"></span>
</div>
<div class="row" style="gap:6px">
<button id="btnMark" class="btn secondary">Marcar</button>
<button id="btnReview" class="btn secondary">Revisão</button>
<button id="btnEnd" class="btn secondary">Finalizar</button>
</div>
</div>
<div style="height:10px"></div>
<div class="progress"><div id="prog"></div></div>
<div style="height:12px"></div>
<div id="qArea"></div>
</section>
<section class="card col-12 hidden" id="results">
<h3 style="margin: 0 0 8px 0">Resultados</h3>
<div class="row" id="scoreLine"></div>
<div style="height:10px"></div>
<table class="table">
<thead><tr><th>Domínio</th><th>% Acerto</th><th>Acertos</th><th>Total</th></tr></thead>
<tbody id="domainBreak"></tbody>
</table>
<div style="height:12px"></div>
<button class="btn" id="btnAgain">Nova Sessão</button>
</section>
</div>
</main>
<footer class="footer">
© <span id="y"></span> SIMULADO RDN MURILO • nutrimurilotoscano.com
</footer>
<script>
const state = { bank: [], session: null, deferredPrompt: null };
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
state.deferredPrompt = e;
document.getElementById('btnInstall').classList.remove('hidden');
});
document.getElementById('btnInstall').addEventListener('click', async () => {
if (!state.deferredPrompt) return;
state.deferredPrompt.prompt();
await state.deferredPrompt.userChoice;
state.deferredPrompt = null;
document.getElementById('btnInstall').classList.add('hidden');
});
const $ = (id) => document.getElementById(id);
const show = (id) => $(id).classList.remove('hidden');
const hide = (id) => $(id).classList.add('hidden');
async function loadBank() {
const urls = ['/assets/bank-d1.json','/assets/bank-d2.json','/assets/bank-d3.json','/assets/bank-d4.json'];
const parts = await Promise.all(urls.map(u => fetch(u).then(r => r.json())));
state.bank = parts.flat();
}
$('startReal').onclick = () => {
show('config'); hide('exam'); hide('results');
$('mode').value = 'real';
$('domainSelWrap').classList.add('hidden');
$('countWrap').classList.add('hidden');
};
$('startDomain').onclick = () => {
show('config'); hide('exam'); hide('results');
$('mode').value = 'domain';
$('domainSelWrap').classList.remove('hidden');
$('countWrap').classList.remove('hidden');
};
$('startCustom').onclick = () => {
show('config'); hide('exam'); hide('results');
$('mode').value = 'custom';
$('domainSelWrap').classList.remove('hidden');
$('countWrap').classList.remove('hidden');
};
$('btnBegin').onclick = () => {
const mode = $('mode').value;
let pool = state.bank.slice();
let count = 125;
let extra = '';
if (mode === 'domain') {
const d = $('domainSel').value;
pool = pool.filter(q => String(q.domain) === String(d));
count = Math.min(pool.length, Number($('count').value || 30));
extra = 'Domínio ' + d + ' • ' + count + ' questões';
} else if (mode === 'custom') {
const d = $('domainSel').value;
if (d) pool = pool.filter(q => String(q.domain) === String(d));
count = Math.min(pool.length, Number($('count').value || 40));
extra = (d ? 'Domínio ' + d + ' • ' : '') + count + ' questões';
} else {
const byD = {1: [], 2: [], 3: [], 4: []};
pool.forEach(q => byD[q.domain]?.push(q));
const target = {1: 35, 2: 40, 3: 30, 4: 20};
pool = [
...shuffle(byD[1]).slice(0, target[1]),
...shuffle(byD[2]).slice(0, target[2]),
...shuffle(byD[3]).slice(0, target[3]),
...shuffle(byD[4]).slice(0, target[4]),
];
count = 125; extra = '125 questões';
}
pool = shuffle(pool).slice(0, count);
state.session = { mode, idx: 0, count, pool, answers: {}, start: Date.now() };
$('sessionMode').textContent = mode === 'real' ? 'Modo Real' : (mode === 'domain' ? 'Domínio' : 'Personalizado');
$('sessionExtra').textContent = extra;
hide('config'); hide('results'); show('exam');
renderQ();
$('prog').style.width = '0%';
};
function shuffle(a) {
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
function renderQ() {
const s = state.session;
if (!s) return;
const q = s.pool[s.idx];
const total = s.count;
$('prog').style.width = Math.round(((s.idx) / total) * 100) + '%';
const wrapper = document.createElement('div');
wrapper.innerHTML = `
<div class="row" style="justify-content: space-between; align-items:center">
<div class="muted">Questão ${s.idx + 1} de ${total}</div>
<div class="row"><span class="chip">Domínio ${q.domain}</span><span class="chip">${q.difficulty || 'Médio'}</span></div>
</div>
<div class="q-stem">${escapeHtml(q.stem)}</div>
<div id="opts"></div>
<div id="explain" class="rationale hidden"></div>
<div class="row" style="margin-top:12px; justify-content: space-between">
<div class="row">
<button class="btn secondary" id="prev">Anterior</button>
<button class="btn secondary" id="next">Próxima</button>
</div>
<button class="btn" id="submit">Confirmar</button>
</div>
`;
$('qArea').innerHTML = '';
$('qArea').appendChild(wrapper);
const opts = $('opts');
q.options.forEach((opt, i) => {
const node = document.createElement('label');
node.className = 'opt';
node.innerHTML = `<input type="radio" name="opt" value="${i}"/><div>${escapeHtml(opt)}</div>`;
opts.appendChild(node);
});
$('prev').onclick = () => { if (s.idx > 0) { s.idx--; renderQ(); } };
$('next').onclick = () => { if (s.idx < total - 1) { s.idx++; renderQ(); } };
$('submit').onclick = () => {
const chosen = opts.querySelector('input[name="opt"]:checked');
if (!chosen) { alert('Selecione uma alternativa.'); return; }
const sel = Number(chosen.value);
s.answers[s.idx] = sel;
const correct = sel === q.answer;
const ex = $('explain');
ex.innerHTML = '<strong>' + (correct ? 'Correto!' : 'Incorreto') + '</strong><br/>' + escapeHtml(q.rationale || '');
ex.classList.remove('hidden');
setTimeout(() => {
if (s.idx < total - 1) { s.idx++; renderQ(); }
else { finish(); }
}, 600);
};
}
function finish() {
const s = state.session;
let correct = 0;
const byDomain = {};
s.pool.forEach((q, i) => {
const isC = s.answers[i] === q.answer;
if (isC) correct++;
const d = q.domain;
if (!byDomain[d]) byDomain[d] = { correct: 0, total: 0 };
byDomain[d].total++;
if (isC) byDomain[d].correct++;
});
$('scoreLine').innerHTML = '<div class="badge">Acertos</div><div class="hl" style="font-size:22px;margin-left:8px">' + correct + ' / ' + s.count + '</div>';
$('domainBreak').innerHTML = Object.keys(byDomain).sort().map(d => {
const o = byDomain[d];
const pct = Math.round((o.correct / o.total) * 100);
return '<tr><td>Domínio ' + d + '</td><td>' + pct + '%</td><td>' + o.correct + '</td><td>' + o.total + '</td></tr>';
}).join('');
hide('exam'); show('results');
}
$('btnAgain').onclick = () => { hide('results'); show('welcome'); };
$('btnEnd').onclick = finish;
$('btnReview').onclick = () => alert('Modo de revisão simples nesta versão.');
$('btnMark').onclick = () => alert('Questão marcada para revisão.');
function escapeHtml(s) {
return String(s).replace(/[&<>"']/g, m => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m]));
}
document.getElementById('y').textContent = new Date().getFullYear();
loadBank();
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => navigator.serviceWorker.register('/service-worker.js'));
}
</script>
</body>
</html>