// ============================================================
// Visualizations — μPAD, charts, phone screens, reaction
// ============================================================
const { useState, useEffect, useRef, useMemo } = React;

// ---- color mapping for UA detection ----
// Maps concentration (mg/dL) to a Prussian-teal RGBA representing the
// chromogenic complex intensity on the paper.
function uaColor(conc, alpha = 1) {
  // Anchor: 0 -> very faint cream-blue, 30 -> deep Prussian
  const t = Math.max(0, Math.min(1, conc / 30));
  const easeT = Math.pow(t, 0.72);
  // base paper R G B around 235,228,210; deep around 18,55,75
  const r = Math.round(235 - (235 - 18)  * easeT);
  const g = Math.round(228 - (228 - 70)  * easeT);
  const b = Math.round(208 - (208 - 95)  * easeT);
  return `rgba(${r},${g},${b},${alpha})`;
}

function classify(conc) {
  if (conc < 2.7) return { label: "Low",    tone: "warn",  note: "Below physiological range — possible neurodegenerative association" };
  if (conc <= 7.2) return { label: "Normal", tone: "good",  note: "Within healthy plasma range (2.7 – 7.2 mg/dL)" };
  return                     { label: "High",   tone: "bad",  note: "Hyperuricemia threshold exceeded — gout / metabolic risk" };
}

const REF_CONCS = [1.5, 2.7, 7.2, 11.7, 16.2, 20.7, 25.2, 29.7];

// ============================================================
// μPAD — the eight-zone paper device
// ============================================================
function MicroPAD({ sample = 16.2, highlightIdx = null, interactive = false, onPickRef = null }) {
  const cx = 200, cy = 200, R = 140;
  // 8 reference zones arranged in a ring, sample in center
  return (
    <svg viewBox="0 0 400 400" style={{ width: "100%", maxWidth: 420, display: "block" }}>
      {/* paper backdrop with fiber texture */}
      <defs>
        <pattern id="fiber" width="6" height="6" patternUnits="userSpaceOnUse">
          <rect width="6" height="6" fill="#f4ecd6" />
          <path d="M0 3 L6 3" stroke="#e9dfc4" strokeWidth="0.4" />
          <path d="M3 0 L3 6" stroke="#e9dfc4" strokeWidth="0.3" />
        </pattern>
        <filter id="softGlow" x="-30%" y="-30%" width="160%" height="160%">
          <feGaussianBlur stdDeviation="2" />
        </filter>
        <radialGradient id="zoneG" cx="50%" cy="45%" r="60%">
          <stop offset="0%" stopColor="rgba(255,255,255,0.5)" />
          <stop offset="100%" stopColor="rgba(0,0,0,0)" />
        </radialGradient>
      </defs>

      <rect x="20" y="20" width="360" height="360" fill="url(#fiber)" stroke="#c7bd9a" strokeWidth="1" />

      {/* hydrophobic channels (lines from sample to each ref) */}
      {REF_CONCS.map((_, i) => {
        const a = (i / 8) * Math.PI * 2 - Math.PI / 2;
        const x2 = cx + R * Math.cos(a);
        const y2 = cy + R * Math.sin(a);
        return (
          <line key={i} x1={cx} y1={cy} x2={x2} y2={y2}
            stroke="#7a6f4d" strokeWidth="2" strokeDasharray="1 3" opacity="0.4" />
        );
      })}

      {/* reference zones */}
      {REF_CONCS.map((c, i) => {
        const a = (i / 8) * Math.PI * 2 - Math.PI / 2;
        const x = cx + R * Math.cos(a);
        const y = cy + R * Math.sin(a);
        const isHi = highlightIdx === i;
        return (
          <g key={i}
             onClick={() => interactive && onPickRef && onPickRef(c)}
             style={{ cursor: interactive ? "pointer" : "default" }}>
            <circle cx={x} cy={y} r="34" fill={uaColor(c)} stroke="#5c4d22" strokeWidth="1.2" />
            <circle cx={x} cy={y} r="34" fill="url(#zoneG)" />
            {isHi && <circle cx={x} cy={y} r="40" fill="none" stroke="#16161a" strokeWidth="1.5" strokeDasharray="3 3" />}
            <text x={x} y={y + 56} textAnchor="middle" fontSize="11"
              fill="#16161a" style={{ fontFamily: "IBM Plex Mono" }}>
              C{i+1} · {c.toFixed(1)}
            </text>
          </g>
        );
      })}

      {/* central sample zone */}
      <circle cx={cx} cy={cy} r="38" fill={uaColor(sample)} stroke="#16161a" strokeWidth="1.5" />
      <circle cx={cx} cy={cy} r="38" fill="url(#zoneG)" />
      <circle cx={cx} cy={cy} r="44" fill="none" stroke="#16161a" strokeWidth="1" strokeDasharray="2 2" />
      <text x={cx} y={cy + 4} textAnchor="middle" fontSize="11"
        fill="#16161a" style={{ fontFamily: "IBM Plex Mono", letterSpacing: "0.05em" }}>
        SAMPLE
      </text>

      {/* corner registration mark */}
      <g fontFamily="IBM Plex Mono" fontSize="9" fill="#6b6660">
        <text x="32" y="36">μPAD · WHATMAN G1</text>
        <text x="368" y="376" textAnchor="end">⌀ 7.9 mm · w 2.3 mm</text>
      </g>
    </svg>
  );
}

