/* eslint-disable no-undef */
// IoT mode — Pulse (overview), Farms, Network, Sensors-by-family, Conditions, Alerts.

const { useState: iotUseState, useEffect: iotUseEffect, useMemo: iotUseMemo, useRef: iotUseRef } = React;

// ---------- Action builders (modals/forms wired to AGB_UI) ----------
function actAddDevice(farms) {
  const { FAMILIES } = window.AGB_DATA;
  AGB_UI.form({
    eyebrow: "Provisioning", title: "Add a device", subtitle: "Register a new Agrobotic sensor node to a farm.",
    submitLabel: "Register device", successMessage: "Device registered · provisioning over LoRaWAN",
    fields: [
      { key: "family", label: "Sensor family", type: "select", options: Object.keys(FAMILIES) },
      { key: "farm", label: "Assign to farm", type: "select", options: farms.map((f) => ({ value: f.id, label: f.name })) },
      { key: "eui", label: "DevEUI", placeholder: "A1-B2-C3-D4-E5-F6", value: "" },
      { key: "class", label: "LoRaWAN class", type: "select", options: ["Class A", "Class B", "Class C"] },
      { key: "depth", label: "Deployment depth / mount", placeholder: 'e.g. 18" stake, pole mount', value: "" },
    ],
  });
}
function actOnboardFarm() {
  const { REGIONS, CROPS } = window.AGB_DATA;
  AGB_UI.form({
    eyebrow: "Onboarding", title: "Onboard a farm", subtitle: "Add a new property to the Agrobotic fleet.", wide: true,
    submitLabel: "Create farm", successMessage: "Farm created · invite sent to owner",
    fields: [
      { key: "name", label: "Farm name", placeholder: "e.g. Cedar Valley Orchard", value: "" },
      { key: "owner", label: "Owner / operator", placeholder: "Name or co-op", value: "" },
      { key: "region", label: "Region", type: "select", options: REGIONS.map((r) => ({ value: r.id, label: r.name })) },
      { key: "crop", label: "Primary crop", type: "select", options: CROPS.map((c) => ({ value: c.id, label: c.name })) },
      { key: "acres", label: "Acreage", type: "number", value: 500, suffix: "ac" },
      { key: "lat", label: "Latitude", type: "number", value: 49.1, suffix: "°" },
      { key: "lng", label: "Longitude", type: "number", value: -122.5, suffix: "°" },
    ],
  });
}
function actProvisionDevice() {
  AGB_UI.form({
    eyebrow: "Network", title: "Provision device", subtitle: "Onboard a device to the LoRaWAN network server.",
    submitLabel: "Provision", successMessage: "Device keys generated · awaiting first uplink",
    fields: [
      { key: "eui", label: "DevEUI", placeholder: "70-B3-D5-7E-D0-00-00-01", value: "" },
      { key: "appkey", label: "AppKey", placeholder: "32-hex OTAA key", value: "" },
      { key: "gw", label: "Preferred gateway", type: "select", options: window.AGB_DATA.GATEWAYS.map((g) => ({ value: g.id, label: g.name })) },
      { key: "adr", label: "Adaptive Data Rate (ADR)", type: "checkbox", value: true },
    ],
  });
}
function actSpecSheet(family) {
  const { FAMILIES } = window.AGB_DATA;
  const meta = FAMILIES[family];
  AGB_UI.modal({
    eyebrow: family, title: `${family} — specification`, subtitle: meta.tagline, wide: true,
    submitLabel: "Download PDF", onSubmit: () => downloadText(`${family}-spec.txt`,
      `AGROBOTIC — ${family}\n${meta.tagline}\n\nDeployment: ${meta.deployment}\nCertifications: ${meta.certifications.join(", ")}\n\nParameters:\n` +
      meta.parameters.map((p) => `  • ${p.name} — ${p.method} — ${p.range} ${p.unit}\n      ${p.what}`).join("\n")),
    successToast: false,
    body: (
      <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
        <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
          {meta.certifications.map((c) => <span key={c} className="tag" style={{ fontSize: 10 }}><Icon name="shield" size={10} /> {c}</span>)}
        </div>
        <div className="dim" style={{ fontSize: 13, lineHeight: 1.5 }}>{meta.deployment}</div>
        <div>
          <div className="section-title">Sensed parameters ({meta.parameters.length})</div>
          <div className="card" style={{ padding: 0, overflow: "hidden" }}>
            <DataTable rows={meta.parameters} idKey="code" columns={[
              { key: "name", label: "Parameter", render: (r) => <span style={{ fontWeight: 500 }}>{r.name}</span> },
              { key: "method", label: "Method", dim: true },
              { key: "range", label: "Range", mono: true, align: "right", render: (r) => `${r.range} ${r.unit}` },
            ]} />
          </div>
        </div>
      </div>
    ),
  });
}
function actDeploy(family, farms) {
  AGB_UI.form({
    eyebrow: family, title: `Deploy ${family}`, subtitle: "Plan a rollout across one or more farms.",
    submitLabel: "Create deployment", successMessage: `${family} deployment scheduled`,
    fields: [
      { key: "farm", label: "Target farm", type: "select", options: farms.map((f) => ({ value: f.id, label: f.name })) },
      { key: "qty", label: "Number of nodes", type: "number", value: 12, suffix: "units" },
      { key: "start", label: "Target install date", type: "date", value: new Date(Date.now() + 14 * 864e5).toISOString().slice(0, 10) },
      { key: "notes", label: "Notes", type: "textarea", placeholder: "Field zones, access, power…", value: "" },
    ],
  });
}
function actExportSensorsCSV(sensors, farmName) {
  downloadCSV(`${(farmName || "agrobotic").replace(/\s+/g, "-").toLowerCase()}-sensors.csv`,
    ["Sensor", "DevEUI", "Family", "Type", "Status", "Battery %", "RSSI dBm", "SNR", "Class", "Last seen"],
    sensors.map((s) => [s.name, s.eui, s.family, s.type, s.status, s.battery, s.rssi, s.snr, s.class, s.last_seen]));
}
function actScheduleCalibration(sensorName) {
  AGB_UI.form({
    eyebrow: "Calibration", title: `Schedule calibration`, subtitle: sensorName ? `${sensorName}` : "",
    submitLabel: "Schedule", successMessage: "Calibration scheduled · technician notified",
    fields: [
      { key: "date", label: "Date", type: "date", value: new Date(Date.now() + 3 * 864e5).toISOString().slice(0, 10) },
      { key: "tech", label: "Assign technician", type: "select", options: ["Auto-assign", "Field team A", "Field team B", "Remote re-cal"] },
    ],
  });
}

