/* eslint-disable no-undef */
// Mock data layer for Agrobotic — derived from financial notebook + sensor codebase.
// Exposed on window so any component file can read it.

// ------------- Regions (from agrobotic_financial_modelling.ipynb) -------------
const REGIONS = [
  { id: "BC",   name: "British Columbia",    country: "CA", climate: "marine",            tax_rate: 0.0148, rain_in: 57, water_usd_m3: 0.018, irrigation: false },
  { id: "ON",   name: "Ontario",             country: "CA", climate: "humid_continental", tax_rate: 0.0162, rain_in: 33, water_usd_m3: 0.022, irrigation: false },
  { id: "AB",   name: "Alberta",             country: "CA", climate: "semi_arid",         tax_rate: 0.0108, rain_in: 17, water_usd_m3: 0.034, irrigation: true  },
  { id: "SK",   name: "Saskatchewan",        country: "CA", climate: "semi_arid",         tax_rate: 0.0095, rain_in: 14, water_usd_m3: 0.029, irrigation: true  },
  { id: "QC",   name: "Quebec",              country: "CA", climate: "humid_continental", tax_rate: 0.0171, rain_in: 41, water_usd_m3: 0.024, irrigation: false },
  { id: "IA",   name: "Iowa",                country: "US", climate: "humid_continental", tax_rate: 0.0132, rain_in: 36, water_usd_m3: 0.020, irrigation: false },
  { id: "IL",   name: "Illinois",            country: "US", climate: "humid_continental", tax_rate: 0.0165, rain_in: 39, water_usd_m3: 0.020, irrigation: false },
  { id: "WI",   name: "Wisconsin",           country: "US", climate: "humid_continental", tax_rate: 0.0185, rain_in: 32, water_usd_m3: 0.019, irrigation: false },
  { id: "CA",   name: "California",          country: "US", climate: "mediterranean",     tax_rate: 0.0073, rain_in: 21, water_usd_m3: 0.082, irrigation: true  },
];

// ------------- Crops -------------
const CROPS = [
  { id: "corn",      name: "Corn",          n_demand_lb_ac: 180, price_per_bu: 4.32, unit: "bu/ac", yield: 178, gwp_per_ac: 0.52 },
  { id: "soy",       name: "Soybeans",      n_demand_lb_ac: 40,  price_per_bu: 10.85, unit: "bu/ac", yield: 51, gwp_per_ac: 0.21 },
  { id: "wheat",     name: "Spring Wheat",  n_demand_lb_ac: 110, price_per_bu: 6.42, unit: "bu/ac", yield: 49, gwp_per_ac: 0.37 },
  { id: "canola",    name: "Canola",        n_demand_lb_ac: 130, price_per_bu: 14.20, unit: "bu/ac", yield: 41, gwp_per_ac: 0.41 },
  { id: "alfalfa",   name: "Alfalfa",       n_demand_lb_ac: 60,  price_per_bu: 215, unit: "$/ton",  yield: 4.1, gwp_per_ac: 0.18 },
  { id: "blueberry", name: "Blueberry",     n_demand_lb_ac: 90,  price_per_bu: 4280, unit: "$/ton", yield: 6.2, gwp_per_ac: 0.29 },
  { id: "dairy",     name: "Dairy (forage)",n_demand_lb_ac: 200, price_per_bu: null, unit: "L/cow/day", yield: 32, gwp_per_ac: 1.34 },
];

