// ============================================================
// Main app for the research paper website
// ============================================================
const { useState, useEffect, useRef } = React;

// ---- TOC + scroll spy ----
const SECTIONS = [
  { id: "abstract",   label: "Abstract" },
  { id: "background", label: "Background" },
  { id: "device",     label: "The μPAD" },
  { id: "reaction",   label: "Chromogenic reaction" },
  { id: "detection",  label: "Live detection" },
  { id: "results",    label: "Results" },
  { id: "app",        label: "Smartphone app" },
  { id: "methods",    label: "Methods" },
  { id: "authors",    label: "Authors & cite" },
  { id: "refs",       label: "References" },
];

function useScrollSpy(ids) {
  const [active, setActive] = useState(ids[0]);
  useEffect(() => {
    const handler = () => {
      const y = window.scrollY + 120;
      let cur = ids[0];
      for (const id of ids) {
        const el = document.getElementById(id);
        if (el && el.offsetTop <= y) cur = id;
      }
      setActive(cur);
    };
    handler();
    window.addEventListener("scroll", handler, { passive: true });
    return () => window.removeEventListener("scroll", handler);
  }, [ids.join("|")]);
  return active;
}

function TOC() {
  const active = useScrollSpy(SECTIONS.map(s => s.id));
  return (
    <nav className="toc">
      <div className="page" style={{ padding: "0 56px", margin: "0 auto", maxWidth: 1240 }}>
        <div className="toc-inner">
          {SECTIONS.map((s, i) => (
            <button key={s.id}
              className={`toc-link ${active === s.id ? "active" : ""}`}
              onClick={() => {
                const el = document.getElementById(s.id);
                if (el) window.scrollTo({ top: el.offsetTop - 70, behavior: "smooth" });
              }}>
              <span className="toc-num">{String(i+1).padStart(2,"0")}</span>{s.label}
            </button>
          ))}
          <span className="doi">doi.org/10.1016/j.ces.2025.122202</span>
        </div>
      </div>
    </nav>
  );
}

// ============================================================
// HERO
// ============================================================
function Hero() {
  const [showAffil, setShowAffil] = useState(false);
  return (
    <header style={{ padding: "0 0 56px" }}>
      <div className="journal-bar">
        <span><b>Chemical Engineering Science</b> · Volume 318 · Article 122202</span>
        <span className="meta">Original research · <b>Open figures · interactive web edition</b></span>
        <span>2025</span>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "1.4fr 1fr", gap: 56, alignItems: "start", marginTop: 56 }}
           className="hero-grid">
        <div>
          <div className="eyebrow">
            <span className="rule"></span>
            <span className="eyebrow-num">01</span>
            <span>Paper microfluidics · Point-of-care diagnostics</span>
          </div>

          <h1 className="title">
            Affordable smartphone-assisted diagnostics: <em>computer vision on paper microfluidics</em> for uric-acid detection.
          </h1>

          <div style={{ display: "flex", flexWrap: "wrap", gap: 6, marginBottom: 28, fontFamily: "var(--sans)", fontSize: 13 }}>
            {[
              "Piyush Mishra", "Sadhak Khanna", "Priyanshi Gupta", "Sankalp Pathak",
              "Hariom Mishra", "Bhupendra Pratap Singh", "Pallavi Mishra",
              "Purushottam Singh Niranjan", "Shug-June Hwang", "Ved Varun Agrawal*"
            ].map((n, i) => (
              <span key={i} style={{
                padding: "2px 10px 2px 0",
                borderRight: i < 9 ? "1px solid var(--rule)" : "none",
                color: n.endsWith("*") ? "var(--accent)" : "var(--ink)"
              }}>
                {n}
              </span>
            ))}
          </div>

          <div>
            <button className="btn ghost sm" onClick={() => setShowAffil(!showAffil)}>
              {showAffil ? "Hide" : "Show"} affiliations
            </button>
          </div>
          {showAffil && (
            <div className="fadein" style={{ marginTop: 16, padding: 18, background: "var(--paper-2)", border: "1px solid var(--rule)", fontFamily: "var(--sans)", fontSize: 12, lineHeight: 1.7, color: "var(--ink-2)" }}>
              <div><sup>a</sup> CSIR-National Physical Laboratory, Dr. K.S. Krishnan Marg, New Delhi 110012, India</div>
              <div><sup>b</sup> Academy of Scientific and Innovative Research (AcSIR), Ghaziabad 201002, India</div>
              <div><sup>c</sup> Jaypee University of Engineering and Technology, Guna 473226, India</div>
              <div><sup>d</sup> National United University, Miao-Li 360, Taiwan</div>
              <div><sup>e</sup> Veer Bahadur Singh Purvanchal University, Jaunpur, India</div>
              <div><sup>f</sup> C.S.J.M. University, Kanpur, India</div>
              <div style={{ marginTop: 8, color: "var(--mute)" }}>* Corresponding author: vedvarun@nplindia.org</div>
            </div>
          )}

          <div style={{ marginTop: 36, display: "flex", flexWrap: "wrap", gap: 10 }}>
            {["Chromogenic", "Computer vision", "DIY XY Plotter", "Microfluidics", "Synthetic blood", "μPADs"].map(k => (
              <span key={k} className="chip dim">{k}</span>
            ))}
          </div>
        </div>

        {/* hero figure */}
        <div style={{ position: "relative" }}>
          <div className="figure">
            <div className="fig-body">
              <MicroPAD sample={20} />
            </div>
            <div className="fig-cap caption">
              <span className="fig">Fig 0</span>
              The fabricated μPAD — eight calibration zones surround a central sample well on Whatman G1 filter paper, partitioned by PDMS-heptane hydrophobic channels.
            </div>
          </div>
          <div style={{
            position: "absolute", top: -16, right: -16,
            background: "var(--accent)", color: "var(--paper-2)",
            padding: "8px 14px",
            fontFamily: "var(--mono)", fontSize: 11, letterSpacing: "0.08em",
            transform: "rotate(2deg)"
          }}>
            LOD · 0.19 mg/dL
          </div>
        </div>
      </div>
    </header>
  );
}