// ==================== PULSE (overview) ====================
function IoTPulse({ navigate }) {
  const { FARMS, KPIS, ALERTS, FAMILIES, SENSORS, GATEWAYS } = window.AGB_DATA;
  const [tick, setTick] = iotUseState(0);
  iotUseEffect(() => { const id = setInterval(() => setTick((t) => t + 1), 2400); return () => clearInterval(id); }, []);

  const totalSensorsByFamily = iotUseMemo(() => {
    const map = { NitroSense: 0, EcoSense: 0, ForestSentinel: 0, AquaSentinel: 0 };
    SENSORS.forEach((s) => { map[s.family] = (map[s.family] || 0) + 1; });
    return map;
  }, []);

  return (
    <div>
      <PageHeader
        eyebrow="IoT · Pulse"
        title="Live operational view across all farms"
        sub="Every Agrobotic device, every gateway, every alert in one place. Tap a farm canvas to drill in."
        actions={
          <>
            <button className="btn ghost"><Icon name="refresh" size={14} /> 4s refresh</button>
            <button className="btn primary" onClick={() => actAddDevice(FARMS)}><Icon name="plus" size={14} /> Add device</button>
          </>
        }
      />

      {/* KPI row */}
      <div style={{ display: "grid", gridTemplateColumns: "repeat(6, 1fr)", gap: 12, padding: "0 var(--space-6) var(--space-4)" }}>
        <Stat label="Sensors online" value={KPIS.iot.sensors_online} unit={`/ ${KPIS.iot.sensors_total}`}
              delta={1.4} sparkline={generateSeries(20, 165, 4, 0.1, 12)} />
        <Stat label="Farms active"   value={KPIS.iot.farms_active} delta={0}
              sparkline={generateSeries(20, 8, 0, 0, 14)} />
        <Stat label="Gateways"       value={`${KPIS.iot.gateways_online}/${KPIS.iot.gateways_total}`} delta={-2.0}
              sparkline={generateSeries(20, 4, 0.4, 0, 23)} spark_color="var(--amber)" />
        <Stat label="Readings · 24h" value={fmtNum(KPIS.iot.readings_24h)} delta={5.3}
              sparkline={generateSeries(24, 52000, 6000, 200, 7)} />
        <Stat label="Open alerts"    value={KPIS.iot.open_alerts} delta={-12.5} spark_color="var(--rose)"
              sparkline={generateSeries(20, 12, 2, -0.2, 9)} />
        <Stat label="Avg uptime"     value={fmtPct(KPIS.iot.avg_uptime, 1)} delta={0.2}
              sparkline={generateSeries(20, 93, 1.2, 0.05, 4)} />
      </div>

      {/* Main split: farm canvas grid + alert feed */}
      <div style={{ display: "grid", gridTemplateColumns: "minmax(0, 1fr) 360px", gap: 12, padding: "0 var(--space-6) var(--space-6)" }}>
        <div>
          <div className="section-title" style={{ padding: "0 4px" }}>
            <span>Farm canvases</span>
            <span className="dim" style={{ textTransform: "none", letterSpacing: 0, fontWeight: 400, marginRight: 8 }}>Live · {tick === 0 ? "synced" : `tick ${tick}`}</span>
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(280px, 1fr))", gap: 12 }}>
            {FARMS.slice(0, 4).map((farm) => (
              <FarmCanvas key={farm.id} farm={farm} tick={tick} onClick={() => navigate(`farm/${farm.id}`)} />
            ))}
          </div>
          <div className="dim" style={{ textAlign: "center", fontSize: 12, padding: "12px 0" }}>
            <a href="#farms" onClick={(e) => { e.preventDefault(); navigate("farms"); }} style={{ color: "var(--primary)", textDecoration: "none" }}>
              View all {FARMS.length} farms →
            </a>
          </div>
        </div>

        <div>
          <div className="section-title" style={{ padding: "0 4px" }}><span>Live alert feed</span><span className="tag bad" style={{ marginRight: 8 }}>{ALERTS.filter(a => a.severity === "high").length} high</span></div>
          <div className="card" style={{ padding: 0, overflow: "hidden" }}>
            {ALERTS.map((a, i) => {
              const farm = FARMS.find((f) => f.id === a.farm_id);
              return (
                <div key={a.id} style={{ padding: "12px 14px", borderBottom: i < ALERTS.length - 1 ? "1px solid var(--border-faint)" : 0, display: "flex", gap: 10, cursor: "pointer", transition: "background .15s" }}
                     onMouseEnter={(e) => e.currentTarget.style.background = "var(--bg-elev)"}
                     onMouseLeave={(e) => e.currentTarget.style.background = "transparent"}>
                  <Severity level={a.severity} />
                  <div style={{ minWidth: 0, flex: 1 }}>
                    <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", gap: 8 }}>
                      <div style={{ fontSize: 13, fontWeight: 500, color: "var(--text)" }}>{a.title}</div>
                      <div className="faint" style={{ fontSize: 10, whiteSpace: "nowrap" }}>{a.t}</div>
                    </div>
                    <div className="dim" style={{ fontSize: 11, marginTop: 3, lineHeight: 1.4 }}>{a.body}</div>
                    <div style={{ display: "flex", gap: 6, marginTop: 6 }}>
                      <span className="tag mono" style={{ fontSize: 10 }}>{a.sensor}</span>
                      <span className="tag" style={{ fontSize: 10, textTransform: "none" }}>{farm?.name}</span>
                      <span className="tag" style={{ fontSize: 10 }}>{a.category}</span>
                    </div>
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      </div>

      {/* Bottom row: family rollups */}
      <div style={{ padding: "0 var(--space-6) var(--space-6)" }}>
        <div className="section-title"><span>Sensor families</span></div>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 12 }}>
          {Object.keys(FAMILIES).map((fam) => (
            <FamilyCard key={fam} family={fam} count={totalSensorsByFamily[fam] || 0} navigate={navigate} />
          ))}
        </div>
      </div>
    </div>
  );
}

// --------- Farm card (clean, data-forward agritech style) ---------
function farmLatest(farm) {
  const rand = mulberry32((farm.id.charCodeAt(2) || 5) * 41);
  const mk = (base, vol, dec = 0) => {
    const series = generateSeries(16, base, vol, 0, base * 100 + farm.id.charCodeAt(2));
    const last = series[series.length - 1], prev = series[series.length - 4];
    return { value: +last.toFixed(dec), delta: +(((last - prev) / prev) * 100).toFixed(1), series };
  };
  const isDairy = farm.crop === "dairy";
  return {
    moisture: mk(24 + rand() * 26, 3),
    ndvi: mk(0.42 + rand() * 0.42, 0.05, 2),
    primary: isDairy ? { label: "CH₄", unit: "ppm", ...mk(1400 + rand() * 300, 60) }
                     : { label: "NO₃⁻", unit: "ppm", ...mk(14 + rand() * 24, 3) },
    soilTemp: mk(9 + rand() * 12, 1, 1),
    fields: Array.from({ length: 6 + Math.floor(rand() * 4) }, () => Math.max(0.12, Math.min(0.9, (farm.health / 100) * 0.7 + rand() * 0.35 - 0.1))),
  };
}

function FarmCanvas({ farm, tick, onClick }) {
  const { SENSORS, ALERTS, FAMILIES } = window.AGB_DATA;
  const sensors = SENSORS.filter((s) => s.farm_id === farm.id);
  const alerts = ALERTS.filter((a) => a.farm_id === farm.id);
  const d = iotUseMemo(() => farmLatest(farm), [farm.id]);
  const healthColor = farm.health > 80 ? "var(--green)" : farm.health > 70 ? "var(--amber)" : "var(--rose)";
  const statusLabel = farm.health > 80 ? "Healthy" : farm.health > 70 ? "Watch" : "Attention";
  const ndviRamp = (t) => window.rgbCss(window.rampColor(window.RAMP_NDVI, t));
  const avgNdvi = (d.fields.reduce((s, v) => s + v, 0) / d.fields.length).toFixed(2);

  const tiles = [
    { label: "Soil moisture", unit: "% VWC", v: d.moisture, color: "var(--cyan)" },
    { label: "Crop NDVI", unit: "", v: d.ndvi, color: "var(--green)" },
    { label: d.primary.label, unit: d.primary.unit, v: d.primary, color: "var(--violet)" },
    { label: "Soil temp", unit: "°C", v: d.soilTemp, color: "var(--amber)" },
  ];

  return (
    <div className="card farm-card" onClick={onClick}
      style={{ overflow: "hidden", cursor: "pointer", display: "flex", flexDirection: "column", position: "relative", transition: "transform .18s ease, border-color .18s ease, box-shadow .18s ease" }}
      onMouseEnter={(e) => { e.currentTarget.style.borderColor = "var(--border-strong)"; e.currentTarget.style.transform = "translateY(-2px)"; e.currentTarget.style.boxShadow = "0 16px 44px -20px rgba(0,0,0,0.7)"; }}
      onMouseLeave={(e) => { e.currentTarget.style.borderColor = "var(--border)"; e.currentTarget.style.transform = "none"; e.currentTarget.style.boxShadow = "none"; }}>
      {/* accent stripe */}
      <div style={{ position: "absolute", left: 0, top: 0, bottom: 0, width: 3, background: healthColor }} />

      {/* Header */}
      <div style={{ padding: "16px 18px 12px", display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 10 }}>
        <div style={{ minWidth: 0 }}>
          <div style={{ fontSize: 15, fontWeight: 600, letterSpacing: "-0.01em", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{farm.name}</div>
          <div className="dim" style={{ fontSize: 11.5, marginTop: 3, textTransform: "capitalize" }}>{farm.crop} · {farm.region} · {fmtNum(farm.acres)} ac</div>
        </div>
        <span className="tag" style={{ fontSize: 10, color: healthColor, borderColor: `color-mix(in oklch, ${healthColor} 35%, transparent)`, background: `color-mix(in oklch, ${healthColor} 12%, transparent)`, flexShrink: 0 }}>
          <span style={{ width: 6, height: 6, borderRadius: 999, background: healthColor }} /> {statusLabel}
        </span>
      </div>

      {/* Latest readings grid */}
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 1, background: "var(--border-faint)", borderTop: "1px solid var(--border-faint)", borderBottom: "1px solid var(--border-faint)" }}>
        {tiles.map((t) => (
          <div key={t.label} style={{ background: "var(--surface)", padding: "12px 16px" }}>
            <div className="faint" style={{ fontSize: 10, textTransform: "uppercase", letterSpacing: "0.05em" }}>{t.label}</div>
            <div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between", gap: 6, marginTop: 5 }}>
              <div style={{ display: "flex", alignItems: "baseline", gap: 3 }}>
                <span className="mono tnum" style={{ fontSize: 19, fontWeight: 600 }}>{t.v.value}</span>
                <span className="dim mono" style={{ fontSize: 10 }}>{t.unit}</span>
              </div>
              <Sparkline data={t.v.series} color={t.color} w={48} h={20} fill={false} strokeWidth={1.3} />
            </div>
            <div style={{ fontSize: 10, marginTop: 3, color: t.v.delta >= 0 ? "var(--green)" : "var(--rose)" }}>
              {t.v.delta >= 0 ? "▲" : "▼"} {Math.abs(t.v.delta)}% <span className="faint">24h</span>
            </div>
          </div>
        ))}
      </div>

      {/* Field NDVI distribution */}
      <div style={{ padding: "12px 18px 4px" }}>
        <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 6 }}>
          <span className="faint" style={{ fontSize: 10, textTransform: "uppercase", letterSpacing: "0.05em" }}>Field health (NDVI)</span>
          <span className="mono dim" style={{ fontSize: 10 }}>{d.fields.length} fields · avg {avgNdvi}</span>
        </div>
        <div style={{ display: "flex", gap: 3 }}>
          {d.fields.map((v, i) => (
            <div key={i} title={`Field ${i + 1}: NDVI ${v.toFixed(2)}`} style={{ flex: 1, height: 8, borderRadius: 2, background: ndviRamp(v) }} />
          ))}
        </div>
      </div>

      {/* Footer */}
      <div style={{ padding: "12px 18px 16px", display: "flex", alignItems: "center", justifyContent: "space-between" }}>
        <div style={{ display: "flex", gap: 14, fontSize: 11 }}>
          <span className="dim" style={{ display: "flex", alignItems: "center", gap: 5 }}><Icon name="sensor" size={12} color="var(--text-faint)" /> {sensors.length}</span>
          <span style={{ display: "flex", alignItems: "center", gap: 5, color: alerts.length ? "var(--amber)" : "var(--text-dim)" }}><Icon name="alert" size={12} color={alerts.length ? "var(--amber)" : "var(--text-faint)"} /> {alerts.length}</span>
          <span className="dim" style={{ display: "flex", alignItems: "center", gap: 5 }}><span className="pulse-dot" style={{ width: 5, height: 5 }} /> synced</span>
        </div>
        <span style={{ fontSize: 11, color: "var(--primary)", display: "flex", alignItems: "center", gap: 3, fontWeight: 500 }}>Open <Icon name="chev" size={12} color="var(--primary)" /></span>
      </div>
    </div>
  );
}