// ------------- Farms -------------
// 8 demo farms across regions, mix of crops + livestock.
const FARMS = [
  { id: "F-001", name: "Hilltop Acres",       region: "BC", lat: 49.15, lng: -122.85, acres: 642,  crop: "blueberry", health: 87, owner: "G. Reyes",      sensors: 24, alerts: 1, since: "2023-06", revenue_usd: 1842000 },
  { id: "F-002", name: "Riverside Dairy",     region: "BC", lat: 49.05, lng: -122.30, acres: 285,  crop: "dairy",     health: 79, owner: "Malahat Co-op", sensors: 18, alerts: 3, since: "2024-01", revenue_usd: 2410000 },
  { id: "F-003", name: "Three Pines Grain",   region: "SK", lat: 51.43, lng: -106.12, acres: 4820, crop: "canola",    health: 82, owner: "J. Olejniczak", sensors: 31, alerts: 0, since: "2023-09", revenue_usd: 3680000 },
  { id: "F-004", name: "Big Sky Wheat",       region: "AB", lat: 50.92, lng: -113.50, acres: 3210, crop: "wheat",     health: 74, owner: "Big Sky LLC",   sensors: 22, alerts: 2, since: "2024-04", revenue_usd: 2150000 },
  { id: "F-005", name: "Story County Corn",   region: "IA", lat: 42.05, lng: -93.50,  acres: 1840, crop: "corn",      health: 91, owner: "Story Holdings",sensors: 28, alerts: 1, since: "2023-03", revenue_usd: 1390000 },
  { id: "F-006", name: "McLean Soy Trust",    region: "IL", lat: 40.60, lng: -88.85,  acres: 2640, crop: "soy",       health: 88, owner: "El-Chami Trust",sensors: 26, alerts: 0, since: "2023-08", revenue_usd: 2880000 },
  { id: "F-007", name: "Vernon Orchard",      region: "CA", lat: 36.95, lng: -119.55, acres: 410,  crop: "blueberry", health: 68, owner: "Vernon Group",  sensors: 19, alerts: 4, since: "2024-07", revenue_usd: 1610000 },
  { id: "F-008", name: "Lake Country Forage", region: "WI", lat: 43.60, lng: -89.20,  acres: 920,  crop: "alfalfa",   health: 84, owner: "Lake Co-op",    sensors: 21, alerts: 1, since: "2023-11", revenue_usd: 1180000 },
];

