/* ============================================================
   MUIY 2.0 — Pre-transfusion Hb intelligence
   Smart status · trend analysis · forecast · achievements.
   Rendered inside Health → Trends when the HB metric is active.
   ============================================================ */

const TONE_COLORS = {
  good: { color: 'var(--success)', wash: 'var(--success-wash)' },
  soft: { color: 'var(--warning)', wash: 'var(--warning-wash)' },
  warn: { color: 'var(--danger)', wash: 'var(--danger-wash)' },
  cool: { color: '#3E9AA0', wash: 'var(--sky-wash)' }
};

/* ---- symptom / physiological context by Hb level (educational) ---- */
function hbSymptom(v) {
  if (v == null || isNaN(v)) return null;
  if (v >= 10) return { color: 'var(--success)', text: 'Oxygen delivery is comfortable — symptoms are usually minimal at this level.' };
  if (v >= 9) return { color: 'var(--warning)', text: 'Fatigue and reduced stamina are common around 9–10 g/dL.' };
  if (v >= 7) return { color: 'var(--alert)', text: 'Significant symptoms and rising organ stress become more likely at 7–8 g/dL.' };
  if (v >= 6) return { color: 'var(--orange)', text: 'Oxygen delivery is becoming insufficient below 7 g/dL — symptoms are usually marked.' };
  if (v >= 5) return { color: 'var(--danger)', text: 'Severe oxygen deprivation with risk to vital organs below 6 g/dL.' };
  return { color: 'var(--danger)', text: 'Medical emergency — life-threateningly low. Seek urgent care.' };
}

/* ---- compact pre-transfusion Hb chart for the status card ----
   A glanceable trend: each reading is a dot coloured by its own status band,
   plotted over the soft target zone, with the trigger threshold marked and the
   latest reading called out. Richer than a bare sparkline, lighter than the
   full MetricChart below it. `rows` are newest-first {date,value}. */