// ============================================================
// 01 — Abstract + key metrics
// ============================================================
function AbstractSection() {
  return (
    <section className="block" id="abstract">
      <div className="section-head">
        <div>
          <div className="eyebrow" style={{ marginBottom: 6 }}>
            <span className="eyebrow-num">01</span>
            <span>Abstract</span>
          </div>
        </div>
        <h2 className="section-title">An enzyme-free, paper-and-pixel platform for serum uric-acid quantification.</h2>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "1.4fr 1fr", gap: 48 }} className="grid-2">
        <div>
          <p className="lede dropcap">
            Uric acid is a critical biomarker of purine metabolism — too high points to <em>gout, hypertension, renal disease</em>; too low correlates with neurodegenerative disorders. This work introduces a <strong>μPAD + smartphone</strong> platform that quantifies UA between <span className="tnum">1.5 – 25 mg/dL</span> without external light sources, enzymes, or nanoparticles.
          </p>
          <p style={{ marginTop: 24, color: "var(--ink-2)" }}>
            Microfluidic channels are drawn directly onto Whatman G1 filter paper by a low-cost DIY XY plotter, using technical pens filled with a PDMS/heptane hydrophobic solution. Detection runs on a chromogenic reaction between potassium ferricyanide and ferric chloride — uric acid intensifies the resulting bluish-green complex, and a custom Android app reads its luminance against eight on-chip reference zones.
          </p>
        </div>

        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
          {[
            { k: "DETECTION RANGE", v: "1.5–25", u: "mg/dL" },
            { k: "LIMIT OF DETECTION", v: "0.19",  u: "mg/dL" },
            { k: "LIMIT OF QUANTIFICATION", v: "6.33", u: "mg/dL" },
            { k: "CONTACT ANGLE", v: "106.04°", u: "fully hydrophobic" },
            { k: "REFERENCE ZONES", v: "8 + 1", u: "calibration + sample" },
            { k: "PRINT SPEED", v: "38", u: "mm/sec" },
          ].map((m, i) => (
            <div key={i} className="metric">
              <div className="k">{m.k}</div>
              <div className="v tnum">{m.v}</div>
              <div className="u">{m.u}</div>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

// ============================================================
// 02 — Background: UA range explainer
// ============================================================
function Background() {
  const [val, setVal] = useState(5.2);
  const cls = classify(val);
  const ranges = [
    { from: 0, to: 2.7, color: "var(--warn)", label: "Hypouricemia" },
    { from: 2.7, to: 7.2, color: "var(--good)", label: "Healthy" },
    { from: 7.2, to: 30, color: "var(--bad)", label: "Hyperuricemia" },
  ];
  return (
    <section className="block" id="background">
      <div className="section-head">
        <div>
          <div className="eyebrow"><span className="eyebrow-num">02</span><span>Background</span></div>
        </div>
        <h2 className="section-title">Why measuring uric acid matters — and why on paper.</h2>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 56 }} className="grid-2">
        <div className="col-text">
          <p style={{ fontSize: 17, lineHeight: 1.65 }}>
            Healthy plasma carries <span className="tnum">2.7 – 7.2 mg/dL</span> of uric acid. Cross the upper bound and you risk <em>gout, hypertension, chronic renal disease, metabolic syndrome, and cardiovascular disease</em>. Cross the lower bound and the literature links you to <em>multiple sclerosis, Alzheimer's, Parkinson's</em>. Most existing detection mechanisms — electrochemical electrodes, enzyme assays, plasmon resonance with silver nanoparticles — are accurate but require lab infrastructure, reagent stability, or expensive nanomaterials.
          </p>
          <p style={{ marginTop: 18, color: "var(--ink-2)" }}>
            Paper is a remarkable substrate: capillary action drives fluid without a pump, the fibre network stores reagents in an active state, and a single sheet costs essentially nothing. Since Whitesides' group introduced μPADs in 2007, fabrication techniques have multiplied — wax printing, photolithography, screen printing, inkjet, 3D printing. This work uses the cheapest of them: an XY plotter holding a technical drawing pen filled with PDMS.
          </p>

          <div style={{ marginTop: 24, padding: 18, background: "var(--paper-2)", border: "1px solid var(--rule)" }}>
            <div className="label" style={{ marginBottom: 8 }}>Drag the marker</div>
            <div style={{ fontFamily: "var(--mono)", fontSize: 12, color: "var(--mute)", marginBottom: 4 }}>
              UA CONCENTRATION · mg/dL
            </div>
            <input type="range" min="0" max="20" step="0.1" value={val}
              onChange={e => setVal(parseFloat(e.target.value))} />
            <div style={{
              position: "relative", height: 36, marginTop: -10,
              border: "1px solid var(--rule)", background: "var(--paper-3)",
              display: "flex"
            }}>
              {ranges.map((r,i) => (
                <div key={i} style={{
                  flex: (r.to - r.from),
                  background: r.color,
                  opacity: 0.85,
                  borderRight: i < 2 ? "1px solid var(--paper)" : "none",
                  display: "flex", alignItems: "center", justifyContent: "center",
                  color: "var(--paper-2)",
                  fontFamily: "var(--sans)", fontSize: 10, fontWeight: 600,
                  letterSpacing: "0.06em", textTransform: "uppercase"
                }}>
                  {r.label}
                </div>
              ))}
              {/* marker */}
              <div style={{
                position: "absolute", top: -6, bottom: -6,
                width: 2, background: "var(--ink)",
                left: `${(val/20)*100}%`
              }} />
              <div style={{
                position: "absolute", top: -28,
                left: `calc(${(val/20)*100}% - 36px)`,
                width: 72, textAlign: "center",
                fontFamily: "var(--mono)", fontSize: 11, color: "var(--ink)"
              }}>
                {val.toFixed(1)} mg/dL
              </div>
            </div>
            <div style={{ display: "flex", justifyContent: "space-between", marginTop: 6, fontFamily: "var(--mono)", fontSize: 10, color: "var(--mute)" }}>
              <span>0</span><span>5</span><span>10</span><span>15</span><span>20</span>
            </div>
            <div style={{ marginTop: 12, padding: "10px 12px", background: "var(--paper)",
              borderLeft: `3px solid ${cls.tone === "good" ? "var(--good)" : cls.tone === "bad" ? "var(--bad)" : "var(--warn)"}`,
              fontFamily: "var(--sans)", fontSize: 13 }}>
              <strong style={{ textTransform: "uppercase", letterSpacing: "0.08em", fontSize: 11 }}>{cls.label}</strong>
              {" · "}
              {cls.note}
            </div>
          </div>
        </div>

        <div>
          <div className="figure">
            <div className="fig-body">
              {/* a timeline of UA detection history */}
              <div className="label" style={{ marginBottom: 16 }}>A short history of detecting UA</div>
              <div style={{ position: "relative", paddingLeft: 22 }}>
                <div style={{ position: "absolute", left: 6, top: 8, bottom: 8, width: 1, background: "var(--rule-2)" }} />
                {[
                  { y: "1776", t: "Scheele detects uric acid in urine — the first isolation." },
                  { y: "1787", t: "Wollaston isolates UA from gout tophi, linking biomarker to disease." },
                  { y: "1848", t: "Garrod connects gout to metabolic imbalance via elevated blood glucose." },
                  { y: "1800s", t: "Litmus paper marks the start of paper as a sensing substrate." },
                  { y: "2007", t: "Martinez & Whitesides demonstrate patterned-paper μPADs." },
                  { y: "2018+", t: "Silver-nanoparticle plasmon, electrochemical and enzyme assays for UA." },
                  { y: "2025", t: "This work · μPAD + smartphone vision, enzyme-free, < $0.10 per chip.", here: true },
                ].map((it, i) => (
                  <div key={i} style={{ position: "relative", padding: "10px 0 10px 22px", marginBottom: 2 }}>
                    <div style={{
                      position: "absolute", left: -2, top: 16,
                      width: 12, height: 12, borderRadius: "50%",
                      background: it.here ? "var(--accent)" : "var(--paper-2)",
                      border: `1.5px solid ${it.here ? "var(--accent)" : "var(--ink)"}`,
                    }} />
                    <div style={{ fontFamily: "var(--mono)", fontSize: 11, color: it.here ? "var(--accent)" : "var(--mute)" }}>{it.y}</div>
                    <div style={{ fontFamily: "var(--serif)", fontSize: 14, lineHeight: 1.45, color: it.here ? "var(--ink)" : "var(--ink-2)", fontWeight: it.here ? 600 : 400 }}>
                      {it.t}
                    </div>
                  </div>
                ))}
              </div>
            </div>
            <div className="fig-cap caption">
              <span className="fig">Fig 1</span>
              250 years of incremental work on uric-acid sensing — from chemical isolation to point-of-care vision.
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

// ============================================================
// 03 — The device (μPAD)
// ============================================================
function DeviceSection() {
  return (
    <section className="block" id="device">
      <div className="section-head">
        <div>
          <div className="eyebrow"><span className="eyebrow-num">03</span><span>The μPAD</span></div>
        </div>
        <h2 className="section-title">A pen, a plotter, and a sheet of filter paper.</h2>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "1.1fr 1fr", gap: 56 }} className="grid-2">
        <div className="col-text">
          <p>
            μPADs are drawn directly onto Whatman Grade-1 filter paper (pore size 11 μm) with a DIY XY plotter controlled by Inkscape v1.2 — no G-code conversion required. A technical drawing pen, fitted with a 0.6 mm nib, dispenses an optimised hydrophobic solution: <span className="tnum">PDMS + Sylgard 184 (10:1)</span> mixed with <span className="tnum">n-heptane (3:1)</span> to tune viscosity. Curing at 150 °C for 15 min anchors the channels to the cellulose fibres.
          </p>
          <p style={{ marginTop: 16 }}>
            The result: a fully hydrophobic boundary — measured at <strong>106.04°</strong> mean contact angle over ten samples — that confines aqueous samples to the patterned zones. SEM imaging confirms that PDMS smooths the fibrous surface, reducing roughness and amplifying the wettability contrast between treated and untreated paper.
          </p>

          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginTop: 28 }}>
            {[
              { k: "Substrate", v: "Whatman G1, 11 μm pore" },
              { k: "Pen nib",   v: "0.6 mm technical drawing" },
              { k: "Hydrophobe", v: "PDMS / Sylgard 10:1 + heptane 3:1" },
              { k: "Cure", v: "150 °C · 15 min · microwave" },
              { k: "Print speed", v: "38 mm / sec, Inkscape + AxiDraw" },
              { k: "Cost / chip", v: "≈ ₹8 · pen + paper only" },
            ].map((m, i) => (
              <div key={i} style={{ padding: "10px 12px", border: "1px solid var(--rule)", background: "var(--paper-2)" }}>
                <div style={{ fontFamily: "var(--mono)", fontSize: 9, letterSpacing: "0.1em", color: "var(--mute)", textTransform: "uppercase", marginBottom: 4 }}>{m.k}</div>
                <div style={{ fontFamily: "var(--sans)", fontSize: 13, color: "var(--ink)" }}>{m.v}</div>
              </div>
            ))}
          </div>
        </div>

        <div>
          <div className="figure">
            <div className="fig-body">
              {/* SEM-style toggle */}
              <SEMToggle />
            </div>
            <div className="fig-cap caption">
              <span className="fig">Fig 2</span>
              Topography of untreated vs PDMS-treated Whatman G1 paper at 1000× / 20 μm. Treatment smooths the fibre mat, dropping surface roughness and lifting the contact angle above 106°.
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

// SEM toggle subcomponent
function SEMToggle() {
  const [treated, setTreated] = useState(false);
  // Generate a deterministic fibre tangle
  const fibres = useMemo(() => {
    const f = [];
    const seed = (n) => {
      let x = Math.sin(n) * 10000;
      return x - Math.floor(x);
    };
    for (let i = 0; i < 120; i++) {
      const x1 = seed(i*3.1)*300, y1 = seed(i*3.1+1)*180;
      const ang = seed(i*3.1+2) * Math.PI * 2;
      const len = 30 + seed(i*3.1+3) * 80;
      const x2 = x1 + Math.cos(ang)*len, y2 = y1 + Math.sin(ang)*len;
      f.push({ x1, y1, x2, y2, w: 1 + seed(i*3.1+4)*1.4 });
    }
    return f;
  }, []);
  return (
    <div>
      <div style={{ display: "flex", gap: 6, marginBottom: 14 }}>
        <button className={`btn sm ${!treated?"":"ghost"}`} onClick={() => setTreated(false)}>Untreated</button>
        <button className={`btn sm ${treated?"":"ghost"}`} onClick={() => setTreated(true)}>PDMS-treated</button>
      </div>
      <div style={{ position: "relative", aspectRatio: "5/3", background: "#0c0c0e", overflow: "hidden", border: "1px solid var(--rule)" }}>
        <svg viewBox="0 0 300 180" style={{ width: "100%", height: "100%", display: "block" }}>
          <defs>
            <filter id="rough">
              <feTurbulence baseFrequency="0.8" numOctaves="2" />
              <feColorMatrix values="0 0 0 0 0.7  0 0 0 0 0.7  0 0 0 0 0.7  0 0 0 0.4 0" />
              <feComposite in2="SourceGraphic" operator="in" />
            </filter>
            <filter id="smooth">
              <feGaussianBlur stdDeviation={treated ? 0.9 : 0} />
            </filter>
          </defs>
          <rect width="300" height="180" fill="#1a1a1c" />
          {fibres.map((f, i) => (
            <line key={i}
              x1={f.x1} y1={f.y1} x2={f.x2} y2={f.y2}
              stroke={treated ? "#b8b6ad" : "#a8a59a"}
              strokeOpacity={treated ? 0.42 : 0.85}
              strokeWidth={f.w * (treated ? 0.85 : 1)}
              strokeLinecap="round"
              filter={treated ? "url(#smooth)" : ""}
              style={{ transition: "all .6s" }}
            />
          ))}
          {/* scalebar */}
          <g transform="translate(220 160)">
            <line x1="0" y1="0" x2="40" y2="0" stroke="#fbf9f3" strokeWidth="2" />
            <text x="20" y="-6" textAnchor="middle" fill="#fbf9f3" style={{ fontFamily: "IBM Plex Mono", fontSize: 9 }}>20 μm</text>
          </g>
          <text x="14" y="20" fill="#fbf9f3" style={{ fontFamily: "IBM Plex Mono", fontSize: 9 }}>
            SEM · 1000× · {treated ? "PDMS-TREATED" : "UNTREATED"}
          </text>
        </svg>
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 6, marginTop: 12 }}>
        <div style={{ padding: "10px 12px", background: "var(--paper-3)", border: "1px solid var(--rule)" }}>
          <div className="label" style={{ fontSize: 10 }}>Contact angle</div>
          <div style={{ fontFamily: "var(--serif)", fontSize: 22, color: "var(--ink)" }}>
            {treated ? "106.04°" : "≈ 0°"}
          </div>
        </div>
        <div style={{ padding: "10px 12px", background: "var(--paper-3)", border: "1px solid var(--rule)" }}>
          <div className="label" style={{ fontSize: 10 }}>Behaviour</div>
          <div style={{ fontFamily: "var(--serif)", fontSize: 14, color: "var(--ink)" }}>
            {treated ? "Hydrophobic — confines fluid" : "Hydrophilic — wicks freely"}
          </div>
        </div>
      </div>
    </div>
  );
}