function FamilyCard({ family, count, navigate }) {
  const { FAMILIES } = window.AGB_DATA;
  const meta = FAMILIES[family];
  const series = iotUseMemo(() => generateSeries(40, 50, 6, 0, family.charCodeAt(0)), [family]);
  return (
    <div className="card" onClick={() => navigate(`family/${family}`)}
         style={{ cursor: "pointer", padding: 14, transition: "border-color .2s" }}
         onMouseEnter={(e) => { e.currentTarget.style.borderColor = meta.color; }}
         onMouseLeave={(e) => { e.currentTarget.style.borderColor = "var(--border)"; }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
        <div>
          <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
            <span style={{ width: 8, height: 8, borderRadius: 2, background: meta.color }} />
            <div style={{ fontWeight: 600, fontSize: 14 }}>{family}</div>
          </div>
          <div className="dim" style={{ fontSize: 11, marginTop: 4 }}>{meta.tagline}</div>
        </div>
        <div style={{ textAlign: "right" }}>
          <div className="mono tnum" style={{ fontSize: 18, fontWeight: 600 }}>{count}</div>
          <div className="faint" style={{ fontSize: 10, letterSpacing: "0.05em", textTransform: "uppercase" }}>devices</div>
        </div>
      </div>
      <div style={{ marginTop: 10 }}>
        <Sparkline data={series} color={meta.color} w={260} h={36} />
      </div>
      <div style={{ display: "flex", gap: 4, marginTop: 8, flexWrap: "wrap" }}>
        {meta.parameters.slice(0, 4).map((p) => (
          <span key={p.code} className="tag" style={{ fontSize: 9, padding: "1px 6px" }}>{p.name.split(" ")[0]}</span>
        ))}
        {meta.parameters.length > 4 && <span className="tag" style={{ fontSize: 9, padding: "1px 6px" }}>+{meta.parameters.length - 4}</span>}
      </div>
    </div>
  );
}

// ==================== FARMS list ====================
function IoTFarms({ navigate }) {
  const { FARMS, SENSORS } = window.AGB_DATA;
  const [view, setView] = iotUseState("grid");
  const [selectedId, setSelectedId] = iotUseState(null);

  return (
    <div>
      <PageHeader
        eyebrow="IoT · Farms"
        title="Connected farms"
        sub={`${FARMS.length} farms · ${SENSORS.length} sensors · ${FARMS.reduce((s, f) => s + f.acres, 0).toLocaleString()} acres under monitoring`}
        actions={
          <>
            <Tabs variant="pill" active={view} onChange={setView} tabs={[
              { id: "grid", label: "Canvas" },
              { id: "list", label: "Table" },
              { id: "map", label: "Map" },
            ]} />
            <button className="btn primary" onClick={actOnboardFarm}><Icon name="plus" size={14} /> Onboard farm</button>
          </>
        }
      />
      <div style={{ padding: "0 var(--space-6) var(--space-6)" }}>
        {view === "grid" && (
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(360px, 1fr))", gap: 12 }}>
            {FARMS.map((f) => <FarmCanvas key={f.id} farm={f} tick={1} onClick={() => navigate(`farm/${f.id}`)} />)}
          </div>
        )}
        {view === "list" && (
          <div className="card" style={{ padding: 0, overflow: "hidden" }}>
            <DataTable
              idKey="id"
              selectedId={selectedId}
              onRowClick={(r) => navigate(`farm/${r.id}`)}
              columns={[
                { key: "name", label: "Farm", render: (r) => <div><div style={{ fontWeight: 500 }}>{r.name}</div><div className="dim" style={{ fontSize: 11 }}>{r.id} · {r.owner}</div></div> },
                { key: "region", label: "Region", mono: true },
                { key: "crop", label: "Crop" },
                { key: "acres", label: "Acres", align: "right", mono: true, render: (r) => fmtNum(r.acres) },
                { key: "sensors", label: "Sensors", align: "right", mono: true },
                { key: "health", label: "Health", align: "right", render: (r) => (
                  <div style={{ display: "flex", alignItems: "center", gap: 8, justifyContent: "flex-end" }}>
                    <Bar value={r.health} color={r.health > 80 ? "var(--green)" : r.health > 70 ? "var(--amber)" : "var(--rose)"} />
                    <span className="mono tnum" style={{ width: 30, textAlign: "right" }}>{r.health}</span>
                  </div>
                ) },
                { key: "alerts", label: "Alerts", align: "right", render: (r) => r.alerts ? <span className={`tag ${r.alerts > 2 ? "bad" : "warn"}`}>{r.alerts}</span> : <span className="dim">—</span> },
                { key: "revenue_usd", label: "Rev YTD", align: "right", mono: true, render: (r) => fmtMoney(r.revenue_usd) },
              ]}
              rows={FARMS}
            />
          </div>
        )}
        {view === "map" && <FarmsMap farms={FARMS} navigate={navigate} />}
      </div>
    </div>
  );
}

