:root{
–bg:#f7fafc;
–card:#ffffff;
–muted:#4a5568;
–text:#1a202c;
–primary:#16a34a; /* verde do botão */
–primary-weak:#dcfce7; /* foco/hover verde suave */
–border:#e2e8f0;
–danger:#dc2626;
}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{
margin:0;
font-family: system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,”Helvetica Neue”,Arial;
background:var(–bg);
color:var(–text);
line-height:1.45;
-webkit-text-size-adjust:100%;
}
.wrap{max-width:1100px;margin:24px auto;padding:0 16px}
header h1{margin:0 0 6px;font-size:clamp(22px,3vw,32px)}
header p{margin:0 0 18px;color:var(–muted)}
/* ===== Layout base ===== */
.grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}
@media (max-width: 980px){ .grid{grid-template-columns:1fr} }
.card{
background:var(–card);
border:1px solid var(–border);
border-radius:16px;
padding:18px;
box-shadow:0 6px 18px rgba(0,0,0,.04);
}
label{font-size:14px;color:var(–muted);display:block;margin-bottom:6px}
input[type=”text”],input[type=”number”],select{
width:100%;
border:1px solid var(–border);
background:#fff;color:var(–text);
border-radius:12px;
padding:12px 14px;
font-size:16px;outline:none;
min-height:44px;
}
input:focus,select:focus{
border-color:var(–primary);
box-shadow:0 0 0 3px var(–primary-weak);
}
.error{ border-color: var(–danger) !important; box-shadow: 0 0 0 3px rgba(220,38,38,.15) !important; }
.err-msg{ font-size:12px; color: var(–danger); margin-top:6px; min-height:1.2em; }
.hint{font-size:12px;color:var(–muted);margin-top:4px}
/* ===== Linhas do formulário ===== */
.row { display: grid; gap: 16px; margin-bottom: 16px; align-items: end; }
.row–2 { grid-template-columns: 1fr 1fr; }
.row–tax-prazo { grid-template-columns: 2fr 1fr 2fr 1fr; }
.row–2b { grid-template-columns: 2fr 1fr; }
/* ===== Responsividade ===== */
@media (max-width: 980px) {
.row–2, .row–2b, .row–tax-prazo { grid-template-columns: 1fr; }
}
@media (max-width: 640px) {
.actions a { width: 100%; text-align:center; }
}
/* ===== Links no lugar dos botões ===== */
.actions{ display:flex; gap:10px; flex-wrap:wrap; }
.actions a{
display:inline-block;
text-decoration:none;
border-radius:14px;
padding:14px 18px;
font-weight:800;
transition:.15s transform ease,.15s box-shadow ease,.15s background-color ease;
}
#btnCalcular{
background:var(–primary);color:#fff;
box-shadow:0 6px 14px rgba(22,163,74,.18);
}
#btnCalcular:hover{transform:translateY(-1px)}
#btnCalcular:focus{box-shadow:0 0 0 4px var(–primary-weak)}
#btnCalcular .sub{ display:block;font-weight:600;opacity:.9;font-size:12px;line-height:1.1;margin-top:2px }
.actions a.secondary{
background:#fff;color:var(–primary);border:1px solid var(–primary);font-weight:700;
}
.actions a.secondary:hover{background:#f8fff9}
/* ===== Resultados ===== */
.result-header{
display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap;
}
.badge{font-size:12px;padding:2px 8px;border-radius:999px;border:1px solid var(–border);color:var(–muted);background:#fff}
.tabs{display:flex;gap:6px;background:#f1f5f9;padding:4px;border-radius:999px;border:1px solid var(–border)}
.tab{padding:8px 14px;border-radius:999px;cursor:pointer;font-weight:700;border:1px solid transparent}
.tab.active{background:#fff;border-color:var(–border);box-shadow:0 2px 6px rgba(0,0,0,.05)}
@media (max-width:480px){
.tabs{width:100%;justify-content:space-between}
.tab{flex:1;text-align:center}
}
.kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-top:14px}
@media (max-width:900px){.kpis{grid-template-columns:1fr 1fr}}
@media (max-width:480px){.kpis{grid-template-columns:1fr}}
.kpi{border:1px solid var(–border);border-radius:14px;padding:12px;background:#fff}
.kpi .title{font-size:12px;color:var(–muted)}
.kpi .value{font-size:clamp(18px,3vw,22px);font-weight:800;margin-top:4px}
table{width:100%;border-collapse:collapse;margin-top:14px;font-size:14px}
thead th{
text-align:left;background:#f8fafc;border-bottom:1px solid var(–border);padding:10px;
position:sticky;top:0;z-index:1;
}
tbody td{border-bottom:1px solid var(–border);padding:8px 10px}
.scroll{max-height:360px;overflow:auto;border:1px solid var(–border);border-radius:12px;background:#fff}
.footer-note{font-size:12px;color:var(–muted);margin-top:10px}
.tag{font-size:11px;background:#ecfeff;color:#155e75;padding:2px 8px;border-radius:999px;border:1px solid #a5f3fc}
.muted{color:var(–muted)}
@media (prefers-reduced-motion: reduce){
*{animation:none !important;transition:none !important;scroll-behavior:auto !important}
}
fBR.format(v||0);
// Parser de moeda super tolerante
function parseMoeda(str){
if (str == null) return 0;
let s = String(str)
.replace(/\u00A0/g, ‘ ‘)
.replace(/R\$\s?/gi, ”)
.replace(/\s+/g, ”)
.trim();
if (!s) return 0;
const hasComma = s.includes(‘,’);
const hasDot = s.includes(‘.’);
if (hasComma && hasDot){
const lastComma = s.lastIndexOf(‘,’);
const lastDot = s.lastIndexOf(‘.’);
const decSep = lastComma > lastDot ? ‘,’ : ‘.’;
const thouSep = decSep === ‘,’ ? ‘.’ : ‘,’;
s = s.replace(new RegExp(‘\\’ + thouSep, ‘g’), ”).replace(decSep, ‘.’);
return parseFloat(s) || 0;
}
if (hasComma){
return parseFloat(s.replace(/\./g, ”).replace(‘,’, ‘.’)) || 0;
}
if (hasDot){
const parts = s.split(‘.’);
const last = parts[parts.length – 1];
if (parts.length > 1 && last.length === 3) {
s = s.replace(/\./g, ”);
return parseFloat(s) || 0;
}
return parseFloat(s) || 0;
}
return parseFloat(s) || 0;
}
function toMoedaInput(el){
if(!el) return;
el.addEventListener(‘blur’,()=>{
const v = parseMoeda(el.value);
el.value = v ? fmt(v) : ”;
});
}
// Liga formatação de moeda
[‘valor’,’entrada’,’seguro’].forEach(id=>toMoedaInput(document.getElementById(id)));
// ===== Cálculos =====
function taxaMensal(taxa,tipo){
const i = (taxa||0)/100;
return (tipo===’a.a.’) ? Math.pow(1+i,1/12)-1 : i;
}
function calcPrice(P,i,n,add=0){
const A = (i>0)? (P*i*Math.pow(1+i,n))/(Math.pow(1+i,n)-1) : (P/n);
let saldo=P,totalPago=0,totalJuros=0;
const rows=[];
for (let k=1;k<=n;k++){
const juros = saldo*i;
const amort = A-juros;
saldo = Math.max(0,saldo-amort);
const parcelaComTaxas = A+add;
totalPago += parcelaComTaxas;
totalJuros += juros;
rows.push({k,parcela:A,juros,amort,add,total:parcelaComTaxas,saldo});
}
return {parcela:A,totalPago,totalJuros,rows};
}
function calcSAC(P,i,n,add=0){
const amortConst = P/n;
let saldo=P,totalPago=0,totalJuros=0;
const rows=[];
for (let k=1;ksetFieldError(id,”));
}
let cache=null;
function calcular(){
clearErrors();
const V = parseMoeda(document.getElementById(‘valor’).value);
const E = parseMoeda(document.getElementById(‘entrada’).value);
const add = parseMoeda(document.getElementById(‘seguro’).value) || 0;
const taxa = parseFloat(String(document.getElementById(‘taxa’).value).replace(‘,’,’.’)) || 0;
const taxaTipo = document.getElementById(‘taxaTipo’).value;
const prazoRaw = String(document.getElementById(‘prazo’).value || ”);
const prazoNum = parseInt(prazoRaw.replace(/[^\d]/g,”),10) || 0;
const prazoTipo = document.getElementById(‘prazoTipo’).value;
const n = (prazoTipo===’anos’) ? prazoNum*12 : prazoNum;
// validações
let hasErr = false;
if (!V || V<=0){
setFieldError('valor','Informe um valor do bem válido (ex.: 350.000,00).');
hasErr = true;
}
if (!n || n0?’ • Com taxas mensais’:”}`;
tbody.innerHTML=”;
R.rows.forEach(row=>{
const tr=document.createElement(‘tr’);
tr.innerHTML = `
${row.k}
${fmt(row.parcela)}
${fmt(row.juros)}
${fmt(row.amort)}
${fmt(row.add)}
${fmt(row.total)}
${fmt(row.saldo)}
`;
tbody.appendChild(tr);
});
tblWrap.style.display=’block’;
nota.style.display=’block’;
}
// ===== Tabs =====
function setTab(tab){
currentTab=tab;
tabPrice.classList.toggle(‘active’,tab===’price’);
tabSAC.classList.toggle(‘active’,tab===’sac’);
render();
}
tabPrice.addEventListener(‘click’,()=>setTab(‘price’));
tabSAC.addEventListener(‘click’,()=>setTab(‘sac’));
document.getElementById(‘tabelaInicial’).addEventListener(‘change’,(e)=>setTab(e.target.value));
// ===== Limpar =====
function limpar(){
[‘valor’,’entrada’,’seguro’,’taxa’,’prazo’].forEach(id=>document.getElementById(id).value=”);
document.getElementById(‘taxaTipo’).value=’a.m.’;
document.getElementById(‘prazoTipo’).value=’meses’;
document.getElementById(‘tabelaInicial’).value=’price’;
setTab(‘price’);
cache=null;
tbody.innerHTML=”;
[‘kParcela’,’kTotalPago’,’kJuros’,’kPrazo’].forEach(id=>document.getElementById(id).textContent=’—’);
resResumo.textContent=”;
document.getElementById(‘tblWrap’).style.display=’none’;
document.getElementById(‘nota’).style.display=’none’;
clearErrors();
// também limpa a URL
history.replaceState({}, ”, location.pathname);
}
// ===== Exportar CSV (tabela visível) =====
function exportCSV(){
if(!cache){ alert(‘Faça uma simulação antes de exportar.’); return; }
const {rPrice, rSAC, add, V, E, taxa, taxaTipo, n} = cache;
const tabela = (currentTab===’price’) ? ‘Price’ : ‘SAC’;
const R = (currentTab===’price’) ? rPrice : rSAC;
const header = [
‘Parâmetro’,’Valor’,
‘Tabela’,tabela,
‘Valor do bem’, V,
‘Entrada’, E||0,
`Taxa (${taxaTipo})`, taxa,
‘Prazo (meses)’, n,
‘Seguros/Taxas mensais’, add
];
const rows = R.rows.map(r=>[
r.k,
r.parcela.toFixed(2),
r.juros.toFixed(2),
r.amort.toFixed(2),
r.add.toFixed(2),
r.total.toFixed(2),
r.saldo.toFixed(2)
]);
const csv = [
‘Resumo,’,
…chunk(header,2).map(([k,v])=>`${k},${v}`),
”,
‘Parcela #,Parcela,Juros,Amortização,Seguro/Taxas,Parcela + Taxas,Saldo Devedor’,
…rows.map(r=>r.join(‘,’)),
”,
`Totais,,`,
`Total pago,${R.totalPago.toFixed(2)}`,
`Juros totais,${R.totalJuros.toFixed(2)}`
].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;
const stamp = new Date().toISOString().slice(0,19).replace(/[:T]/g,’-‘);
a.download = `simulacao-${tabela.toLowerCase()}-${stamp}.csv`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
function chunk(arr,size){ const out=[]; for(let i=0;i{
const el = document.getElementById(id);
if(!el) return;
if(formatMoney) el.value = v ? fmt(Number(v)) : ”;
else el.value = (v ?? ”);
};
setVal(‘valor’, q.get(‘valor’), true);
setVal(‘entrada’, q.get(‘entrada’), true);
setVal(‘seguro’, q.get(‘seguro’), true);
setVal(‘taxa’, q.get(‘taxa’));
const taxaTipo = q.get(‘taxaTipo’); if(taxaTipo) document.getElementById(‘taxaTipo’).value = taxaTipo;
setVal(‘prazo’, q.get(‘prazo’));
const prazoTipo = q.get(‘prazoTipo’); if(prazoTipo) document.getElementById(‘prazoTipo’).value = prazoTipo;
const tabela = q.get(‘tabela’); if(tabela) document.getElementById(‘tabelaInicial’).value = tabela;
// calcula e ativa a aba solicitada (se houver)
calcular();
const tab = (q.get(‘tab’) || ”).toLowerCase();
if(tab === ‘sac’) setTab(‘sac’); else setTab(‘price’);
}
// ===== Eventos (links com preventDefault) =====
document.getElementById(‘btnCalcular’).addEventListener(‘click’,(e)=>{ e.preventDefault(); calcular(); });
document.getElementById(‘btnCSV’).addEventListener(‘click’,(e)=>{ e.preventDefault(); exportCSV(); });
document.getElementById(‘btnShare’).addEventListener(‘click’,(e)=>{ e.preventDefault(); shareLink(); });
document.getElementById(‘btnLimpar’).addEventListener(‘click’,(e)=>{ e.preventDefault(); limpar(); });
// Enter para calcular quando focado nos campos principais
document.addEventListener(‘keydown’,(e)=>{
if(e.key===’Enter’){
const ids=[‘valor’,’entrada’,’seguro’,’taxa’,’prazo’];
const active=document.activeElement;
if(active && ids.includes(active.id)){
e.preventDefault();
calcular();
}
}
});
// Inicializa a partir da URL (se houver parâmetros)
applyFromURL();
})();
//–>
Simule parcelas de financiamento imobiliário ou financiamento de veículos pelos sistemas Price (parcela fixa) e SAC (amortização constante). Informe os dados abaixo e clique em CALCULAR.
Formulário de Simulação
Aceita 350000, 350.000,00 ou 350,000.00
ao mês (a.m.)
ao ano (a.a.)
meses
anos
Ex.: MIP/DFI, tarifa mensal, etc. Somado à parcela.
Price
SAC
Resultados
Price
SAC
Parcela inicial
—
Total pago
—
Juros totais
—
Prazo
—
| # | Parcela | Juros | Amortização | Seguro/Taxas | Parcela + Taxas | Saldo Devedor |
|---|