// ========================================
// CONTINUATION OF CALCULATION FUNCTIONS
// ========================================
// Bolts Calculation
function calcularParafusos() {
const fy = parseFloat(document.getElementById('bolt-type').value) || 400;
const d = parseFloat(document.getElementById('bolt-d').value) || 20;
const qty = parseInt(document.getElementById('bolt-qty').value) || 1;
const planes = parseInt(document.getElementById('bolt-planes').value) || 1;
const force = parseFloat(document.getElementById('bolt-force').value) || 0;
const area = Math.PI * Math.pow(d / 2, 2);
const Fv = 0.6 * fy * area * planes / 1000;
const capacity = Fv * qty;
const utilization = (force / capacity) * 100;
let alertClass = 'alert-success';
let status = '✅ OK - Capacidade adequada';
if (utilization > 100) {
alertClass = 'alert-error';
status = '❌ FALHA - Capacidade insuficiente';
} else if (utilization > 80) {
alertClass = 'alert-warning';
status = '⚠️ ATENÇÃO - Utilização elevada';
}
document.getElementById('bolt-result').innerHTML = `
Verificação ao Cisalhamento
Área Parafuso
${area.toFixed(0)} mm²
Fv por Parafuso
${Fv.toFixed(1)} kN
Capacidade Total
${capacity.toFixed(1)} kN
Utilização
${utilization.toFixed(1)}%
${status}
`;
addToHistory('Ligações Parafusadas', `${qty} parafusos Ø${d}mm, Utilização = ${utilization.toFixed(1)}%`);
}
// Layout Verification
function verificarLayout() {
const d = parseFloat(document.getElementById('layout-d').value) || 20;
const edge = parseFloat(document.getElementById('layout-edge').value) || 0;
const spacing = parseFloat(document.getElementById('layout-spacing').value) || 0;
const minEdge = 1.5 * d;
const maxEdge = 12 * 10;
const minSpacing = 2.67 * d;
const maxSpacing = 300;
let edgeStatus = '✅ Conforme';
let edgeClass = 'alert-success';
if (edge < minEdge) {
edgeStatus = `❌ Abaixo do mínimo (${minEdge.toFixed(1)}mm)`;
edgeClass = 'alert-error';
} else if (edge > maxEdge) {
edgeStatus = `⚠️ Acima do máximo (${maxEdge}mm)`;
edgeClass = 'alert-warning';
}
let spacingStatus = '✅ Conforme';
let spacingClass = 'alert-success';
if (spacing < minSpacing) {
spacingStatus = `❌ Abaixo do mínimo (${minSpacing.toFixed(1)}mm)`;
spacingClass = 'alert-error';
} else if (spacing > maxSpacing) {
spacingStatus = `⚠️ Acima do máximo (${maxSpacing}mm)`;
spacingClass = 'alert-warning';
}
document.getElementById('layout-result').innerHTML = `
Verificação NBR 8800
Dist. Borda Mín
${minEdge.toFixed(1)} mm
Dist. Borda Máx
${maxEdge} mm
Espaç. Mínimo
${minSpacing.toFixed(1)} mm
Espaç. Máximo
${maxSpacing} mm
Distância de Borda: ${edgeStatus}
Espaçamento: ${spacingStatus}
`;
addToHistory('Layout de Furação', `Ø${d}mm, Borda: ${edge}mm, Espaç: ${spacing}mm`);
}
// Bolt vs Weld Comparison
function compararParafusoSolda() {
const force = parseFloat(document.getElementById('comp-force').value) || 0;
const length = parseFloat(document.getElementById('comp-length').value) || 0;
const fy = parseFloat(document.getElementById('comp-fy').value) || 345;
// Parafusos A325 Ø20mm
const boltCapacity = 60;
const boltQty = Math.ceil(force / boltCapacity);
const boltCost = boltQty * 15;
// Solda
const weldLeg = (force * 1000) / (0.707 * length * 0.65 * fy);
const weldLegRounded = Math.ceil(weldLeg);
const weldCost = (weldLegRounded * length / 1000) * 25;
document.getElementById('comparison-result').innerHTML = `
Comparação de Soluções
🔩 Solução Parafusada
Tipo: A325 Ø20mm
Quantidade: ${boltQty} parafusos
Capacidade: ${(boltQty * boltCapacity).toFixed(1)} kN
Custo estimado: R$ ${boltCost.toFixed(2)}
Vantagens: Desmontável, inspeção visual
Desvantagens: Maior tempo de instalação
🔥 Solução Soldada
Tipo: Solda de filete
Perna: ${weldLegRounded} mm
Comprimento: ${length} mm
Custo estimado: R$ ${weldCost.toFixed(2)}
Vantagens: Melhor rigidez, econômica
Desvantagens: Permanente, requer qualificação
Recomendação: ${weldCost < boltCost ? 'Solda de filete é mais econômica' : 'Parafusos mais econômicos'}
`;
addToHistory('Parafuso vs Solda', `${boltQty} parafusos vs solda ${weldLegRounded}mm`);
}
// Preheat Calculation
function calcularPreaquecimento() {
const cev = parseFloat(document.getElementById('preheat-cev').value) || 0;
const thickness = parseFloat(document.getElementById('preheat-thickness').value) || 0;
const ambient = parseFloat(document.getElementById('preheat-ambient').value) || 20;
const preheatTemp = 50 + (cev * 100) + (thickness / 10 * 20) + ((20 - ambient) / 2);
const maxInterpass = preheatTemp + 100;
let pwhtRecommendation = '';
if (thickness > 50 || cev > 0.60) {
pwhtRecommendation = '⚠️ PWHT (Tratamento Térmico Pós-Soldagem) recomendado';
} else {
pwhtRecommendation = '✅ PWHT não obrigatório';
}
document.getElementById('preheat-result').innerHTML = `
Temperatura de Pré-Aquecimento (AWS D1.1)
Temp. Mínima
${Math.round(preheatTemp)}°C
Temp. Interpasse Máx
${Math.round(maxInterpass)}°C
${pwhtRecommendation}
Procedimento:
1. Aquecer uniformemente até ${Math.round(preheatTemp)}°C
2. Medir temperatura a 75mm da junta
3. Manter durante toda a soldagem
4. Temperatura interpasse máxima: ${Math.round(maxInterpass)}°C
`;
addToHistory('Pré-Aquecimento', `CEV=${cev}, esp=${thickness}mm → ${Math.round(preheatTemp)}°C`);
}
// Weld Fillet Calculation
function calcularSoldaFilete() {
const force = parseFloat(document.getElementById('weld-force').value) || 0;
const length = parseFloat(document.getElementById('weld-length').value) || 0;
const fy = parseFloat(document.getElementById('weld-fy').value) || 345;
const fyWeld = fy * 0.6;
const leg = (force * 1000) / (0.707 * length * 0.65 * fyWeld);
const throat = leg * 0.707;
const legCommercial = Math.ceil(leg);
const passes = legCommercial <= 5 ? 1 : legCommercial <= 8 ? 2 : 3;
document.getElementById('weld-result').innerHTML = `
Dimensionamento da Solda de Filete
Perna Calculada
${leg.toFixed(2)} mm
Perna Adotada
${legCommercial} mm
Garganta Efetiva
${throat.toFixed(2)} mm
Número de Passes
${passes}
Eletrodo recomendado: E${Math.round(fy * 1.15)} (resistência compatível com o aço base)
`;
addToHistory('Solda de Filete', `Perna ${legCommercial}mm, ${passes} passe(s)`);
}
// Heat Input Calculation
function calcularEnergiaSoldagem() {
const voltage = parseFloat(document.getElementById('hi-voltage').value) || 0;
const current = parseFloat(document.getElementById('hi-current').value) || 0;
const speed = parseFloat(document.getElementById('hi-speed').value) || 0;
const heatInput = (voltage * current * 60) / (speed * 1000);
let interpretation = '';
let alertClass = '';
if (heatInput < 1.0) {
interpretation = 'Energia baixa - Risco de falta de fusão ou trincas a frio';
alertClass = 'alert-warning';
} else if (heatInput <= 2.0) {
interpretation = 'Energia adequada - Dentro da faixa recomendada';
alertClass = 'alert-success';
} else {
interpretation = 'Energia alta - Risco de fragilização da ZTA e distorção';
alertClass = 'alert-error';
}
document.getElementById('hi-result').innerHTML = `
Energia de Soldagem (Heat Input)
Energia
${heatInput.toFixed(2)} kJ/mm
${interpretation}
Fórmula: HI = (V × I × 60) / (v × 1000)
Recomendações:
• Aços carbono: 0.8 - 2.0 kJ/mm
• Alta resistência: 0.8 - 1.5 kJ/mm
• Baixa liga: 1.0 - 2.5 kJ/mm
`;
addToHistory('Energia de Soldagem', `HI = ${heatInput.toFixed(2)} kJ/mm`);
}
// Electrode Consumption
function calcularConsumoEletrodos() {
const leg = parseFloat(document.getElementById('elec-leg').value) || 0;
const length = parseFloat(document.getElementById('elec-length').value) || 0;
const factor = parseFloat(document.getElementById('elec-type').value) || 1.10;
const loss = parseFloat(document.getElementById('elec-loss').value) || 15;
const throat = leg * 0.707;
const volume = throat * leg * length * 1000;
const mass = (volume / 1000000) * 7850 / 1000;
const consumption = mass * factor * (1 + loss / 100);
document.getElementById('elec-result').innerHTML = `
Consumo de Eletrodos
Volume de Solda
${(volume / 1000).toFixed(1)} cm³
Massa de Solda
${mass.toFixed(2)} kg
Consumo Total
${consumption.toFixed(2)} kg
Estimativa de embalagens:
Eletrodos Ø3,25mm (1 kg cada): ${Math.ceil(consumption)} embalagens
`;
addToHistory('Consumo de Eletrodos', `${consumption.toFixed(2)} kg para ${length}m de solda`);
}
// Hardness Converter
function converterDureza(source) {
let HB = 0;
if (source === 'hb') {
HB = parseFloat(document.getElementById('hard-hb').value) || 0;
} else if (source === 'hrc') {
const HRC = parseFloat(document.getElementById('hard-hrc').value) || 0;
HB = (HRC + 9.8) / 0.0338;
document.getElementById('hard-hb').value = Math.round(HB);
} else if (source === 'hv') {
const HV = parseFloat(document.getElementById('hard-hv').value) || 0;
HB = HV / 0.95;
document.getElementById('hard-hb').value = Math.round(HB);
}
if (HB === 0) return;
const HRC = HB * 0.0338 - 9.8;
const HV = HB * 0.95;
const fu = HB * 10;
const fy = fu * 0.7;
if (source !== 'hrc') document.getElementById('hard-hrc').value = HRC.toFixed(1);
if (source !== 'hv') document.getElementById('hard-hv').value = Math.round(HV);
document.getElementById('hardness-result').innerHTML = `
Conversão de Dureza
HB (Brinell)
${Math.round(HB)}
HRC (Rockwell C)
${HRC.toFixed(1)}
HV (Vickers)
${Math.round(HV)}
Estimativa de Resistência:
fu ≈ ${fu.toFixed(0)} MPa
fy ≈ ${fy.toFixed(0)} MPa (aproximado)
`;
}
// Charpy Analysis
function analisarCharpy() {
const temps = [
parseFloat(document.getElementById('charpy-t1').value),
parseFloat(document.getElementById('charpy-t2').value),
parseFloat(document.getElementById('charpy-t3').value),
parseFloat(document.getElementById('charpy-t4').value)
];
const energies = [
parseFloat(document.getElementById('charpy-e1').value),
parseFloat(document.getElementById('charpy-e2').value),
parseFloat(document.getElementById('charpy-e3').value),
parseFloat(document.getElementById('charpy-e4').value)
];
const validPoints = temps.map((t, i) => ({ temp: t, energy: energies[i] }))
.filter(p => !isNaN(p.temp) && !isNaN(p.energy))
.sort((a, b) => a.temp - b.temp);
if (validPoints.length < 2) {
alert('Insira pelo menos 2 pontos válidos');
return;
}
let ttdf = null;
for (let i = 0; i < validPoints.length - 1; i++) {
if ((validPoints[i].energy >= 27 && validPoints[i+1].energy < 27) ||
(validPoints[i].energy < 27 && validPoints[i+1].energy >= 27)) {
const t1 = validPoints[i].temp;
const e1 = validPoints[i].energy;
const t2 = validPoints[i+1].temp;
const e2 = validPoints[i+1].energy;
ttdf = t1 + (27 - e1) * (t2 - t1) / (e2 - e1);
break;
}
}
document.getElementById('charpy-result').innerHTML = `
Curva de Transição Dúctil-Frágil
${ttdf !== null ? `
TTDF (Temperatura de Transição): ${ttdf.toFixed(1)}°C
Temperatura onde a energia de impacto = 27J
` : `
Não foi possível calcular TTDF
A curva não intercepta 27J no intervalo medido
`}
`;
if (currentChart) {
currentChart.destroy();
}
const ctx = document.getElementById('charpy-chart').getContext('2d');
currentChart = new Chart(ctx, {
type: 'line',
data: {
labels: validPoints.map(p => p.temp + '°C'),
datasets: [{
label: 'Energia (J)',
data: validPoints.map(p => p.energy),
borderColor: '#1FB8CD',
backgroundColor: '#1FB8CD40',
tension: 0.4,
fill: true
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: 'Curva de Transição Charpy'
}
},
scales: {
y: {
title: {
display: true,
text: 'Energia (J)'
}
},
x: {
title: {
display: true,
text: 'Temperatura (°C)'
}
}
}
}
});
addToHistory('Análise Charpy', `${validPoints.length} pontos, TTDF = ${ttdf ? ttdf.toFixed(1) : 'N/A'}°C`);
}
// Certificate Checklist
function gerarChecklistCertificado() {
const norm = document.getElementById('cert-norm').value;
const requirements = certRequirements[norm] || [];
document.getElementById('cert-result').innerHTML = `
Checklist de Requisitos - ${norm.toUpperCase().replace('_', ' ')}
${requirements.map((req, index) => `
${req}
`).join('')}
`;
}
// Paint Area Calculation
function updatePaintFields() {
const type = document.getElementById('paint-type').value;
const field3 = document.getElementById('paint-field3');
if (type === 'chapa') {
document.getElementById('paint-label1').textContent = 'Comprimento (mm)';
document.getElementById('paint-label2').textContent = 'Largura (mm)';
field3.style.display = 'none';
} else if (type === 'perfilW') {
document.getElementById('paint-label1').textContent = 'Comprimento (mm)';
document.getElementById('paint-label2').textContent = 'Altura (mm)';
field3.style.display = 'none';
} else if (type === 'tubo') {
document.getElementById('paint-label1').textContent = 'Comprimento (mm)';
document.getElementById('paint-label2').textContent = 'Diâmetro (mm)';
field3.style.display = 'none';
} else if (type === 'rhs') {
document.getElementById('paint-label1').textContent = 'Comprimento (mm)';
document.getElementById('paint-label2').textContent = 'Largura (mm)';
document.getElementById('paint-label3').textContent = 'Altura (mm)';
field3.style.display = 'block';
}
}
function calcularAreaPintura() {
const type = document.getElementById('paint-type').value;
const dim1 = parseFloat(document.getElementById('paint-dim1').value) || 0;
const dim2 = parseFloat(document.getElementById('paint-dim2').value) || 0;
const dim3 = parseFloat(document.getElementById('paint-dim3').value) || 0;
const qty = parseInt(document.getElementById('paint-qty').value) || 1;
let area = 0;
if (type === 'chapa') {
area = (dim1 * dim2 * 2) / 1000000;
} else if (type === 'perfilW') {
const perimeter = dim2 * 3.5;
area = (perimeter * dim1) / 1000000;
} else if (type === 'tubo') {
area = (Math.PI * dim2 * dim1) / 1000000;
} else if (type === 'rhs') {
const perimeter = 2 * (dim2 + dim3);
area = (perimeter * dim1) / 1000000;
}
const totalArea = area * qty;
document.getElementById('paint-area-result').innerHTML = `
Área de Pintura
Área Unitária
${area.toFixed(2)} m²
Área Total
${totalArea.toFixed(2)} m²
`;
document.getElementById('tinta-area').value = totalArea.toFixed(2);
addToHistory('Área de Pintura', `${totalArea.toFixed(2)}m² (${qty} unidades)`);
}
// Paint Consumption
function calcularConsumoTinta() {
const area = parseFloat(document.getElementById('tinta-area').value) || 0;
const dft = parseFloat(document.getElementById('tinta-dft').value) || 0;
const solids = parseFloat(document.getElementById('tinta-solids').value) || 0;
const loss = parseFloat(document.getElementById('tinta-loss').value) || 0;
const coats = parseInt(document.getElementById('tinta-coats').value) || 1;
const cost = parseFloat(document.getElementById('tinta-cost').value) || 0;
const volumeTheoretical = (dft * area) / (1000 * (solids / 100));
const volumeWithLoss = volumeTheoretical / (1 - loss / 100);
const volumePerCoat = volumeWithLoss;
const volumeTotal = volumePerCoat * coats;
const totalCost = volumeTotal * cost;
document.getElementById('tinta-result').innerHTML = `
Consumo de Tinta
Volume Teórico
${volumeTheoretical.toFixed(2)} L
Volume por Demão
${volumePerCoat.toFixed(2)} L
Volume Total
${volumeTotal.toFixed(2)} L
Custo Total
R$ ${totalCost.toFixed(2)}
Recomendação de embalagens:
${volumeTotal > 20 ? `Galões 20L: ${Math.ceil(volumeTotal / 20)} unidades` :
volumeTotal > 5 ? `Galões 5L: ${Math.ceil(volumeTotal / 5)} unidades` :
`Latas 1L: ${Math.ceil(volumeTotal)} unidades`}
`;
addToHistory('Consumo de Tinta', `${volumeTotal.toFixed(2)}L para ${area}m²`);
}
// Galvanization
function calcularGalvanizacao() {
const env = document.getElementById('galv-env').value;
const area = parseFloat(document.getElementById('galv-area').value) || 0;
const thickness = parseFloat(document.getElementById('galv-thickness').value) || 85;
const corrosionRate = {
'interno': 0.5,
'urbano': 1.5,
'marinho': 3.0,
'industrial': 2.5
};
const rate = corrosionRate[env] || 1.5;
const lifeYears = thickness / rate;
const zincDensity = 7140;
const zincMass = (area * thickness / 1000) * (zincDensity / 1000000);
const zincCost = zincMass * 12;
document.getElementById('galv-result').innerHTML = `
Galvanização a Quente
Taxa de Corrosão
${rate.toFixed(1)} μm/ano
Vida Útil Estimada
${lifeYears.toFixed(0)} anos
Consumo de Zinco
${zincMass.toFixed(2)} kg
Custo Estimado
R$ ${(zincCost + area * 180).toFixed(2)}
Normas aplicáveis: ASTM A123, ISO 1461, NBR 6323
`;
addToHistory('Galvanização', `${area}m², ${thickness}μm → ${lifeYears.toFixed(0)} anos`);
}
// Budget
function adicionarItemOrcamento() {
const type = document.getElementById('budget-type').value;
const spec = document.getElementById('budget-spec').value;
const qty = parseFloat(document.getElementById('budget-qty').value) || 0;
const unit = document.getElementById('budget-unit').value;
const price = parseFloat(document.getElementById('budget-price').value) || 0;
if (!spec || qty <= 0 || price <= 0) {
alert('Preencha todos os campos corretamente');
return;
}
const item = {
id: Date.now(),
type: type,
spec: spec,
qty: qty,
unit: unit,
price: price,
total: qty * price
};
appState.budgetItems.push(item);
atualizarTabelaOrcamento();
document.getElementById('budget-spec').value = '';
document.getElementById('budget-qty').value = '1';
document.getElementById('budget-price').value = '0';
}
function removerItemOrcamento(id) {
appState.budgetItems = appState.budgetItems.filter(item => item.id !== id);
atualizarTabelaOrcamento();
}
function atualizarTabelaOrcamento() {
const tbody = document.getElementById('budget-tbody');
if (appState.budgetItems.length === 0) {
tbody.innerHTML = 'Nenhum item adicionado ';
} else {
tbody.innerHTML = appState.budgetItems.map(item => `
${item.type}
${item.spec}
${item.qty.toFixed(2)}
${item.unit}
R$ ${item.price.toFixed(2)}
R$ ${item.total.toFixed(2)}
Remover
`).join('');
}
atualizarTotalOrcamento();
}
function atualizarTotalOrcamento() {
const subtotal = appState.budgetItems.reduce((sum, item) => sum + item.total, 0);
const bdi = parseFloat(document.getElementById('budget-bdi').value) || 0;
const total = subtotal * (1 + bdi / 100);
const resultDiv = document.getElementById('budget-total');
resultDiv.innerHTML = `
Subtotal
R$ ${subtotal.toFixed(2)}
BDI (${bdi}%)
R$ ${(total - subtotal).toFixed(2)}
TOTAL GERAL
R$ ${total.toFixed(2)}
`;
}
// Weight & Rigging
function updateWeightFields() {
const type = document.getElementById('weight-type').value;
const field3 = document.getElementById('weight-field3');
const field4 = document.getElementById('weight-field4');
if (type === 'perfilW') {
document.getElementById('weight-label1').textContent = 'Altura (mm)';
document.getElementById('weight-label2').textContent = 'Comprimento (m)';
field3.style.display = 'none';
field4.style.display = 'none';
} else if (type === 'chapa') {
document.getElementById('weight-label1').textContent = 'Largura (mm)';
document.getElementById('weight-label2').textContent = 'Altura (mm)';
document.getElementById('weight-label3').textContent = 'Espessura (mm)';
field3.style.display = 'block';
field4.style.display = 'none';
} else if (type === 'tubo') {
document.getElementById('weight-label1').textContent = 'Diâmetro Externo (mm)';
document.getElementById('weight-label2').textContent = 'Comprimento (m)';
document.getElementById('weight-label3').textContent = 'Espessura Parede (mm)';
field3.style.display = 'block';
field4.style.display = 'none';
} else if (type === 'barra') {
document.getElementById('weight-label1').textContent = 'Diâmetro (mm)';
document.getElementById('weight-label2').textContent = 'Comprimento (m)';
field3.style.display = 'none';
field4.style.display = 'none';
}
}
function calcularPeso() {
const type = document.getElementById('weight-type').value;
const dim1 = parseFloat(document.getElementById('weight-dim1').value) || 0;
const dim2 = parseFloat(document.getElementById('weight-dim2').value) || 0;
const dim3 = parseFloat(document.getElementById('weight-dim3').value) || 0;
let weight = 0;
if (type === 'perfilW') {
weight = (dim1 / 100) * 31.8 * dim2;
} else if (type === 'chapa') {
weight = (dim1 / 1000) * (dim2 / 1000) * (dim3 / 1000) * 7850;
} else if (type === 'tubo') {
const dExt = dim1;
const dInt = dExt - 2 * dim3;
const area = Math.PI * (Math.pow(dExt/2, 2) - Math.pow(dInt/2, 2));
weight = area / 1000000 * dim2 * 7850;
} else if (type === 'barra') {
const area = Math.PI * Math.pow(dim1/2, 2);
weight = area / 1000000 * dim2 * 7850;
}
document.getElementById('weight-result').innerHTML = `
Cálculo de Peso
Peso Total
${weight.toFixed(2)} kg
`;
document.getElementById('rigging-weight').value = weight.toFixed(0);
addToHistory('Cálculo de Peso', `${weight.toFixed(2)}kg`);
}
function calcularRigging() {
const weight = parseFloat(document.getElementById('rigging-weight').value) || 0;
const points = parseInt(document.getElementById('rigging-points').value) || 2;
const angle = parseInt(document.getElementById('rigging-angle').value) || 60;
const fs = parseFloat(document.getElementById('rigging-fs').value) || 4;
const angleRad = angle * Math.PI / 180;
const forcePerCable = (weight * 9.81 / 1000) / (points * Math.cos(angleRad)) * fs;
const cableSteelCapacity = 21;
const chainCapacity = 15;
const syntheticCapacity = 12;
let recommendation = '';
let alertClass = 'alert-success';
if (forcePerCable <= syntheticCapacity) {
recommendation = '✅ Cabo sintético (12 kN)';
} else if (forcePerCable <= chainCapacity) {
recommendation = '✅ Corrente grau 80 (15 kN)';
} else if (forcePerCable <= cableSteelCapacity) {
recommendation = '⚠️ Cabo de aço (21 kN)';
alertClass = 'alert-warning';
} else {
recommendation = '❌ Requer cabo especial ou mais pontos de içamento';
alertClass = 'alert-error';
}
document.getElementById('rigging-result').innerHTML = `
Plano de Rigging
Força por Cabo
${forcePerCable.toFixed(1)} kN
Recomendação: ${recommendation}
Capacidades de Referência:
• Cabo sintético: 12 kN
• Corrente grau 80: 15 kN
• Cabo de aço: 21 kN
`;
addToHistory('Rigging', `${weight}kg, ${points} pontos, ${angle}° → ${forcePerCable.toFixed(1)}kN/cabo`);
}
// ========================================
// INITIALIZATION
// ========================================
document.addEventListener('DOMContentLoaded', function() {
showSection('cev');
mostrarEquivalencias();
gerarChecklistCertificado();
updatePaintFields();
updateWeightFields();
});