function HbMiniChart({ rows, targets, color }) {
  const series = rows.slice(0, 10).reverse(); // oldest → newest
  if (series.length < 2) return null;
  const T = targets || TC.DEFAULT_HB_TARGETS;
  const gid = React.useMemo(() => 'hbm' + Math.random().toString(36).slice(2, 8), []);

  const W = 300, H = 96, padL = 8, padR = 10, padT = 14, padB = 18;
  const vals = series.map((d) => d.value);
  const lo = Math.min(...vals, T.belowMax);
  const hi = Math.max(...vals, T.onMax);
  const min = Math.floor((lo - 0.4) * 2) / 2;
  const max = Math.ceil((hi + 0.4) * 2) / 2;
  const n = series.length;
  const x = (i) => padL + (i / (n - 1)) * (W - padL - padR);
  const y = (v) => padT + (1 - (v - min) / (max - min || 1)) * (H - padT - padB);
  const clampY = (v) => Math.max(padT, Math.min(H - padB, y(v)));

  const pts = series.map((d, i) => [x(i), y(d.value)]);
  const line = pts.map((p, i) => `${i ? 'L' : 'M'} ${p[0].toFixed(1)} ${p[1].toFixed(1)}`).join(' ');
  const area = `${line} L ${pts[n - 1][0].toFixed(1)} ${H - padB} L ${pts[0][0].toFixed(1)} ${H - padB} Z`;

  const bandTop = clampY(T.onMax), bandBot = clampY(T.nearMax);
  const trigY = clampY(T.belowMax);
  const dotColor = (v) => (TC.hbStatus(v, T) || {}).color || color;
  const latest = series[n - 1];
  const lc = dotColor(latest.value);
  const fmtMD = (d) => TC.fmtDate(d, { day: 'numeric', month: 'short' });

  return (
    <svg viewBox={`0 0 ${W} ${H}`} width="100%" height="96" preserveAspectRatio="none"
      style={{ display: 'block', overflow: 'visible' }} aria-hidden="true">
      <defs>
        <linearGradient id={gid} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0" stopColor={lc} stopOpacity="0.14" />
          <stop offset="1" stopColor={lc} stopOpacity="0" />
        </linearGradient>
      </defs>

      {/* soft target zone */}
      <rect x={padL} y={bandTop} width={W - padL - padR} height={Math.max(0, bandBot - bandTop)}
        fill="var(--green-wash)" rx="2" />
      <text x={padL + 2} y={bandTop - 3} fontSize="8" fontWeight="800" fill="var(--success)"
        style={{ textTransform: 'uppercase', letterSpacing: '.05em' }}>Target</text>

      {/* trigger threshold */}
      {T.belowMax > min && (
        <g>
          <line x1={padL} y1={trigY} x2={W - padR} y2={trigY} stroke="var(--danger)" strokeWidth="1"
            strokeDasharray="2 3" opacity="0.5" />
          <text x={W - padR} y={trigY - 3} fontSize="8" fontWeight="800" textAnchor="end"
            fill="var(--danger)" opacity="0.8">Trigger</text>
        </g>
      )}

      {/* area + line */}
      <path d={area} fill={`url(#${gid})`} stroke="none" />
      <path d={line} fill="none" stroke="var(--ink-3)" strokeOpacity="0.55" strokeWidth="2"
        strokeLinecap="round" strokeLinejoin="round" vectorEffect="non-scaling-stroke" />

      {/* status-coloured dots */}
      {pts.map((p, i) => {
        const isLast = i === n - 1;
        const c = dotColor(series[i].value);
        return (
          <g key={i}>
            {isLast && <circle cx={p[0]} cy={p[1]} r="6" fill={c} opacity="0.16" />}
            <circle cx={p[0]} cy={p[1]} r={isLast ? 3.4 : 2.4} fill={isLast ? c : 'var(--surface)'}
              stroke={c} strokeWidth={isLast ? 0 : 1.8} vectorEffect="non-scaling-stroke" />
          </g>
        );
      })}

      {/* latest value callout */}
      <text x={Math.min(pts[n - 1][0], W - padR)} y={Math.max(padT - 4, clampY(latest.value) - 9)}
        fontSize="11" fontWeight="800" textAnchor="end" fill={lc}>{latest.value.toFixed(1)}</text>

      {/* date axis */}
      <text x={padL} y={H - 4} fontSize="8" fontWeight="700" fill="var(--ink-3)">{fmtMD(series[0].date)}</text>
      <text x={W - padR} y={H - 4} fontSize="8" fontWeight="700" textAnchor="end" fill="var(--ink-3)">{fmtMD(latest.date)}</text>
    </svg>);
}