// ------------- Sensor product families (full spec) -------------
// Each family has: deployment notes, certifications, edge AI capabilities, and a parameter list.
// Parameters: { code, name, method, range, unit, threshold?, baseline, vol, color, what }
const FAMILIES = {
  NitroSense: {
    color: "var(--green)",
    tagline: "Soil & water nitrogen monitor",
    short: "Nitrate + N₂O, real-time",
    deployment: "In-ground stake · electrochemical + UV-chemiresistive · deployable to 24\" depth",
    certifications: ["AAFC", "NSERC", "4R Stewardship", "AEOS verified"],
    flagship_param: "no3",
    edge_ai: [
      "On-device anomaly detection flags nitrate spikes and saturation events",
      "Predictive models estimate denitrification risk by cross-correlating NO₃⁻ + soil moisture + temperature",
      "Time-series nitrogen trend analysis drives just-in-time fertilizer alerts",
      "Cloud dashboard outputs precision application recommendations aligned with 4R stewardship",
      "Verified N₂O flux data feeds carbon credit and emissions-reduction reporting",
    ],
    parameters: [
      { code: "no3",       name: "Nitrate (NO₃⁻)",         method: "Electrochemical ISE",                  range: "1–500", unit: "ppm",   baseline: 22,    vol: 4,    what: "Root-zone nitrate in soil water and irrigation/drainage channels" },
      { code: "n2o_g",     name: "N₂O (gaseous)",          method: "UV-activated chemiresistive",          range: "sub-ppm", unit: "ppm", baseline: 0.8,  vol: 0.15, what: "Above-canopy nitrous oxide flux from soil surface" },
      { code: "n2o_d",     name: "N₂O (dissolved)",        method: "Electrochemical potentiometric",        range: "sub-ppm", unit: "ppm", baseline: 0.6,  vol: 0.12, what: "Dissolved N₂O in soil water; denitrification activity indicator" },
      { code: "vwc",       name: "Soil moisture",          method: "Volumetric probe",                      range: "0–100", unit: "% VWC", baseline: 42,  vol: 4,    what: "Volumetric water content at deployment depth" },
      { code: "soil_temp", name: "Soil temperature",       method: "Probe",                                 range: "-40 to +85", unit: "°C", baseline: 14, vol: 1.5,  what: "Root-zone temperature at probe depth (affects denitrification rates)" },
      { code: "ph",        name: "Soil pH",                method: "ISE",                                   range: "4–8", unit: "pH",     baseline: 6.5,  vol: 0.15, what: "Soil acidity/alkalinity at deployment depth" },
    ],
  },
  EcoSense: {
    color: "var(--cyan)",
    tagline: "Livestock GHG + air quality monitor",
    short: "Dairy GHG monitoring",
    deployment: "Solar-powered outdoor/barn unit · GHG-micro-1-CA · IP66 · CE/RoHS/FCC",
    certifications: ["NIST-traceable", "ISO 20581:2018", "Verra VCS", "COMET-Farm", "Alberta AEOS"],
    flagship_param: "ch4",
    edge_ai: [
      "Real-time CH₄ and N₂O concentration feeds barn ventilation optimization alerts",
      "Anomaly detection identifies manure storage events prompting inhibitor or aeration action",
      "Feed formulation feedback loop reduces enteric methane by correlating herd size with emission flux",
      "PM + NOx stack supports livestock welfare and worker health alerts",
      "Calibrated to NIST-traceable standards with 6-month validity",
    ],
    parameters: [
      { code: "ch4",   name: "CH₄ (methane)",         method: "NDIR (NH₃/CO₂ compensated)", range: "0–10,000", unit: "ppm", baseline: 1500, vol: 80,  what: "Enteric fermentation and manure storage methane; barn ventilation adequacy" },
      { code: "n2o",   name: "N₂O (nitrous oxide)",   method: "Electrochemical",            range: "sub-ppm",  unit: "ppm", baseline: 0.9,  vol: 0.2, what: "Manure management N₂O; pasture and feedlot emissions" },
      { code: "co2",   name: "CO₂",                   method: "NDIR (±3%)",                 range: "400–10,000", unit: "ppm", baseline: 720, vol: 40, what: "Carbon dioxide; barn air quality and ventilation indicator" },
      { code: "pm",    name: "PM1 / PM2.5 / PM10",    method: "Laser scattering (±5%)",     range: "0–1,000", unit: "µg/m³", baseline: 18, vol: 5, what: "Fine and coarse particulate matter; dust, manure aerosols, wildfire smoke" },
      { code: "tvoc",  name: "TVOCs",                 method: "Electrochemical",            range: "0–500",    unit: "VOC idx", baseline: 80, vol: 12, what: "Total volatile organic compounds; barn air quality, chemical exposure" },
      { code: "nox",   name: "NO₂ / NOx",             method: "Electrochemical",            range: "50–10,000", unit: "ppb", baseline: 140, vol: 18, what: "Nitrogen dioxide / reactive nitrogen oxides" },
      { code: "o3",    name: "O₃ (ozone)",            method: "Electrochemical",            range: "0–500",    unit: "ppb", baseline: 28,  vol: 4,  what: "Ground-level ozone; photochemical smog indicator" },
      { code: "temp",  name: "Temperature",           method: "Sensor (±0.2°C)",            range: "-40 to +125", unit: "°C", baseline: 18, vol: 1.5, what: "Ambient air temperature" },
      { code: "rh",    name: "Humidity",              method: "Sensor (±2%)",               range: "0–100",    unit: "% RH", baseline: 62, vol: 4,  what: "Relative humidity" },
    ],
  },
  AquaSentinel: {
    color: "var(--violet)",
    tagline: "Edge AI water quality monitor",
    short: "Water quality + runoff",
    deployment: "Solar-powered ESP32 nodes · IP65/IP67 · LoRaWAN mesh · offline-first",
    certifications: ["UBC validated", "WHO threshold", "Alberta EPA"],
    flagship_param: "ph",
    edge_ai: [
      "TinyML on-device: TensorFlow Lite neural net classifies water as CLEAN / RISKY / POLLUTED",
      ">94% accuracy on POLLUTED class (543/543 test cases) · 0.778 AUC overall",
      "Infers biological contamination risk (e.g., fecal coliform) without lab testing",
      "Offline-first Record ID architecture · zero data loss during connectivity gaps",
      "SMS + LED alerts trigger when WHO/Alberta EPA thresholds exceeded",
      "SHAP analysis identifies TDS and ORP as top predictors",
    ],
    parameters: [
      { code: "ph",        name: "pH",            method: "ISE",                  range: "0–14",       unit: "pH",   baseline: 6.8,  vol: 0.2,  what: "Acidity/alkalinity of water source; indicator of contamination events" },
      { code: "turbidity", name: "Turbidity",     method: "Optical",              range: "0–1,000",    unit: "NTU",  baseline: 22,   vol: 6,    what: "Water cloudiness from suspended sediments, algae, or pathogens" },
      { code: "tds",       name: "TDS",           method: "Conductivity",         range: "0–2,000+",   unit: "ppm",  baseline: 480,  vol: 35,   what: "Total dissolved solids / total mineral content; agricultural runoff indicator" },
      { code: "orp",       name: "ORP",           method: "Potentiometric",       range: "-999 to +999", unit: "mV", baseline: 220,  vol: 25,   what: "Sanitization power; disinfectant effectiveness and contamination risk" },
      { code: "do",        name: "Dissolved O₂",  method: "Optical / membrane",   range: "0–20",       unit: "mg/L", baseline: 8.4,  vol: 0.4,  what: "Available oxygen for aquatic life; eutrophication and contamination indicator" },
      { code: "temp",      name: "Temperature",   method: "Sensor",               range: "-10 to +60", unit: "°C",   baseline: 14,   vol: 1.2,  what: "Water temperature; affects pathogen growth rates" },
      { code: "lux",       name: "Brightness",    method: "Ambient light",        range: "0–100,000",  unit: "lux",  baseline: 18000, vol: 4000, what: "Light penetration; algae photosynthesis activity proxy" },
    ],
  },
  AirWatch: {
    color: "var(--pink)",
    tagline: "Distributed air quality monitor",
    short: "Community air & worker safety",
    deployment: "Solar-powered ESP32 mesh · deep-sleep duty-cycle · cellular + LoRa uplink · UBC capstone (LS-065)",
    certifications: ["UBC validated", "WHO/EPA thresholds", "UNICEF data-share"],
    flagship_param: "pm",
    edge_ai: [
      "Color-coded hazard classification (Normal / Hazard) using WHO/EPA threshold comparisons",
      "Deep-sleep duty-cycle power management → long-term autonomous off-grid deployment",
      "Real-time + historical trend visualization on cloud dashboard",
      "Mesh networking relays data hop-by-hop across 2.4 GHz nodes",
      "Supports early detection of industrial emissions, agricultural hazards, community air quality awareness",
    ],
    parameters: [
      { code: "co",   name: "CO",            method: "MiCS-4514 electrochemical", range: "1–1,000",    unit: "ppm",  baseline: 4,    vol: 0.6,  what: "Carbon monoxide from combustion; indoor/outdoor safety indicator" },
      { code: "no2",  name: "NO₂",           method: "MiCS-4514 electrochemical", range: "0.05–10",    unit: "ppm",  baseline: 0.12, vol: 0.02, what: "Nitrogen dioxide from combustion and agricultural activity" },
      { code: "nh3",  name: "NH₃ (ammonia)", method: "MiCS-4514 electrochemical", range: "1–300",      unit: "ppm",  threshold: 25, baseline: 8, vol: 1.5, what: "Ammonia from livestock operations and fertilizer application" },
      { code: "o3",   name: "O₃ (ozone)",    method: "SEN0321 electrochemical",   range: "0–10",       unit: "ppm",  baseline: 0.04, vol: 0.008, what: "Ground-level ozone; photochemical smog and crop damage indicator" },
      { code: "pm",   name: "PM2.5 / PM10",  method: "Laser SEN0460",             range: "0–999",      unit: "µg/m³", baseline: 14, vol: 3, what: "Fine and coarse particulate matter; dust, smoke, aerosols" },
      { code: "co2",  name: "CO₂",           method: "Sensirion SCD30 NDIR",      range: "400–10,000", unit: "ppm",  threshold: 1200, baseline: 540, vol: 35, what: "Carbon dioxide; ventilation, combustion, crop stress indicator" },
      { code: "temp", name: "Temperature",   method: "SCD30",                     range: "-10 to +60", unit: "°C",   baseline: 17, vol: 1.5, what: "Ambient air temperature" },
      { code: "rh",   name: "Humidity",      method: "SCD30",                     range: "0–100",      unit: "% RH", baseline: 58, vol: 4, what: "Relative humidity" },
    ],
  },
  ForestSentinel: {
    color: "var(--amber)",
    tagline: "Early wildfire detection network",
    short: "Air quality + fire detection",
    deployment: "Solar-powered LoRa mesh · rugged weatherproof enclosure · pole/tree mount · 5-day battery reserve · UBC capstone (LS-066)",
    certifications: ["EPA Tier II calibrated", "UBC validated", "500m+ mesh validated"],
    flagship_param: "co",
    edge_ai: [
      "TinyML logistic regression on ESP32 flash — 8-input fire classifier",
      "80% detection accuracy · 0.778 AUC",
      "Distinguishes smouldering-phase wildfire signatures from blown-in smoke or ambient pollution",
      "Detects fires BEFORE they produce a visible plume",
      "ML-calibrated CO, PM2.5, NOx via 5-method comparison against UBC reference sensors",
      "Self-healing LoRa mesh routes alerts to gateway (500+ m node range validated in forest)",
      "LTE-M uplink pushes real-time alerts to web dashboard with node health and location data",
    ],
    parameters: [
      { code: "co",   name: "CO",                  method: "Electrochemical",          range: "smouldering combustion", unit: "ppm", baseline: 3.5, vol: 1.2, what: "Carbon monoxide; earliest detectable wildfire indicator" },
      { code: "pm",   name: "PM1 / PM2.5 / PM10",  method: "Laser PMS5003",            range: "0–1,000", unit: "µg/m³", baseline: 14, vol: 4, what: "Particulate matter; smoke particles; required for TinyML inference" },
      { code: "voc",  name: "VOCs / NOx",          method: "NDIR (I²C)",               range: "combustion + vegetation", unit: "idx", baseline: 90, vol: 18, what: "Volatile organic compounds and nitrogen oxides from combustion and vegetation" },
      { code: "temp", name: "Temperature",         method: "Sensor",                   range: "-15 to +40", unit: "°C", baseline: 12, vol: 4, what: "Ambient temperature; thermal anomaly indicator in fire proximity" },
      { code: "rh",   name: "Humidity",            method: "Sensor",                   range: "0–100", unit: "% RH",   baseline: 54, vol: 6, what: "Relative humidity; fuel moisture proxy and fire spread risk factor" },
    ],
  },
  EcoNitro: {
    color: "oklch(0.78 0.16 320)",
    tagline: "Integrated farm-to-atmosphere node",
    short: "Unified GHG + soil + air node",
    deployment: "Sub-$3,000 unified node · NitroSense + EcoSense + AirWatch + ForestSentinel · provisional patent filed",
    certifications: ["Provisional patent", "AEOS verified", "EPA Tier II benchmarked", "ISO 20581:2018"],
    flagship_param: "n2o_g",
    edge_ai: [
      "3 concurrent TinyML models on-device, zero cloud dependency",
      "(1) N₂O event detection — distinguishes denitrification spikes from sensor noise via soil moisture + temp",
      "(2) CH₄ anomaly detection — identifies enteric/manure surges → barn ventilation alerts",
      "(3) Air-quality hazard classification — NH₃, PM2.5, combined GHG exposure risk for operator safety",
      "(4) Wildfire smoke discrimination — distinguishes wildfire from agricultural emissions (0.778 AUC)",
      "AEOS-verified data pipeline · EPA Tier II calibration · offline-first Record ID integrity",
      "Self-healing LoRa mesh (500+ m) + LTE/cellular gateway uplink",
      "NH₃ sensor doubles as correction variable for N₂O readings — eliminates barn cross-interference",
    ],
    parameters: [
      { code: "no3",      name: "NO₃⁻",              method: "Electrochemical ISE",                 range: "1–500", unit: "ppm",    baseline: 22, vol: 4, what: "Root-zone soil nitrate concentration" },
      { code: "n2o_d",    name: "N₂O (dissolved)",   method: "Electrochemical potentiometric",       range: "sub-ppm", unit: "ppm",  baseline: 0.6, vol: 0.12, what: "Dissolved N₂O; denitrification activity" },
      { code: "n2o_g",    name: "N₂O (gaseous) ⚡",   method: "UV-activated chemiresistive",          range: "sub-ppm", unit: "ppm",  baseline: 0.8, vol: 0.15, what: "Above-canopy gaseous N₂O — novel dual-mode, not in any other sub-$3K node" },
      { code: "ch4",      name: "CH₄",               method: "NDIR (NH₃/CO₂ compensated)",          range: "0–10,000", unit: "ppm", baseline: 1500, vol: 80, what: "Methane from enteric fermentation, manure, oil & gas" },
      { code: "co",       name: "CO",                method: "Electrochemical",                      range: "1–1,000", unit: "ppm",  baseline: 4, vol: 1, what: "Carbon monoxide; combustion/fire indicator" },
      { code: "pm25",     name: "PM2.5",             method: "Laser",                                range: "0–1,000", unit: "µg/m³", baseline: 16, vol: 4, what: "Fine particulate matter; dust, smoke, manure aerosols" },
      { code: "nox",      name: "NOx",               method: "Electrochemical",                      range: "0–10,000", unit: "ppb", baseline: 130, vol: 20, what: "Reactive nitrogen oxides from soil and combustion" },
      { code: "nh3",      name: "NH₃ ⚡",            method: "Electrochemical",                      range: "1–300", unit: "ppm",    baseline: 8, vol: 1.5, what: "Ammonia; also used as N₂O correction variable" },
      { code: "co2",      name: "CO₂",               method: "NDIR",                                 range: "400–10,000", unit: "ppm", baseline: 580, vol: 35, what: "Carbon dioxide; ventilation and combustion indicator" },
      { code: "o3",       name: "O₃",                method: "Electrochemical",                      range: "0–10", unit: "ppm",     baseline: 0.04, vol: 0.008, what: "Ozone; photochemical smog and crop stress" },
      { code: "vwc",      name: "Soil moisture",     method: "Volumetric probe",                     range: "0–100", unit: "% VWC",  baseline: 42, vol: 4, what: "Cross-correlation with N₂O denitrification risk events" },
      { code: "soil_temp", name: "Soil temperature", method: "Probe",                                range: "-40 to +85", unit: "°C", baseline: 14, vol: 1.5, what: "Root-zone temperature; affects reaction rates" },
      { code: "ambient",  name: "Temp / RH",         method: "Combined sensor",                      range: "-25 to +60 / 0–100", unit: "°C / % RH", baseline: 18, vol: 2, what: "Ambient conditions; sensor compensation inputs" },
    ],
  },
};