// ============================================================
// Calibration curve — Intensity vs Concentration (interactive)
// ============================================================
function CalibrationCurve({ activeConc, onHover }) {
  // Mean gray value decreases as conc increases (per paper). Linear fit:
  // Y = a - b * X  (approximate values)
  const W = 540, H = 320;
  const padL = 56, padR = 24, padT = 24, padB = 48;
  const xMin = 0, xMax = 32;
  const yMin = 80, yMax = 230;
  const sx = (x) => padL + (x - xMin) / (xMax - xMin) * (W - padL - padR);
  const sy = (y) => padT + (yMax - y) / (yMax - yMin) * (H - padT - padB);

  // Calibration data (derived from paper, slight noise)
  const pts = REF_CONCS.map((c, i) => {
    const y = 215 - 4.4 * c + (i % 2 === 0 ? 1.6 : -1.4);
    return { c, y };
  });
  const fitY = (c) => 215 - 4.4 * c;

  const activeY = activeConc != null ? fitY(activeConc) : null;
  const [hover, setHover] = useState(null);

  return (
    <svg viewBox={`0 0 ${W} ${H}`} style={{ width: "100%", display: "block" }}
      onMouseLeave={() => { setHover(null); onHover && onHover(null); }}
      onMouseMove={(e) => {
        const r = e.currentTarget.getBoundingClientRect();
        const px = (e.clientX - r.left) * W / r.width;
        if (px < padL || px > W - padR) return;
        const c = xMin + (px - padL) / (W - padL - padR) * (xMax - xMin);
        setHover(c);
        onHover && onHover(c);
      }}>
      {/* gridlines */}
      {[0,5,10,15,20,25,30].map(x => (
        <line key={"vx"+x} x1={sx(x)} y1={padT} x2={sx(x)} y2={H - padB}
          className="gridline" />
      ))}
      {[80,120,160,200,240].map(y => (
        <line key={"hy"+y} x1={padL} y1={sy(y)} x2={W - padR} y2={sy(y)}
          className="gridline" />
      ))}

      {/* fit line */}
      <line x1={sx(0)} y1={sy(fitY(0))} x2={sx(30)} y2={sy(fitY(30))}
        stroke="#1f5266" strokeWidth="1.5" />

      {/* axes */}
      <line x1={padL} y1={H - padB} x2={W - padR} y2={H - padB} className="axis" strokeWidth="1" />
      <line x1={padL} y1={padT} x2={padL} y2={H - padB} className="axis" strokeWidth="1" />

      {/* x labels */}
      {[0,5,10,15,20,25,30].map(x => (
        <g key={"xl"+x}>
          <line x1={sx(x)} y1={H-padB} x2={sx(x)} y2={H-padB+4} className="axis" />
          <text x={sx(x)} y={H-padB+18} textAnchor="middle">{x}</text>
        </g>
      ))}
      {[80,120,160,200,240].map(y => (
        <g key={"yl"+y}>
          <line x1={padL} y1={sy(y)} x2={padL-4} y2={sy(y)} className="axis" />
          <text x={padL-8} y={sy(y)+4} textAnchor="end">{y}</text>
        </g>
      ))}

      {/* data points */}
      {pts.map((p, i) => (
        <g key={i}>
          <circle cx={sx(p.c)} cy={sy(p.y)} r="5"
            fill={uaColor(p.c)} stroke="#16161a" strokeWidth="1.2" />
        </g>
      ))}

      {/* active marker (from slider) */}
      {activeY != null && (
        <g>
          <line x1={sx(activeConc)} y1={padT} x2={sx(activeConc)} y2={H-padB}
            stroke="#16161a" strokeWidth="1" strokeDasharray="3 3" />
          <line x1={padL} y1={sy(activeY)} x2={W-padR} y2={sy(activeY)}
            stroke="#16161a" strokeWidth="1" strokeDasharray="3 3" />
          <circle cx={sx(activeConc)} cy={sy(activeY)} r="7"
            fill="#16161a" stroke="#f6f3ec" strokeWidth="2" />
          <text x={sx(activeConc)+12} y={sy(activeY)-10}
            style={{ fontFamily: "IBM Plex Mono", fontSize: 11, fill: "#16161a" }}>
            {activeConc.toFixed(1)} mg/dL · I={activeY.toFixed(0)}
          </text>
        </g>
      )}

      {/* axis titles */}
      <text x={(W)/2} y={H-8} textAnchor="middle" className="label-l">Uric acid concentration (mg/dL)</text>
      <text transform={`translate(16 ${H/2}) rotate(-90)`} textAnchor="middle" className="label-l">Mean gray value (intensity)</text>

      {/* fit equation */}
      <g transform={`translate(${W-padR-180} ${padT+8})`}>
        <rect width="170" height="36" fill="#fbf9f3" stroke="#d6d1c4" />
        <text x="10" y="14" fontSize="10" fill="#16161a">FIT</text>
        <text x="10" y="28" style={{ fontFamily: "IBM Plex Mono", fontSize: 11, fill: "#16161a" }}>
          I = 215 − 4.40 · C
        </text>
      </g>
    </svg>
  );
}