// ============================================================
// 04 — Reaction mechanism
// ============================================================
function ReactionSection() {
  return (
    <section className="block" id="reaction">
      <div className="section-head">
        <div>
          <div className="eyebrow"><span className="eyebrow-num">04</span><span>Chromogenic reaction</span></div>
        </div>
        <h2 className="section-title">How a drop turns Prussian blue — three reactions, two metals, one electron.</h2>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "1.1fr 1fr", gap: 48 }} className="grid-2">
        <div>
          <ReactionMechanism />
        </div>
        <div className="col-text">
          <p>
            Detection runs on a redox cascade between two iron-containing reagents and uric acid. Conventional protocols introduce UA before ferric chloride, leaving the –CN group strongly bonded to Fe²⁺ and producing only a weak signal. <strong>This work reverses the order</strong>: ferricyanide first meets Fe³⁺ to form a pre-complex, then UA arrives and drives the electron transfer that yields a deeply-coloured Prussian-blue product.
          </p>
          <p style={{ marginTop: 16 }}>
            FTIR confirms the mechanistic shift — the modified sequence shows a free, lower-bond-order –CN peak (2086 cm⁻¹) and a weaker C-O stretch (1035 cm⁻¹), both signatures of incomplete coordination prior to the UA encounter. UV–Vis at <span className="tnum">λ_max = 285 nm</span> traces a first-order kinetic relationship — exactly what would be expected of a slow rate-determining step releasing two hydrogens from UA.
          </p>

          <div style={{ marginTop: 28 }}>
            <FTIRFig />
          </div>
          <div className="caption" style={{ marginTop: 8 }}>
            <span className="fig">Fig 3</span>
            FTIR spectra · key peaks shifted between the traditional reagent order (KCN → UA → FeCl₃) and the modified order (KCN → FeCl₃ → UA) used in this work.
          </div>
        </div>
      </div>
    </section>
  );
}