// ------------- Gateways (LoRaWAN) -------------
const GATEWAYS = [
  { id: "GW-001", name: "LoRa-GW-001", farm_id: "F-001", status: "online",      uptime_d: 37.2, devices: 45, rssi: -78.5, lat: 49.15, lng: -122.85 },
  { id: "GW-002", name: "LoRa-GW-002", farm_id: "F-002", status: "online",      uptime_d: 12.7, devices: 28, rssi: -82.1, lat: 49.05, lng: -122.30 },
  { id: "GW-003", name: "LoRa-GW-003", farm_id: "F-003", status: "warning",     uptime_d: 22.5, devices: 37, rssi: -85.3, lat: 51.43, lng: -106.12 },
  { id: "GW-004", name: "LoRa-GW-004", farm_id: "F-005", status: "online",      uptime_d: 41.8, devices: 28, rssi: -71.2, lat: 42.05, lng: -93.50  },
  { id: "GW-005", name: "LoRa-GW-005", farm_id: "F-007", status: "maintenance", uptime_d: 3.1,  devices: 12, rssi: -76.8, lat: 36.95, lng: -119.55 },
];

// ------------- Sensors — generate ~120 across farms -------------
function makeSensors() {
  const out = [];
  const families = Object.keys(FAMILIES);
  let n = 0;
  FARMS.forEach((farm) => {
    const count = farm.sensors;
    for (let i = 0; i < count; i++) {
      const fam = families[(i + farm.id.charCodeAt(2)) % families.length];
      const offset_lat = (Math.sin(n * 1.7) * 0.02);
      const offset_lng = (Math.cos(n * 1.3) * 0.025);
      const status = Math.random() < 0.05 ? "warning"
                    : Math.random() < 0.02 ? "offline"
                    : "active";
      const battery = Math.max(8, Math.round(40 + Math.random() * 60));
      const rssi = -Math.round(60 + Math.random() * 40);
      const snr = +(4 + Math.random() * 12).toFixed(2);
      const famPrefix = {
        NitroSense: "NIT", EcoSense: "ECO", AquaSentinel: "AQU",
        AirWatch: "AIR", ForestSentinel: "FOR", EcoNitro: "ENT",
      }[fam] || fam.slice(0, 3).toUpperCase();
      out.push({
        id: `S-${String(1000 + n).padStart(4, "0")}`,
        eui: hexId(n),
        name: `${famPrefix}-${String(i + 1).padStart(3, "0")}`,
        family: fam,
        farm_id: farm.id,
        type: FAMILIES[fam].parameters[0].code,
        gateway: pickGateway(farm.id),
        status,
        battery,
        rssi, snr,
        class: ["A", "B", "C"][n % 3],
        last_seen: minutesAgo(Math.round(Math.random() * 720)),
        lat: farm.lat + offset_lat,
        lng: farm.lng + offset_lng,
      });
      n++;
    }
  });
  return out;
}
function pickGateway(farm_id) {
  const direct = GATEWAYS.find((g) => g.farm_id === farm_id);
  return direct ? direct.id : GATEWAYS[Math.floor(Math.random() * GATEWAYS.length)].id;
}
function hexId(n) {
  const r = (s) => s.toString(16).toUpperCase().padStart(2, "0");
  return Array.from({ length: 6 }, (_, i) => r((n * 7 + i * 11) % 256)).join("-");
}
function minutesAgo(m) {
  if (m < 1) return "just now";
  if (m < 60) return `${m}m ago`;
  if (m < 1440) return `${Math.floor(m / 60)}h ago`;
  return `${Math.floor(m / 1440)}d ago`;
}
const SENSORS = makeSensors();

