import { parseAgeMonths, formatParsedAge } from './calculators/age.js';
import { calculateAapBilirubinAssessment, classifyBhutaniRisk, loadAapBilirubinThresholds, loadBhutaniZones } from './calculators/bilirubin.js';
import { calculateBMIAssessment, loadBmiLmsData } from './calculators/bmi.js';
import { calculateBPAssessment, computeBPPercentile, loadBpReferenceData } from './calculators/bp.js';
import { calculateBsa, calculateWeightBasedDose } from './calculators/dosing.js';
import { loadEquipmentData, renderEquipmentResult } from './calculators/equipment.js';
import { calculateGcs } from './calculators/gcs.js';
import { calcMidParentalHeight, calcZFromLMS, interpolateLMS, loadGrowthLmsData, normalCDF, renderGrowthResult, valueFromLMS } from './calculators/growth.js';
import { initImageLightbox } from './calculators/lightbox.js';
import { loadResusMedsData, renderResusMeds } from './calculators/resus.js';
import { loadVitalsData, renderVitalsResult } from './calculators/vitals.js';

// ============================================================
// CALCULATORS.JS — Pediatric clinical calculators
// ============================================================

initImageLightbox();
  var _inited = false;
  document.addEventListener('tabChanged', function(e) {
    if (e.detail.tab !== 'calculators' || _inited) return;
    _inited = true;
    initCalculators();
  });

  // Expose for debugging / other modules
  window._parseAgeMonths = parseAgeMonths;

  function initCalculators() {
    // ── Top-level pill navigation (scoped to .calc-nav-pill only) ──
    function activateCalcPill(key) {
      var pill = document.querySelector('.calc-nav-pill[data-calc="' + key + '"]');
      if (!pill) return false;
      document.querySelectorAll('.calc-nav-pill').forEach(function(p) { p.classList.remove('active'); });
      document.querySelectorAll('.calc-panel').forEach(function(p) { p.classList.add('hidden'); });
      pill.classList.add('active');
      var panel = document.getElementById('calc-' + key);
      if (panel) panel.classList.remove('hidden');
      return true;
    }
    document.querySelectorAll('.calc-nav-pill').forEach(function(pill) {
      pill.addEventListener('click', function() {
        activateCalcPill(pill.dataset.calc);
        if (window.UIState) window.UIState.set('calc.pill', pill.dataset.calc);
      });
    });
    // Restore the last-selected pill across page reloads / sign-out-sign-in.
    if (window.UIState) {
      var saved = window.UIState.get('calc.pill');
      if (saved) activateCalcPill(saved);
    }

    // ═══════════════════════════════════════════════════════════
    // BP PERCENTILE — BCM/Rosner Quantile Spline Regression
    // AAP 2017 classification with exact percentile computation
    // ═══════════════════════════════════════════════════════════

    // BP reference tables load from /data/calculators/bp-reference.json.

    var classLabels = {
      normal: { text: 'Normal', color: '#10b981', bg: '#d1fae5' },
      elevated: { text: 'Elevated', color: '#f59e0b', bg: '#fef3c7' },
      stage1: { text: 'Stage 1 Hypertension', color: '#f97316', bg: '#ffedd5' },
      stage2: { text: 'Stage 2 Hypertension', color: '#ef4444', bg: '#fee2e2' }
    };

    var BP_REFERENCE = null;
    loadBpReferenceData()
      .then(function(data) { BP_REFERENCE = data; })
      .catch(function(err) { console.warn('[calculators] BP reference data unavailable:', err && err.message); });

    document.getElementById('btn-calc-bp').addEventListener('click', function() {
      var age = parseFloat(document.getElementById('bp-age').value);
      var sex = document.getElementById('bp-sex').value;
      var sys = parseFloat(document.getElementById('bp-systolic').value);
      var dia = parseFloat(document.getElementById('bp-diastolic').value);
      var heightCm = parseFloat(document.getElementById('bp-height').value);

      if (!age || !sex || !sys || !dia || !heightCm) { showToast('Fill in all fields including height', 'error'); return; }
      if (age < 1 || age > 17) { showToast('Age must be 1-17 years', 'error'); return; }
      if (!BP_REFERENCE) { showToast('BP percentile data is still loading.', 'info'); return; }

      var assessment = calculateBPAssessment(BP_REFERENCE, age, sex, heightCm, sys, dia);
      var htResult = assessment.height;
      var sysResult = assessment.systolic;
      var diaResult = assessment.diastolic;
      var bpClass = assessment.classification;

      // Get 50th, 90th, 95th predicted values for display
      var sys50 = sysResult.predicted[49].toFixed(0), sys90 = sysResult.predicted[89].toFixed(0), sys95 = sysResult.predicted[94].toFixed(0);
      var dia50 = diaResult.predicted[49].toFixed(0), dia90 = diaResult.predicted[89].toFixed(0), dia95 = diaResult.predicted[94].toFixed(0);

      var cl = classLabels[bpClass.classification];
      var resultDiv = document.getElementById('bp-result');
      resultDiv.classList.remove('hidden');
      resultDiv.innerHTML =
        '<div class="calc-result-header" style="background:' + cl.bg + ';border-color:' + cl.color + ';">' +
          '<div style="font-size:18px;font-weight:700;color:' + cl.color + ';">' + cl.text + '</div>' +
          '<div style="font-size:13px;color:var(--g600);margin-top:4px;">' + sys + '/' + dia + ' mmHg &mdash; ' + age + ' year old ' + sex + ', ' + heightCm + ' cm</div>' +
        '</div>' +
        '<div class="calc-result-grid">' +
          '<div class="calc-result-item">' +
            '<div class="calc-result-label">Systolic</div>' +
            '<div class="calc-result-value">' + sysResult.percentile + '<span style="font-size:12px;">th %ile</span></div>' +
            '<div class="calc-result-sub" style="color:' + classLabels[bpClass.sysClass].color + ';">' + classLabels[bpClass.sysClass].text + '</div>' +
          '</div>' +
          '<div class="calc-result-item">' +
            '<div class="calc-result-label">Diastolic</div>' +
            '<div class="calc-result-value">' + diaResult.percentile + '<span style="font-size:12px;">th %ile</span></div>' +
            '<div class="calc-result-sub" style="color:' + classLabels[bpClass.diaClass].color + ';">' + classLabels[bpClass.diaClass].text + '</div>' +
          '</div>' +
          '<div class="calc-result-item">' +
            '<div class="calc-result-label">Height</div>' +
            '<div class="calc-result-value">' + Math.round(htResult.percentile) + '<span style="font-size:12px;">th %ile</span></div>' +
            '<div class="calc-result-sub">Z = ' + htResult.z.toFixed(2) + '</div>' +
          '</div>' +
          '<div class="calc-result-item">' +
            '<div class="calc-result-label">Reference (50th %ile)</div>' +
            '<div class="calc-result-value" style="font-size:16px;">' + sys50 + '/' + dia50 + '</div>' +
            '<div class="calc-result-sub">90th: ' + sys90 + '/' + dia90 + ' &bull; 95th: ' + sys95 + '/' + dia95 + '</div>' +
          '</div>' +
        '</div>' +
        '<p style="font-size:11px;color:var(--g400);margin:12px 0 0;">BCM/Rosner quantile regression percentiles adjusted to patient height. AAP 2017 classification.</p>';

      // Generate BP curves across ages 1-17 for this sex/height
      renderBPChart(age, sex, heightCm, sys, dia);
    });


    function renderBPChart(patientAge, sex, heightCm, patientSys, patientDia) {
      if (typeof Chart === 'undefined') return;
      var container = document.getElementById('bp-chart-container');
      if (!container) return;
      container.classList.remove('hidden');
      if (_chartInstances['bp-chart-canvas']) { _chartInstances['bp-chart-canvas'].destroy(); _chartInstances['bp-chart-canvas'] = null; }

      // Generate percentile curves across ages 1-17
      var sys50 = [], sys90 = [], sys95 = [], dia50 = [], dia90 = [], dia95 = [];
      for (var a = 1; a <= 17; a++) {
        var sr = computeBPPercentile(BP_REFERENCE, a, sex, heightCm, 'sys', 100);
        var dr = computeBPPercentile(BP_REFERENCE, a, sex, heightCm, 'dia', 60);
        sys50.push({x:a, y:Math.round(sr.predicted[49])}); sys90.push({x:a, y:Math.round(sr.predicted[89])}); sys95.push({x:a, y:Math.round(sr.predicted[94])});
        dia50.push({x:a, y:Math.round(dr.predicted[49])}); dia90.push({x:a, y:Math.round(dr.predicted[89])}); dia95.push({x:a, y:Math.round(dr.predicted[94])});
      }

      var datasets = [
        { label: 'SBP 95th', data: sys95, borderColor: 'rgba(239,68,68,0.7)', borderWidth: 2, borderDash: [4,4], pointRadius: 0, fill: false, tension: 0.3 },
        { label: 'SBP 90th', data: sys90, borderColor: 'rgba(249,115,22,0.6)', borderWidth: 1.5, borderDash: [3,3], pointRadius: 0, fill: false, tension: 0.3 },
        { label: 'SBP 50th', data: sys50, borderColor: 'rgba(16,185,129,0.8)', borderWidth: 2, pointRadius: 0, fill: false, tension: 0.3 },
        { label: 'DBP 95th', data: dia95, borderColor: 'rgba(239,68,68,0.4)', borderWidth: 1.5, borderDash: [4,4], pointRadius: 0, fill: false, tension: 0.3 },
        { label: 'DBP 90th', data: dia90, borderColor: 'rgba(249,115,22,0.3)', borderWidth: 1, borderDash: [3,3], pointRadius: 0, fill: false, tension: 0.3 },
        { label: 'DBP 50th', data: dia50, borderColor: 'rgba(16,185,129,0.4)', borderWidth: 1.5, pointRadius: 0, fill: false, tension: 0.3 },
        { label: 'Patient SBP', data: [{x:patientAge, y:patientSys}], backgroundColor: '#2563eb', borderColor: '#1d4ed8', pointRadius: 8, pointHoverRadius: 10, showLine: false, type: 'scatter', order: 0 },
        { label: 'Patient DBP', data: [{x:patientAge, y:patientDia}], backgroundColor: '#7c3aed', borderColor: '#6d28d9', pointRadius: 8, pointHoverRadius: 10, pointStyle: 'triangle', showLine: false, type: 'scatter', order: 0 }
      ];

      _chartInstances['bp-chart-canvas'] = new Chart(document.getElementById('bp-chart-canvas'), {
        type: 'line',
        data: { datasets: datasets },
        options: {
          responsive: true, maintainAspectRatio: true, aspectRatio: 1.6,
          animation: { duration: 300 },
          scales: {
            x: { type: 'linear', title: { display: true, text: 'Age (years)', font: { size: 12, weight: 'bold' } }, min: 1, max: 17, ticks: { stepSize: 1 }, grid: { color: 'rgba(0,0,0,0.05)' } },
            y: { title: { display: true, text: 'Blood Pressure (mmHg)', font: { size: 12, weight: 'bold' } }, min: 30, grid: { color: 'rgba(0,0,0,0.05)' } }
          },
          plugins: {
            legend: { display: true, position: 'bottom', labels: { font: { size: 10 }, usePointStyle: true } },
            tooltip: {
              callbacks: {
                label: function(ctx) {
                  if (ctx.dataset.label.indexOf('Patient') !== -1) return ctx.dataset.label + ': ' + ctx.parsed.y + ' mmHg';
                  return ctx.dataset.label + ': ' + ctx.parsed.y + ' mmHg at age ' + ctx.parsed.x;
                }
              }
            }
          }
        }
      });
    }

    document.getElementById('btn-clear-bp').addEventListener('click', function() {
      ['bp-age', 'bp-sex', 'bp-height', 'bp-systolic', 'bp-diastolic'].forEach(function(id) {
        var el = document.getElementById(id); if (el) el.value = '';
      });
      document.getElementById('bp-result').classList.add('hidden');
      var bpc = document.getElementById('bp-chart-container'); if (bpc) bpc.classList.add('hidden');
    });

    // ═══════════════════════════════════════════════════════════
    // BMI PERCENTILE — CDC 2000
    // ═══════════════════════════════════════════════════════════

    // CDC BMI-for-age LMS values load from /data/calculators/bmi-lms.json.

    // ═══════════════════════════════════════════════════════════
    // CHART.JS GROWTH CURVE RENDERING
    // ═══════════════════════════════════════════════════════════

    // 7-line reference set: 3, 10, 25, 50, 75, 90, 97.
    // Drops 5th and 95th (they crowded the 3rd/10th and 90th/97th labels
    // on mobile without adding clinical value — 3rd/97th are the
    // abnormal-threshold lines, the inner percentiles give the trend).
    var PERCENTILE_LINES = [
      { p: 3,  z: -1.88079, color: 'rgba(220,38,38,0.9)',   dash: [6,4], width: 1.4 },  // red
      { p: 10, z: -1.28155, color: 'rgba(234,88,12,0.9)',   dash: [],    width: 1.2 },  // orange
      { p: 25, z: -0.67449, color: 'rgba(202,138,4,0.85)',  dash: [],    width: 1.1 },  // amber
      { p: 50, z: 0,        color: 'rgba(22,163,74,0.95)',  dash: [],    width: 2.4 },  // green (center)
      { p: 75, z: 0.67449,  color: 'rgba(37,99,235,0.85)',  dash: [],    width: 1.1 },  // blue
      { p: 90, z: 1.28155,  color: 'rgba(124,58,237,0.9)',  dash: [],    width: 1.2 },  // violet
      { p: 97, z: 1.88079,  color: 'rgba(219,39,119,0.9)',  dash: [6,4], width: 1.4 }   // pink
    ];

    function generatePercentileCurves(lmsTable, percentileLines) {
      var ages = Object.keys(lmsTable).map(Number).sort(function(a,b){return a-b;});
      var datasets = [];
      percentileLines.forEach(function(pl, idx) {
        var points = [];
        ages.forEach(function(age) {
          var lms = lmsTable[age];
          var val = valueFromLMS(lms.L, lms.M, lms.S, pl.z);
          if (isFinite(val) && val > 0) points.push({ x: age, y: Math.round(val * 100) / 100 });
        });
        datasets.push({
          label: pl.p + 'th',
          data: points,
          borderColor: pl.color,
          borderWidth: pl.width,
          borderDash: pl.dash,
          pointRadius: 0,
          pointHoverRadius: 0,
          fill: false,
          tension: 0.4,
          order: 10
        });
      });
      // Fill bands between symmetric percentile pairs.
      // Indexes: 3=0, 10=1, 25=2, 50=3, 75=4, 90=5, 97=6
      var fills = [
        { outer: 0, inner: 6, color: 'rgba(220,38,38,0.04)' },   // 3rd-97th (widest)
        { outer: 1, inner: 5, color: 'rgba(234,88,12,0.04)' },   // 10th-90th
        { outer: 2, inner: 4, color: 'rgba(163,163,163,0.05)' }  // 25th-75th (IQR)
      ];
      fills.forEach(function(f) {
        if (datasets[f.outer] && datasets[f.inner]) {
          datasets[f.outer].fill = { target: f.inner, above: f.color, below: f.color };
        }
      });
      return datasets;
    }

    var _chartInstances = {};

    // Chart.js plugin — labels each percentile curve at its right edge,
    // spreads labels bidirectionally so top (97/95/90) and bottom (3/5/10)
    // clusters both breathe, draws a thin leader from each label to the
    // actual curve position when nudged, then repaints the patient dot
    // ON TOP so it's never obscured by a label.
    var percentileLabelPlugin = {
      id: 'percentileLabels',
      afterDatasetsDraw: function(chart) {
        var ctx = chart.ctx;
        var font = '700 11px -apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif';
        var MIN_GAP = 15;

        // Collect percentile-line labels with native screen positions
        var items = [];
        chart.data.datasets.forEach(function(ds, i) {
          if (!ds.label || !/^\d+th$/.test(ds.label)) return;
          var meta = chart.getDatasetMeta(i);
          if (!meta || meta.hidden || !meta.data || !meta.data.length) return;
          var last = meta.data[meta.data.length - 1];
          if (!last) return;
          items.push({ label: ds.label, color: ds.borderColor, x: last.x, nativeY: last.y, targetY: last.y });
        });
        // Top to bottom (ascending canvas Y)
        items.sort(function(a, b) { return a.nativeY - b.nativeY; });

        // Forward pass — push items DOWN when crowded by item above
        for (var k = 1; k < items.length; k++) {
          if (items[k].targetY - items[k - 1].targetY < MIN_GAP) {
            items[k].targetY = items[k - 1].targetY + MIN_GAP;
          }
        }
        // Backward pass — pull items UP when still crowded by item below
        for (var j = items.length - 2; j >= 0; j--) {
          if (items[j + 1].targetY - items[j].targetY < MIN_GAP) {
            items[j].targetY = items[j + 1].targetY - MIN_GAP;
          }
        }
        // Clamp to chart area so nothing clips top/bottom
        var area = chart.chartArea;
        if (area) {
          items.forEach(function(it) {
            if (it.targetY < area.top + 6) it.targetY = area.top + 6;
            if (it.targetY > area.bottom - 6) it.targetY = area.bottom - 6;
          });
        }

        // Draw leader + label
        items.forEach(function(it) {
          var stroke = typeof it.color === 'string' ? it.color : '#555';
          var m = stroke.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
          if (m) stroke = 'rgb(' + m[1] + ',' + m[2] + ',' + m[3] + ')';
          ctx.save();
          // Leader — only if the label was nudged away from its native Y
          if (Math.abs(it.targetY - it.nativeY) > 2) {
            ctx.strokeStyle = stroke;
            ctx.globalAlpha = 0.55;
            ctx.lineWidth = 0.8;
            ctx.beginPath();
            ctx.moveTo(it.x, it.nativeY);
            ctx.lineTo(it.x + 4, it.targetY);
            ctx.stroke();
            ctx.globalAlpha = 1;
          }
          // Label with white halo
          ctx.font = font;
          ctx.textAlign = 'left';
          ctx.textBaseline = 'middle';
          ctx.lineWidth = 3;
          ctx.strokeStyle = 'rgba(255,255,255,0.92)';
          ctx.strokeText(it.label, it.x + 5, it.targetY);
          ctx.fillStyle = stroke;
          ctx.fillText(it.label, it.x + 5, it.targetY);
          ctx.restore();
        });

        // Repaint the patient dot ON TOP of the labels so it's never
        // covered by a nearby percentile label.
        chart.data.datasets.forEach(function(ds, i) {
          if (ds.label !== 'Patient') return;
          var meta = chart.getDatasetMeta(i);
          if (!meta || !meta.data || !meta.data.length) return;
          var pt = meta.data[0];
          if (!pt) return;
          ctx.save();
          ctx.beginPath();
          ctx.arc(pt.x, pt.y, 5, 0, Math.PI * 2);
          ctx.fillStyle = '#2563eb';
          ctx.fill();
          ctx.lineWidth = 1.8;
          ctx.strokeStyle = '#ffffff';
          ctx.stroke();
          ctx.restore();
        });
      }
    };

    function renderGrowthChart(canvasId, containerId, lmsTable, patientX, patientY, xLabel, yLabel, extraDatasets) {
      if (typeof Chart === 'undefined') return; // Chart.js not loaded yet

      var container = document.getElementById(containerId);
      if (!container) return;
      container.classList.remove('hidden');

      if (_chartInstances[canvasId]) { _chartInstances[canvasId].destroy(); _chartInstances[canvasId] = null; }

      var datasets = generatePercentileCurves(lmsTable, PERCENTILE_LINES);

      // Patient data point — drawn on top of every curve. White ring so
      // it reads cleanly even when it lands directly on a colored line.
      datasets.push({
        label: 'Patient',
        data: [{ x: patientX, y: patientY }],
        backgroundColor: '#2563eb',
        borderColor: '#ffffff',
        borderWidth: 1.8,
        pointRadius: 5,
        pointHoverRadius: 7,
        pointStyle: 'circle',
        showLine: false,
        type: 'scatter',
        order: -1
      });

      if (extraDatasets) extraDatasets.forEach(function(d) { datasets.push(d); });

      _chartInstances[canvasId] = new Chart(document.getElementById(canvasId), {
        type: 'line',
        data: { datasets: datasets },
        plugins: [percentileLabelPlugin],
        options: {
          responsive: true,
          maintainAspectRatio: true,
          aspectRatio: 1.6,
          animation: { duration: 300 },
          // Leave ~28px of padding on the right so the percentile labels
          // ("50th", "95th", ...) drawn past the last data point are not
          // clipped by the canvas edge.
          layout: { padding: { right: 30 } },
          scales: {
            x: {
              type: 'linear',
              title: { display: true, text: xLabel, font: { size: 12, weight: 'bold' } },
              grid: { color: 'rgba(0,0,0,0.05)' }
            },
            y: {
              title: { display: true, text: yLabel, font: { size: 12, weight: 'bold' } },
              grid: { color: 'rgba(0,0,0,0.05)' }
            }
          },
          plugins: {
            legend: { display: false },
            tooltip: {
              callbacks: {
                label: function(ctx) {
                  if (ctx.dataset.label === 'Patient') return 'Patient: ' + ctx.parsed.y.toFixed(1) + ' ' + yLabel.split('(').pop().replace(')', '');
                  return ctx.dataset.label + ': ' + ctx.parsed.y.toFixed(1);
                }
              }
            }
          }
        }
      });
    }

    var BMI_LMS = null;
    loadBmiLmsData()
      .then(function(data) { BMI_LMS = data; })
      .catch(function(err) { console.warn('[calculators] BMI LMS data unavailable:', err && err.message); });

    document.getElementById('btn-calc-bmi').addEventListener('click', function() {
      var ageYr = parseFloat(document.getElementById('bmi-age-yr').value) || 0;
      var ageMo = parseInt(document.getElementById('bmi-age-mo').value) || 0;
      var age = ageYr + ageMo / 12;
      var sex = document.getElementById('bmi-sex').value;
      var weight = parseFloat(document.getElementById('bmi-weight').value);
      var height = parseFloat(document.getElementById('bmi-height').value);

      if (!age || !sex || !weight || !height) { showToast('Fill in all fields', 'error'); return; }
      if (age < 2 || age > 20) { showToast('Age must be 2-20 years', 'error'); return; }
      if (!BMI_LMS) { showToast('BMI percentile data is still loading.', 'info'); return; }

      var assessment = calculateBMIAssessment(BMI_LMS, sex, age, weight, height);
      var bmi = assessment.bmi;
      var ageMonths = assessment.ageMonths;
      var result = { percentile: assessment.percentile, z: assessment.z };
      var cl = assessment.classification;

      var extendedInfo = '';
      if (result.percentile >= 85) {
        extendedInfo = '<div class="calc-result-item"><div class="calc-result-label">% of 95th</div><div class="calc-result-value" style="font-size:16px;">' + cl.pctOf95.toFixed(0) + '%</div><div class="calc-result-sub">95th = ' + cl.bmi95.toFixed(1) + ' kg/m&sup2;</div></div>';
      }

      var resultDiv = document.getElementById('bmi-result');
      resultDiv.classList.remove('hidden');
      resultDiv.innerHTML =
        '<div class="calc-result-header" style="background:' + cl.bg + ';border-color:' + cl.color + ';">' +
          '<div style="font-size:18px;font-weight:700;color:' + cl.color + ';">' + cl.text + '</div>' +
          '<div style="font-size:13px;color:var(--g600);margin-top:4px;">BMI ' + bmi.toFixed(1) + ' kg/m&sup2; &mdash; ' + result.percentile + 'th percentile' + (cl.pctOf95 && result.percentile >= 95 ? ' (' + cl.pctOf95.toFixed(0) + '% of 95th)' : '') + '</div>' +
        '</div>' +
        '<div class="calc-result-grid">' +
          '<div class="calc-result-item"><div class="calc-result-label">BMI</div><div class="calc-result-value">' + bmi.toFixed(1) + '</div><div class="calc-result-sub">kg/m&sup2;</div></div>' +
          '<div class="calc-result-item"><div class="calc-result-label">Percentile</div><div class="calc-result-value">' + result.percentile + '<span style="font-size:12px;">th</span></div></div>' +
          '<div class="calc-result-item"><div class="calc-result-label">Z-Score</div><div class="calc-result-value" style="font-size:16px;">' + result.z.toFixed(2) + '</div></div>' +
          extendedInfo +
        '</div>';
      var bmiYears = {}; Object.keys(BMI_LMS[sex]).forEach(function(k) { bmiYears[+(k/12).toFixed(1)] = BMI_LMS[sex][k]; });
      renderGrowthChart('bmi-chart-canvas', 'bmi-chart-container', bmiYears, +(ageMonths/12).toFixed(1), bmi, 'Age (years)', 'BMI (kg/m2)');
    });

    document.getElementById('btn-clear-bmi').addEventListener('click', function() {
      ['bmi-age-yr', 'bmi-sex', 'bmi-weight', 'bmi-height'].forEach(function(id) {
        var el = document.getElementById(id); if (el) el.value = '';
      });
      document.getElementById('bmi-result').classList.add('hidden');
    });

    // ═══════════════════════════════════════════════════════════
    // BODY SURFACE AREA — Mosteller
    // ═══════════════════════════════════════════════════════════

    document.getElementById('btn-calc-bsa').addEventListener('click', function() {
      var weight = parseFloat(document.getElementById('bsa-weight').value);
      var height = parseFloat(document.getElementById('bsa-height').value);
      if (!weight || !height) { showToast('Enter weight and height', 'error'); return; }

      var bsa = calculateBsa(weight, height);
      var resultDiv = document.getElementById('bsa-result');
      resultDiv.classList.remove('hidden');
      resultDiv.innerHTML =
        '<div class="calc-result-header" style="background:#e0f2fe;border-color:#0ea5e9;">' +
          '<div style="font-size:18px;font-weight:700;color:#0369a1;">BSA: ' + bsa.toFixed(3) + ' m&sup2;</div>' +
          '<div style="font-size:13px;color:var(--g600);margin-top:4px;">' + weight + ' kg, ' + height + ' cm (Mosteller formula)</div>' +
        '</div>';
    });

    document.getElementById('btn-clear-bsa').addEventListener('click', function() {
      ['bsa-weight', 'bsa-height'].forEach(function(id) {
        var el = document.getElementById(id); if (el) el.value = '';
      });
      document.getElementById('bsa-result').classList.add('hidden');
    });

    // ═══════════════════════════════════════════════════════════
    // WEIGHT-BASED DOSING
    // ═══════════════════════════════════════════════════════════

    document.getElementById('btn-calc-dose').addEventListener('click', function() {
      var weight = parseFloat(document.getElementById('dose-weight').value);
      var dosePerKg = parseFloat(document.getElementById('dose-per-kg').value);
      var freq = parseInt(document.getElementById('dose-freq').value);
      var maxDose = parseFloat(document.getElementById('dose-max').value) || 0;
      var conc = parseFloat(document.getElementById('dose-conc').value) || 0;

      if (!weight || !dosePerKg) { showToast('Enter weight and dose', 'error'); return; }

      var dose = calculateWeightBasedDose(weight, dosePerKg, freq, maxDose, conc);
      var singleDose = dose.singleDose;
      var dailyDose = dose.dailyDose;
      var capped = dose.capped;

      var rows = '<div class="calc-result-item"><div class="calc-result-label">Single Dose</div><div class="calc-result-value">' + singleDose.toFixed(1) + ' <span style="font-size:12px;">mg</span></div>'
        + (capped ? '<div class="calc-result-sub" style="color:var(--red);">Capped at max ' + maxDose + ' mg</div>' : '') + '</div>'
        + '<div class="calc-result-item"><div class="calc-result-label">Daily Total</div><div class="calc-result-value">' + dailyDose.toFixed(1) + ' <span style="font-size:12px;">mg/day</span></div>'
        + '<div class="calc-result-sub">' + singleDose.toFixed(1) + ' mg x ' + freq + '/day</div></div>';

      if (conc > 0) {
        var vol = dose.volumeMl;
        rows += '<div class="calc-result-item"><div class="calc-result-label">Volume per Dose</div><div class="calc-result-value">' + vol.toFixed(1) + ' <span style="font-size:12px;">mL</span></div>'
          + '<div class="calc-result-sub">at ' + conc + ' mg/mL</div></div>';
      }

      var resultDiv = document.getElementById('dose-result');
      resultDiv.classList.remove('hidden');
      resultDiv.innerHTML =
        '<div class="calc-result-header" style="background:#ede9fe;border-color:#7c3aed;">' +
          '<div style="font-size:18px;font-weight:700;color:#6d28d9;">' + singleDose.toFixed(1) + ' mg per dose</div>' +
          '<div style="font-size:13px;color:var(--g600);margin-top:4px;">' + weight + ' kg x ' + dosePerKg + ' mg/kg' + (capped ? ' (max ' + maxDose + ' mg)' : '') + '</div>' +
        '</div>' +
        '<div class="calc-result-grid">' + rows + '</div>' +
        '<p style="font-size:11px;color:var(--g400);margin:12px 0 0;">Always verify dosing against your formulary and institutional guidelines.</p>';
    });

    document.getElementById('btn-clear-dose').addEventListener('click', function() {
      ['dose-weight', 'dose-per-kg', 'dose-max', 'dose-conc'].forEach(function(id) {
        var el = document.getElementById(id); if (el) el.value = '';
      });
      document.getElementById('dose-freq').value = '1';
      document.getElementById('dose-result').classList.add('hidden');
    });

    // ═══════════════════════════════════════════════════════════
    // GROWTH CHARTS — WHO 0-2y / CDC 2-20y + Fenton Preterm
    // ═══════════════════════════════════════════════════════════

    var currentGrowthType = 'wfa';

    // WHO/CDC/Fenton growth LMS values load from /data/calculators/growth-lms.json.
    var wfaLMS = null;
    var cdcWfaLMS = null;
    var lfaLMS = null;
    var cdcLfaLMS = null;
    var hcfaLMS = null;
    var wflLMS = null;
    var fentonLMS = null;

    loadGrowthLmsData()
      .then(function(data) {
        wfaLMS = data.wfaLMS;
        cdcWfaLMS = data.cdcWfaLMS;
        lfaLMS = data.lfaLMS;
        cdcLfaLMS = data.cdcLfaLMS;
        hcfaLMS = data.hcfaLMS;
        wflLMS = data.wflLMS;
        fentonLMS = data.fentonLMS;
      })
      .catch(function(err) { console.warn('[calculators] Growth LMS data unavailable:', err && err.message); });

    // Growth sub-pill navigation
    document.querySelectorAll('[data-growth]').forEach(function(pill) {
      pill.addEventListener('click', function() {
        document.querySelectorAll('[data-growth]').forEach(function(p) { p.classList.remove('active'); });
        pill.classList.add('active');
        currentGrowthType = pill.dataset.growth;
        var isFenton = currentGrowthType === 'fenton';
        var isHC = currentGrowthType === 'hcfa';
        var isWFL = currentGrowthType === 'wfl';
        document.getElementById('growth-age-field').style.display = (isFenton || isWFL) ? 'none' : '';
        document.getElementById('growth-ga-field').style.display = isFenton ? '' : 'none';
        document.getElementById('growth-hc-field').style.display = isHC ? '' : 'none';
        document.getElementById('growth-weight-field').style.display = isHC ? 'none' : '';
        document.getElementById('growth-length-field').style.display = (currentGrowthType === 'wfa') ? 'none' : (isFenton ? 'none' : '');
        var src = document.getElementById('growth-source-text');
        if (isFenton) src.textContent = 'Fenton 2013 preterm growth charts (22-50 weeks GA). Weight in grams.';
        else if (isHC) src.textContent = 'WHO head circumference-for-age (0-36 months).';
        else if (isWFL) src.textContent = 'WHO weight-for-length (45-110 cm, 0-2 years).';
        else src.textContent = 'WHO growth standards (0-2y) / CDC 2000 (2-20y).';
        document.getElementById('growth-result').classList.add('hidden');
        var chartContainer = document.getElementById('growth-chart-container');
        if (chartContainer) chartContainer.classList.add('hidden');
        if (_chartInstances['growth-chart-canvas']) { _chartInstances['growth-chart-canvas'].destroy(); _chartInstances['growth-chart-canvas'] = null; }
        // Show MPH section only for LFA
        var mphSection = document.getElementById('growth-mph-section');
        if (mphSection) mphSection.style.display = (currentGrowthType === 'lfa') ? '' : 'none';
      });
    });

    document.getElementById('btn-calc-growth').addEventListener('click', function() {
      var sex = document.getElementById('growth-sex').value;
      if (!sex) { showToast('Select sex', 'error'); return; }
      if (!wfaLMS || !cdcWfaLMS || !lfaLMS || !cdcLfaLMS || !hcfaLMS || !wflLMS || !fentonLMS) {
        showToast('Growth chart data is still loading.', 'info'); return;
      }

      var resultDiv = document.getElementById('growth-result');

      if (currentGrowthType === 'fenton') {
        var ga = parseFloat(document.getElementById('growth-ga').value);
        var wt = parseFloat(document.getElementById('growth-weight').value);
        if (!ga || !wt) { showToast('Enter gestational age and weight', 'error'); return; }
        var wtGrams = wt * 1000;
        var lms = interpolateLMS(fentonLMS[sex], ga);
        var z = calcZFromLMS(wtGrams, lms.L, lms.M, lms.S);
        var p = normalCDF(z) * 100;
        resultDiv.classList.remove('hidden');
        resultDiv.innerHTML = renderGrowthResult('Weight at ' + ga + ' weeks GA', wt + ' kg (' + Math.round(wtGrams) + 'g)', '', z, p);
        renderGrowthChart('growth-chart-canvas', 'growth-chart-container', fentonLMS[sex], ga, wtGrams, 'Gestational Age (weeks)', 'Weight (g)');
        return;
      }

      var yrEl = document.getElementById('growth-age-yr');
      var moEl = document.getElementById('growth-age-mo');
      var dyEl = document.getElementById('growth-age-d');
      var yrRaw = yrEl ? yrEl.value.trim() : '';
      var moRaw = moEl ? moEl.value.trim() : '';
      var dyRaw = dyEl ? dyEl.value.trim() : '';
      // Treat blank fields as 0, but require at least ONE field to have any value.
      // "0 days" (newborn at birth) is valid — explicit zero counts.
      if (yrRaw === '' && moRaw === '' && dyRaw === '') {
        showToast('Enter age (years, months, or days)', 'error'); return;
      }
      var yr = parseFloat(yrRaw) || 0;
      var mo = parseFloat(moRaw) || 0;
      var dy = parseFloat(dyRaw) || 0;
      var age = yr * 12 + mo + dy / 30.4375; // age in months, fractional OK
      if (age < 0) { showToast('Age cannot be negative', 'error'); return; }

      if (currentGrowthType === 'wfa') {
        var wt = parseFloat(document.getElementById('growth-weight').value);
        if (!wt) { showToast('Enter weight', 'error'); return; }
        // Use WHO for 0-60mo, CDC for >60mo
        var wfaTable = (age > 24) ? cdcWfaLMS[sex] : wfaLMS[sex];
        var wfaChartTable = (age > 24) ? cdcWfaLMS[sex] : wfaLMS[sex];
        var lms = interpolateLMS(wfaTable, age);
        var z = calcZFromLMS(wt, lms.L, lms.M, lms.S);
        var p = normalCDF(z) * 100;
        var src = (age > 24) ? 'CDC' : 'WHO';
        resultDiv.classList.remove('hidden');
        resultDiv.innerHTML = renderGrowthResult('Weight-for-Age (' + age + ' mo, ' + src + ')', wt, 'kg', z, p);
        if (age > 24) {
          var wfaYears = {}; Object.keys(wfaChartTable).forEach(function(k) { wfaYears[+(k/12).toFixed(2)] = wfaChartTable[k]; });
          renderGrowthChart('growth-chart-canvas', 'growth-chart-container', wfaYears, +(age/12).toFixed(2), wt, 'Age (years)', 'Weight (kg)');
        } else {
          renderGrowthChart('growth-chart-canvas', 'growth-chart-container', wfaChartTable, age, wt, 'Age (months)', 'Weight (kg)');
        }
      }
      else if (currentGrowthType === 'lfa') {
        var len = parseFloat(document.getElementById('growth-length').value);
        if (!len) { showToast('Enter length/height', 'error'); return; }
        // Use WHO for 0-60mo, CDC for >60mo
        var lfaTable = (age > 24) ? cdcLfaLMS[sex] : lfaLMS[sex];
        var lms = interpolateLMS(lfaTable, age);
        var z = calcZFromLMS(len, lms.L, lms.M, lms.S);
        var p = normalCDF(z) * 100;
        var src = (age > 24) ? 'CDC' : 'WHO';
        resultDiv.classList.remove('hidden');
        resultDiv.innerHTML = renderGrowthResult('Length/Height-for-Age (' + age + ' mo, ' + src + ')', len, 'cm', z, p);
        // Mid-parental height
        var mphDatasets = [];
        var motherHt = parseFloat((document.getElementById('growth-mother-ht') || {}).value);
        var fatherHt = parseFloat((document.getElementById('growth-father-ht') || {}).value);
        var mph = calcMidParentalHeight(motherHt, fatherHt, sex);
        if (mph) {
          // Calculate what percentile the MPH corresponds to using adult height LMS (~18y = 216mo)
          var adultLms = interpolateLMS(cdcLfaLMS[sex], 216);
          var mphZ = calcZFromLMS(mph.target, adultLms.L, adultLms.M, adultLms.S);
          var mphPctile = (Math.round(normalCDF(mphZ) * 10000) / 100).toFixed(2);
          resultDiv.innerHTML += '<div style="margin-top:8px;padding:8px 12px;background:#ede9fe;border-radius:6px;font-size:13px;color:#6d28d9;"><strong>Mid-Parental Height:</strong> ' + mph.target.toFixed(1) + ' cm (' + mphPctile + 'th percentile) &mdash; target range: ' + mph.low.toFixed(1) + ' - ' + mph.high.toFixed(1) + ' cm</div>';
          // MPH coordinates: use years if age > 24mo
          var mphX1 = age > 24 ? 17 : 204;
          var mphX2 = age > 24 ? 20 : 240;
          mphDatasets.push({ label: 'MPH Range', data: [{x:mphX1,y:mph.low},{x:mphX2,y:mph.low}], borderColor:'rgba(139,92,246,0.3)', backgroundColor:'rgba(139,92,246,0.1)', fill:'+1', pointRadius:0, borderWidth:1, tension:0, order:5 });
          mphDatasets.push({ label: 'MPH Upper', data: [{x:mphX1,y:mph.high},{x:mphX2,y:mph.high}], borderColor:'rgba(139,92,246,0.3)', fill:false, pointRadius:0, borderWidth:1, tension:0, order:5 });
          mphDatasets.push({ label: 'MPH ' + mph.target.toFixed(0) + 'cm (' + mphPctile + 'th %ile)', data: [{x:mphX1,y:mph.target},{x:mphX2,y:mph.target}], borderColor:'rgba(139,92,246,0.8)', borderDash:[6,3], fill:false, pointRadius:3, pointHoverRadius:6, borderWidth:2, tension:0, order:5 });
        }
        if (age > 24) {
          var lfaYears = {}; Object.keys(lfaTable).forEach(function(k) { lfaYears[+(k/12).toFixed(2)] = lfaTable[k]; });
          renderGrowthChart('growth-chart-canvas', 'growth-chart-container', lfaYears, +(age/12).toFixed(2), len, 'Age (years)', 'Length/Height (cm)', mphDatasets);
        } else {
          renderGrowthChart('growth-chart-canvas', 'growth-chart-container', lfaTable, age, len, 'Age (months)', 'Length/Height (cm)', mphDatasets);
        }
      }
      else if (currentGrowthType === 'hcfa') {
        var hc = parseFloat(document.getElementById('growth-hc').value);
        if (!hc) { showToast('Enter head circumference', 'error'); return; }
        if (age > 60) { showToast('HC charts available 0-60 months only', 'error'); return; }
        var lms = interpolateLMS(hcfaLMS[sex], age);
        var z = calcZFromLMS(hc, lms.L, lms.M, lms.S);
        var p = normalCDF(z) * 100;
        resultDiv.classList.remove('hidden');
        resultDiv.innerHTML = renderGrowthResult('Head Circumference (' + age + ' mo)', hc, 'cm', z, p);
        if (age > 24) {
          var hcYears = {}; Object.keys(hcfaLMS[sex]).forEach(function(k) { hcYears[+(k/12).toFixed(2)] = hcfaLMS[sex][k]; });
          renderGrowthChart('growth-chart-canvas', 'growth-chart-container', hcYears, +(age/12).toFixed(2), hc, 'Age (years)', 'Head Circumference (cm)');
        } else {
          renderGrowthChart('growth-chart-canvas', 'growth-chart-container', hcfaLMS[sex], age, hc, 'Age (months)', 'Head Circumference (cm)');
        }
      }
      else if (currentGrowthType === 'wfl') {
        var wt = parseFloat(document.getElementById('growth-weight').value);
        var len = parseFloat(document.getElementById('growth-length').value);
        if (!wt || !len) { showToast('Enter weight and length', 'error'); return; }
        // Use WHO Weight-for-Length if length is within 45-110cm range
        if (len >= 45 && len <= 110 && wflLMS[sex]) {
          var lms = interpolateLMS(wflLMS[sex], len);
          var z = calcZFromLMS(wt, lms.L, lms.M, lms.S);
          var p = normalCDF(z) * 100;
          resultDiv.classList.remove('hidden');
          resultDiv.innerHTML = renderGrowthResult('Weight-for-Length (' + wt + 'kg / ' + len + 'cm, WHO)', wt, 'kg', z, p);
          renderGrowthChart('growth-chart-canvas', 'growth-chart-container', wflLMS[sex], len, wt, 'Length (cm)', 'Weight (kg)');
        } else {
          // Fall back to BMI-for-Age for children outside WFL range
          if (!BMI_LMS) { showToast('BMI percentile data is still loading.', 'info'); return; }
          var bmi = wt / Math.pow(len / 100, 2);
          var ageMonths = Math.max(24, Math.min(240, Math.round(age)));
          var lms = interpolateLMS(BMI_LMS[sex], ageMonths);
          var z = calcZFromLMS(bmi, lms.L, lms.M, lms.S);
          var p = normalCDF(z) * 100;
          resultDiv.classList.remove('hidden');
          resultDiv.innerHTML = renderGrowthResult('Weight-for-Length (' + wt + 'kg / ' + len + 'cm, BMI proxy)', 'BMI ' + bmi.toFixed(1), 'kg/m2', z, p);
        }
      }
    });

    document.getElementById('btn-clear-growth').addEventListener('click', function() {
      ['growth-sex', 'growth-age-yr', 'growth-age-mo', 'growth-age-d', 'growth-ga', 'growth-weight', 'growth-length', 'growth-hc'].forEach(function(id) {
        var el = document.getElementById(id); if (el) el.value = '';
      });
      var hint = document.getElementById('growth-age-parsed'); if (hint) hint.textContent = '';
      document.getElementById('growth-result').classList.add('hidden');
    });

    // Live feedback as the age fields change
    var ageHint = document.getElementById('growth-age-parsed');
    function updateAgeHint() {
      if (!ageHint) return;
      var yrRaw = ((document.getElementById('growth-age-yr') || {}).value || '').trim();
      var moRaw = ((document.getElementById('growth-age-mo') || {}).value || '').trim();
      var dyRaw = ((document.getElementById('growth-age-d') || {}).value || '').trim();
      if (yrRaw === '' && moRaw === '' && dyRaw === '') { ageHint.textContent = ''; return; }
      var total = (parseFloat(yrRaw) || 0) * 12 + (parseFloat(moRaw) || 0) + (parseFloat(dyRaw) || 0) / 30.4375;
      ageHint.style.color = 'var(--g500)';
      ageHint.textContent = formatParsedAge(total);
    }
    ['growth-age-yr', 'growth-age-mo', 'growth-age-d'].forEach(function(id) {
      var el = document.getElementById(id);
      if (el) el.addEventListener('input', updateAgeHint);
    });

    // ═══════════════════════════════════════════════════════════
    // BILIRUBIN — AAP 2022 Phototherapy + Bhutani Nomogram
    // ═══════════════════════════════════════════════════════════

    // ── Bilirubin chart helpers ──

    function generateThresholdCurve(table) {
      var keys = Object.keys(table).map(Number).sort(function(a,b){return a-b;});
      return keys.map(function(h) { return { x: h, y: table[h] }; });
    }

    function renderBiliChart(patientHours, patientTSB, curveDatasets) {
      if (typeof Chart === 'undefined') return;
      var container = document.getElementById('bili-chart-container');
      if (!container) return;
      container.classList.remove('hidden');

      if (_chartInstances['bili-chart-canvas']) { _chartInstances['bili-chart-canvas'].destroy(); _chartInstances['bili-chart-canvas'] = null; }

      var datasets = curveDatasets.map(function(ds) {
        return Object.assign({ pointRadius: 0, pointHoverRadius: 0, tension: 0.3, order: 10 }, ds);
      });

      // Patient point
      datasets.push({
        label: 'Patient',
        data: [{ x: patientHours, y: patientTSB }],
        backgroundColor: '#2563eb',
        borderColor: '#1d4ed8',
        pointRadius: 8,
        pointHoverRadius: 10,
        showLine: false,
        type: 'scatter',
        order: 0
      });

      _chartInstances['bili-chart-canvas'] = new Chart(document.getElementById('bili-chart-canvas'), {
        type: 'line',
        data: { datasets: datasets },
        options: {
          responsive: true,
          maintainAspectRatio: true,
          aspectRatio: 1.6,
          animation: { duration: 300 },
          scales: {
            x: { type: 'linear', title: { display: true, text: 'Age (hours)', font: { size: 12, weight: 'bold' } }, min: 12, max: 150, grid: { color: 'rgba(0,0,0,0.05)' } },
            y: { title: { display: true, text: 'TSB (mg/dL)', font: { size: 12, weight: 'bold' } }, min: 0, grid: { color: 'rgba(0,0,0,0.05)' } }
          },
          plugins: {
            legend: { display: true, position: 'bottom', labels: { font: { size: 11 }, usePointStyle: true, pointStyle: 'line' } },
            tooltip: {
              callbacks: {
                label: function(ctx) {
                  if (ctx.dataset.label === 'Patient') return 'Patient: ' + ctx.parsed.y.toFixed(1) + ' mg/dL at ' + ctx.parsed.x + 'h';
                  return ctx.dataset.label + ': ' + ctx.parsed.y.toFixed(1) + ' mg/dL';
                }
              }
            }
          }
        }
      });
    }

    // AAP 2022 bilirubin treatment thresholds load from /data/calculators/aap-bilirubin-thresholds.json.

    // Bili sub-pill nav
    document.querySelectorAll('[data-bili]').forEach(function(pill) {
      pill.addEventListener('click', function() {
        document.querySelectorAll('[data-bili]').forEach(function(p) { p.classList.remove('active'); });
        pill.classList.add('active');
        document.getElementById('bili-aap-panel').style.display = pill.dataset.bili === 'aap' ? '' : 'none';
        document.getElementById('bili-bhutani-panel').style.display = pill.dataset.bili === 'bhutani' ? '' : 'none';
        document.getElementById('bili-result').classList.add('hidden');
        var biliChartC = document.getElementById('bili-chart-container');
        if (biliChartC) biliChartC.classList.add('hidden');
        if (_chartInstances['bili-chart-canvas']) { _chartInstances['bili-chart-canvas'].destroy(); _chartInstances['bili-chart-canvas'] = null; }
      });
    });

    var AAP_BILI_THRESHOLDS = null;
    loadAapBilirubinThresholds()
      .then(function(data) { AAP_BILI_THRESHOLDS = data; })
      .catch(function(err) { console.warn('[calculators] AAP bilirubin threshold data unavailable:', err && err.message); });

    document.getElementById('btn-calc-bili-aap').addEventListener('click', function() {
      var ga = document.getElementById('bili-ga').value;
      var hours = parseFloat(document.getElementById('bili-age-hours').value);
      var tsb = parseFloat(document.getElementById('bili-tsb').value);
      var risk = document.getElementById('bili-risk').value;
      if (!ga || isNaN(hours) || isNaN(tsb)) { showToast('Fill in all fields', 'error'); return; }
      if (!AAP_BILI_THRESHOLDS) { showToast('AAP bilirubin threshold data is still loading.', 'info'); return; }

      var gaNum = parseInt(ga);
      var assessment = calculateAapBilirubinAssessment(AAP_BILI_THRESHOLDS, gaNum, hours, tsb, risk);
      var table = assessment.phototherapyTable;
      var threshold = assessment.phototherapyThreshold;
      var aboveThreshold = assessment.abovePhototherapy;
      var exchangeTable = assessment.exchangeTable;
      var exchangeThreshold = assessment.exchangeThreshold;
      var aboveExchange = assessment.aboveExchange;

      var cl;
      if (aboveExchange) cl = { text: 'ABOVE EXCHANGE TRANSFUSION THRESHOLD', color: '#7f1d1d', bg: '#fecaca' };
      else if (aboveThreshold) cl = { text: 'Above Phototherapy Threshold', color: '#ef4444', bg: '#fee2e2' };
      else cl = { text: 'Below Phototherapy Threshold', color: '#10b981', bg: '#d1fae5' };

      var resultDiv = document.getElementById('bili-result');
      resultDiv.classList.remove('hidden');
      resultDiv.innerHTML =
        '<div class="calc-result-header" style="background:' + cl.bg + ';border-color:' + cl.color + ';">' +
          '<div style="font-size:18px;font-weight:700;color:' + cl.color + ';">' + cl.text + '</div>' +
          '<div style="font-size:13px;color:var(--g600);margin-top:4px;">TSB ' + tsb + ' mg/dL at ' + hours + ' hours of life</div>' +
        '</div>' +
        '<div class="calc-result-grid">' +
          '<div class="calc-result-item"><div class="calc-result-label">Patient TSB</div><div class="calc-result-value">' + tsb + '</div><div class="calc-result-sub">mg/dL</div></div>' +
          '<div class="calc-result-item"><div class="calc-result-label">Photo Threshold</div><div class="calc-result-value">' + threshold.toFixed(1) + '</div><div class="calc-result-sub">mg/dL at ' + hours + 'h</div></div>' +
          '<div class="calc-result-item"><div class="calc-result-label">Margin</div><div class="calc-result-value" style="color:' + cl.color + ';">' + (tsb >= threshold ? '+' : '') + (tsb - threshold).toFixed(1) + '</div><div class="calc-result-sub">mg/dL ' + (aboveThreshold ? 'above' : 'below') + ' photo</div></div>' +
          (exchangeThreshold ? '<div class="calc-result-item"><div class="calc-result-label">Exchange Threshold</div><div class="calc-result-value" style="color:#7f1d1d;">' + exchangeThreshold.toFixed(1) + '</div><div class="calc-result-sub">mg/dL' + (aboveExchange ? ' — EXCEEDED' : '') + '</div></div>' : '') +
        '</div>' +
        '<p style="font-size:11px;color:var(--g400);margin:12px 0 0;">GA ' + ga + ' weeks, ' + (risk === 'medium' ? 'with' : 'without') + ' neurotoxicity risk factors. AAP 2022 CPG (exact values from published nomograms). Always use clinical judgment.</p>';

      // Chart: phototherapy + exchange threshold lines vs patient
      var chartDatasets = [
        { label: 'Phototherapy Threshold', data: generateThresholdCurve(table), borderColor: '#ef4444', borderWidth: 2, borderDash: [], fill: { target: 'origin', above: 'rgba(239,68,68,0.08)' } }
      ];
      if (exchangeTable) {
        chartDatasets.push({ label: 'Exchange Transfusion', data: generateThresholdCurve(exchangeTable), borderColor: '#7f1d1d', borderWidth: 2, borderDash: [6,3], fill: false });
      }
      renderBiliChart(hours, tsb, chartDatasets);
    });

    document.getElementById('btn-clear-bili-aap').addEventListener('click', function() {
      ['bili-ga', 'bili-age-hours', 'bili-tsb', 'bili-risk'].forEach(function(id) {
        var el = document.getElementById(id); if (el) el.value = '';
      });
      document.getElementById('bili-result').classList.add('hidden');
      var bc = document.getElementById('bili-chart-container'); if (bc) bc.classList.add('hidden');
    });

    // Bhutani 1999 hour-specific risk nomogram — 40th / 75th / 95th
    // percentile TSB boundaries. Values are a pre-digitized transcription
    // of the original Figure 2 (Bhutani VK et al., Pediatrics 1999;103(1):6-14),
    // lifted verbatim from the JS data arrays used by the codingace.net
    // Bhutani calculator:
    //   https://codingace.net/health/neonate_bilirubin_bhutani.html
    // Cross-checked against the AAP 2004 CPG reproduction of the same chart:
    //   https://staffnet.southernhealth.ca/wp-content/uploads/Bhutani-Nomogram.pdf
    //   (Reproduced with AAP permission from Pediatrics 2004;114:297-316)
    // 6-hour granularity through the first 72 h, 12-hour after.
    //
    // Zone semantics (per AAP 2004 reproduction legend):
    //   High-risk:         TSB >= 95th percentile
    //   High-intermediate: 75th <= TSB < 95th
    //   Low-intermediate:  40th <= TSB < 75th
    //   Low-risk:          TSB < 40th
    //
    // Clinical note: this nomogram is for RISK STRATIFICATION only.
    // For phototherapy/exchange decisions use the AAP 2022 tab
    // (which supersedes the 2004 treatment thresholds).
    var BHUTANI_ZONES = null;
    loadBhutaniZones()
      .then(function(data) { BHUTANI_ZONES = data; })
      .catch(function(err) { console.warn('[calculators] Bhutani zone data unavailable:', err && err.message); });

    document.getElementById('btn-calc-bhutani').addEventListener('click', function() {
      var hours = parseFloat(document.getElementById('bhutani-age').value);
      var tsb = parseFloat(document.getElementById('bhutani-tsb').value);
      if (isNaN(hours) || isNaN(tsb)) { showToast('Fill in all fields', 'error'); return; }
      if (!BHUTANI_ZONES) { showToast('Bhutani zone data is still loading.', 'info'); return; }

      var riskZone = classifyBhutaniRisk(BHUTANI_ZONES, hours, tsb);

      var resultDiv = document.getElementById('bili-result');
      resultDiv.classList.remove('hidden');
      resultDiv.innerHTML =
        '<div class="calc-result-header" style="background:' + riskZone.bg + ';border-color:' + riskZone.color + ';">' +
          '<div style="font-size:18px;font-weight:700;color:' + riskZone.color + ';">' + riskZone.zone + '</div>' +
          '<div style="font-size:13px;color:var(--g600);margin-top:4px;">TSB ' + tsb + ' mg/dL at ' + hours + ' hours of life</div>' +
        '</div>' +
        '<div class="calc-result-grid">' +
          '<div class="calc-result-item"><div class="calc-result-label">95th %ile</div><div class="calc-result-value" style="font-size:16px;color:#ef4444;">' + riskZone.p95.toFixed(1) + '</div><div class="calc-result-sub">High-risk</div></div>' +
          '<div class="calc-result-item"><div class="calc-result-label">75th %ile</div><div class="calc-result-value" style="font-size:16px;color:#f97316;">' + riskZone.p75.toFixed(1) + '</div><div class="calc-result-sub">High-intermediate</div></div>' +
          '<div class="calc-result-item"><div class="calc-result-label">40th %ile</div><div class="calc-result-value" style="font-size:16px;color:#f59e0b;">' + riskZone.p40.toFixed(1) + '</div><div class="calc-result-sub">Low-intermediate</div></div>' +
        '</div>' +
        '<p style="font-size:11px;color:var(--g400);margin:12px 0 0;">Bhutani hour-specific risk nomogram for infants &ge; 35 weeks GA. Digitized values from Bhutani et al., Pediatrics 1999 (cross-checked against the AAP 2004 CPG reproduction). For phototherapy decisions use the AAP 2022 tab.</p>';

      // Chart: Bhutani zone curves with patient
      renderBiliChart(hours, tsb, [
        { label: '95th (High-Risk)', data: generateThresholdCurve(BHUTANI_ZONES.p95), borderColor: '#ef4444', borderWidth: 2, fill: false, borderDash: [] },
        { label: '75th (High-Intermed)', data: generateThresholdCurve(BHUTANI_ZONES.p75), borderColor: '#f97316', borderWidth: 1.5, fill: { target: '-1', above: 'rgba(239,68,68,0.08)' }, borderDash: [3,3] },
        { label: '40th (Low-Intermed)', data: generateThresholdCurve(BHUTANI_ZONES.p40), borderColor: '#f59e0b', borderWidth: 1.5, fill: { target: '-1', above: 'rgba(249,115,22,0.08)' }, borderDash: [3,3] }
      ]);
    });

    document.getElementById('btn-clear-bhutani').addEventListener('click', function() {
      ['bhutani-age', 'bhutani-tsb'].forEach(function(id) {
        var el = document.getElementById(id); if (el) el.value = '';
      });
      document.getElementById('bili-result').classList.add('hidden');
      var bc = document.getElementById('bili-chart-container'); if (bc) bc.classList.add('hidden');
    });

    // ═══════════════════════════════════════════════════════════
    // VITAL SIGNS — Interactive age selector
    // Harriet Lane Handbook 23rd Edition reference values
    // ═══════════════════════════════════════════════════════════

    var VITALS_DATA = {};
    loadVitalsData()
      .then(function(data) { VITALS_DATA = data; })
      .catch(function(err) { console.warn('[calculators] Vitals data unavailable:', err && err.message); });

    // Use event delegation on the vitals panel so the listener works even
    // when the panel is hidden at init time (hidden elements are still in the DOM,
    // but a setTimeout-based approach can race with dynamic content loading).
    var vitalsPanel = document.getElementById('calc-vitals');
    if (vitalsPanel) {
      vitalsPanel.addEventListener('change', function(e) {
        if (e.target.id !== 'vitals-age-select') return;
        var key = e.target.value;
        var resultDiv = document.getElementById('vitals-result');
        if (!key || !VITALS_DATA[key]) { resultDiv.classList.add('hidden'); return; }

        var v = VITALS_DATA[key];
        resultDiv.classList.remove('hidden');
        resultDiv.innerHTML = renderVitalsResult(v);
      });
    }

    // ═══════════════════════════════════════════════════════════
    // RESUSCITATION MEDICATIONS — Harriet Lane Handbook / AHA PALS 2020
    // ═══════════════════════════════════════════════════════════

    var RESUS_DATA = null;
    loadResusMedsData()
      .then(function(data) { RESUS_DATA = data; })
      .catch(function(err) { console.warn('[calculators] Resus meds data unavailable:', err && err.message); });

    // Use event delegation for resus panel
    var resusPanel = document.getElementById('calc-resus');
    if (resusPanel) {
      resusPanel.addEventListener('click', function(e) {
        var btn = e.target.closest('#btn-calc-resus, #btn-clear-resus');
        if (!btn) return;

        if (btn.id === 'btn-clear-resus') {
          var wi = document.getElementById('resus-weight'); if (wi) wi.value = '';
          var rd = document.getElementById('resus-result'); if (rd) { rd.classList.add('hidden'); rd.innerHTML = ''; }
          return;
        }

        // Calculate
        var weightEl = document.getElementById('resus-weight');
        var resultDiv = document.getElementById('resus-result');
        var w = parseFloat(weightEl.value);
        if (!w || w <= 0) { showToast('Please enter a valid weight.', 'error'); return; }

        if (!RESUS_DATA) { showToast('Resus medication data is still loading.', 'info'); return; }
        resultDiv.innerHTML = renderResusMeds(RESUS_DATA, w);
        resultDiv.classList.remove('hidden');
      });
    }

    // ═══════════════════════════════════════════════════════════
    // GLASGOW COMA SCALE
    // ═══════════════════════════════════════════════════════════

    // GCS sub-pill navigation
    var gcsPanel = document.getElementById('calc-gcs');
    if (gcsPanel) {
      gcsPanel.addEventListener('click', function(e) {
        var pill = e.target.closest('[data-gcs]');
        if (!pill) return;
        gcsPanel.querySelectorAll('[data-gcs]').forEach(function(p) { p.classList.remove('active'); });
        pill.classList.add('active');
        var isInfant = pill.dataset.gcs === 'infant';
        var childP = document.getElementById('gcs-child-panel');
        var infantP = document.getElementById('gcs-infant-panel');
        if (childP) childP.style.display = isInfant ? 'none' : '';
        if (infantP) infantP.style.display = isInfant ? '' : 'none';
        updateGCS();
      });

      gcsPanel.addEventListener('change', function(e) {
        if (e.target.tagName === 'SELECT') updateGCS();
      });
    }

    function updateGCS() {
      var isInfant = document.querySelector('[data-gcs="infant"].active') !== null;
      var prefix = isInfant ? 'gcs-infant' : 'gcs-child';
      var eye = parseInt(document.getElementById(prefix + '-eye').value) || 0;
      var verbal = parseInt(document.getElementById(prefix + '-verbal').value) || 0;
      var motor = parseInt(document.getElementById(prefix + '-motor').value) || 0;
      var score = calculateGcs(eye, verbal, motor);

      var resultDiv = document.getElementById('gcs-result');
      if (resultDiv) {
        resultDiv.innerHTML =
          '<div class="calc-result-header" style="background:' + score.bg + ';border-color:' + score.color + ';">' +
            '<div style="font-size:24px;font-weight:700;color:' + score.color + ';">GCS: ' + score.total + '/15</div>' +
            '<div style="font-size:14px;color:var(--g600);margin-top:4px;">' + score.severity + '</div>' +
          '</div>' +
          '<div class="calc-result-grid" style="margin-top:12px;">' +
            '<div class="calc-result-item"><div class="calc-result-label">Eye (E)</div><div class="calc-result-value">' + eye + '</div></div>' +
            '<div class="calc-result-item"><div class="calc-result-label">Verbal (V)</div><div class="calc-result-value">' + verbal + '</div></div>' +
            '<div class="calc-result-item"><div class="calc-result-label">Motor (M)</div><div class="calc-result-value">' + motor + '</div></div>' +
          '</div>' +
          '<div style="margin-top:12px;font-size:12px;color:var(--g500);">' +
            '<strong>Interpretation:</strong> 13-15 Mild, 9-12 Moderate, 3-8 Severe/Coma. ' +
            'Intubation typically considered at GCS &le; 8. ' +
            (isInfant ? 'Using infant-modified verbal and motor scales.' : '') +
          '</div>';
      }
    }
    // Initial GCS render
    setTimeout(updateGCS, 200);

    // ═══════════════════════════════════════════════════════════
    // EQUIPMENT SIZING — Harriet Lane Handbook
    // ═══════════════════════════════════════════════════════════

    var EQUIP_DATA = {};
    loadEquipmentData()
      .then(function(data) { EQUIP_DATA = data; })
      .catch(function(err) { console.warn('[calculators] Equipment data unavailable:', err && err.message); });

    var equipPanel = document.getElementById('calc-equipment');
    if (equipPanel) {
      equipPanel.addEventListener('change', function(e) {
        if (e.target.id !== 'equip-age-select') return;
        var key = e.target.value;
        var resultDiv = document.getElementById('equip-result');
        if (!key || !EQUIP_DATA[key]) { resultDiv.classList.add('hidden'); return; }

        var eq = EQUIP_DATA[key];
        resultDiv.classList.remove('hidden');
        resultDiv.innerHTML = renderEquipmentResult(eq);
      });
    }
  }