// ============================================================
// 05 — Live detection demo (the centrepiece)
// ============================================================
function DetectionDemo() {
  const [conc, setConc] = useState(7.2);
  const cls = classify(conc);
  const intensity = (215 - 4.4 * conc);

  return (
    <section className="block" id="detection" style={{ background: "var(--paper-2)", margin: "0 -56px", padding: "80px 56px", borderTop: "1px solid var(--rule)", borderBottom: "1px solid var(--rule)" }}>
      <div className="section-head">
        <div>
          <div className="eyebrow"><span className="eyebrow-num">05</span><span>Live demonstration</span></div>
        </div>
        <h2 className="section-title">Try it. Drag a uric-acid concentration onto the chip.</h2>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 56, alignItems: "start" }} className="grid-2">
        <div>
          <div className="figure">
            <div className="fig-body" style={{ display: "flex", justifyContent: "center" }}>
              <MicroPAD sample={conc} interactive={true}
                onPickRef={(c) => setConc(c)}
                highlightIdx={REF_CONCS.findIndex(c => Math.abs(c - conc) < 0.05)}
              />
            </div>
            <div className="fig-cap caption">
              <span className="fig">Fig 4</span>
              Click any calibration zone to load that concentration into the sample well, or scrub the slider for arbitrary values. Colour intensity follows the chromogenic calibration in real time.
            </div>
          </div>
        </div>

        <div>
          <div className="panel" style={{ background: "var(--paper)" }}>
            <span className="panel-num">Live readout</span>

            <div style={{ display: "flex", alignItems: "baseline", gap: 12, marginTop: 14 }}>
              <div style={{ fontFamily: "var(--serif)", fontSize: 64, lineHeight: 1, color: "var(--ink)", letterSpacing: "-0.02em" }} className="tnum">
                {conc.toFixed(1)}
              </div>
              <div style={{ fontFamily: "var(--mono)", fontSize: 13, color: "var(--mute)" }}>
                mg/dL
              </div>
              <div style={{ marginLeft: "auto" }}>
                <span className={`chip ${cls.tone === "good" ? "good" : cls.tone === "bad" ? "warn" : "warn"}`}>
                  {cls.label}
                </span>
              </div>
            </div>

            <div style={{ marginTop: 22 }}>
              <input type="range" min="0" max="30" step="0.1" value={conc}
                onChange={e => setConc(parseFloat(e.target.value))} />
              <div className="ticks">
                {[0,5,10,15,20,25,30].map(v => <span key={v}>{v}</span>)}
              </div>
            </div>

            <div style={{ marginTop: 18, padding: "12px 14px",
              background: cls.tone === "good" ? "rgba(63,112,72,0.08)" : cls.tone === "bad" ? "rgba(162,58,58,0.08)" : "rgba(185,97,42,0.08)",
              borderLeft: `3px solid ${cls.tone === "good" ? "var(--good)" : cls.tone === "bad" ? "var(--bad)" : "var(--warn)"}`,
              fontFamily: "var(--sans)", fontSize: 13, color: "var(--ink-2)"
            }}>
              {cls.note}
            </div>

            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, marginTop: 18 }}>
              <div style={{ padding: 12, border: "1px solid var(--rule)" }}>
                <div className="label" style={{ fontSize: 10 }}>Mean grey value</div>
                <div style={{ fontFamily: "var(--serif)", fontSize: 26 }} className="tnum">{intensity.toFixed(0)}</div>
              </div>
              <div style={{ padding: 12, border: "1px solid var(--rule)" }}>
                <div className="label" style={{ fontSize: 10 }}>Closest reference</div>
                <div style={{ fontFamily: "var(--serif)", fontSize: 18 }} className="tnum">
                  {(() => {
                    let best = REF_CONCS[0]; let dMin = Infinity;
                    REF_CONCS.forEach(r => { const d = Math.abs(r - conc); if (d < dMin) { dMin = d; best = r; }});
                    return `C${REF_CONCS.indexOf(best)+1} · ${best.toFixed(1)} mg/dL`;
                  })()}
                </div>
              </div>
            </div>

            <div style={{ marginTop: 18, display: "flex", flexWrap: "wrap", gap: 6 }}>
              {[1.5, 5.0, 7.2, 12.0, 18.0, 26.1].map(p => (
                <button key={p} className="btn sm ghost" onClick={() => setConc(p)}>
                  {p} mg/dL
                </button>
              ))}
            </div>
          </div>

          <div style={{ marginTop: 22, fontFamily: "var(--sans)", fontSize: 12, color: "var(--mute)", display: "flex", gap: 16 }}>
            <span><span className="kbd">→</span> raise</span>
            <span><span className="kbd">←</span> lower</span>
            <span>Click any zone on the μPAD to load its concentration.</span>
          </div>
        </div>
      </div>
    </section>
  );
}