// ------------- Alerts -------------
const ALERTS = [
  { id: "A-01", severity: "high",  sensor: "NIT-003", farm_id: "F-002", title: "N₂O spike near manure storage",  body: "Sub-ppm reading climbed to 4.8 ppm — recommend ventilation check.", t: "12m ago", category: "GHG" },
  { id: "A-02", severity: "high",  sensor: "AQU-007", farm_id: "F-007", title: "EC exceeds threshold in canal 3", body: "Electrical conductivity at 3.1 mS/cm — possible runoff event.", t: "47m ago", category: "Water" },
  { id: "A-03", severity: "med",   sensor: "FOR-004", farm_id: "F-003", title: "PM2.5 trending up, possible smoke", body: "30s avg climbing — cross-check with NOAA fire layer.", t: "1h ago", category: "Air" },
  { id: "A-04", severity: "med",   sensor: "NIT-012", farm_id: "F-004", title: "Calibration recommended",         body: "CO baseline drift > 12% since last service.", t: "3h ago", category: "Calibration" },
  { id: "A-05", severity: "low",   sensor: "ECO-002", farm_id: "F-002", title: "CH₄ daily average above 1700 ppm", body: "Suggest reviewing feed composition window.", t: "5h ago", category: "GHG" },
  { id: "A-06", severity: "low",   sensor: "AQU-002", farm_id: "F-001", title: "Battery at 18%",                  body: "Field 3 water probe — schedule replacement within 7d.", t: "8h ago", category: "Hardware" },
  { id: "A-07", severity: "med",   sensor: "FOR-011", farm_id: "F-006", title: "VOC drift detected",              body: "SGP41 raw signal -2.8σ over 48h.", t: "1d ago", category: "Calibration" },
];