// --------- Farms map (continental, schematic) ---------
function FarmsMap({ farms, navigate }) {
  // Simple equirect projection of North America
  const w = 900, h = 480;
  const project = (lat, lng) => {
    // bbox: lng -130 to -65, lat 25 to 55
    const x = ((lng + 130) / (130 - 65)) * w;
    const y = (1 - (lat - 25) / (55 - 25)) * h;
    return [x, y];
  };
  const [hover, setHover] = iotUseState(null);
  return (
    <div className="card" style={{ position: "relative", overflow: "hidden", padding: 0 }}>
      <svg viewBox={`0 0 ${w} ${h}`} style={{ width: "100%", display: "block" }} className="grid-bg">
        {/* Simplified continental outline */}
        <path d="M 50 200 Q 100 140 180 140 L 280 110 L 380 100 L 480 100 L 580 130 L 680 130 L 760 160 L 820 200 L 820 280 L 760 320 L 700 360 L 620 380 L 540 370 L 460 390 L 380 380 L 320 360 L 260 340 L 200 340 L 140 320 L 90 280 Z"
              fill="color-mix(in oklch, var(--primary) 4%, var(--surface))" stroke="var(--border-strong)" strokeWidth="1" />
        {/* Region labels */}
        {[
          { l: "BC", x: 130, y: 200 },
          { l: "AB", x: 230, y: 220 },
          { l: "SK", x: 310, y: 220 },
          { l: "ON", x: 510, y: 240 },
          { l: "QC", x: 590, y: 220 },
          { l: "WI", x: 460, y: 290 },
          { l: "IA", x: 440, y: 320 },
          { l: "IL", x: 490, y: 320 },
          { l: "CA", x: 180, y: 320 },
        ].map((r) => (
          <text key={r.l} x={r.x} y={r.y} fontSize="11" fill="var(--text-faint)" fontFamily="var(--font-mono)">{r.l}</text>
        ))}
        {/* Farms */}
        {farms.map((f) => {
          const [x, y] = project(f.lat, f.lng);
          const c = f.alerts > 0 ? "var(--amber)" : "var(--green)";
          return (
            <g key={f.id} style={{ cursor: "pointer" }}
               onMouseEnter={() => setHover(f.id)}
               onMouseLeave={() => setHover(null)}
               onClick={() => navigate(`farm/${f.id}`)}>
              <circle cx={x} cy={y} r="14" fill={c} opacity="0.15" />
              <circle cx={x} cy={y} r="6" fill={c} stroke="var(--bg)" strokeWidth="1.5" />
              {hover === f.id && (
                <g>
                  <rect x={x + 10} y={y - 30} width="200" height="50" rx="6" fill="var(--bg-elev)" stroke="var(--border-strong)" />
                  <text x={x + 18} y={y - 14} fontSize="11" fontWeight="600" fill="var(--text)">{f.name}</text>
                  <text x={x + 18} y={y - 1} fontSize="10" fill="var(--text-dim)">{f.crop} · {fmtNum(f.acres)} ac · {f.sensors} sensors</text>
                  <text x={x + 18} y={y + 12} fontSize="10" fill="var(--text-faint)" fontFamily="var(--font-mono)">{f.region} · health {f.health}</text>
                </g>
              )}
            </g>
          );
        })}
      </svg>
    </div>
  );
}

// ==================== FARM DETAIL ====================
function IoTFarmDetail({ farmId, navigate }) {
  const { FARMS, SENSORS, GATEWAYS, ALERTS, FAMILIES } = window.AGB_DATA;
  const farm = FARMS.find((f) => f.id === farmId);
  if (!farm) return <Empty title="Farm not found" body="Pick one from the list" />;
  const sensors = SENSORS.filter((s) => s.farm_id === farmId);
  const alerts = ALERTS.filter((a) => a.farm_id === farmId);
  const gateway = GATEWAYS.find((g) => g.farm_id === farmId);
  const [tab, setTab] = iotUseState("overview");

  const series_moisture = iotUseMemo(() => generateSeries(96, 42, 4, 0, 31), []);
  const series_no3 = iotUseMemo(() => generateSeries(96, 18, 3, 0.02, 17), []);
  const series_n2o = iotUseMemo(() => generateSeries(96, 0.8, 0.15, 0, 23), []);
  const series_ch4 = iotUseMemo(() => generateSeries(96, 1500, 80, 0.5, 7), []);

  const familyCount = {};
  sensors.forEach((s) => { familyCount[s.family] = (familyCount[s.family] || 0) + 1; });

  return (
    <div>
      <div style={{ padding: "var(--space-4) var(--space-6) 0", display: "flex", alignItems: "center", gap: 8, fontSize: 13 }}>
        <button className="btn ghost" onClick={() => navigate("farms")}><Icon name="chev" size={14} color="var(--text-dim)" style={{ transform: "rotate(180deg)" }} /> Farms</button>
        <span className="faint">/</span>
        <span className="mono dim">{farm.id}</span>
      </div>
      <PageHeader
        eyebrow={`${farm.region} · ${farm.crop}`}
        title={farm.name}
        sub={`${fmtNum(farm.acres)} acres · onboarded ${farm.since} · owner: ${farm.owner}`}
        actions={
          <>
            <button className="btn" onClick={() => actExportSensorsCSV(sensors, farm.name)}><Icon name="download" size={14} /> Export</button>
            <button className="btn primary" onClick={() => actAddDevice(FARMS)}><Icon name="plus" size={14} /> Add sensor</button>
          </>
        }
      />
      <div style={{ padding: "0 var(--space-6)" }}>
        <Tabs active={tab} onChange={setTab} tabs={[
          { id: "overview", label: "Overview", icon: "pulse" },
          { id: "sensors",  label: "Sensors", icon: "sensor", badge: sensors.length },
          { id: "alerts",   label: "Alerts", icon: "alert", badge: alerts.length || null },
          { id: "map",      label: "Live map", icon: "map" },
        ]} />
      </div>
      <div style={{ padding: "var(--space-5) var(--space-6) var(--space-6)" }}>
        {tab === "overview" && (
          <div>
            <div style={{ display: "grid", gridTemplateColumns: "repeat(5, 1fr)", gap: 12, marginBottom: 16 }}>
              <Stat label="Health score" value={farm.health} unit="/ 100" delta={1.4}
                    sparkline={generateSeries(20, farm.health, 1, 0, farm.id.charCodeAt(2))}
                    color={farm.health > 80 ? "var(--green)" : "var(--amber)"} />
              <Stat label="Sensors" value={sensors.length} sparkline={generateSeries(20, sensors.length, 0.5, 0, 8)} />
              <Stat label="Open alerts" value={alerts.length} sparkline={generateSeries(20, 2, 1, 0, 11)} spark_color="var(--rose)" />
              <Stat label="Rev YTD" value={fmtMoney(farm.revenue_usd)} delta={3.2}
                    sparkline={generateSeries(20, 100, 5, 0.5, 17)} />
              <Stat label="Carbon offset" value={`${Math.round(farm.acres * 0.4)}`} unit="tCO₂e" delta={8.1} spark_color="var(--cyan)" />
            </div>

            <div style={{ display: "grid", gridTemplateColumns: "1.4fr 1fr", gap: 12 }}>
              <div className="card">
                <div className="card-hd">
                  <div>
                    <div style={{ fontSize: 13, fontWeight: 500 }}>Readings · last 24 hours</div>
                    <div className="dim" style={{ fontSize: 11, marginTop: 2 }}>Soil moisture trend across this farm</div>
                  </div>
                  <Tabs variant="pill" active="24h" onChange={() => {}} tabs={[{ id: "1h", label: "1H" }, { id: "24h", label: "24H" }, { id: "7d", label: "7D" }, { id: "30d", label: "30D" }]} />
                </div>
                <div className="card-bd">
                  <AreaChart series={series_moisture} h={240} color="var(--cyan)" yLabel="moisture %"
                             labels={Array.from({ length: 96 }, (_, i) => i % 12 === 0 ? `${i / 4}h` : "")} />
                </div>
              </div>
              <div className="card">
                <div className="card-hd"><div style={{ fontSize: 13, fontWeight: 500 }}>Sensor mix</div></div>
                <div className="card-bd" style={{ display: "flex", flexDirection: "column", gap: 14 }}>
                  {Object.keys(FAMILIES).map((fam) => {
                    const ct = familyCount[fam] || 0;
                    return (
                      <div key={fam} style={{ display: "flex", alignItems: "center", gap: 12 }}>
                        <div style={{ width: 10, height: 10, borderRadius: 2, background: FAMILIES[fam].color, flexShrink: 0 }} />
                        <div style={{ flex: 1, minWidth: 0 }}>
                          <div style={{ display: "flex", justifyContent: "space-between", fontSize: 12 }}>
                            <span>{fam}</span>
                            <span className="mono dim">{ct}</span>
                          </div>
                          <Bar value={ct} max={sensors.length} color={FAMILIES[fam].color} h={4} />
                        </div>
                      </div>
                    );
                  })}
                  <div className="h-rule" />
                  <div style={{ display: "flex", justifyContent: "space-between", fontSize: 12 }}>
                    <span className="dim">Gateway</span>
                    <span className="mono">{gateway?.name || "—"}</span>
                  </div>
                  <div style={{ display: "flex", justifyContent: "space-between", fontSize: 12 }}>
                    <span className="dim">RSSI avg</span>
                    <span className="mono">{gateway?.rssi || "—"} dBm</span>
                  </div>
                  <div style={{ display: "flex", justifyContent: "space-between", fontSize: 12 }}>
                    <span className="dim">Uptime</span>
                    <span className="mono">{gateway?.uptime_d || "—"} days</span>
                  </div>
                </div>
              </div>
            </div>

            <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 12, marginTop: 12 }}>
              <MiniSeries title="Nitrate (NO₃⁻)" unit="ppm" value={series_no3[series_no3.length-1]} delta={2.3} color="var(--green)" data={series_no3} />
              <MiniSeries title="N₂O" unit="ppm" value={series_n2o[series_n2o.length-1].toFixed(2)} delta={-4.1} color="var(--amber)" data={series_n2o} />
              <MiniSeries title="CH₄" unit="ppm" value={Math.round(series_ch4[series_ch4.length-1])} delta={0.8} color="var(--cyan)" data={series_ch4} />
            </div>
          </div>
        )}

        {tab === "sensors" && (
          <SensorTable sensors={sensors} />
        )}

        {tab === "alerts" && (
          <div className="card" style={{ padding: 0 }}>
            {alerts.length ? alerts.map((a, i) => (
              <div key={a.id} style={{ padding: "14px 18px", display: "flex", gap: 12, alignItems: "flex-start", borderBottom: i < alerts.length-1 ? "1px solid var(--border-faint)" : 0 }}>
                <Severity level={a.severity} />
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 13, fontWeight: 500 }}>{a.title}</div>
                  <div className="dim" style={{ fontSize: 12, marginTop: 4 }}>{a.body}</div>
                </div>
                <button className="btn" onClick={() => AGB_UI.toast(`Acknowledged "${a.title}"`, { type: "ok" })}>Acknowledge</button>
              </div>
            )) : <Empty title="No active alerts" body="Everything is healthy on this farm." />}
          </div>
        )}

        {tab === "map" && (
          <div style={{ display: "grid", gridTemplateColumns: "minmax(0, 1fr) 340px", gap: 12, alignItems: "start" }}>
            <LiveFieldMap farm={farm} sensors={sensors} height={580} />
            <LiveEnvPanel lat={farm.lat} lng={farm.lng} seed={farm.id.charCodeAt(2)} />
          </div>
        )}
      </div>
    </div>
  );
}

