/* =====================================================================
   AI-COS Calibration — Master Dashboard (Gas Monitor Calibration Mgmt)
   KPIs + charts per the Master Dashboard specification.
   ===================================================================== */
function Dashboard({ onNav, openJob }) {
  const DB = window.DB;

  /* ---- every figure below is computed from the live registers ---- */
  const now = DB.TODAY;
  const _js = (j) => ({ 'In progress': 'In Calibration', 'Review': 'QA/QC Review', 'Approved': 'Certified' }[j.stage] || j.stage);
  const daysTo = (d) => { if (!d) return null; const x = new Date(d); return isNaN(x) ? null : Math.round((x - now) / 864e5); };
  const inMonth = (d) => { if (!d) return false; const x = new Date(d); return x.getMonth() === now.getMonth() && x.getFullYear() === now.getFullYear(); };

  const mons = DB.monitors.filter((m) => m.status !== 'Retired');
  const totalAssets = mons.length;
  const activeCustomers = DB.customers.length;
  const failed = mons.filter((m) => m.status === 'Failed').length;
  const underRepair = mons.filter((m) => m.status === 'Under repair').length;
  const inCal = mons.filter((m) => m.status === 'In calibration').length;
  const sched = mons.filter((m) => !['Failed', 'Under repair', 'In calibration'].includes(m.status));
  const overdue = sched.filter((m) => { const d = daysTo(m.due); return d != null && d < 0; }).length;
  const due15 = sched.filter((m) => { const d = daysTo(m.due); return d != null && d >= 0 && d <= 15; }).length;
  const due30 = sched.filter((m) => { const d = daysTo(m.due); return d != null && d > 15 && d <= 30; }).length;
  const valid = Math.max(0, totalAssets - failed - underRepair - inCal - overdue - due15 - due30);
  const dueThisMonth = sched.filter((m) => inMonth(m.due)).length;
  const completedToday = (DB.worksheets || []).filter((w) => w.calDate && new Date(w.calDate).toDateString() === now.toDateString()).length;
  const monthName = now.toLocaleString('en', { month: 'short' });
  const certsIssued = DB.certificates.filter((c) => inMonth(c.issued)).length;
  const openNCRs = DB.capa.filter((c) => c.status !== 'Closed').length;
  const openCAPAs = DB.capa.filter((c) => c.status !== 'Closed' && c.stage && c.stage !== 'Open').length;
  const techs = DB.staff.filter((s) => /technician/i.test(s.role));
  const activeJobs = DB.jobs.filter((j) => ['Assigned', 'In Calibration', 'QA/QC Review'].includes(_js(j)));
  const busyTechs = new Set(activeJobs.map((j) => j.tech).filter(Boolean));
  const techUtil = techs.length ? Math.round(busyTechs.size / techs.length * 100) : 0;
  const compliance = totalAssets ? Math.round(valid / totalAssets * 100) : null;

  /* ---- chart datasets ---- */
  const statusData = [
    { k: 'Valid', n: valid, c: '#15a06b' },
    { k: 'Due in 30 days', n: due30, c: '#2f6fed' },
    { k: 'Due in 15 days', n: due15, c: '#c9820a' },
    { k: 'Overdue', n: overdue, c: '#e0483c' },
    { k: 'In calibration', n: inCal, c: '#0891b2' },
    { k: 'Failed', n: failed, c: '#b5341a' },
    { k: 'Under repair', n: underRepair, c: '#7a5bff' },
  ];
  const manufOf = (m) => { const s = (m.model || '').toLowerCase(); return s.includes('honeywell') || s.includes('bw') ? 'Honeywell / BW' : s.includes('dr') ? 'Dräger' : s.includes('msa') ? 'MSA' : s.includes('crowcon') ? 'Crowcon' : s.includes('rae') ? 'RAE Systems' : (s.includes('industrial') || s.includes('ventis') || s.includes('ind.')) ? 'Industrial Scientific' : 'Other'; };
  const manuCount = {}; DB.monitors.forEach((m) => { const k = m.manufacturer || manufOf(m); manuCount[k] = (manuCount[k] || 0) + 1; });
  const manufData = Object.entries(manuCount).map(([k, n]) => ({ k, n })).sort((a, b) => b.n - a.n);
  const gasColor = {}; DB.gases.forEach((g) => gasColor[g.sym] = g.color);
  const gasCount = {}; DB.monitors.forEach((m) => (m.sensors || []).forEach((g) => gasCount[g] = (gasCount[g] || 0) + 1));
  const gasData = Object.entries(gasCount).map(([k, n]) => ({ k, n, c: gasColor[k] || '#2f6fed' })).sort((a, b) => b.n - a.n);
  const trend = Array.from({ length: 12 }, (_, i) => {
    const d = new Date(now.getFullYear(), now.getMonth() - 11 + i, 1);
    const n = DB.certificates.filter((c) => c.issued && new Date(c.issued).getMonth() === d.getMonth() && new Date(c.issued).getFullYear() === d.getFullYear()).length;
    return { k: d.toLocaleString('en', { month: 'short' }), n };
  });
  const techPerf = techs.map((s) => {
    const myJobs = DB.jobs.filter((j) => j.tech === s.id);
    const myWs = (DB.worksheets || []).filter((w) => w.tech === s.name);
    const fails = myWs.filter((w) => w.result === 'Fail').length;
    const passPct = myWs.length ? Math.round((myWs.length - fails) / myWs.length * 100) : null;
    const durs = myWs.map((w) => { const j = DB.jobs.find((x) => x.id === w.job); return j && j.recvd && w.calDate ? Math.max(0, (new Date(w.calDate) - new Date(j.recvd)) / 864e5) : null; }).filter((x) => x != null);
    const avg = durs.length ? (durs.reduce((a, b) => a + b, 0) / durs.length).toFixed(1) : null;
    return { s, assigned: myJobs.length, completed: myWs.length, avg, passPct, failures: fails };
  });
  const custStatus = DB.customers.map((c) => {
    const cm = mons.filter((m) => m.customer === c.id);
    const od = cm.filter((m) => { const d = daysTo(m.due); return m.status === 'Failed' || (d != null && d < 0); }).length;
    const soon = cm.filter((m) => { const d = daysTo(m.due); return m.status !== 'Failed' && d != null && d >= 0 && d <= 30; }).length;
    return { c, n: cm.length, valid: Math.max(0, cm.length - od - soon), soon, od, pct: cm.length ? Math.round((cm.length - od) / cm.length * 100) : 100 };
  }).sort((a, b) => a.pct - b.pct);

  /* ---- ambient conditions from recorded worksheets (ISO 17025 §6.3) ---- */
  const envWs = (DB.worksheets || []).filter((w) => w.temp != null && w.temp !== '');
  const latestEnv = envWs[0] || null; // dbAdd unshifts — newest first
  const envSeries = envWs.slice(0, 24).reverse().map((w) => ({ temp: parseFloat(w.temp) })).filter((x) => !isNaN(x.temp));
  const envOk = latestEnv && parseFloat(latestEnv.temp) >= 18 && parseFloat(latestEnv.temp) <= 25 && parseFloat(latestEnv.humidity) >= 30 && parseFloat(latestEnv.humidity) <= 60;

  const stageCounts = DB.stages.map((s) => ({ s, n: DB.jobs.filter((j) => _js(j) === s).length }));
  const reviewJobs = DB.jobs.filter((j) => _js(j) === 'QA/QC Review' || _js(j) === 'In Calibration');
  const R = window.calReadiness ? window.calReadiness() : DB.readiness; // computed live
  const health = R.score >= 85 ? { t: 'All systems nominal', c: 'var(--ok)' } : R.score >= 70 ? { t: 'Minor gaps — see guidance', c: 'var(--warn)' } : { t: 'Action needed — see guidance', c: 'var(--bad)' };

  return (
    <div className="page page-wide rise">
      {/* Header */}
      <div className="row" style={{ justifyContent: 'space-between', alignItems: 'flex-end', marginBottom: 20 }}>
        <div>
          <div className="h-page">Calibration command centre</div>
          <div className="h-sub">Al Duqum Al Masiya ENT. · gas monitor calibration management · <span style={{ color: health.c, fontWeight: 600 }}>{health.t}</span></div>
        </div>
        <div className="row" style={{ gap: 8 }}>
          <Btn icon="refresh" size="sm" onClick={async () => { try { await bootCAL(); window.toast('Data refreshed from the server', 'ok'); } catch (e) { window.toast('Refresh failed — check your connection', 'bad'); } }}>Sync</Btn>
          <Btn variant="ghost" icon="download" size="sm" onClick={() => { if (!DB.monitors.length) { window.toast('No assets registered yet — nothing to export', 'info'); return; } csvExport('asset-register.csv', ['id', 'customer', 'model', 'serial', 'status', 'due'], DB.monitors.map((m) => ({ id: m.id, customer: (DB.custById(m.customer) || {}).name, model: m.model, serial: m.serial, status: m.status, due: m.due ? DB.fmt(m.due) : '' }))); }}>Export</Btn>
          <Btn variant="ghost" icon="documents" size="sm" onClick={() => window.print()}>Print</Btn>
          <Btn variant="primary" icon="plus" onClick={() => onNav('jobs')}>New job</Btn>
        </div>
      </div>

      {/* KPI grid — 12 cards per spec */}
      <div className="kpi-grid" style={{ gridTemplateColumns: 'repeat(6, 1fr)', gap: 12, marginBottom: 14 }}>
        <Kpi label="Total customer assets" value={totalAssets.toLocaleString()} icon="cpu" tone="info" delta={activeCustomers + ' customers'} />
        <Kpi label="Active customers" value={activeCustomers} icon="users" tone="info" delta="on the register" />
        <Kpi label="Assets due this month" value={dueThisMonth} icon="calendar" tone="warn" delta="see Email Reminders" />
        <Kpi label="Overdue calibrations" value={overdue} icon="alert" tone={overdue ? 'bad' : 'ok'} delta={overdue ? 'critical' : 'none'} />
        <Kpi label="Completed today" value={completedToday} icon="check-circle" tone="ok" delta="worksheets recorded" />
        <Kpi label={'Certificates issued (' + monthName + ')'} value={certsIssued} icon="certificate" tone="ok" delta="this month" />
        <Kpi label="Failed calibration" value={failed} icon="x-circle" tone={failed ? 'bad' : 'ok'} delta={failed ? 'quarantined' : 'none'} />
        <Kpi label="Under repair" value={underRepair} icon="wrench" tone={underRepair ? 'warn' : 'ok'} delta="out of service" />
        <Kpi label="Open NCRs" value={openNCRs} icon="flag" tone={openNCRs ? 'warn' : 'ok'} delta="quality register" />
        <Kpi label="Open CAPAs" value={openCAPAs} icon="refresh" tone={openCAPAs ? 'warn' : 'ok'} delta="in progress" />
        <Kpi label="Technician utilization" value={techUtil} unit="%" icon="workflow" tone="info" delta={techs.length + (techs.length === 1 ? ' tech' : ' techs')} />
        <Kpi label="Calibration compliance" value={compliance == null ? '—' : compliance} unit={compliance == null ? '' : '%'} icon="shield" tone={compliance == null || compliance >= 85 ? 'ok' : 'warn'} delta={R.verdict} />
      </div>

      {/* Quick actions — per the Master Dashboard specification */}
      <div className="card card-pad" style={{ marginBottom: 16 }}>
        <div className="row wrap" style={{ gap: 8, alignItems: 'center' }}>
          <span className="eyebrow" style={{ marginRight: 8 }}>Quick actions</span>
          <Btn size="sm" icon="briefcase" onClick={() => window.openCustomerForm ? window.openCustomerForm() : onNav('assets')}>New customer</Btn>
          <Btn size="sm" icon="cpu" onClick={() => window.openAssetForm ? window.openAssetForm() : onNav('assets')}>Register new asset</Btn>
          <Btn size="sm" icon="box" onClick={() => window.newJobFlow ? window.newJobFlow() : onNav('jobs')}>Receive equipment</Btn>
          <Btn size="sm" icon="workflow" onClick={() => onNav('jobs')}>Start calibration</Btn>
          <Btn size="sm" icon="certificate" onClick={() => onNav('certs')}>Generate certificate</Btn>
          <Btn size="sm" icon="flag" onClick={() => window.openNcrForm ? window.openNcrForm() : onNav('qms')}>Issue NCR / CAPA</Btn>
          <Btn size="sm" icon="documents" onClick={() => window.openDocForm ? window.openDocForm() : onNav('documents')}>Upload document</Btn>
          <Btn size="sm" icon="qr" onClick={() => { onNav('assets'); window.toast('Open the ⋮ menu on an asset to print its calibration label', 'info'); }}>Print label</Btn>
        </div>
      </div>

      {/* Charts row 1 */}
      <div className="dash-row-3" style={{ display: 'grid', gridTemplateColumns: '1fr 1.35fr 1fr', gap: 16, marginBottom: 16, alignItems: 'start' }}>
        <div className="card">
          <div className="card-head"><Icon name="chart" size={16} style={{ color: 'var(--primary-600)' }} /><div className="h-sec">Calibration status</div><span className="spacer" /><Badge kind="ok">{compliance == null ? 'no assets yet' : compliance + '% valid'}</Badge></div>
          <div className="card-body"><Donut data={statusData} total={totalAssets} /></div>
        </div>
        <div className="card">
          <div className="card-head"><Icon name="trend" size={16} style={{ color: 'var(--primary-600)' }} /><div className="h-sec">Monthly calibration trend</div><span className="spacer" /><span className="faint" style={{ fontSize: 11 }}>last 12 months</span></div>
          <div className="card-body"><VBars data={trend} /></div>
        </div>
        <div className="card">
          <div className="card-head"><Icon name="cylinder" size={16} style={{ color: 'var(--primary-600)' }} /><div className="h-sec">By gas type</div><span className="spacer" /><span className="faint" style={{ fontSize: 11 }}>sensors calibrated</span></div>
          <div className="card-body"><HBars data={gasData} /></div>
        </div>
      </div>

      {/* Charts row 2 */}
      <div className="dash-row-2" style={{ display: 'grid', gridTemplateColumns: '1fr 1.5fr', gap: 16, marginBottom: 16, alignItems: 'start' }}>
        <div className="card">
          <div className="card-head"><Icon name="box" size={16} style={{ color: 'var(--primary-600)' }} /><div className="h-sec">By manufacturer</div><span className="spacer" /><span className="faint" style={{ fontSize: 11 }}>{DB.monitors.length} registered</span></div>
          <div className="card-body"><HBars data={manufData.map((d, i) => ({ ...d, c: ['#2f6fed', '#15a06b', '#c9820a', '#7a5bff', '#df463c', '#0891b2', '#94a3b8'][i % 7] }))} /></div>
        </div>
        <div className="card">
          <div className="card-head"><Icon name="users" size={16} style={{ color: 'var(--primary-600)' }} /><div className="h-sec">Technician performance</div><span className="spacer" /><Btn size="sm" onClick={() => onNav('users')}>Team</Btn></div>
          <table className="tbl">
            <thead><tr><th>Technician</th><th>Assigned</th><th>Completed</th><th>Avg time</th><th>Pass %</th><th>Failures</th></tr></thead>
            <tbody>{techPerf.length ? techPerf.map((t) => (
              <tr key={t.s.id}>
                <td><div className="row" style={{ gap: 7 }}><Avatar id={t.s.id} size="sm" /><span style={{ fontSize: 12.5, fontWeight: 600 }}>{t.s.name}</span></div></td>
                <td className="mono" style={{ fontSize: 12.5 }}>{t.assigned}</td>
                <td className="mono" style={{ fontSize: 12.5 }}>{t.completed}</td>
                <td style={{ fontSize: 12.5 }}>{t.avg == null ? '—' : t.avg + ' d'}</td>
                <td>{t.passPct == null ? <span className="faint">—</span> : <div className="row" style={{ gap: 6 }}><div style={{ width: 40 }}><Bar pct={t.passPct} color={t.passPct >= 95 ? 'var(--ok)' : t.passPct >= 90 ? 'var(--primary-600)' : 'var(--warn)'} /></div><span className="mono faint" style={{ fontSize: 11 }}>{t.passPct}%</span></div>}</td>
                <td><Badge kind={t.failures === 0 ? 'ok' : t.failures > 2 ? 'bad' : 'warn'}>{t.failures}</Badge></td>
              </tr>
            )) : (
              <tr><td colSpan={6} className="faint" style={{ textAlign: 'center', padding: '22px 12px', fontSize: 12.5 }}>No technician activity yet — add users with the Technician role and assign them calibration jobs.</td></tr>
            )}</tbody>
          </table>
        </div>
      </div>

      {/* Customer calibration status */}
      <div className="card" style={{ marginBottom: 16 }}>
        <div className="card-head"><Icon name="briefcase" size={16} style={{ color: 'var(--primary-600)' }} /><div className="h-sec">Customer calibration status</div><span className="spacer" /><Btn size="sm" onClick={() => onNav('assets')}>All customers</Btn></div>
        <div className="card-body" style={{ display: 'grid', gridTemplateColumns: custStatus.length ? '1fr 1fr' : '1fr', gap: '10px 26px' }}>
          {custStatus.length ? custStatus.map((r) => (
            <div key={r.c.id} className="row" style={{ gap: 10, cursor: 'pointer' }} onClick={() => window.openDetail('customer', r.c)}>
              <span className="avatar avatar-sm" style={{ background: r.c.color, borderRadius: 6, flex: 'none' }}>{r.c.short}</span>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div className="row" style={{ justifyContent: 'space-between' }}><span style={{ fontSize: 12.5, fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{r.c.name}</span><span className="mono faint" style={{ fontSize: 11 }}>{r.pct}%</span></div>
                <div style={{ display: 'flex', height: 7, borderRadius: 4, overflow: 'hidden', marginTop: 4, background: 'var(--bg)' }}>
                  {r.n > 0 && <React.Fragment>
                    <span style={{ width: (r.valid / r.n * 100) + '%', background: 'var(--ok)' }} />
                    <span style={{ width: (r.soon / r.n * 100) + '%', background: '#c9820a' }} />
                    <span style={{ width: (r.od / r.n * 100) + '%', background: 'var(--bad)' }} />
                  </React.Fragment>}
                </div>
              </div>
              <span className="faint" style={{ fontSize: 11, whiteSpace: 'nowrap' }}>{r.n} assets</span>
            </div>
          )) : (
            <div className="faint" style={{ textAlign: 'center', padding: '18px 12px', fontSize: 12.5 }}>No customers registered yet — start in Equipment &amp; Assets → Customers.</div>
          )}
        </div>
      </div>

      {/* Operational strip: readiness + pipeline, attention queue, environment */}
      <div className="dash-ops" style={{ display: 'grid', gridTemplateColumns: '1.62fr 1fr', gap: 16, alignItems: 'start' }}>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
          <div className="card">
            <div className="card-head"><Icon name="shield" size={17} style={{ color: 'var(--primary-600)' }} /><div className="h-sec">Audit readiness &amp; live pipeline</div><span className="spacer" /><Badge kind={window.EDITION === 'essential' ? 'ok' : 'ai'} icon={window.EDITION === 'essential' ? 'check-circle' : 'sparkles'}>{window.EDITION === 'essential' ? 'Live score' : 'Continuously scored'}</Badge></div>
            <div className="card-body" style={{ display: 'grid', gridTemplateColumns: '180px 1fr', gap: 22, alignItems: 'center' }}>
              <div style={{ display: 'grid', placeItems: 'center' }}>
                <Ring value={R.score} color={R.score >= 85 ? "var(--ok)" : R.score >= 70 ? "var(--warn)" : "var(--bad)"}><div><div style={{ fontSize: 34, fontWeight: 800, letterSpacing: "-1px", lineHeight: 1 }}>{R.score}</div><div className="faint" style={{ fontSize: 11, fontWeight: 600 }}>/ 100</div><div className={"badge " + (R.score >= 85 ? "b-ok" : R.score >= 70 ? "b-warn" : "b-bad")} style={{ marginTop: 6 }}>{R.verdict}</div></div></Ring>
              </div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 7 }}>
                {R.factors.map((f, i) => (
                  <div key={i} className="row" style={{ gap: 10 }} title={f.detail || ''}>
                    <span style={{ width: 150, fontSize: 12.5, color: 'var(--text-2)', fontWeight: 600 }}>{f.label}</span>
                    <div style={{ flex: 1 }}><Bar pct={Math.max(4, 100 + f.value * 3)} color={f.value <= -8 ? 'var(--bad)' : f.value < 0 ? 'var(--warn)' : 'var(--ok)'} /></div>
                    <span className="mono" style={{ width: 30, textAlign: 'right', fontSize: 12, fontWeight: 700, color: f.value <= -8 ? 'var(--bad)' : f.value < 0 ? 'var(--warn)' : 'var(--ok)' }}>{f.value === 0 ? '✓' : f.value}</span>
                  </div>
                ))}
              </div>
            </div>
            <div style={{ display: 'grid', gridTemplateColumns: `repeat(${DB.stages.length}, 1fr)`, borderTop: '1px solid var(--border)' }}>
              {stageCounts.map((sc, i) => (
                <div key={sc.s} style={{ padding: '13px 12px', borderLeft: i ? '1px solid var(--border)' : 'none', cursor: 'pointer' }} onClick={() => onNav('jobs')} className="hover-lift">
                  <div className="row" style={{ gap: 7 }}><span style={{ width: 7, height: 7, borderRadius: '50%', background: ['#94a3b8', '#0891b2', '#2f6fed', '#c9820a', '#7a5bff', '#15a06b', '#0f7a4f'][i % 7] }} /><span style={{ fontSize: 11.5, fontWeight: 600, color: 'var(--text-2)' }}>{sc.s}</span></div>
                  <div style={{ fontSize: 23, fontWeight: 800, letterSpacing: '-.5px', marginTop: 4 }}>{sc.n}</div>
                </div>
              ))}
            </div>
          </div>

          <div className="card">
            <div className="card-head"><Icon name="flag" size={16} style={{ color: 'var(--warn)' }} /><div className="h-sec">Needs your attention</div><span className="spacer" /><Btn size="sm" iconR="arrowR" onClick={() => onNav('jobs')}>All jobs</Btn></div>
            <table className="tbl">
              <thead><tr><th>Job</th><th>Customer</th><th>Stage</th><th>Tech</th><th>SLA</th><th>Due</th><th></th></tr></thead>
              <tbody>{reviewJobs.length === 0 && <tr><td colSpan={7} className="faint" style={{ textAlign: 'center', padding: '20px 12px', fontSize: 12.5 }}>Nothing waiting on you — jobs in calibration or QA/QC review appear here.</td></tr>}
              {reviewJobs.map((j) => { const c = DB.custById(j.customer); return (
                <tr key={j.id} className="clickable" onClick={() => openJob(j.id)}>
                  <td><span className="mono cell-strong" style={{ fontSize: 12.5 }}>{j.id}</span><div className="cell-sub">{j.items} items · {j.type}</div></td>
                  <td><div className="row" style={{ gap: 7 }}><span className="avatar avatar-sm" style={{ background: c.color, borderRadius: 6 }}>{c.short}</span><span style={{ fontSize: 12.5 }}>{c.short}</span></div></td>
                  <td><StatusBadge status={j.stage} /></td>
                  <td>{j.tech ? <Avatar id={j.tech} size="sm" /> : <span className="faint">—</span>}</td>
                  <td style={{ width: 92 }}><Bar pct={j.sla} color={j.sla > 85 ? 'var(--ok)' : j.sla > 50 ? 'var(--primary-600)' : 'var(--warn)'} /></td>
                  <td><span style={{ fontSize: 12.5, fontWeight: 600, color: j.due <= DB.TODAY ? 'var(--bad)' : 'var(--text)' }}>{DB.rel(j.due)}</span></td>
                  <td><Icon name="chevR" size={16} style={{ color: 'var(--text-3)' }} /></td>
                </tr>
              ); })}</tbody>
            </table>
          </div>
        </div>

        <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
          <GuidePanel onNav={onNav} />
          <div className="card">
            <div className="card-head"><Icon name="wrench" size={16} style={{ color: 'var(--primary-600)' }} /><div className="h-sec">Asset health</div><span className="spacer" /><span className="faint" style={{ fontSize: 11 }}>physical condition</span></div>
            <div className="card-body">
              {totalAssets ? <HBars data={(window.calOpt ? window.calOpt('assetHealth') : ['Healthy', 'Needs Repair', 'Condemned', 'Spare', 'Out of Service']).map((k, i) => ({ k, n: mons.filter((m) => (m.health || 'Healthy') === k).length, c: ['#15a06b', '#c9820a', '#b5341a', '#2f6fed', '#7a5bff', '#0891b2'][i % 6] }))} />
                : <div className="faint" style={{ fontSize: 12, padding: '6px 0' }}>Health states appear here once assets are registered.</div>}
            </div>
          </div>
          <div className="card">
            <div className="card-head"><Icon name="thermometer" size={16} style={{ color: 'var(--primary-600)' }} /><div className="h-sec">Laboratory environment</div><span className="spacer" />{latestEnv ? <Badge kind={envOk ? 'ok' : 'warn'} dot>{envOk ? 'In range' : 'Check range'}</Badge> : <Badge kind="info">No records yet</Badge>}</div>
            <div className="card-body">
              <div className="row" style={{ gap: 18, marginBottom: 12 }}>
                <EnvStat icon="thermometer" label="Temp" value={latestEnv ? String(latestEnv.temp) : '—'} unit="°C" range="18–25 °C" />
                <EnvStat icon="droplet" label="Humidity" value={latestEnv ? String(latestEnv.humidity || '—') : '—'} unit="%RH" range="30–60 %" />
                <EnvStat icon="gauge" label="Records" value={String(envWs.length)} unit="" range="worksheets" />
              </div>
              {envSeries.length >= 2 ? <EnvSpark series={envSeries} /> : <div className="faint" style={{ fontSize: 11.5, padding: '10px 0' }}>Ambient temperature trends appear here as calibration worksheets are recorded.</div>}
              <div className="faint" style={{ fontSize: 11, marginTop: 8 }}>Conditions as recorded on calibration worksheets · ISO 17025 §6.3</div>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

/* ---- inline chart helpers (SVG, no deps) ---- */
function Donut({ data, total }) {
  const real = data.reduce((a, d) => a + d.n, 0);
  const sum = real || 1;
  const R = 52, C = 2 * Math.PI * R; let off = 0;
  return (
    <div className="row" style={{ gap: 18, alignItems: 'center' }}>
      <svg viewBox="0 0 140 140" style={{ width: 140, height: 140, flex: 'none' }}>
        <circle cx="70" cy="70" r={R} fill="none" stroke="var(--bg)" strokeWidth="16" />
        {data.map((d, i) => { const frac = d.n / sum; const dash = frac * C; const el = <circle key={i} cx="70" cy="70" r={R} fill="none" stroke={d.c} strokeWidth="16" strokeDasharray={`${dash} ${C - dash}`} strokeDashoffset={-off} transform="rotate(-90 70 70)" />; off += dash; return el; })}
        <text x="70" y="66" textAnchor="middle" style={{ fontSize: 22, fontWeight: 800, fill: 'var(--ink)' }}>{(total != null ? total : real).toLocaleString()}</text>
        <text x="70" y="83" textAnchor="middle" style={{ fontSize: 9.5, fontWeight: 600, fill: 'var(--text-3)', letterSpacing: '.5px' }}>ASSETS</text>
      </svg>
      <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 6 }}>
        {data.map((d, i) => (
          <div key={i} className="row" style={{ gap: 8, fontSize: 12 }}>
            <span style={{ width: 9, height: 9, borderRadius: 3, background: d.c, flex: 'none' }} />
            <span style={{ flex: 1, color: 'var(--text-2)' }}>{d.k}</span>
            <span className="mono cell-strong">{d.n.toLocaleString()}</span>
            <span className="mono faint" style={{ width: 34, textAlign: 'right' }}>{Math.round(d.n / sum * 100)}%</span>
          </div>
        ))}
      </div>
    </div>
  );
}
function VBars({ data }) {
  const max = Math.max(...data.map((d) => d.n)) || 1;
  return (
    <div className="row" style={{ alignItems: 'flex-end', gap: 6, height: 150, paddingTop: 6 }}>
      {data.map((d, i) => (
        <div key={i} style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 5, height: '100%', justifyContent: 'flex-end' }}>
          <span className="mono faint" style={{ fontSize: 9.5 }}>{d.n}</span>
          <div title={d.k + ': ' + d.n} style={{ width: '78%', height: (d.n / max * 108) + 'px', borderRadius: '4px 4px 0 0', background: i === data.length - 1 ? 'var(--primary-600)' : 'linear-gradient(180deg, #7ea6f2, #4f83ea)' }} />
          <span className="faint" style={{ fontSize: 9.5 }}>{d.k}</span>
        </div>
      ))}
    </div>
  );
}
function HBars({ data }) {
  const max = Math.max(...data.map((d) => d.n)) || 1;
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 9 }}>
      {data.map((d, i) => (
        <div key={i} className="row" style={{ gap: 9, fontSize: 12 }}>
          <span style={{ width: 108, color: 'var(--text-2)', fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{d.k}</span>
          <div style={{ flex: 1, height: 16, borderRadius: 5, background: 'var(--bg)', overflow: 'hidden' }}>
            <div style={{ width: (d.n / max * 100) + '%', height: '100%', background: d.c || 'var(--primary-600)', borderRadius: 5 }} />
          </div>
          <span className="mono cell-strong" style={{ width: 26, textAlign: 'right' }}>{d.n}</span>
        </div>
      ))}
    </div>
  );
}