// ============================================================
// 06 — Results: calibration + selectivity
// ============================================================
function ResultsSection() {
  const [hoverConc, setHoverConc] = useState(7.2);
  return (
    <section className="block" id="results">
      <div className="section-head">
        <div><div className="eyebrow"><span className="eyebrow-num">06</span><span>Results</span></div></div>
        <h2 className="section-title">Linear, selective, and quantifiable from 1.5 to 25 mg/dL.</h2>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "1.3fr 1fr", gap: 48 }} className="grid-2">
        <div>
          <div className="figure">
            <div className="fig-body">
              <CalibrationCurve activeConc={hoverConc} onHover={(c) => c != null && setHoverConc(c)} />
            </div>
            <div className="fig-cap caption">
              <span className="fig">Fig 5</span>
              Calibration · Mean grey value (intensity) declines linearly with UA concentration, as the chromogenic complex darkens. Hover anywhere along the axis to read the fit value. R² &gt; 0.99 across the eight reference zones.
            </div>
          </div>

          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 10, marginTop: 18 }}>
            <div className="metric"><div className="k">LOD</div><div className="v tnum">0.19</div><div className="u">mg/dL · 3σ / slope</div></div>
            <div className="metric"><div className="k">LOQ</div><div className="v tnum">6.33</div><div className="u">mg/dL · 10σ / slope</div></div>
            <div className="metric"><div className="k">σ</div><div className="v tnum">0.012</div><div className="u">RSS 0.000933 · df 6</div></div>
          </div>
        </div>

        <div>
          <div className="figure">
            <div className="fig-body">
              <div style={{ marginBottom: 14 }}>
                <div className="label" style={{ marginBottom: 4 }}>Selectivity panel</div>
                <div style={{ fontFamily: "var(--serif)", fontSize: 15, color: "var(--ink-2)" }}>
                  Response of the μPAD to UA versus four common interferents.
                </div>
              </div>
              <SelectivityChart />
            </div>
            <div className="fig-cap caption">
              <span className="fig">Fig 6</span>
              The assay shows minimal cross-reactivity. Urea, glucose, creatinine, and ascorbic acid at clinically realistic concentrations produce &lt;10% of the UA response — well outside the LOD window.
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