// ============================================================
// Selectivity bar chart
// ============================================================
function SelectivityChart() {
  const data = [
    { name: "Uric acid",   conc: "7.0 mg/dL", val: 100, target: true },
    { name: "Urea",        conc: "4.0 mg/dL", val: 7 },
    { name: "Glucose",     conc: "2.0 mg/dL", val: 4 },
    { name: "Creatinine",  conc: "2.5 mg/dL", val: 6 },
    { name: "Ascorbic acid", conc: "1.0 mg/dL", val: 9 },
  ];
  const max = 100;
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
      {data.map((d, i) => (
        <div key={i}>
          <div style={{
            display: "flex", justifyContent: "space-between", alignItems: "baseline",
            marginBottom: 4, fontFamily: "var(--sans)", fontSize: 12
          }}>
            <span style={{ color: d.target ? "var(--ink)" : "var(--mute)", fontWeight: d.target ? 600 : 400 }}>
              {d.name}
              <span style={{ color: "var(--mute-2)", marginLeft: 8, fontFamily: "var(--mono)", fontSize: 10 }}>
                {d.conc}
              </span>
            </span>
            <span style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--ink)" }}>
              {d.val}%
            </span>
          </div>
          <div style={{ height: 22, background: "var(--paper-3)", border: "1px solid var(--rule)", position: "relative" }}>
            <div style={{
              width: `${(d.val/max)*100}%`,
              height: "100%",
              background: d.target ? "var(--accent)" : "var(--mute-2)",
              transition: "width .8s cubic-bezier(.2,.6,.2,1)"
            }} />
          </div>
        </div>
      ))}
    </div>
  );
}

