:root{
–bg:#f9fafb; /* mais claro */
–panel:#ffffff; /* branco */
–muted:#64748b; /* cinza médio */
–text:#1e293b; /* quase preto */
–accent:#22c55e; /* verde-500 */
–accent-2:#16a34a; /* verde-600 */
–danger:#ef4444; /* vermelho-500 */
–card:#f1f5f9; /* cinza-claro */
–border:#cbd5e1; /* borda clara */
}
*{box-sizing:border-box}
html,body{height:100%}
body{
margin:0; font-family:Inter,system-ui,Segoe UI,Roboto,Arial,Helvetica,sans-serif;
background:var(–bg);
color:var(–text);
}
.container{max-width:1100px; margin:40px auto; padding:0 16px}
.title{font-weight:800; font-size:clamp(22px, 2.6vw, 34px); margin:0 0 6px}
.subtitle{color:var(–muted); margin:0 0 24px; font-size:clamp(13px,1.6vw,15px)}
.grid{display:grid; grid-template-columns:1.15fr .85fr; gap:20px}
@media (max-width:900px){.grid{grid-template-columns:1fr}}
.card{background:var(–panel); border:1px solid var(–border); border-radius:18px; box-shadow:0 4px 12px rgba(0,0,0,.08);}
.card .card-body{padding:18px}
.form{display:grid; grid-template-columns:1fr 1fr; gap:14px}
@media (max-width:600px){.form{grid-template-columns:1fr}}
label{font-weight:600; font-size:13px; color:var(–muted)}
.field{display:flex; flex-direction:column; gap:8px}
input[type=”number”], input[type=”text”], select{
width:100%; padding:12px; background:#fff; color:var(–text);
border:1px solid var(–border); border-radius:12px; outline:none; transition:.2s;
}
input:focus, select:focus{border-color:var(–accent); box-shadow:0 0 0 3px rgba(34,197,94,.3)}
.row-3{grid-column:span 2}
@media (max-width:600px){.row-3{grid-column:span 1}}
a.btn{display:inline-flex; align-items:center; justify-content:center; gap:8px; text-decoration:none;
border-radius:12px; padding:12px 14px; border:1px solid transparent; font-weight:700}
a.btn-primary{background:linear-gradient(180deg, var(–accent), var(–accent-2)); color:#fff}
a.btn-primary:hover{filter:brightness(1.05)}
a.btn-outline{background:transparent; color:var(–text); border-color:var(–border)}
a.btn-outline:hover{background:#f8fafc}
a.btn-danger{background:linear-gradient(180deg,#ef4444,#b91c1c); color:#fff}
.actions{display:flex; gap:10px; flex-wrap:wrap}
.kpis{display:grid; grid-template-columns:repeat(3,1fr); gap:12px; margin-top:14px}
@media (max-width:900px){.kpis{grid-template-columns:1fr}}
.kpi{background:var(–card); border:1px solid var(–border); padding:12px; border-radius:14px}
.kpi .label{color:var(–muted); font-size:12px}
.kpi .value{font-size:clamp(18px, 2.4vw, 26px); font-weight:800; margin-top:6px}
details{background:transparent; border:1px dashed var(–border); border-radius:14px; padding:10px 12px}
details[open]{background:rgba(148,163,184,.06)}
summary{cursor:pointer; color:var(–text); font-weight:700}
table{width:100%; border-collapse:collapse; margin-top:12px; font-size:14px}
th,td{border-bottom:1px solid var(–border); padding:10px 8px; text-align:right}
th:first-child, td:first-child{text-align:left}
thead th{position:sticky; top:0; background:#f1f5f9; z-index:2}
tbody tr:hover{background:rgba(148,163,184,.1)}
.scroll{max-height:360px; overflow:auto; border:1px solid var(–border); border-radius:12px}
.hint{color:#16a34a; font-size:12px}
.note{color:var(–muted); font-size:12px; margin-top:8px}
.error{color:var(–danger); font-size:13px}
.badge{display:inline-block; padding:4px 8px; border-radius:999px; font-size:11px; color:#fff; background:var(–accent)}
.footer{color:var(–muted); font-size:12px; margin-top:14px}
const BRL = new Intl.NumberFormat(‘pt-BR’, { style: ‘currency’, currency: ‘BRL’ });
const PCT = new Intl.NumberFormat(‘pt-BR’, { style: ‘percent’, maximumFractionDigits: 4 });
const $ = (id)=>document.getElementById(id);
function syncTipoTaxa(){
const tipo = document.getElementById(‘tipoTaxa’).value;
document.getElementById(‘labelPeriodoTaxa’).textContent = tipo === ‘mensal’ ? ‘mês’ : ‘ano’;
}
function rateMonthlyFromInputs(){
const taxa = parseFloat(document.getElementById(‘taxa’).value || ‘0’) / 100; // em decimal
const tipo = document.getElementById(‘tipoTaxa’).value;
if (tipo === ‘mensal’) return taxa;
// equivalente mensal a partir da taxa anual
return Math.pow(1 + taxa, 1/12) – 1;
}
function totalMeses(){
const n = parseInt(document.getElementById(‘periodo’).value || ‘0’, 10);
const unidade = document.getElementById(‘unidadePeriodo’).value;
return unidade === ‘anos’ ? n * 12 : n;
}
function validar(){
const erro = document.getElementById(‘erro’);
erro.style.display = ‘none’;
erro.textContent = ”;
const meses = totalMeses();
const r = rateMonthlyFromInputs();
if (meses 0).’);
if (r < 0) return showError('A taxa não pode ser negativa.');
return true;
function showError(msg){ erro.textContent = msg; erro.style.display = 'block'; return false; }
}
function calcular(){
if (!validar()) return;
const inicial = parseFloat(document.getElementById('inicial').value || '0');
const aporte = parseFloat(document.getElementById('mensal').value || '0');
const r = rateMonthlyFromInputs();
const meses = totalMeses();
const aporteInicio = document.getElementById('diaAporte').value === 'inicio';
let saldo = inicial;
let totalInvestido = inicial;
const linhas = [];
for (let m = 1; m <= meses; m++){
const saldoInicial = saldo;
// Se aporte no início, entra antes de render
const aporteDoMes = aporteInicio ? aporte : 0;
saldo += aporteDoMes;
const juros = saldo * r;
saldo += juros;
// Se aporte no fim, entra depois de render
if (!aporteInicio){
saldo += aporte;
}
const aporteRegistrado = aporteInicio ? aporteDoMes : aporte;
totalInvestido += aporte;
linhas.push({
mes: m,
saldoInicial,
juros,
aporte: aporteRegistrado,
saldoFinal: saldo
});
}
const valorFinal = saldo;
const jurosAcumulados = valorFinal – totalInvestido;
// KPIs
document.getElementById('kpiFinal').textContent = BRL.format(valorFinal);
document.getElementById('kpiInvestido').textContent = BRL.format(totalInvestido);
document.getElementById('kpiJuros').textContent = BRL.format(jurosAcumulados);
// Resumo
const rMensal = r;
const rAnualEquiv = Math.pow(1 + rMensal, 12) – 1;
document.getElementById('resumo').innerHTML = `
Período: ${meses} ${meses === 1 ? ‘mês’ : ‘meses’} •
Taxa efetiva: ${(rMensal*100).toFixed(4)}% a.m. (${(rAnualEquiv*100).toFixed(2)}% a.a.) •
Aportes ${document.getElementById(‘diaAporte’).value === ‘inicio’ ? ‘no início’ : ‘no final’} do mês.
`;
// Tabela
const tbody = document.querySelector(‘#tabela tbody’);
tbody.innerHTML = ”;
for (const row of linhas){
const tr = document.createElement(‘tr’);
tr.innerHTML = `
${row.mes}
${BRL.format(row.saldoInicial)}
${BRL.format(row.juros)}
${BRL.format(row.aporte)}
${BRL.format(row.saldoFinal)}
`;
tbody.appendChild(tr);
}
// Ajuste por inflação (opcional)
const inflAA = parseFloat(document.getElementById(‘taxaInflacao’).value || ‘0’)/100;
const infoInfl = document.getElementById(‘ajusteInflacao’);
if (inflAA > 0){
const inflMensal = Math.pow(1 + inflAA, 1/12) – 1;
const fator = Math.pow(1 + inflMensal, meses);
const valorReal = valorFinal / fator;
infoInfl.style.display = ‘block’;
infoInfl.innerHTML = `Ajustado pela inflação de ${ (inflAA*100).toFixed(2) }% a.a. (~${ (inflMensal*100).toFixed(3) }% a.m.), o valor final equivale a ${BRL.format(valorReal)} em poder de compra de hoje.`;
} else {
infoInfl.style.display = ‘none’;
infoInfl.textContent = ”;
}
// Guarda última simulação (para export/compartilhar)
window.__simulacao = { linhas, valorFinal, totalInvestido, jurosAcumulados };
}
function exportarCSV(){
if (!window.__simulacao){ alert(‘Faça uma simulação primeiro.’); return; }
const rows = [[‘Mes’,’Saldo Inicial’,’Juros do Mes’,’Aporte’,’Saldo Final’]];
for (const r of window.__simulacao.linhas){
rows.push([
r.mes,
r.saldoInicial.toFixed(2).replace(‘.’,’,’),
r.juros.toFixed(2).replace(‘.’,’,’),
r.aporte.toFixed(2).replace(‘.’,’,’),
r.saldoFinal.toFixed(2).replace(‘.’,’,’),
]);
}
const csv = rows.map(a => a.join(‘;’)).join(‘\n’);
const blob = new Blob([csv], {type:’text/csv;charset=utf-8;’});
const url = URL.createObjectURL(blob);
const a = document.createElement(‘a’);
a.href = url;
a.download = ‘simulacao-juros-compostos.csv’;
a.click();
URL.revokeObjectURL(url);
}
function limpar(){
document.getElementById(‘form’).reset();
syncTipoTaxa();
document.querySelector(‘#tabela tbody’).innerHTML=”;
document.getElementById(‘kpiFinal’).textContent = ‘—’;
document.getElementById(‘kpiInvestido’).textContent = ‘—’;
document.getElementById(‘kpiJuros’).textContent = ‘—’;
document.getElementById(‘resumo’).textContent = ‘Preencha os campos e clique em Calcular.’;
document.getElementById(‘ajusteInflacao’).style.display=’none’;
document.getElementById(‘erro’).style.display=’none’;
window.__simulacao = null;
}
function copiarLink(){
const p = new URLSearchParams({
inicial: document.getElementById(‘inicial’).value || ‘0’,
mensal: document.getElementById(‘mensal’).value || ‘0’,
taxa: document.getElementById(‘taxa’).value || ‘0’,
tipoTaxa: document.getElementById(‘tipoTaxa’).value,
periodo: document.getElementById(‘periodo’).value || ‘0’,
unidade: document.getElementById(‘unidadePeriodo’).value,
diaAporte: document.getElementById(‘diaAporte’).value,
inflacaoAA: document.getElementById(‘taxaInflacao’).value || ‘0’
}).toString();
const url = location.origin + location.pathname + ‘?’ + p;
navigator.clipboard.writeText(url).then(()=>{
alert(‘Link copiado! Cole para compartilhar esta simulação.’);
});
}
function carregarURL(){
const q = new URLSearchParams(location.search);
const get = (k, d=”) => q.get(k) ?? d;
if ([…q.keys()].length === 0) return;
if(q.get(“inicial”) != null){
document.getElementById(‘inicial’).value = get(‘inicial’,’0′);
document.getElementById(‘mensal’).value = get(‘mensal’,’0′);
document.getElementById(‘taxa’).value = get(‘taxa’,’0′);
document.getElementById(‘tipoTaxa’).value = get(‘tipoTaxa’,’mensal’);
document.getElementById(‘periodo’).value = get(‘periodo’,’12’);
document.getElementById(‘unidadePeriodo’).value = get(‘unidade’,’meses’);
document.getElementById(‘diaAporte’).value = get(‘diaAporte’,’fim’);
document.getElementById(‘taxaInflacao’).value = get(‘inflacaoAA’,’0′);
syncTipoTaxa();
calcular();
}
}
// Eventos (com âncoras)
$(‘btnCalcular’).addEventListener(‘click’, (e)=>{ e.preventDefault(); calcular(); });
$(‘btnExportar’).addEventListener(‘click’, (e)=>{ e.preventDefault(); exportarCSV(); });
$(‘btnCompartilhar’).addEventListener(‘click’, (e)=>{ e.preventDefault(); copiarLink(); });
$(‘btnLimpar’).addEventListener(‘click’, (e)=>{ e.preventDefault(); limpar(); });
// UX: Enter para calcular
document.addEventListener(‘keydown’, (e)=>{
if(e.key === ‘Enter’) calcular();
});
// Init
syncTipoTaxa();
window.addEventListener(‘DOMContentLoaded’, carregarURL);
Simule a evolução do seu investimento com aportes mensais, taxa mensal ou anual e veja quanto será valor final, total investido e juros acumulados.
Mensal
Anual (equivalente mensal será usado)
Meses
Anos
Ao final do mês
No início do mês
Aporte no início do mês rende durante o mês corrente.
Se preenchida, mostra também o valor ajustado pela inflação (poder de compra).
Valor final
—
Total investido
—
Juros acumulados
—
Resumo da simulação
Preencha os campos e clique em Calcular.
| Mês | Saldo inicial | Juros do mês | Aporte | Saldo final |
|---|