// ------------- Time series generator -------------
function generateSeries(points = 96, base = 50, vol = 8, trend = 0, seed = 1) {
  const rand = mulberry32(seed);
  const out = [];
  let v = base;
  for (let i = 0; i < points; i++) {
    v += (rand() - 0.5) * vol + trend;
    v = Math.max(0, v);
    out.push(+v.toFixed(2));
  }
  return out;
}
function mulberry32(a) {
  return function () {
    a |= 0; a = a + 0x6d2b79f5 | 0;
    let t = a;
    t = Math.imul(t ^ t >>> 15, t | 1);
    t ^= t + Math.imul(t ^ t >>> 7, t | 61);
    return ((t ^ t >>> 14) >>> 0) / 4294967296;
  };
}

// ------------- Carbon credit market -------------
const CARBON_MARKET = [
  { id: "VCS-1142", name: "BC Dairy CH₄ Reduction", standard: "Verra VCS", price: 28.40, vol: 14200, change: +2.1, vintage: 2025, source: "EcoSense pilots", tons_avail: 2400 },
  { id: "GS-7821",  name: "Prairie N₂O Avoidance",  standard: "Gold Standard", price: 34.10, vol: 8700,  change: +0.8, vintage: 2025, source: "NitroSense", tons_avail: 4100 },
  { id: "ACR-3320", name: "California Soil Carbon", standard: "American Carbon Registry", price: 41.85, vol: 22300, change: -1.4, vintage: 2024, source: "Soil sensors", tons_avail: 1820 },
  { id: "VCS-2208", name: "Forest Smoke Avoidance",  standard: "Verra VCS", price: 18.95, vol: 5600,  change: +4.2, vintage: 2025, source: "ForestSentinel", tons_avail: 3300 },
  { id: "CFR-118",  name: "Canadian Fertilizer Roadmap", standard: "ECCC Pilot", price: 22.50, vol: 11400, change: +1.1, vintage: 2025, source: "NitroSense + 4R", tons_avail: 6700 },
];