/* ---- 1 · Smart status + reassuring message ---- */
function HbSmartStatus({ store, rows, targets }) {
  if (!rows.length) {
    return (
      <div className="card" style={{ marginBottom: 14 }}>
        <div className="eyebrow" style={{ marginBottom: 9 }}>Pre-transfusion Hb</div>
        <div className="body">No pre-transfusion readings yet. Your Hb-before value is saved each time you log a transfusion.</div>
      </div>);

  }
  const latest = rows[0];
  const st = TC.hbStatus(latest.value, targets);
  const msg = TC.hbMessage(st.key);
  const sym = hbSymptom(latest.value);
  const tone = TONE_COLORS[msg.tone] || TONE_COLORS.cool;
  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div className="between" style={{ marginBottom: 9 }}>
        <span className="eyebrow">Pre-transfusion Hb</span>
      </div>
      <div className="row" style={{ alignItems: 'baseline', gap: 8 }}>
        <span className="num" style={{ fontSize: 40, fontWeight: 800, letterSpacing: '-.02em', lineHeight: 1, color: st.color }}>{latest.value.toFixed(1)}</span>
        <span style={{ fontWeight: 800, fontSize: 14, color: 'var(--ink-3)' }}>g/dL</span>
        <span className="chip-status" style={{ marginLeft: 'auto', background: st.wash, color: st.color, fontSize: 12.5, padding: '5px 11px' }}>
          <span style={{ width: 8, height: 8, borderRadius: 999, background: st.color }} />{st.label}
        </span>
      </div>
      {store && store.sparklines && rows.length > 1 &&
      <div className="spark-hb">
          <div className="spark-cap"><span>Recent trend</span><span className="spark-range">last {Math.min(rows.length, 10)} readings</span></div>
          <HbMiniChart rows={rows} targets={targets} color={st.color} />
        </div>
      }
      <div className="row" style={{ gap: 11, alignItems: 'flex-start', marginTop: 14, padding: '13px 13px', borderRadius: 14, background: tone.wash }}>
        <div className="leaf" style={{ width: 34, height: 34, flex: 'none', background: '#fff', color: tone.color, borderRadius: 11 }}>
          <Icon name={msg.icon} size={17} color={tone.color} fill={msg.icon === 'sparkle' ? tone.color : 'none'} />
        </div>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontWeight: 800, fontSize: 13.5, color: tone.color }}>{msg.lead}</div>
          <div className="body" style={{ marginTop: 3, fontSize: 13, color: 'var(--ink)', lineHeight: 1.5 }}>{msg.body}</div>
        </div>
      </div>
      {sym &&
      <div className="row" style={{ gap: 9, alignItems: 'flex-start', marginTop: 12, paddingTop: 12, borderTop: '1px solid var(--hairline)' }}>
          <span style={{ width: 9, height: 9, borderRadius: 999, background: sym.color, flex: 'none', marginTop: 4 }} />
          <div className="body" style={{ fontSize: 12.5, color: 'var(--ink)', lineHeight: 1.5 }}><b style={{ color: sym.color }}>At {latest.value.toFixed(1)} g/dL:</b> {sym.text}</div>
        </div>
      }
      <div className="dmeta muted" style={{ marginTop: 11 }}>Target {st.range.replace(' g/dL', '')} · measured {TC.fmtDate(latest.date)}</div>
    </div>);

}

/* ---- 2 · Current / previous / windowed trend ---- */
// `range` follows the Hb-trend card's dropdown ('90' | '180' | '270' | '365' | 'all').
// The moving average + its label both track whatever window the user selects.
const HB_AVG_LABELS = { '90': '3-mo avg', '180': '6-mo avg', '270': '9-mo avg', '365': '1-yr avg', all: 'All-time avg' };
function HbTrendAnalysis({ rows, targets, bare, range }) {
  const rng = range || '180';
  const winDays = rng === 'all' ? Infinity : parseInt(rng, 10);
  const avgLabel = HB_AVG_LABELS[rng] || '6-mo avg';
  const tr = TC.hbTrendStats(rows, winDays);
  if (!tr) return null;
  const stCur = TC.hbStatus(tr.current.value, targets);
  const cell = (label, val, color, sub) =>
  <div style={{ flex: 1, textAlign: 'center' }}>
      <div className="num" style={{ fontSize: 22, fontWeight: 800, color: color || 'var(--ink)', lineHeight: 1.05 }}>{val}</div>
      <div className="dmeta muted" style={{ marginTop: 4, fontWeight: 800 }}>{label}</div>
      {sub && <div className="dmeta muted" style={{ marginTop: 1, fontSize: 10.5 }}>{sub}</div>}
    </div>;

  let sentence = null;
  if (tr.delta != null) {
    const a = Math.abs(tr.delta).toFixed(1);
    if (tr.delta > 0.05) sentence = { icon: 'trendUp', color: 'var(--success)', text: `Improved by ${a} g/dL from ${tr.previous.value.toFixed(1)} at your previous visit.` };else
    if (tr.delta < -0.05) sentence = { icon: 'trendDown', color: 'var(--warning)', text: `Down ${a} g/dL from ${tr.previous.value.toFixed(1)} at your previous visit.` };else
    sentence = { icon: 'trendFlat', color: 'var(--ink-2)', text: `Holding steady — about the same as your previous visit (${tr.previous.value.toFixed(1)}).` };
  }
  const inner =
  <React.Fragment>
      <div className="sec-title" style={{ marginBottom: 12 }}><Icon name="chart" size={15} color="var(--sky-deep)" />Trend</div>
      <div className="row" style={{ alignItems: 'stretch' }}>
        {cell('Current', tr.current.value.toFixed(1), stCur.color)}
        <div style={{ width: 1, background: 'var(--hairline)' }} />
        {cell('Previous', tr.previous ? tr.previous.value.toFixed(1) : '—', 'var(--ink-2)')}
        <div style={{ width: 1, background: 'var(--hairline)' }} />
        {cell(avgLabel, tr.avg6 != null ? tr.avg6.toFixed(1) : '—', 'var(--sky-deep)', tr.win6Count ? `${tr.win6Count} readings` : null)}
      </div>
      {sentence &&
    <div className="row" style={{ gap: 9, alignItems: 'flex-start', marginTop: 14, paddingTop: 13, borderTop: '1.5px solid var(--hairline)' }}>
          <Icon name={sentence.icon} size={16} color={sentence.color} style={{ marginTop: 1, flex: 'none' }} />
          <div className="body" style={{ fontSize: 13.5, color: 'var(--ink)', lineHeight: 1.5 }}>{sentence.text}</div>
        </div>
    }
    </React.Fragment>;

  if (bare) return <div style={{ marginTop: 16, paddingTop: 16, borderTop: '1.5px solid var(--hairline)' }}>{inner}</div>;
  return <div className="card" style={{ marginBottom: 14 }}>{inner}</div>;
}