// ============================================================
// FTIR toggle figure
// ============================================================
function FTIRFig() {
  const [mode, setMode] = useState("modified"); // "traditional" | "modified"
  const peaks = {
    traditional: [
      { wn: 3307, label: "H₂O", h: 0.4 },
      { wn: 2075, label: "−CN (Fe²⁺)", h: 0.85, key: true },
      { wn: 1647, label: "C=O of UA", h: 0.65 },
      { wn: 1317, label: "C−N", h: 0.45 },
      { wn: 1037, label: "C−O strong", h: 0.7, key: true },
    ],
    modified: [
      { wn: 3328, label: "H₂O", h: 0.4 },
      { wn: 2086, label: "−CN (free)", h: 0.55, key: true },
      { wn: 1640, label: "C=O of UA", h: 0.5 },
      { wn: 1318, label: "C−N", h: 0.4 },
      { wn: 1035, label: "C−O weak", h: 0.3, key: true },
    ],
  };
  const W = 540, H = 240, padL = 50, padR = 20, padT = 20, padB = 36;
  const wnMin = 600, wnMax = 3600;
  const sx = (wn) => padL + (wnMax - wn) / (wnMax - wnMin) * (W - padL - padR); // inverted axis
  const sy = (h) => padT + (1 - h) * (H - padT - padB);

  return (
    <div>
      <div style={{ display: "flex", gap: 8, marginBottom: 14 }}>
        {["traditional","modified"].map(m => (
          <button key={m}
            className={`btn sm ${mode===m?"":"ghost"}`}
            onClick={() => setMode(m)}>
            {m === "traditional" ? "Traditional · KCN→UA→FeCl₃" : "Modified · KCN→FeCl₃→UA"}
          </button>
        ))}
      </div>
      <svg viewBox={`0 0 ${W} ${H}`} style={{ width: "100%", display: "block" }}>
        {/* axis */}
        <line x1={padL} y1={H-padB} x2={W-padR} y2={H-padB} className="axis" />
        <line x1={padL} y1={padT} x2={padL} y2={H-padB} className="axis" />
        {[1000,1500,2000,2500,3000,3500].map(wn => (
          <g key={wn}>
            <line x1={sx(wn)} y1={H-padB} x2={sx(wn)} y2={H-padB+4} className="axis" />
            <text x={sx(wn)} y={H-padB+18} textAnchor="middle">{wn}</text>
          </g>
        ))}
        {/* baseline curve placeholder (smooth wavy line) */}
        <path d={`M${padL} ${sy(0.05)} ${Array.from({length: 100}).map((_,i)=>{
          const x = padL + i*(W-padL-padR)/100;
          const noise = Math.sin(i*0.6)*1.5 + Math.cos(i*1.3)*0.8;
          return `L${x} ${sy(0.05)+noise}`;
        }).join(' ')}`} fill="none" stroke="#6b6660" strokeWidth="0.8" opacity="0.5" />

        {/* peaks */}
        {peaks[mode].map((p, i) => (
          <g key={i}>
            <path
              d={`M${sx(p.wn)-14} ${sy(0.05)} Q${sx(p.wn)-6} ${sy(p.h)} ${sx(p.wn)} ${sy(p.h)} Q${sx(p.wn)+6} ${sy(p.h)} ${sx(p.wn)+14} ${sy(0.05)}`}
              fill="none" stroke={p.key ? "var(--accent)" : "var(--ink)"}
              strokeWidth={p.key ? 1.8 : 1.2}
              style={{ transition: "all .4s" }}
            />
            <text x={sx(p.wn)} y={sy(p.h) - 6} textAnchor="middle"
              style={{
                fontFamily: "IBM Plex Mono", fontSize: 10,
                fill: p.key ? "var(--accent)" : "var(--mute)"
              }}>
              {p.wn}
            </text>
            {p.key && (
              <text x={sx(p.wn)} y={sy(p.h) - 20} textAnchor="middle"
                style={{ fontFamily: "IBM Plex Sans", fontSize: 10, fill: "var(--ink)" }}>
                {p.label}
              </text>
            )}
          </g>
        ))}
        <text x={(W)/2} y={H-6} textAnchor="middle" className="label-l">Wavenumber (cm⁻¹)</text>
        <text transform={`translate(14 ${H/2}) rotate(-90)`} textAnchor="middle" className="label-l">Transmittance</text>
      </svg>
    </div>
  );
}