// ------------- ROI presets (sensor adoption) -------------
const ROI_PRESETS = {
  NitroSense: {
    capex_per_ac: 14.80,
    opex_per_ac_yr: 3.20,
    n_savings_pct: 0.18,
    yield_lift_pct: 0.03,
    n2o_reduction_pct: 0.40,
    payback_months: 14,
  },
  EcoSense: {
    capex_per_cow: 38.00,
    opex_per_cow_yr: 6.50,
    ch4_reduction_pct: 0.22,
    feed_savings_pct: 0.06,
    carbon_credit_per_cow_yr: 18.40,
    payback_months: 18,
  },
  ForestSentinel: {
    capex_per_node: 420,
    opex_per_node_yr: 64,
    fire_prevention_value: 0.4, // proxy
    insurance_discount_pct: 0.08,
  },
  AquaSentinel: {
    capex_per_zone: 380,
    opex_per_zone_yr: 48,
    water_savings_pct: 0.14,
    runoff_compliance_value: 0.6,
  },
  AirWatch: {
    capex_per_node: 320,
    opex_per_node_yr: 52,
    worker_safety_value: 0.5,
    community_engagement_value: 0.3,
  },
  EcoNitro: {
    capex_per_node: 2950,
    opex_per_node_yr: 180,
    n_savings_pct: 0.18,
    ch4_reduction_pct: 0.22,
    n2o_reduction_pct: 0.40,
    combined_credit_per_node_yr: 880,
  },
};