// ============================================================
// 07 — Phone app
// ============================================================
function AppSection() {
  const [stage, setStage] = useState(0);
  // auto-advance
  useEffect(() => {
    const t = setTimeout(() => setStage((stage + 1) % 4), 3200);
    return () => clearTimeout(t);
  }, [stage]);

  const stages = [
    { t: "Capture", d: "Phone camera fixed at 24 cm. CameraX feed into MainActivity.java." },
    { t: "Detect",  d: "OpenCV Hough Circle Transform with Gaussian-blurred grayscale finds 9 wells." },
    { t: "Calibrate", d: "User enters known concentrations for C1–C8. App validates and ticks the sample." },
    { t: "Quantify", d: "Linear interpolation against the on-chip curve. Output ‘Low / Normal / High’ + value." },
  ];

  return (
    <section className="block" id="app">
      <div className="section-head">
        <div><div className="eyebrow"><span className="eyebrow-num">07</span><span>Smartphone app</span></div></div>
        <h2 className="section-title">Computer vision on a $200 phone, no external light.</h2>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "1fr 1.4fr", gap: 56, alignItems: "center" }} className="grid-2">
        <div style={{ display: "flex", justifyContent: "center" }}>
          <PhoneApp stage={stage} />
        </div>
        <div>
          <p className="col-text">
            The Android app captures or imports an image of the μPAD, detects the wells via OpenCV's Hough Circle Transform, and calculates per-zone luminance using the standard relation
          </p>
          <div style={{
            margin: "16px 0", padding: "12px 16px",
            background: "var(--paper-2)", border: "1px solid var(--rule)",
            fontFamily: "var(--mono)", fontSize: 14, color: "var(--accent)"
          }}>
            I = 0.299 · R + 0.587 · G + 0.114 · B
          </div>
          <p className="col-text">
            Calibration intensities are interpolated linearly to estimate the unknown — and the app then maps the value into a non-technical category. The whole pipeline is split across three activities: <code>MainActivity</code> for capture, <code>AnalysisActivity</code> for user input, <code>GraphActivity</code> for interpolation and plotting via MPAndroidChart.
          </p>

          <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 6, marginTop: 24 }}>
            {stages.map((s, i) => (
              <button key={i}
                onClick={() => setStage(i)}
                style={{
                  textAlign: "left", padding: "12px 14px",
                  background: i === stage ? "var(--ink)" : "var(--paper-2)",
                  color: i === stage ? "var(--paper-2)" : "var(--ink)",
                  border: "1px solid " + (i === stage ? "var(--ink)" : "var(--rule)"),
                  cursor: "pointer",
                  transition: "all .2s",
                  fontFamily: "var(--sans)"
                }}>
                <div style={{ fontFamily: "var(--mono)", fontSize: 10, opacity: 0.7, marginBottom: 2 }}>
                  STEP {String(i+1).padStart(2,"0")}
                </div>
                <div style={{ fontSize: 14, fontWeight: 600 }}>{s.t}</div>
              </button>
            ))}
          </div>
          <div style={{ marginTop: 14, fontFamily: "var(--serif)", fontSize: 14, color: "var(--ink-2)", lineHeight: 1.5 }}>
            {stages[stage].d}
          </div>
        </div>
      </div>
    </section>
  );
}