// ============================================================
// Reaction mechanism viewer (3 steps)
// ============================================================
function ReactionMechanism() {
  const [step, setStep] = useState(0);
  const steps = [
    {
      title: "Step 1 · Reagent priming",
      formula: "3 K₃[Fe(CN)₆] + 4 FeCl₃ → Fe₄[Fe(CN)₆]₃ + 12 KCl",
      caption: "Potassium ferricyanide and ferric chloride combine on-paper to form ferric ferricyanide — the priming complex.",
      color: "#bcc6c4"
    },
    {
      title: "Step 2 · Sample arrival",
      formula: "Fe₄[Fe(CN)₆]₃ + Uric Acid → Fe⁺³-CN-Fe⁺²-UA complex",
      caption: "When uric acid reaches the immobilized complex, electron transfer reduces Fe³⁺ → Fe²⁺, producing a bluish-green ferric ferrocyanide-UA intermediate.",
      color: "#6b8b94"
    },
    {
      title: "Step 3 · Prussian-blue intensification",
      formula: "Excess K₃[Fe(CN)₆] + Fe²⁺ → Fe⁺³-CN-Fe⁺² · UA   (intense)",
      caption: "Residual ferricyanide reacts with the newly-formed Fe²⁺ to amplify the chromogenic signal — intensity is proportional to UA concentration.",
      color: "#1f5266"
    },
  ];

  // auto-cycle on hover stop
  return (
    <div>
      <div style={{ display: "flex", gap: 8, marginBottom: 18 }}>
        {steps.map((s, i) => (
          <button key={i}
            className={`btn sm ${step===i?"":"ghost"}`}
            onClick={() => setStep(i)}>
            {String(i+1).padStart(2,"0")}
          </button>
        ))}
        <div style={{ flex: 1 }}></div>
        <button className="btn sm ghost" onClick={() => setStep((step+1)%3)}>
          Next →
        </button>
      </div>

      {/* visual: three molecules flowing into a complex */}
      <svg viewBox="0 0 540 200" style={{ width: "100%", display: "block" }}>
        <defs>
          <radialGradient id="mol1"><stop offset="0" stopColor="#b56b3a"/><stop offset="1" stopColor="#7c4321"/></radialGradient>
          <radialGradient id="mol2"><stop offset="0" stopColor="#3a5b6b"/><stop offset="1" stopColor="#1f3a48"/></radialGradient>
          <radialGradient id="mol3"><stop offset="0" stopColor="#7a8a3a"/><stop offset="1" stopColor="#4a571f"/></radialGradient>
        </defs>

        {/* molecule A: Fe3+ */}
        <g style={{ transition: "all .6s cubic-bezier(.2,.6,.2,1)",
                    transform: step > 0 ? "translate(180px, 0)" : "translate(0,0)",
                    opacity: step === 2 ? 0.2 : 1 }}>
          <circle cx="80" cy="100" r="22" fill="url(#mol1)" />
          <text x="80" y="104" textAnchor="middle" fill="#fbf9f3" style={{fontFamily:"IBM Plex Mono",fontSize:11}}>Fe³⁺</text>
        </g>

        {/* molecule B: Ferricyanide */}
        <g style={{ transition: "all .6s cubic-bezier(.2,.6,.2,1)",
                    transform: step > 0 ? "translate(120px, 0)" : "translate(0,0)",
                    opacity: step === 2 ? 0.2 : 1 }}>
          <circle cx="200" cy="100" r="22" fill="url(#mol2)" />
          <text x="200" y="98" textAnchor="middle" fill="#fbf9f3" style={{fontFamily:"IBM Plex Mono",fontSize:9}}>K₃[Fe</text>
          <text x="200" y="110" textAnchor="middle" fill="#fbf9f3" style={{fontFamily:"IBM Plex Mono",fontSize:9}}>(CN)₆]</text>
        </g>

        {/* UA */}
        <g style={{ transition: "all .6s cubic-bezier(.2,.6,.2,1)",
                    transform: step >= 1 ? "translate(20px, 0)" : "translate(180px, 0)",
                    opacity: step >= 1 ? 1 : 0 }}>
          <circle cx="320" cy="100" r="22" fill="url(#mol3)" />
          <text x="320" y="104" textAnchor="middle" fill="#fbf9f3" style={{fontFamily:"IBM Plex Mono",fontSize:10}}>UA</text>
        </g>

        {/* arrow */}
        <g>
          <line x1="370" y1="100" x2="430" y2="100" stroke="#16161a" strokeWidth="1" markerEnd="url(#arrow)" />
          <polygon points="430,100 423,96 423,104" fill="#16161a" />
        </g>

        {/* product blob */}
        <g style={{ transition: "all .8s", opacity: step >= 1 ? 1 : 0.2 }}>
          <circle cx="480" cy="100" r="32" fill={steps[step].color}
            stroke="#16161a" strokeWidth="1" />
          <circle cx="480" cy="100" r="32" fill="url(#zoneG2)" />
          <text x="480" y="100" textAnchor="middle" fill="#fbf9f3" style={{fontFamily:"IBM Plex Mono",fontSize:9}}>
            COMPLEX
          </text>
          <text x="480" y="112" textAnchor="middle" fill="#fbf9f3" style={{fontFamily:"IBM Plex Mono",fontSize:9}}>
            STEP {step+1}
          </text>
        </g>

        {/* labels */}
        <text x="80" y="148" textAnchor="middle" className="label-l">Ferric chloride</text>
        <text x="200" y="148" textAnchor="middle" className="label-l">Ferricyanide</text>
        <text x="320" y="148" textAnchor="middle" className="label-l" opacity={step>=1?1:0.3}>Uric acid</text>
        <text x="480" y="150" textAnchor="middle" className="label-l">Chromogenic complex</text>
      </svg>

      <div style={{ marginTop: 20, padding: 18, background: "var(--paper-2)", border: "1px solid var(--rule)" }}>
        <div className="label" style={{ marginBottom: 8 }}>{steps[step].title}</div>
        <div style={{ fontFamily: "var(--mono)", fontSize: 13, marginBottom: 10, color: "var(--accent)" }}>
          {steps[step].formula}
        </div>
        <div style={{ fontFamily: "var(--serif)", fontSize: 15, color: "var(--ink-2)", lineHeight: 1.5 }}>
          {steps[step].caption}
        </div>
      </div>
    </div>
  );
}