function MiniSeries({ title, unit, value, delta, color, data }) {
  const positive = delta >= 0;
  return (
    <div className="card" style={{ padding: 16 }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
        <div>
          <div className="dim" style={{ fontSize: 11, textTransform: "uppercase", letterSpacing: "0.06em" }}>{title}</div>
          <div style={{ display: "flex", alignItems: "baseline", gap: 5, marginTop: 4 }}>
            <span className="mono tnum" style={{ fontSize: 22, fontWeight: 600 }}>{value}</span>
            <span className="dim mono" style={{ fontSize: 11 }}>{unit}</span>
          </div>
          <div style={{ fontSize: 11, color: positive ? "var(--green)" : "var(--rose)", marginTop: 4 }}>
            {positive ? "↑" : "↓"} {Math.abs(delta)}%
          </div>
        </div>
        <Sparkline data={data} color={color} w={120} h={56} />
      </div>
    </div>
  );
}

function FieldMap({ farm, sensors }) {
  const { FAMILIES } = window.AGB_DATA;
  // Bigger version of the canvas
  const fields = iotUseMemo(() => {
    const seed = farm.id.charCodeAt(2) || 1;
    const rand = mulberry32(seed);
    const n = 5 + Math.floor(rand() * 5);
    return Array.from({ length: n }, (_, i) => ({
      x: 4 + rand() * 70, y: 4 + rand() * 70,
      w: 18 + rand() * 24, h: 14 + rand() * 22,
      health: 60 + rand() * 35, label: `Field ${i + 1}`,
    }));
  }, [farm.id]);
  const dots = iotUseMemo(() => {
    const seed = farm.id.charCodeAt(2) * 3 || 7;
    const rand = mulberry32(seed);
    return sensors.map((s) => ({
      id: s.id, name: s.name, family: s.family,
      x: 4 + rand() * 92, y: 4 + rand() * 84,
      color: FAMILIES[s.family].color,
    }));
  }, [farm.id, sensors.length]);

  return (
    <div>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 12 }}>
        <div>
          <div style={{ fontSize: 14, fontWeight: 500 }}>{farm.name} — field map</div>
          <div className="dim" style={{ fontSize: 12, marginTop: 2 }}>Top-down schematic. Sensor placement, field health by color.</div>
        </div>
        <div style={{ display: "flex", gap: 10 }}>
          {Object.keys(FAMILIES).map((fam) => (
            <div key={fam} style={{ display: "flex", alignItems: "center", gap: 5, fontSize: 11 }}>
              <span style={{ width: 8, height: 8, borderRadius: 999, background: FAMILIES[fam].color }} />
              <span className="dim">{fam}</span>
            </div>
          ))}
        </div>
      </div>
      <svg viewBox="0 0 100 92" style={{ width: "100%", height: 540, background: "color-mix(in oklch, var(--green) 4%, var(--bg))", borderRadius: 8 }} className="grid-bg">
        {fields.map((f, i) => {
          const c = f.health > 80 ? "var(--green)" : f.health > 65 ? "var(--amber)" : "var(--rose)";
          return (
            <g key={i}>
              <rect x={f.x} y={f.y} width={f.w} height={f.h} rx="1" fill={c} opacity="0.15" stroke={c} strokeWidth="0.3" strokeOpacity="0.5" />
              <text x={f.x + 1.5} y={f.y + 4} fontSize="2.4" fill="var(--text-dim)" fontFamily="var(--font-mono)">{f.label}</text>
              <text x={f.x + 1.5} y={f.y + f.h - 1.5} fontSize="2" fill="var(--text-faint)" fontFamily="var(--font-mono)">h:{Math.round(f.health)}</text>
            </g>
          );
        })}
        {dots.map((d) => (
          <g key={d.id}>
            <circle cx={d.x} cy={d.y} r="1.6" fill={d.color} stroke="var(--bg)" strokeWidth="0.3" />
            <title>{d.name} — {d.family}</title>
          </g>
        ))}
      </svg>
    </div>
  );
}

function SensorTable({ sensors }) {
  const { FAMILIES } = window.AGB_DATA;
  return (
    <div className="card" style={{ padding: 0, overflow: "hidden" }}>
      <DataTable rows={sensors} columns={[
        { key: "name", label: "Sensor", render: (r) => (
          <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
            <span style={{ width: 8, height: 8, borderRadius: 2, background: FAMILIES[r.family].color }} />
            <div>
              <div className="mono" style={{ fontWeight: 500 }}>{r.name}</div>
              <div className="dim mono" style={{ fontSize: 10 }}>{r.eui}</div>
            </div>
          </div>
        )},
        { key: "family", label: "Family" },
        { key: "type", label: "Type", mono: true, dim: true },
        { key: "status", label: "Status", render: (r) => <StatusDot status={r.status} /> },
        { key: "battery", label: "Battery", render: (r) => <Battery pct={r.battery} /> },
        { key: "rssi", label: "RSSI", mono: true, align: "right", render: (r) => r.rssi + " dBm" },
        { key: "snr", label: "SNR", mono: true, align: "right" },
        { key: "last_seen", label: "Last seen", dim: true, align: "right" },
      ]} />
    </div>
  );
}