function EnvStat({ icon, label, value, unit, range }) {
  return (
    <div style={{ flex: 1 }}>
      <div className="row" style={{ gap: 6, color: 'var(--text-2)', fontSize: 11.5, fontWeight: 600 }}><Icon name={icon} size={13} />{label}</div>
      <div style={{ fontSize: 20, fontWeight: 800, letterSpacing: '-.5px', marginTop: 3 }}>{value}<span style={{ fontSize: 12, color: 'var(--text-3)', marginLeft: 2 }}>{unit}</span></div>
      <div className="faint" style={{ fontSize: 10.5 }}>{range}</div>
    </div>
  );
}
function EnvSpark({ series }) {
  const w = 100, h = 34;
  const temps = series.map((s) => s.temp);
  const min = Math.min(...temps) - 0.5, max = Math.max(...temps) + 0.5;
  const pts = series.map((s, i) => `${(i / (series.length - 1)) * w},${h - ((s.temp - min) / (max - min)) * h}`).join(' ');
  return (
    <svg viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" style={{ width: '100%', height: 40 }}>
      <defs><linearGradient id="envg" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stopColor="rgba(47,111,237,.22)" /><stop offset="1" stopColor="rgba(47,111,237,0)" /></linearGradient></defs>
      <polygon points={`0,${h} ${pts} ${w},${h}`} fill="url(#envg)" />
      <polyline points={pts} fill="none" stroke="var(--primary-600)" strokeWidth="1.4" vectorEffect="non-scaling-stroke" />
    </svg>
  );
}

window.Dashboard = Dashboard;