/* ---- 3 · Predictive next-transfusion forecast ---- */
function HbForecast({ store, data, targets }) {
  const f = TC.hbForecast(data, targets);
  const [drivingOpen, setDrivingOpen] = React.useState(false);
  if (!f) {
    return (
      <div className="card" id="hb-forecast" style={{ marginBottom: 14 }}>
        <div className="sec-title" style={{ marginBottom: 8 }}><Icon name="calendar" size={15} color="var(--orange)" />Next-transfusion forecast</div>
        <div className="body" style={{ fontSize: 13, lineHeight: 1.5 }}>Log at least two transfusions and MUIY will estimate when your Hb is likely to dip again.</div>
      </div>);

  }
  const wl = f.windowLikely,ww = f.windowWide;
  const sameDay = wl.loDays === wl.hiDays;
  const windowText = f.overdue ? 'Likely due now' : sameDay ? `~${wl.hiDays}` : `${wl.loDays}–${wl.hiDays}`;
  const oneIn10 = Math.round(f.likelihoodPct / 10);

  const CONF = {
    high: { label: 'Higher confidence', color: 'var(--success)', wash: 'var(--green-wash)' },
    medium: { label: 'Moderate confidence', color: 'var(--warning)', wash: 'var(--yellow-wash)' },
    low: { label: 'Lower confidence', color: 'var(--ink-2)', wash: 'var(--surface-2)' } };

  const conf = CONF[f.confidence] || CONF.low;
  const methodText = f.method === 'volume' ?
  'Learned from your donor Hb, transfused volume, your fitted Hb-decline rate and the rhythm of your own history — recent transfusions count for more.' :
  f.method === 'decline' ?
  'Modelled from your fitted Hb-decline rate and interval history. Add transfused volume and donor Hb to personalise it further.' :
  'Based on the rhythm of your transfusions so far. Add Hb-before, volume and donor Hb to unlock a fuller clinical estimate.';
  const EFFECT = {
    up: { color: 'var(--success)', icon: 'trendUp' },
    down: { color: 'var(--warning)', icon: 'trendDown' },
    flat: { color: 'var(--ink-3)', icon: 'trendFlat' } };

  // ---- runway: elapsed since last → likely window ahead, on one timeline ----
  const total = Math.max(1, f.daysSince + ww.hiDays);
  const pct = (d) => `${Math.max(0, Math.min(1, d / total)) * 100}%`;
  const elapsed = pct(f.daysSince);
  const wideL = pct(f.daysSince + ww.loDays),wideR = pct(f.daysSince + ww.hiDays);
  const likeL = pct(f.daysSince + wl.loDays),likeR = pct(f.daysSince + wl.hiDays);

  return (
    <div className="card" id="hb-forecast" style={{ marginBottom: 14 }}>
      <div className="between" style={{ marginBottom: 4, gap: 8, alignItems: 'flex-start' }}>
        <span className="sec-title" style={{ flex: 1, minWidth: 0 }}><Icon name="calendar" size={15} color="var(--orange)" />Next-transfusion forecast</span>
        <span className="chip-status" style={{ background: conf.wash, color: conf.color, flex: 'none', whiteSpace: 'nowrap' }}><span style={{ width: 7, height: 7, borderRadius: 999, background: conf.color }} />{conf.label}</span>
      </div>
      <div className="body" style={{ fontSize: 12.5, marginBottom: 14 }}>“When is my Hb likely to dip again?”</div>

      {/* likely window — a range, never a single hard date */}
      <div className="dmeta muted" style={{ fontWeight: 800, textTransform: 'uppercase', letterSpacing: '.04em', fontSize: 10.5, marginBottom: 6 }}>Likely next-transfusion window</div>
      <div className="row" style={{ alignItems: 'baseline', gap: 8, flexWrap: 'wrap' }}>
        <span className="num" style={{ fontSize: 34, fontWeight: 800, letterSpacing: '-.02em', lineHeight: 1, color: 'var(--orange)' }}>{windowText}</span>
        {!f.overdue && <span style={{ fontWeight: 800, fontSize: 15, color: 'var(--ink-2)' }}>day{sameDay && wl.hiDays === 1 ? '' : 's'} from now</span>}
        {!f.overdue &&
        <span className="chip-status" style={{ marginLeft: 'auto', background: 'var(--orange-wash)', color: 'var(--orange)' }}>
          <Icon name="calendar" size={12} color="var(--orange)" />{TC.fmtDate(wl.loDate, { day: 'numeric', month: 'short' })} – {TC.fmtDate(wl.hiDate, { day: 'numeric', month: 'short' })}
        </span>}
      </div>
      <div className="body" style={{ fontSize: 12, lineHeight: 1.5, marginTop: 8, color: 'var(--ink-2)' }}>
        {f.overdue ?
        `Based on your pattern, you’re likely around the point where your Hb dips to its trigger. A wider read is anytime up to ~${ww.hiDays} day${ww.hiDays === 1 ? '' : 's'} from now.` :
        <>Roughly a <strong>{oneIn10} in 10</strong> chance your next transfusion falls in this window. A wider read is about <strong>{ww.loDays}–{ww.hiDays} days</strong> from now.</>}
      </div>

      {/* runway — elapsed since last transfusion, then the window ahead */}
      <div style={{ marginTop: 16, height: 12, borderRadius: 999, background: 'var(--surface-2)', position: 'relative', overflow: 'hidden' }}>
        {/* elapsed */}
        <div style={{ position: 'absolute', top: 0, bottom: 0, left: 0, width: elapsed, background: 'linear-gradient(90deg, color-mix(in oklab, var(--orange) 28%, var(--surface)), color-mix(in oklab, var(--orange) 50%, var(--surface)))' }} />
        {/* wider possible range */}
        <div style={{ position: 'absolute', top: 0, bottom: 0, left: wideL, right: `calc(100% - ${wideR})`, background: 'var(--orange-wash)' }} />
        {/* likely window */}
        <div style={{ position: 'absolute', top: 0, bottom: 0, left: likeL, right: `calc(100% - ${likeR})`, background: 'linear-gradient(90deg, var(--orange-2), var(--orange))', borderRadius: 999 }} />
        {/* today marker */}
        <div style={{ position: 'absolute', top: -2, bottom: -2, left: elapsed, width: 2.5, background: 'var(--ink)', transform: 'translateX(-1px)' }} />
      </div>
      <div className="between" style={{ marginTop: 7 }}>
        <span className="dmeta muted">Last · {f.daysSince}d ago</span>
        <span className="dmeta muted" style={{ color: 'var(--orange)', fontWeight: 800 }}>Likely window</span>
        <span className="dmeta muted">≈ {Math.round(f.blendedCycle || f.avgCycle)}d cycle</span>
      </div>

      {/* factors that shaped the prediction — collapsed by default, tap to expand */}
      <div style={{ marginTop: 13 }}>
        <button onClick={() => setDrivingOpen((v) => !v)} className="between" style={{ width: '100%', gap: 8, background: 'none', border: 'none', padding: 0, cursor: 'pointer', font: 'inherit', textAlign: 'left' }} aria-expanded={drivingOpen}>
          <span className="dmeta muted" style={{ fontWeight: 800, textTransform: 'uppercase', letterSpacing: '.04em', fontSize: 10.5 }}>What's driving this</span>
          <Icon name="chevD" size={15} color="var(--ink-3)" style={{ flex: 'none', transition: 'transform .2s', transform: drivingOpen ? 'rotate(180deg)' : 'none' }} />
        </button>
        {drivingOpen &&
        <>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 11, marginTop: 9 }}>
          {f.factors.map((fc) => {
              const missing = fc.effect === 'missing';
              const eff = EFFECT[fc.effect];
              return (
                <div key={fc.key} className="row" style={{ gap: 10, alignItems: 'flex-start', opacity: missing ? 0.72 : 1 }}>
                <div className="leaf" style={{ width: 30, height: 30, flex: 'none', borderRadius: 10, background: 'var(--surface-2)', color: missing ? 'var(--ink-3)' : 'var(--ink-2)' }}>
                  <Icon name={fc.icon} size={15} color={missing ? 'var(--ink-3)' : 'var(--ink-2)'} />
                </div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div className="between" style={{ gap: 8 }}>
                    <span style={{ fontWeight: 800, fontSize: 12.5, color: 'var(--ink)' }}>{fc.label}</span>
                    <span className="num" style={{ fontWeight: 800, fontSize: 12.5, color: missing ? 'var(--ink-3)' : 'var(--ink)', flex: 'none', whiteSpace: 'nowrap' }}>{fc.value}</span>
                  </div>
                  <div className="row" style={{ gap: 6, marginTop: 3, alignItems: 'flex-start' }}>
                    {eff && <Icon name={eff.icon} size={12} color={eff.color} style={{ flex: 'none', marginTop: 1 }} />}
                    <span className="body" style={{ fontSize: 11.5, lineHeight: 1.4, color: eff ? eff.color : 'var(--ink-3)' }}>{fc.detail}</span>
                  </div>
                </div>
              </div>);

            })}
        </div>
        {/* clinical numbers when a decline model is available */}
        {f.hasDecline &&
          <div className="card-flat" style={{ padding: '12px 14px', marginTop: 14, background: 'var(--surface-2)' }}>
          <div className="between"><span className="body" style={{ fontWeight: 800, fontSize: 12.5 }}>Estimated decline</span><span className="num" style={{ fontWeight: 800, fontSize: 13.5, color: 'var(--orange)' }}>≈ {f.rate.toFixed(2)} g/dL / day</span></div>
          <div className="between" style={{ marginTop: 8 }}><span className="body" style={{ fontWeight: 800, fontSize: 12.5 }}>Estimated Hb today</span><span className="num" style={{ fontWeight: 800, fontSize: 13.5, color: 'var(--ink)' }}>≈ {f.estCurrent.toFixed(1)} g/dL</span></div>
          {f.peak != null && <div className="between" style={{ marginTop: 8 }}><span className="body" style={{ fontWeight: 800, fontSize: 12.5 }}>Post-transfusion peak{f.lastDonorHb ? ' (donor-adjusted)' : ''}</span><span className="num" style={{ fontWeight: 800, fontSize: 13.5, color: 'var(--success)' }}>≈ {f.peak.toFixed(1)} g/dL</span></div>}
          <div className="body" style={{ fontSize: 11.5, marginTop: 9, lineHeight: 1.45 }}>Projecting from your last transfusion's estimated peak down to a {f.trigger.toFixed(1)} g/dL trigger.</div>
        </div>
          }
        </>
        }
      </div>

      {/* what's softening the confidence — honest about uncertainty */}
      {f.caveats && f.caveats.length > 0 &&
      <div style={{ marginTop: 12, padding: '12px 14px', borderRadius: 14, background: 'var(--yellow-wash)' }}>
          <div className="row" style={{ gap: 8, alignItems: 'center', marginBottom: 7 }}>
            <Icon name="info" size={14} color="var(--waiting)" style={{ flex: 'none' }} />
            <span style={{ fontWeight: 800, fontSize: 12, color: 'var(--ink)' }}>Why this window stays soft</span>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            {f.caveats.map((c, i) =>
          <div key={i} className="row" style={{ gap: 7, alignItems: 'flex-start' }}>
                <span style={{ width: 5, height: 5, borderRadius: 999, background: 'var(--waiting)', flex: 'none', marginTop: 6 }} />
                <span className="body" style={{ fontSize: 11.5, lineHeight: 1.5, color: 'var(--ink-2)' }}>{c}</span>
              </div>)}
          </div>
        </div>
      }

      <div className="row" style={{ gap: 11, alignItems: 'flex-start', marginTop: 14, padding: '13px 14px', borderRadius: 14, background: 'rgba(229,72,77,0.09)', border: '1.5px solid rgba(229,72,77,0.22)' }}>
        <div className="leaf" style={{ width: 32, height: 32, flex: 'none', borderRadius: 10, background: 'var(--surface)', color: 'var(--danger)' }}>
          <Icon name="warn" size={17} color="var(--danger)" />
        </div>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontWeight: 800, fontSize: 13, color: 'var(--danger)' }}>A guide, not a clinical instruction</div>
          <div className="body" style={{ fontSize: 12, lineHeight: 1.5, color: 'var(--ink-2)', marginTop: 3 }}>Estimate only. Hb levels can change for reasons MUIY doesn't track. Always follow your healthcare team's advice.</div>
        </div>
      </div>
    </div>);

}