// ==================== NETWORK (LoRaWAN topology) ====================
function IoTNetwork({ navigate }) {
  const { GATEWAYS, SENSORS, FARMS, KPIS } = window.AGB_DATA;
  const [tab, setTab] = iotUseState("topology");

  return (
    <div>
      <PageHeader
        eyebrow="IoT · Network"
        title="LoRaWAN mesh topology"
        sub="Live gateway health, device assignments, signal quality across the entire network."
        actions={<button className="btn primary" onClick={actProvisionDevice}><Icon name="plus" size={14} /> Provision device</button>}
      />
      <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 12, padding: "0 var(--space-6) var(--space-4)" }}>
        <Stat label="Gateways online" value={`${KPIS.iot.gateways_online}/${KPIS.iot.gateways_total}`} delta={-2.0} sparkline={generateSeries(20, 4, 0.4, 0, 1)} spark_color="var(--amber)" />
        <Stat label="Connected devices" value={GATEWAYS.reduce((s, g) => s + g.devices, 0)} delta={5.3} sparkline={generateSeries(20, 145, 4, 0.2, 2)} />
        <Stat label="Avg RSSI" value={(GATEWAYS.reduce((s, g) => s + g.rssi, 0) / GATEWAYS.length).toFixed(1)} unit="dBm" delta={-0.5} sparkline={generateSeries(20, -78, 2, 0, 3)} spark_color="var(--rose)" />
        <Stat label="Packet success" value="96.8" unit="%" delta={-0.5} sparkline={generateSeries(20, 96, 0.5, 0, 5)} spark_color="var(--amber)" />
      </div>

      <div style={{ padding: "0 var(--space-6)" }}>
        <Tabs active={tab} onChange={setTab} tabs={[
          { id: "topology", label: "Topology", icon: "network" },
          { id: "gateways", label: "Gateways", icon: "wifi", badge: GATEWAYS.length },
          { id: "devices", label: "Devices", icon: "cpu", badge: SENSORS.length },
        ]} />
      </div>
      <div style={{ padding: "var(--space-5) var(--space-6) var(--space-6)" }}>
        {tab === "topology" && <NetworkTopology gateways={GATEWAYS} sensors={SENSORS} />}
        {tab === "gateways" && (
          <div className="card" style={{ padding: 0 }}>
            <DataTable rows={GATEWAYS} columns={[
              { key: "name", label: "Gateway", mono: true, render: (r) => <div><div className="mono" style={{ fontWeight: 500 }}>{r.name}</div><div className="dim mono" style={{ fontSize: 10 }}>{r.id}</div></div> },
              { key: "status", label: "Status", render: (r) => <StatusDot status={r.status} /> },
              { key: "farm_id", label: "Site", render: (r) => FARMS.find((f) => f.id === r.farm_id)?.name || "—" },
              { key: "devices", label: "Devices", mono: true, align: "right" },
              { key: "uptime_d", label: "Uptime", mono: true, align: "right", render: (r) => r.uptime_d + " d" },
              { key: "rssi", label: "Avg RSSI", mono: true, align: "right", render: (r) => r.rssi + " dBm" },
              { key: "lat", label: "Coords", dim: true, mono: true, render: (r) => `${r.lat.toFixed(2)}, ${r.lng.toFixed(2)}` },
            ]} />
          </div>
        )}
        {tab === "devices" && <SensorTable sensors={SENSORS.slice(0, 40)} />}
      </div>
    </div>
  );
}

// --------- Force-style topology (deterministic, no physics) ---------
function NetworkTopology({ gateways, sensors }) {
  const w = 1100, h = 580;
  const cx = w / 2, cy = h / 2;
  const [hover, setHover] = iotUseState(null);
  const [tick, setTick] = iotUseState(0);
  iotUseEffect(() => { const id = setInterval(() => setTick((t) => t + 1), 90); return () => clearInterval(id); }, []);

  // Lay out gateways in a ring
  const gwPos = gateways.map((g, i) => {
    const a = (i / gateways.length) * 2 * Math.PI - Math.PI / 2;
    const r = 180;
    return { ...g, x: cx + Math.cos(a) * r, y: cy + Math.sin(a) * r };
  });
  // Hub in center = backend / cloud
  const hub = { x: cx, y: cy, name: "Agrobotic Cloud" };
  // Sensors per gateway in fan
  const sensorPos = [];
  gwPos.forEach((g, gi) => {
    const own = sensors.filter((s) => s.gateway === g.id).slice(0, 14);
    const baseA = Math.atan2(g.y - cy, g.x - cx);
    own.forEach((s, si) => {
      const arc = (si - (own.length - 1) / 2) * 0.10;
      const a = baseA + arc;
      const r = 120;
      sensorPos.push({ ...s, x: g.x + Math.cos(a) * r, y: g.y + Math.sin(a) * r, gx: g.x, gy: g.y, ga: a });
    });
  });

  return (
    <div className="card" style={{ padding: 0, overflow: "hidden", position: "relative" }}>
      <svg viewBox={`0 0 ${w} ${h}`} style={{ width: "100%", display: "block", background: "var(--bg)" }} className="grid-bg">
        <defs>
          <radialGradient id="hubGrad">
            <stop offset="0%" stopColor="var(--primary)" stopOpacity="0.3" />
            <stop offset="100%" stopColor="var(--primary)" stopOpacity="0" />
          </radialGradient>
        </defs>
        {/* Hub glow */}
        <circle cx={hub.x} cy={hub.y} r="80" fill="url(#hubGrad)" />
        {/* Gateway -> Hub lines */}
        {gwPos.map((g) => {
          const color = g.status === "online" ? "var(--primary)" : g.status === "warning" ? "var(--amber)" : "var(--cyan)";
          return (
            <g key={`l-${g.id}`}>
              <line x1={hub.x} y1={hub.y} x2={g.x} y2={g.y} stroke={color} strokeWidth="0.7" strokeOpacity="0.4" />
              {/* Animated packet */}
              <circle r="3" fill={color}>
                <animateMotion dur={`${2 + (g.devices / 50)}s`} repeatCount="indefinite"
                  path={`M${g.x},${g.y} L${hub.x},${hub.y}`} />
              </circle>
            </g>
          );
        })}
        {/* Gateway -> sensor lines */}
        {sensorPos.map((s) => (
          <line key={`sl-${s.id}`} x1={s.gx} y1={s.gy} x2={s.x} y2={s.y}
                stroke="var(--border-strong)" strokeWidth="0.4" strokeOpacity="0.6" />
        ))}
        {/* Sensors */}
        {sensorPos.map((s) => (
          <g key={s.id} onMouseEnter={() => setHover({ kind: "s", id: s.id })} onMouseLeave={() => setHover(null)}>
            <circle cx={s.x} cy={s.y} r="3.5"
                    fill={window.AGB_DATA.FAMILIES[s.family].color}
                    opacity={s.status === "active" ? 0.9 : 0.4} />
          </g>
        ))}
        {/* Gateways */}
        {gwPos.map((g) => {
          const color = g.status === "online" ? "var(--primary)" : g.status === "warning" ? "var(--amber)" : "var(--cyan)";
          return (
            <g key={g.id} onMouseEnter={() => setHover({ kind: "g", id: g.id })} onMouseLeave={() => setHover(null)}>
              <circle cx={g.x} cy={g.y} r="14" fill="var(--bg)" stroke={color} strokeWidth="2" />
              <circle cx={g.x} cy={g.y} r="6" fill={color} opacity="0.6" />
              <text x={g.x} y={g.y + 30} textAnchor="middle" fontSize="11" fill="var(--text-2)" fontFamily="var(--font-mono)">{g.name}</text>
              <text x={g.x} y={g.y + 42} textAnchor="middle" fontSize="9" fill="var(--text-faint)" fontFamily="var(--font-mono)">{g.devices} dev · {g.rssi}dBm</text>
            </g>
          );
        })}
        {/* Hub */}
        <g>
          <circle cx={hub.x} cy={hub.y} r="28" fill="var(--surface-2)" stroke="var(--primary)" strokeWidth="1.5" />
          <circle cx={hub.x} cy={hub.y} r="12" fill="var(--primary)" opacity="0.3" />
          <text x={hub.x} y={hub.y + 3} textAnchor="middle" fontSize="11" fontWeight="600" fill="var(--primary)" fontFamily="var(--font-mono)">CLOUD</text>
          <text x={hub.x} y={hub.y + 50} textAnchor="middle" fontSize="11" fill="var(--text-dim)">agrobotic.io</text>
        </g>
      </svg>
      {hover?.kind === "g" && (() => {
        const g = gwPos.find((x) => x.id === hover.id);
        if (!g) return null;
        return (
          <div style={{ position: "absolute", left: `${(g.x / w) * 100}%`, top: `${(g.y / h) * 100 + 8}%`, transform: "translate(-50%, 0)", background: "var(--bg-elev)", border: "1px solid var(--border-strong)", borderRadius: 8, padding: "10px 14px", fontSize: 12, minWidth: 200, pointerEvents: "none" }}>
            <div style={{ fontWeight: 600, marginBottom: 4 }}>{g.name}</div>
            <div className="dim" style={{ fontSize: 11 }}>{g.devices} devices · {g.rssi} dBm · {g.uptime_d}d uptime</div>
          </div>
        );
      })()}
    </div>
  );
}