// ------------- KPI snapshots (top of pages) -------------
const KPIS = {
  iot: {
    sensors_online: 167,
    sensors_total: 189,
    farms_active: 8,
    gateways_online: 4,
    gateways_total: 5,
    readings_24h: 1284100,
    open_alerts: 7,
    avg_uptime: 0.937,
  },
  fintech: {
    aum_usd: 16140000,        // multi-farm portfolio
    revenue_ytd: 8420000,
    carbon_offset_tons: 4280,
    carbon_revenue_usd: 122300,
    insurance_savings_usd: 84500,
    avg_roi_pct: 0.224,
    tax_liability_usd: 184200,
    eligible_credits_usd: 312800,
  },
};

// ------------- GHG factors (from notebook concepts) -------------
const GHG = {
  n2o_factor_per_lb_n: 0.0146,    // kg N2O per lb N applied (IPCC tier 1)
  n2o_gwp: 265,
  ch4_gwp: 28,
  co2e_per_tonne_usd_default: 28, // carbon price
};

// Expose
Object.assign(window, {
  AGB_DATA: {
    REGIONS, CROPS, FARMS, FAMILIES, GATEWAYS, SENSORS,
    ALERTS, CARBON_MARKET, ROI_PRESETS, KPIS, GHG,
  },
  generateSeries, mulberry32, minutesAgo,
});