/* ---- 4 · Achievements / milestones ---- */
function HbAchievements({ rows, targets }) {
  if (!rows.length) return null;
  const list = TC.hbAchievements(rows, targets);
  const earnedN = list.filter((a) => a.earned).length;
  return (
    <div className="card" style={{ marginTop: 12 }}>
      <div className="between" style={{ marginBottom: 12 }}>
        <span className="sec-title"><Icon name="sparkle" size={15} color="var(--yellow)" />Milestones</span>
        <span className="chip-status" style={{ background: 'var(--green-wash)', color: 'var(--success)' }}>{earnedN} / {list.length} earned</span>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
        {list.map((a) =>
        <div key={a.id} style={{ position: 'relative', padding: 13, borderRadius: 15, border: '1.5px solid var(--hairline)', background: a.earned ? 'var(--green-wash)' : 'var(--surface-2)', opacity: a.earned ? 1 : 0.82 }}>
            <div className="leaf" style={{ width: 36, height: 36, borderRadius: 12, background: a.earned ? '#fff' : 'var(--surface)', color: a.earned ? 'var(--success)' : 'var(--ink-3)' }}>
              <Icon name={a.earned ? a.icon : 'shield'} size={18} color={a.earned ? 'var(--success)' : 'var(--ink-3)'} fill={a.earned && (a.icon === 'sparkle' || a.icon === 'heart') ? 'var(--success)' : 'none'} />
            </div>
            <div style={{ fontWeight: 800, fontSize: 13, color: a.earned ? 'var(--ink)' : 'var(--ink-2)', marginTop: 10, lineHeight: 1.25 }}>{a.title}</div>
            <div className="dmeta muted" style={{ marginTop: 4, lineHeight: 1.4, fontSize: 11 }}>{a.desc}</div>
            <div className="chip-status" style={{ marginTop: 9, background: a.earned ? '#fff' : 'var(--surface)', color: a.earned ? 'var(--success)' : 'var(--ink-3)', fontSize: 11 }}>
              {a.earned ? <Icon name="check" size={11} color="var(--success)" sw={3} /> : null}{a.progress}
            </div>
          </div>
        )}
      </div>
    </div>);

}

Object.assign(window, { HbMiniChart, HbSmartStatus, HbTrendAnalysis, HbForecast, HbAchievements });