// ============================================================
// 08 — Methods / chemicals
// ============================================================
function MethodsSection() {
  const chems = [
    { name: "Sodium chloride", role: "Artificial serum base", amt: "0.24 g/dL" },
    { name: "Potassium dihydrogen phosphate", role: "Buffer", amt: "0.12 g/dL" },
    { name: "Disodium hydrogen phosphate", role: "Buffer", amt: "0.43 g/dL" },
    { name: "Carboxymethyl cellulose", role: "Viscosity modifier", amt: "0.02 g/dL" },
    { name: "Tween 20", role: "Surfactant", amt: "4 μL/dL" },
    { name: "Amaranth dye", role: "Plasma colourant", amt: "100 μL/dL" },
    { name: "Potassium ferricyanide", role: "Reagent · primer", amt: "0.01 M · 1 μL" },
    { name: "Ferric chloride (anhydrous)", role: "Reagent · oxidiser", amt: "0.02 M · 1 μL" },
    { name: "HCl (conc.)", role: "Acidifier", amt: "1 μL · 11.45 M" },
  ];
  return (
    <section className="block" id="methods">
      <div className="section-head">
        <div><div className="eyebrow"><span className="eyebrow-num">08</span><span>Methods</span></div></div>
        <h2 className="section-title">Materials, reagents, and protocol — short version.</h2>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 48 }} className="grid-2">
        <div>
          <h3>Reagent table · artificial serum + assay</h3>
          <div style={{ border: "1px solid var(--rule)", marginTop: 12 }}>
            {chems.map((c, i) => (
              <div key={i} style={{
                display: "grid", gridTemplateColumns: "1.4fr 1fr 0.8fr",
                padding: "10px 14px",
                borderTop: i > 0 ? "1px solid var(--rule)" : "none",
                fontFamily: "var(--sans)", fontSize: 13,
                background: i % 2 === 0 ? "var(--paper-2)" : "var(--paper)"
              }}>
                <div>{c.name}</div>
                <div style={{ color: "var(--mute)" }}>{c.role}</div>
                <div style={{ fontFamily: "var(--mono)", fontSize: 11, textAlign: "right" }}>{c.amt}</div>
              </div>
            ))}
          </div>
        </div>

        <div>
          <h3>Protocol · in five steps</h3>
          <ol style={{ counterReset: "li", listStyle: "none", padding: 0, marginTop: 14 }}>
            {[
              "Print PDMS/heptane channels onto Whatman G1 with the XY plotter at 38 mm/s.",
              "Microwave-cure at 150 °C for 15 minutes to anchor the hydrophobic boundary.",
              "Pipette 1 μL each of potassium ferricyanide and ferric chloride into the well, let dry.",
              "Drop the unknown UA sample on the central well and the eight calibration concentrations on the reference wells.",
              "Photograph at 24 cm with the Android app. The pipeline reads luminance, fits, and classifies."
            ].map((step, i) => (
              <li key={i} style={{
                display: "grid", gridTemplateColumns: "auto 1fr", gap: 14,
                padding: "12px 0", borderTop: i > 0 ? "1px dotted var(--rule)" : "none"
              }}>
                <span style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--accent)", paddingTop: 4 }}>
                  {String(i+1).padStart(2,"0")}
                </span>
                <span style={{ fontFamily: "var(--serif)", fontSize: 15, color: "var(--ink-2)", lineHeight: 1.5 }}>
                  {step}
                </span>
              </li>
            ))}
          </ol>
        </div>
      </div>
    </section>
  );
}

// ============================================================
// 09 — Authors + citation
// ============================================================
function AuthorsSection() {
  const [copied, setCopied] = useState(false);
  const cite = `Mishra, P., Khanna, S., Gupta, P., Pathak, S., Mishra, H., Singh, B. P., Mishra, P., Niranjan, P. S., Hwang, S.-J., & Agrawal, V. V. (2025). Affordable smartphone-assisted diagnostics: computer vision on paper microfluidics for uric acid detection. Chemical Engineering Science, 318, 122202. https://doi.org/10.1016/j.ces.2025.122202`;

  const copy = async () => {
    try { await navigator.clipboard.writeText(cite); setCopied(true); setTimeout(() => setCopied(false), 1600); } catch {}
  };

  const authors = [
    { n: "Piyush Mishra", r: "Lead · Writing, methodology, conceptualization", aff: "CSIR-NPL · AcSIR" },
    { n: "Sadhak Khanna", r: "Visualization, data curation", aff: "CSIR-NPL · AcSIR" },
    { n: "Priyanshi Gupta", r: "Writing, methodology", aff: "CSIR-NPL · AcSIR" },
    { n: "Sankalp Pathak", r: "Software · android app", aff: "JUET, Guna" },
    { n: "Hariom Mishra", r: "Software · android app", aff: "JUET, Guna" },
    { n: "Bhupendra Pratap Singh", r: "Supervision, conceptualization", aff: "National United University, Taiwan" },
    { n: "Pallavi Mishra", r: "Writing, methodology", aff: "VBS Purvanchal University" },
    { n: "Purushottam Singh Niranjan", r: "Writing, methodology", aff: "CSJM University, Kanpur" },
    { n: "Shug-June Hwang", r: "Supervision, formal analysis", aff: "National United University, Taiwan" },
    { n: "Ved Varun Agrawal", r: "Corresponding · supervision, funding", aff: "CSIR-NPL · vedvarun@nplindia.org", lead: true },
  ];

  return (
    <section className="block" id="authors">
      <div className="section-head">
        <div><div className="eyebrow"><span className="eyebrow-num">09</span><span>Authors & cite</span></div></div>
        <h2 className="section-title">CRediT contributions and the bibliographic record.</h2>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "1.3fr 1fr", gap: 48 }} className="grid-2">
        <div>
          <div style={{ border: "1px solid var(--rule)" }}>
            {authors.map((a, i) => (
              <div key={i} style={{
                display: "grid", gridTemplateColumns: "auto 1fr auto",
                gap: 14, padding: "14px 18px",
                borderTop: i > 0 ? "1px solid var(--rule)" : "none",
                background: a.lead ? "rgba(31,82,102,0.05)" : "transparent"
              }}>
                <div style={{ fontFamily: "var(--mono)", fontSize: 10, color: "var(--mute)", paddingTop: 4 }}>
                  {String(i+1).padStart(2,"0")}
                </div>
                <div>
                  <div style={{ fontFamily: "var(--serif)", fontSize: 16, fontWeight: a.lead ? 600 : 500 }}>
                    {a.n}{a.lead && <span style={{ color: "var(--accent)", marginLeft: 8 }}>*</span>}
                  </div>
                  <div style={{ fontFamily: "var(--sans)", fontSize: 12, color: "var(--mute)", marginTop: 2 }}>
                    {a.r}
                  </div>
                </div>
                <div style={{ fontFamily: "var(--sans)", fontSize: 11, color: "var(--mute-2)", textAlign: "right", paddingTop: 4 }}>
                  {a.aff}
                </div>
              </div>
            ))}
          </div>
        </div>

        <div>
          <div className="panel">
            <span className="panel-num">Cite this article</span>
            <div style={{ marginTop: 14, fontFamily: "var(--serif)", fontSize: 14, lineHeight: 1.55, color: "var(--ink-2)" }}>
              {cite}
            </div>
            <button className="btn" style={{ marginTop: 18 }} onClick={copy}>
              {copied ? "✓ Copied" : "Copy citation"}
            </button>
          </div>

          <div className="panel" style={{ marginTop: 18 }}>
            <span className="panel-num">Funding & ethics</span>
            <div style={{ fontFamily: "var(--sans)", fontSize: 12, color: "var(--ink-2)", marginTop: 14, lineHeight: 1.6 }}>
              CSIR India · award 31/001(0631)/2020-EMR-I (P.M. SRF). Ministry of Science and Technology Taiwan · NSTC 111-2221-E-239-010-MY3 and NSTC 111-2811-E-239-001-MY3 (B.P.S., S.J.H.). No animal or human subjects involved.
            </div>
          </div>

          <div className="panel" style={{ marginTop: 18 }}>
            <span className="panel-num">Conflict of interest</span>
            <div style={{ fontFamily: "var(--sans)", fontSize: 12, color: "var(--ink-2)", marginTop: 14, lineHeight: 1.6 }}>
              None declared. Data available from the corresponding author on request.
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