// ==================== FAMILY DETAIL (NitroSense / EcoSense / etc.) ====================
function IoTFamily({ family, navigate }) {
  const { FAMILIES, SENSORS, FARMS } = window.AGB_DATA;
  const meta = FAMILIES[family];
  if (!meta) return <Empty title="Unknown family" />;
  const sensors = SENSORS.filter((s) => s.family === family);
  const [tab, setTab] = iotUseState("live");
  const [selectedParam, setSelectedParam] = iotUseState(meta.flagship_param || meta.parameters[0].code);

  // All parameters as live series
  const paramSeries = iotUseMemo(() => {
    const obj = {};
    meta.parameters.forEach((p, i) => {
      obj[p.code] = generateSeries(96, p.baseline, p.vol, 0, family.charCodeAt(0) + p.code.charCodeAt(0) + i);
    });
    return obj;
  }, [family]);

  const param = meta.parameters.find((p) => p.code === selectedParam) || meta.parameters[0];
  const heroVals = paramSeries[param.code];

  // Multi-farm comparison for selected param
  const farmsForFamily = FARMS.filter((f) => sensors.some((s) => s.farm_id === f.id)).slice(0, 5);
  const cmpDatasets = farmsForFamily.map((f, i) => ({
    label: f.name,
    color: [meta.color, "var(--cyan)", "var(--amber)", "var(--violet)", "var(--pink)"][i],
    values: generateSeries(60, param.baseline + (i - 2) * param.vol * 0.6, param.vol, 0, f.id.charCodeAt(2) + param.code.charCodeAt(0)),
  }));

  return (
    <div>
      <div style={{ padding: "var(--space-4) var(--space-6) 0", display: "flex", alignItems: "center", gap: 8, fontSize: 13 }}>
        <button className="btn ghost" onClick={() => navigate("pulse")}><Icon name="chev" size={14} color="var(--text-dim)" style={{ transform: "rotate(180deg)" }} /> Pulse</button>
        <span className="faint">/</span>
        <span className="mono dim">families / {family}</span>
      </div>
      <PageHeader
        eyebrow={<span style={{ color: meta.color }}>● {family}</span>}
        title={meta.tagline}
        sub={meta.deployment}
        actions={<>
          <button className="btn" onClick={() => actSpecSheet(family)}><Icon name="book" size={14} /> Spec sheet</button>
          <button className="btn primary" onClick={() => actDeploy(family, FARMS)}><Icon name="plus" size={14} /> Deploy</button>
        </>}
      />

      {/* Certifications strip */}
      <div style={{ padding: "0 var(--space-6) var(--space-3)", display: "flex", gap: 6, flexWrap: "wrap", alignItems: "center" }}>
        <span className="faint" style={{ fontSize: 11, letterSpacing: "0.06em", textTransform: "uppercase", marginRight: 4 }}>Verified by</span>
        {meta.certifications.map((c) => (
          <span key={c} className="tag" style={{ fontSize: 10, padding: "2px 8px", textTransform: "none", letterSpacing: 0, color: meta.color, borderColor: `color-mix(in oklch, ${meta.color} 30%, transparent)`, background: `color-mix(in oklch, ${meta.color} 8%, transparent)` }}>
            <Icon name="shield" size={10} color={meta.color} /> {c}
          </span>
        ))}
      </div>

      {/* KPI row */}
      <div style={{ display: "grid", gridTemplateColumns: "repeat(5, 1fr)", gap: 12, padding: "0 var(--space-6) var(--space-4)" }}>
        <Stat label="Devices deployed" value={sensors.length} sparkline={generateSeries(20, sensors.length, 1, 0.1, 5)} spark_color={meta.color} />
        <Stat label="Farms covered"    value={farmsForFamily.length} sparkline={generateSeries(20, 5, 0.3, 0.05, 9)} spark_color={meta.color} />
        <Stat label="Online"           value={`${sensors.filter(s => s.status === "active").length}/${sensors.length}`} delta={1.4} sparkline={generateSeries(20, 96, 1, 0, 11)} spark_color={meta.color} />
        <Stat label="Avg battery"      value={`${Math.round(sensors.reduce((s, x) => s + x.battery, 0) / Math.max(1, sensors.length))}%`} delta={-2.1} spark_color={meta.color} sparkline={generateSeries(20, 78, 1.5, -0.05, 13)} />
        <Stat label="Parameters"       value={meta.parameters.length} unit="sensed" sparkline={generateSeries(20, meta.parameters.length, 0, 0, 15)} spark_color={meta.color} />
      </div>

      <div style={{ padding: "0 var(--space-6)" }}>
        <Tabs active={tab} onChange={setTab} tabs={[
          { id: "live",    label: "Live parameters", icon: "pulse", badge: meta.parameters.length },
          { id: "ai",      label: "Edge AI",         icon: "cpu" },
          { id: "compare", label: "Cross-farm",      icon: "chartBar" },
          { id: "devices", label: "Devices",         icon: "sensor", badge: sensors.length },
          { id: "calib",   label: "Calibration",     icon: "target" },
        ]} />
      </div>

      <div style={{ padding: "var(--space-5) var(--space-6) var(--space-6)" }}>
        {tab === "live" && (
          <div style={{ display: "grid", gridTemplateColumns: "minmax(0, 1.5fr) 1fr", gap: 12 }}>
            {/* Big chart for selected param */}
            <div className="card">
              <div className="card-hd">
                <div>
                  <div style={{ fontSize: 13, fontWeight: 500 }}>{param.name}</div>
                  <div className="dim" style={{ fontSize: 11, marginTop: 2 }}>{param.what}</div>
                </div>
                <div style={{ display: "flex", alignItems: "baseline", gap: 6 }}>
                  <span className="mono tnum" style={{ fontSize: 24, fontWeight: 600, color: meta.color }}>{heroVals[heroVals.length - 1].toFixed(param.vol < 1 ? 2 : 0)}</span>
                  <span className="dim mono" style={{ fontSize: 12 }}>{param.unit}</span>
                </div>
              </div>
              <div className="card-bd">
                <AreaChart series={heroVals} h={260} color={meta.color} yLabel={param.unit}
                           labels={Array.from({ length: 96 }, (_, i) => i % 8 === 0 ? `${i / 4}h` : "")} />
                <div style={{ display: "flex", justifyContent: "space-between", marginTop: 12, fontSize: 11 }}>
                  <div className="dim"><strong style={{ color: "var(--text-2)" }}>Method:</strong> {param.method}</div>
                  <div className="dim"><strong style={{ color: "var(--text-2)" }}>Range:</strong> {param.range} {param.unit}</div>
                  {param.threshold && <div style={{ color: "var(--amber)" }}><strong>Threshold:</strong> {param.threshold} {param.unit}</div>}
                </div>
              </div>
            </div>

            {/* Parameter selector / micro-readings */}
            <div className="card" style={{ padding: 0, overflow: "hidden" }}>
              <div className="card-hd"><div style={{ fontSize: 13, fontWeight: 500 }}>All sensed parameters</div><span className="dim" style={{ fontSize: 11 }}>tap to inspect</span></div>
              <div style={{ display: "flex", flexDirection: "column", maxHeight: 540, overflowY: "auto" }}>
                {meta.parameters.map((p) => {
                  const series = paramSeries[p.code];
                  const last = series[series.length - 1];
                  const prev = series[series.length - 8];
                  const delta = ((last - prev) / prev * 100);
                  const isSel = p.code === selectedParam;
                  return (
                    <button key={p.code} onClick={() => setSelectedParam(p.code)}
                      style={{
                        appearance: "none", border: 0, font: "inherit", textAlign: "left",
                        background: isSel ? `color-mix(in oklch, ${meta.color} 10%, transparent)` : "transparent",
                        padding: "12px 16px", cursor: "pointer", display: "grid",
                        gridTemplateColumns: "minmax(0, 1fr) 70px 50px", gap: 10, alignItems: "center",
                        borderLeft: `2px solid ${isSel ? meta.color : "transparent"}`,
                        borderBottom: "1px solid var(--border-faint)",
                      }}>
                      <div style={{ minWidth: 0 }}>
                        <div style={{ fontSize: 12, fontWeight: 500, color: "var(--text)" }}>{p.name}</div>
                        <div className="dim mono" style={{ fontSize: 10, marginTop: 2, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{p.method}</div>
                      </div>
                      <Sparkline data={series.slice(-30)} color={meta.color} w={70} h={24} fill={false} strokeWidth={1.2} />
                      <div style={{ textAlign: "right" }}>
                        <div className="mono tnum" style={{ fontSize: 12, fontWeight: 500 }}>{last.toFixed(p.vol < 1 ? 2 : 0)}</div>
                        <div className="mono" style={{ fontSize: 9, color: delta >= 0 ? "var(--green)" : "var(--rose)" }}>{delta >= 0 ? "+" : ""}{delta.toFixed(1)}%</div>
                      </div>
                    </button>
                  );
                })}
              </div>
            </div>
          </div>
        )}

        {tab === "ai" && (
          <div style={{ display: "grid", gridTemplateColumns: "1.4fr 1fr", gap: 12 }}>
            <div className="card">
              <div className="card-hd">
                <div>
                  <div style={{ fontSize: 13, fontWeight: 500 }}>Edge AI capabilities</div>
                  <div className="dim" style={{ fontSize: 11, marginTop: 2 }}>On-device analytics, no cloud dependency</div>
                </div>
                <span className="tag info"><Icon name="cpu" size={10} /> TinyML</span>
              </div>
              <div className="card-bd" style={{ display: "flex", flexDirection: "column", gap: 14 }}>
                {meta.edge_ai.map((line, i) => (
                  <div key={i} style={{ display: "flex", gap: 12, alignItems: "flex-start" }}>
                    <div style={{ width: 22, height: 22, borderRadius: 6, background: `color-mix(in oklch, ${meta.color} 15%, var(--surface-2))`, display: "grid", placeItems: "center", flexShrink: 0, marginTop: 1 }}>
                      <span className="mono" style={{ fontSize: 11, fontWeight: 600, color: meta.color }}>{i + 1}</span>
                    </div>
                    <div style={{ fontSize: 13, lineHeight: 1.55, color: "var(--text-2)" }}>{line}</div>
                  </div>
                ))}
              </div>
            </div>

            <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
              <div className="card">
                <div className="card-hd"><div style={{ fontSize: 13, fontWeight: 500 }}>Model performance</div></div>
                <div className="card-bd" style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
                  {(family === "AquaSentinel" || family === "ForestSentinel" || family === "EcoNitro") ? (
                    <>
                      <Ring value={family === "AquaSentinel" ? 94 : 80} max={100} color={meta.color} label="Accuracy" />
                      <Ring value={77.8} max={100} color={meta.color} label="AUC" />
                    </>
                  ) : (
                    <>
                      <Ring value={92} max={100} color={meta.color} label="Precision" />
                      <Ring value={88} max={100} color={meta.color} label="Recall" />
                    </>
                  )}
                </div>
              </div>
              <div className="card">
                <div className="card-hd"><div style={{ fontSize: 13, fontWeight: 500 }}>Deployment notes</div></div>
                <div className="card-bd" style={{ fontSize: 12, color: "var(--text-2)", lineHeight: 1.6 }}>
                  {meta.deployment}
                </div>
              </div>
            </div>
          </div>
        )}

        {tab === "compare" && (
          <div className="card">
            <div className="card-hd">
              <div>
                <div style={{ fontSize: 13, fontWeight: 500 }}>Cross-farm comparison · {param.name}</div>
                <div className="dim" style={{ fontSize: 11, marginTop: 2 }}>60-day rolling</div>
              </div>
              <div style={{ display: "flex", gap: 12, flexWrap: "wrap" }}>{cmpDatasets.map((d, i) => (
                <div key={i} style={{ display: "flex", alignItems: "center", gap: 5, fontSize: 11 }}>
                  <span style={{ width: 10, height: 2, background: d.color }} />
                  <span className="dim">{d.label}</span>
                </div>
              ))}</div>
            </div>
            <div className="card-bd"><MultiLine datasets={cmpDatasets} h={320} /></div>
          </div>
        )}

        {tab === "devices" && <SensorTable sensors={sensors} />}

        {tab === "calib" && (
          <div className="card">
            <div className="card-hd"><div style={{ fontSize: 13, fontWeight: 500 }}>Calibration recommendations</div></div>
            <div className="card-bd" style={{ display: "flex", flexDirection: "column", gap: 12 }}>
              {sensors.slice(0, 5).map((s, i) => {
                const drift = (mulberry32(s.id.charCodeAt(2) + i)() * 25 + 5).toFixed(1);
                const needs = +drift > 15;
                return (
                  <div key={s.id} style={{ display: "flex", alignItems: "center", gap: 16, padding: 14, background: "var(--bg-elev)", borderRadius: 8 }}>
                    <div style={{ width: 6, height: 36, borderRadius: 3, background: needs ? "var(--rose)" : "var(--amber)" }} />
                    <div style={{ flex: 1 }}>
                      <div style={{ fontSize: 13, fontWeight: 500 }} className="mono">{s.name} <span className="dim" style={{ fontWeight: 400 }}>· {window.AGB_DATA.FARMS.find(f => f.id === s.farm_id)?.name}</span></div>
                      <div className="dim" style={{ fontSize: 12, marginTop: 2 }}>{needs ? `${drift}% baseline drift detected — calibration overdue.` : `${drift}% drift, within tolerance.`}</div>
                    </div>
                    <button className="btn primary" onClick={() => actScheduleCalibration(s.name)}>Schedule</button>
                  </div>
                );
              })}
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

// ==================== CONDITIONS — see conditions.jsx (IoTConditions) ====================

// ==================== ALERTS ====================
function IoTAlerts({ navigate }) {
  const { ALERTS, FARMS } = window.AGB_DATA;
  const [filter, setFilter] = iotUseState("all");
  const filtered = ALERTS.filter((a) => filter === "all" || a.severity === filter);
  return (
    <div>
      <PageHeader eyebrow="IoT · Alerts"
                  title="Alerts & calibration recommendations"
                  sub={`${ALERTS.filter(a => a.severity === "high").length} high · ${ALERTS.filter(a => a.severity === "med").length} medium · ${ALERTS.filter(a => a.severity === "low").length} low`}
                  actions={<button className="btn" onClick={() => downloadCSV("agrobotic-alerts.csv", ["ID", "Severity", "Sensor", "Farm", "Title", "Detail", "Category", "Age"], ALERTS.map((a) => [a.id, a.severity, a.sensor, FARMS.find((f) => f.id === a.farm_id)?.name, a.title, a.body, a.category, a.t]))}><Icon name="download" size={14} /> Export CSV</button>} />
      <div style={{ padding: "0 var(--space-6) var(--space-3)" }}>
        <Tabs variant="pill" active={filter} onChange={setFilter} tabs={[
          { id: "all", label: "All" }, { id: "high", label: "High" }, { id: "med", label: "Med" }, { id: "low", label: "Low" },
        ]} />
      </div>
      <div style={{ padding: "0 var(--space-6) var(--space-6)" }}>
        <div className="card" style={{ padding: 0 }}>
          {filtered.map((a, i) => {
            const farm = FARMS.find((f) => f.id === a.farm_id);
            return (
              <div key={a.id} style={{ padding: "16px 18px", display: "flex", gap: 14, alignItems: "flex-start", borderBottom: i < filtered.length - 1 ? "1px solid var(--border-faint)" : 0 }}>
                <Severity level={a.severity} />
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", gap: 12 }}>
                    <div style={{ fontSize: 14, fontWeight: 500 }}>{a.title}</div>
                    <div className="faint" style={{ fontSize: 11, whiteSpace: "nowrap" }}>{a.t}</div>
                  </div>
                  <div className="dim" style={{ fontSize: 12, marginTop: 6, maxWidth: 720 }}>{a.body}</div>
                  <div style={{ display: "flex", gap: 6, marginTop: 8, alignItems: "center" }}>
                    <span className="tag mono" style={{ fontSize: 10 }}>{a.sensor}</span>
                    <span className="tag" style={{ fontSize: 10, textTransform: "none" }}>{farm?.name}</span>
                    <span className="tag info" style={{ fontSize: 10 }}>{a.category}</span>
                  </div>
                </div>
                <div style={{ display: "flex", gap: 8 }}>
                  <button className="btn ghost" onClick={() => AGB_UI.toast(`Snoozed "${a.title}" for 4h`, { type: "info" })}>Snooze</button>
                  <button className="btn" onClick={() => AGB_UI.toast(`Acknowledged "${a.title}"`, { type: "ok" })}>Acknowledge</button>
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, {
  IoTPulse, IoTFarms, IoTFarmDetail, IoTNetwork, IoTFamily, IoTAlerts,
});