// ============================================================
// Phone screens for the app walkthrough
// ============================================================
function PhoneApp({ stage, sample }) {
  const conc = sample ?? 26.1;
  const cls = classify(conc);
  return (
    <div className="phone">
      <div className="phone-screen">
        {/* status bar */}
        <div style={{
          padding: "12px 16px 6px", display: "flex", justifyContent: "space-between",
          fontFamily: "var(--mono)", fontSize: 10, color: "var(--ink)"
        }}>
          <span>9:41</span>
          <span>UA • DETECT</span>
          <span>◉ ▮▮▮</span>
        </div>

        {/* header */}
        <div style={{ padding: "0 16px 12px", borderBottom: "1px solid var(--rule)" }}>
          <div className="label" style={{ fontSize: 9, color: "var(--mute)" }}>μPAD ANALYSIS</div>
          <div style={{ fontFamily: "var(--serif)", fontSize: 16, marginTop: 4 }}>
            {stage === 0 && "Capture image"}
            {stage === 1 && "Detect circles"}
            {stage === 2 && "Set concentrations"}
            {stage === 3 && "Compute result"}
          </div>
        </div>

        {/* screen body */}
        <div style={{ padding: 16 }}>
          {stage === 0 && (
            <div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 14, padding: "24px 0" }}>
              <div style={{
                width: 130, height: 130,
                border: "2px dashed var(--accent)", borderRadius: 8,
                position: "relative",
                display: "flex", alignItems: "center", justifyContent: "center"
              }}>
                <div style={{ width: 80, height: 80, borderRadius: "50%",
                  background: "var(--paper-3)", border: "1px solid var(--rule)" }} />
                <div style={{ position: "absolute", top: -1, left: -1, width: 12, height: 12, borderTop: "2px solid var(--accent)", borderLeft: "2px solid var(--accent)" }}/>
                <div style={{ position: "absolute", top: -1, right: -1, width: 12, height: 12, borderTop: "2px solid var(--accent)", borderRight: "2px solid var(--accent)" }}/>
                <div style={{ position: "absolute", bottom: -1, left: -1, width: 12, height: 12, borderBottom: "2px solid var(--accent)", borderLeft: "2px solid var(--accent)" }}/>
                <div style={{ position: "absolute", bottom: -1, right: -1, width: 12, height: 12, borderBottom: "2px solid var(--accent)", borderRight: "2px solid var(--accent)" }}/>
              </div>
              <div style={{ fontFamily: "var(--sans)", fontSize: 10, color: "var(--mute)", textAlign: "center" }}>
                Hold camera 24 cm above μPAD
              </div>
            </div>
          )}
          {stage === 1 && (
            <div style={{ position: "relative", width: "100%", paddingTop: "100%" }}>
              <div style={{ position: "absolute", inset: 0 }}>
                <MicroPAD sample={conc} />
                <div style={{ position: "absolute", top: 8, right: 8, fontFamily: "var(--mono)", fontSize: 8, background: "var(--accent)", color: "var(--paper-2)", padding: "2px 6px" }}>
                  HOUGH ✓ 9
                </div>
              </div>
            </div>
          )}
          {stage === 2 && (
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 6 }}>
              {REF_CONCS.map((c, i) => (
                <div key={i} style={{
                  padding: "6px 8px", border: "1px solid var(--rule)",
                  background: "var(--paper-2)", fontFamily: "var(--mono)", fontSize: 10,
                  display: "flex", justifyContent: "space-between"
                }}>
                  <span style={{ color: "var(--mute)" }}>C{i+1}</span>
                  <span>{c.toFixed(1)}</span>
                </div>
              ))}
              <div style={{ gridColumn: "1 / 3",
                padding: "8px", background: "var(--accent)", color: "var(--paper-2)",
                fontFamily: "var(--mono)", fontSize: 10, textAlign: "center" }}>
                SAMPLE ☑ centre
              </div>
            </div>
          )}
          {stage === 3 && (
            <div>
              <div style={{ background: "var(--paper-3)", border: "1px solid var(--rule)", padding: 12, marginBottom: 12 }}>
                <div className="label" style={{ fontSize: 9 }}>RESULT</div>
                <div style={{ fontFamily: "var(--serif)", fontSize: 32, color: "var(--accent)", lineHeight: 1, margin: "6px 0 2px" }}>
                  {conc.toFixed(1)}
                </div>
                <div style={{ fontFamily: "var(--mono)", fontSize: 10, color: "var(--mute)" }}>mg/dL</div>
              </div>
              <div style={{
                padding: "8px 12px",
                background: cls.tone === "good" ? "var(--good)" : cls.tone === "bad" ? "var(--bad)" : "var(--warn)",
                color: "var(--paper-2)",
                fontFamily: "var(--sans)", fontSize: 12, fontWeight: 600,
                letterSpacing: "0.1em", textTransform: "uppercase", textAlign: "center"
              }}>
                {cls.label}
              </div>
              <div style={{ marginTop: 12, fontFamily: "var(--sans)", fontSize: 10, color: "var(--mute)", lineHeight: 1.4 }}>
                Linear interpolation against C1–C8 calibration. View graph for fit.
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

// expose
Object.assign(window, { MicroPAD, CalibrationCurve, SelectivityChart, FTIRFig, ReactionMechanism, PhoneApp, uaColor, classify, REF_CONCS });