// ============================================================
// 10 — Selected references (top 12)
// ============================================================
function ReferencesSection() {
  const refs = [
    "Camici, M., et al. (2023). Inborn errors of purine salvage and catabolism. Metabolites 13(7), 787.",
    "Otani, N., et al. (2023). Dysuricemia — a new concept encompassing hyperuricemia and hypouricemia. Biomedicines 11(5), 1255.",
    "Desideri, G., et al. (2014). Is it time to revise the normal range of serum uric acid levels? Eur. Rev. Med. Pharmacol. Sci. 18(9), 1295–1306.",
    "Yip, K., Cohen, R.E., Pillinger, M.H. (2020). Asymptomatic hyperuricemia: is it really asymptomatic? Curr. Opin. Rheumatol. 32(1), 71–79.",
    "Martinez, A.W., Phillips, S.T., Butte, M.J., Whitesides, G.M. (2007). Patterned paper as a platform for inexpensive, low-volume, portable bioassays. Angew. Chem. 119(8), 1340–1342.",
    "Mishra, P., et al. (2024). A novel approach to low-cost, rapid and simultaneous colorimetric detection of multiple analytes using 3D printed microfluidic channels. R. Soc. Open Sci. 11(1), 231168.",
    "Hamedpour, V., et al. (2018). Chemometrics-assisted microfluidic paper-based analytical device for the determination of uric acid by silver nanoparticle plasmon resonance. Anal. Bioanal. Chem. 410(9), 2305–2313.",
    "Cincotto, F.H., et al. (2019). A new disposable microfluidic electrochemical paper-based device for the simultaneous determination of clinical biomarkers. Talanta 195, 62–68.",
    "Saadati, A., et al. (2021). A microfluidic paper-based colorimetric device for the visual detection of uric acid in human urine samples. Anal. Methods 13(35), 3909–3921.",
    "Dalvand, K., et al. (2023). A simple 3D printed microfluidic device for point-of-care analysis of urinary uric acid. Aust. J. Chem. 76(2), 74–80.",
    "Zheng, J., et al. (2022). Microfluidic paper-based analytical device by using Pt nanoparticles as highly active peroxidase mimic for simultaneous detection of glucose and uric acid with a smartphone. Talanta 237, 122954.",
    "Liu, W., et al. (2024). Machine-learning-based colorimetric sensor on smartphone for salivary uric acid detection. IEEE Sens. J.",
  ];
  return (
    <section className="block" id="refs">
      <div className="section-head">
        <div><div className="eyebrow"><span className="eyebrow-num">10</span><span>References</span></div></div>
        <h2 className="section-title">Selected references.</h2>
      </div>
      <div style={{ columnCount: 2, columnGap: 48 }} className="refs-cols">
        {refs.map((r, i) => (
          <div key={i} className="ref-item" style={{ breakInside: "avoid" }}>
            <div className="ref-n">[{String(i+1).padStart(2,"0")}]</div>
            <div style={{ fontFamily: "var(--serif)", color: "var(--ink-2)" }}>{r}</div>
          </div>
        ))}
      </div>
      <div style={{ marginTop: 32, paddingTop: 24, borderTop: "1px solid var(--rule)",
                    display: "flex", justifyContent: "space-between", alignItems: "baseline",
                    fontFamily: "var(--mono)", fontSize: 11, color: "var(--mute)" }}>
        <span>Chemical Engineering Science · 318 (2025) 122202</span>
        <span>© 2025 Elsevier Ltd. · Interactive edition prepared for online reading.</span>
      </div>
    </section>
  );
}

// ============================================================
// Root
// ============================================================
function App() {
  // keyboard arrows for the detection demo concentration
  useEffect(() => {
    const onKey = (e) => {
      if (e.target && (e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA")) return;
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, []);
  return (
    <div>
      <TOC />
      <div className="page">
        <Hero />
        <AbstractSection />
        <Background />
        <DeviceSection />
        <ReactionSection />
        <DetectionDemo />
        <ResultsSection />
        <AppSection />
        <MethodsSection />
        <AuthorsSection />
        <ReferencesSection />
      </div>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
