// Sections: Live widget, Products, Dashboard mock, Customers, About, Footer.
const { useState: useS, useEffect: useE, useMemo: useM, useRef: useR } = React;

// ---------------- Live widget ----------------
function Sparkline({ data, accent, height = 96 }) {
  const w = 600, h = height, pad = 4;
  const safeLength = Math.max(data.length - 1, 1);
  const validPoints = data
    .map((value, index) => (typeof value === "number" && Number.isFinite(value) ? { index, value } : null))
    .filter(Boolean);

  if (!validPoints.length) {
    return (
      <svg viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" width="100%" height={h}>
        {[0.25, 0.5, 0.75].map((g, i) => (
          <line key={i} x1="0" x2={w} y1={h * g} y2={h * g} stroke="rgba(255,255,255,0.05)" strokeDasharray="2 4" />
        ))}
      </svg>
    );
  }

  const values = validPoints.map((point) => point.value);
  const min = Math.min(...values), max = Math.max(...values);
  const range = max - min || 1;
  const pts = validPoints.map(({ index, value }) => {
    const x = pad + (index / safeLength) * (w - pad * 2);
    const y = h - pad - ((value - min) / range) * (h - pad * 2);
    return [x, y];
  });
  const path = pts.map((p, i) => (i ? "L" : "M") + p[0].toFixed(1) + "," + p[1].toFixed(1)).join(" ");
  const area = path + ` L ${pts[pts.length - 1][0]},${h - pad} L ${pts[0][0]},${h - pad} Z`;
  const last = pts[pts.length - 1];
  return (
    <svg viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" width="100%" height={h}>
      <defs>
        <linearGradient id="sparkArea" x1="0" x2="0" y1="0" y2="1">
          <stop offset="0%" stopColor={accent} stopOpacity="0.22" />
          <stop offset="100%" stopColor={accent} stopOpacity="0" />
        </linearGradient>
      </defs>
      {[0.25, 0.5, 0.75].map((g, i) => (
        <line key={i} x1="0" x2={w} y1={h * g} y2={h * g} stroke="rgba(255,255,255,0.05)" strokeDasharray="2 4" />
      ))}
      <path d={area} fill="url(#sparkArea)" />
      <path d={path} stroke={accent} strokeWidth="1.5" fill="none" />
      <circle cx={last[0]} cy={last[1]} r="3" fill={accent} />
      <circle cx={last[0]} cy={last[1]} r="6" fill={accent} fillOpacity="0.25">
        <animate attributeName="r" values="4;9;4" dur="1.6s" repeatCount="indefinite" />
        <animate attributeName="fill-opacity" values="0.4;0;0.4" dur="1.6s" repeatCount="indefinite" />
      </circle>
    </svg>
  );
}

function getPath(source, path) {
  return path.split(".").reduce((acc, key) => (acc && acc[key] !== undefined ? acc[key] : undefined), source);
}

function firstValue(source, paths) {
  for (let i = 0; i < paths.length; i += 1) {
    const value = getPath(source, paths[i]);
    if (value !== undefined && value !== null) return value;
  }
  return null;
}

function firstNumber(source, paths) {
  const value = firstValue(source, paths);
  return typeof value === "number" && Number.isFinite(value) ? value : null;
}

function shortTimeLabel(value) {
  if (!value || typeof value !== "string") return "--:--";
  const match = value.match(/T(\d{2}):(\d{2})/);
  return match ? `${match[1]}:${match[2]}` : "--:--";
}

function normaliseSeriesPoint(point) {
  if (typeof point === "number" && Number.isFinite(point)) {
    return { ts: null, value: point, activeCellShare: null };
  }
  if (!point || typeof point !== "object") {
    return { ts: null, value: null, activeCellShare: null };
  }
  return {
    ts: firstValue(point, ["ts", "timestamp", "at"]),
    value: firstNumber(point, ["network_output_index", "activity_index", "value", "index"]),
    activeCellShare: firstNumber(point, ["active_cell_share_pct", "cell_activity_pct", "activity_pct"]),
  };
}

function buildFallbackPublicLiveData() {
  const pointCount = 288;
  const intervalMinutes = 5;
  const observedIndex = 169;
  const points = [];
  let peakValue = 0;
  let peakTs = "2026-05-06T12:40:00+08:00";
  let cumulative = 0;

  for (let index = 0; index < pointCount; index += 1) {
    const hour = (index * intervalMinutes) / 60;
    const hh = String(Math.floor(hour)).padStart(2, "0");
    const mm = String((index * intervalMinutes) % 60).padStart(2, "0");
    const ts = `2026-05-06T${hh}:${mm}:00+08:00`;
    let outputIndex = null;
    let activeCellShare = null;

    if (index <= observedIndex) {
      const daylight = Math.max(0, Math.sin(((hour - 6.1) / 11.8) * Math.PI));
      const cloud = Math.max(0, Math.sin(index * 0.17)) * 8 + Math.max(0, Math.cos(index * 0.05)) * 4;
      outputIndex = Math.max(0, Math.min(100, daylight * 88 - cloud + Math.sin(index * 0.81) * 1.4));
      outputIndex = Number(outputIndex.toFixed(1));
      activeCellShare = Math.max(32, Math.min(97, 48 + daylight * 44 - cloud * 0.55));
      activeCellShare = Number(activeCellShare.toFixed(1));
      cumulative += outputIndex;
      if (outputIndex >= peakValue) {
        peakValue = outputIndex;
        peakTs = ts;
      }
    }

    points.push({
      ts,
      network_output_index: outputIndex,
      active_cell_share_pct: activeCellShare,
    });
  }

  const currentPoint = points[observedIndex] || points[0];
  const currentValue = currentPoint && typeof currentPoint.network_output_index === "number" ? currentPoint.network_output_index : 0;
  const currentCellShare = currentPoint && typeof currentPoint.active_cell_share_pct === "number" ? currentPoint.active_cell_share_pct : 0;
  const daylightCaptureIndex = cumulative / Math.max(observedIndex + 1, 1);

  return {
    summary: {
      as_of: currentPoint.ts,
      coverage: {
        public_cell_count: 24,
        geography_label: "Singapore",
      },
      aggregate: {
        network_output_index: currentValue,
        daylight_capture_index: Number(daylightCaptureIndex.toFixed(1)),
        active_cell_share_pct: currentCellShare,
        variability_index: 18.6,
      },
      comparisons: {
        vs_28d_norm_pct: 3.8,
      },
      peak_today: {
        network_output_index: peakValue,
        at: peakTs,
      },
      series: {
        metric: "network_output_index",
        unit: "index",
        interval: "PT5M",
        points,
      },
    },
    series: {
      metric: "network_output_index",
      unit: "index",
      interval: "PT5M",
      points,
    },
  };
}

function LiveWidget({ accent, summaryData, seriesData }) {
  const fallbackData = useM(() => buildFallbackPublicLiveData(), []);
  const [tick, setTick] = useS(0);

  useE(() => {
    const id = setInterval(() => setTick((value) => value + 1), 1500);
    return () => clearInterval(id);
  }, []);

  const liveModel = useM(() => {
    const resolvedSummary = summaryData || fallbackData.summary;
    const resolvedSeries = seriesData || (resolvedSummary && resolvedSummary.series) || fallbackData.series;
    const rawPoints = resolvedSeries && Array.isArray(resolvedSeries.points) ? resolvedSeries.points : fallbackData.series.points;
    const points = rawPoints.map(normaliseSeriesPoint);
    const spark = points.map((point) => point.value);
    let lastObservedIndex = -1;
    for (let index = spark.length - 1; index >= 0; index -= 1) {
      if (typeof spark[index] === "number" && Number.isFinite(spark[index])) {
        lastObservedIndex = index;
        break;
      }
    }

    if (lastObservedIndex >= 0) {
      const amplitude = Math.max((spark[lastObservedIndex] || 0) * 0.003, 0.25);
      spark[lastObservedIndex] = Number((spark[lastObservedIndex] + Math.sin(tick) * amplitude).toFixed(2));
    }

    const currentValue = firstNumber(resolvedSummary, ["activity_index", "aggregate.network_output_index"]) || 0;
    const previousObservedIndex = lastObservedIndex > 0 ? lastObservedIndex - 1 : -1;
    const previousObservedValue = previousObservedIndex >= 0 ? points[previousObservedIndex].value : null;
    const deltaPct = firstNumber(resolvedSummary, ["comparisons.vs_28d_norm_pct", "comparisons.vs_28d_avg_pct"])
      || (typeof previousObservedValue === "number" && previousObservedValue > 0
        ? ((currentValue - previousObservedValue) / previousObservedValue) * 100
        : 0);
    const peakFromSummary = firstNumber(resolvedSummary, ["peak_today.network_output_index"]);
    const peakIndex = spark.reduce((bestIndex, value, index) => {
      if (!(typeof value === "number" && Number.isFinite(value))) return bestIndex;
      if (bestIndex === -1 || value >= spark[bestIndex]) return index;
      return bestIndex;
    }, -1);
    const peakValue = typeof peakFromSummary === "number"
      ? peakFromSummary
      : (peakIndex >= 0 ? spark[peakIndex] : 0);
    const networkHealth = firstNumber(resolvedSummary, ["network_health_pct"]) || 0;
    const streamingClusters = firstNumber(resolvedSummary, ["streaming_clusters"]) || null;
    const activeCellShare = firstNumber(resolvedSummary, ["aggregate.active_cell_share_pct"])
      || (lastObservedIndex >= 0 ? points[lastObservedIndex].activeCellShare : null)
      || 0;
    const observationCells = firstNumber(resolvedSummary, ["observation_cells", "aggregate.daylight_capture_index"]) || 0;
    const cellCount = firstNumber(resolvedSummary, ["coverage.public_cell_count", "coverage.cell_count", "observation_cells"]) || null;
    const geographyLabel = firstValue(resolvedSummary, ["coverage.geography_label", "coverage.market_label"]) || "Singapore";
    const asOf = firstValue(resolvedSummary, ["as_of", "freshness.as_of"]) || (lastObservedIndex >= 0 ? points[lastObservedIndex].ts : null);
    const peakAt = firstValue(resolvedSummary, ["peak_today.at"]) || (peakIndex >= 0 ? points[peakIndex].ts : null);
    const variability = firstNumber(resolvedSummary, ["aggregate.variability_index"]) || null;
    const unitLabel = "IDX";
    const displayCurrent = currentValue.toFixed(1);
    const peakDisplay = peakValue.toFixed(1);
    const deltaClass = deltaPct >= 0 ? "delta-up" : "delta-down";
    const deltaArrow = deltaPct >= 0 ? "↑" : "↓";
    const deltaPrefix = deltaPct >= 0 ? "+ " : "- ";
    const secondaryValue = `${observationCells.toFixed(0)} cells`;
    const healthDisplay = networkHealth > 0 ? `${networkHealth.toFixed(1)}%` : `${activeCellShare.toFixed(0)}%`;
    const healthDetail = streamingClusters ? `${streamingClusters} live clusters` : (variability !== null ? `var ${variability.toFixed(1)}` : null);

    return {
      spark,
      tagTime: shortTimeLabel(asOf),
      geographyLabel,
      cellCount,
      unitLabel,
      displayCurrent,
      secondaryLabel: "Observation coverage",
      secondaryValue,
      peakDisplay,
      peakAt: shortTimeLabel(peakAt),
      deltaClass,
      deltaText: `${deltaPrefix}${Math.abs(deltaPct).toFixed(1)}%`,
      deltaArrow,
      healthDisplay,
      healthDetail,
    };
  }, [fallbackData, seriesData, summaryData, tick]);

  const coverageLine = liveModel.cellCount
    ? `${liveModel.geographyLabel} public network signal across ${liveModel.cellCount} anonymised cells.`
    : `${liveModel.geographyLabel} public network signal from anonymised external cells.`;

  return (
    <div className="live">
      <div className="live-main">
        <div className="live-tag"><span className="dot"></span>LIVE · PUBLIC NETWORK · {liveModel.tagTime} SGT</div>
        <div className="live-num">
          {liveModel.displayCurrent}
          <span className="unit">{liveModel.unitLabel}</span>
        </div>
        <div className="live-sub">
          {coverageLine} Privacy-safe indices only, refreshed every five minutes.
        </div>
        <div className="live-spark">
          <Sparkline data={liveModel.spark} accent={accent} />
        </div>
        <div className="mono" style={{ display: "flex", justifyContent: "space-between", fontSize: 11, color: "var(--ink-3)", marginTop: 6, letterSpacing: "0.04em" }}>
          <span>00:00</span><span>06:00</span><span>12:00</span><span>18:00</span><span>23:59</span>
        </div>
      </div>
      <div className="live-side">
        <div className="live-stat">
          <div className="k">{liveModel.secondaryLabel}</div>
          <div className="v">{liveModel.secondaryValue}</div>
        </div>
        <div className="live-stat">
          <div className="k">vs. 28-day norm</div>
          <div className={`v ${liveModel.deltaClass}`}>{liveModel.deltaText} <small>{liveModel.deltaArrow}</small></div>
        </div>
        <div className="live-stat">
          <div className="k">Peak index</div>
          <div className="v">{liveModel.peakDisplay} <small>{liveModel.unitLabel} · {liveModel.peakAt}</small></div>
        </div>
        <div className="live-stat">
          <div className="k">Network health</div>
          <div className="v">{liveModel.healthDisplay}{liveModel.healthDetail ? <small>{liveModel.healthDetail}</small> : null}</div>
        </div>
        <div style={{ marginTop: "auto", paddingTop: 10, borderTop: "1px solid var(--line)", fontSize: 11, fontFamily: "IBM Plex Mono, monospace", color: "var(--ink-3)", letterSpacing: "0.04em" }}>
          src: graph.sg/v1/sg/summary · external anonymised cells · refresh 5m
        </div>
      </div>
    </div>
  );
}

// ---------------- Featured product art (Fomonitor) ----------------
function FomonitorArt({ accent }) {
  const [t, setT] = useS(0);
  useE(() => {
    const id = setInterval(() => setT(x => x + 1), 1200);
    return () => clearInterval(id);
  }, []);
  const strings = useM(() => Array.from({ length: 8 * 6 }).map((_, i) => ({
    i,
    base: 0.55 + (Math.sin(i * 0.7) * 0.15) + (Math.cos(i * 0.31) * 0.1),
    fault: i === 19 || i === 34, // two fault strings
  })), []);

  return (
    <div style={{ position: "relative", width: "100%", height: "100%", padding: "26px 26px 22px", display: "flex", flexDirection: "column" }}>
      <div className="mono" style={{ display: "flex", justifyContent: "space-between", fontSize: 10, color: "var(--ink-3)", letterSpacing: "0.1em", textTransform: "uppercase", marginBottom: 14 }}>
        <span>ANONYMISED SAMPLE · STRING MAP</span><span>48 STRINGS · 2 WATCH FLAGS</span>
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(8, 1fr)", gap: 6, flex: 1 }}>
        {strings.map((s) => {
          const flicker = (t + s.i) % 9 === 0 ? 0.85 : 1;
          const v = Math.max(0.05, Math.min(1, s.base * flicker));
          const color = s.fault ? "rgba(255,92,92,0.6)" : `rgba(255,92,170,${(v * 0.85).toFixed(2)})`;
          return (
            <div key={s.i} style={{
              background: s.fault ? "rgba(255,92,92,0.12)" : `rgba(255,92,170,${(v * 0.18).toFixed(2)})`,
              border: s.fault ? "1px solid rgba(255,92,92,0.55)" : "1px solid rgba(255,92,170,0.22)",
              borderRadius: 3,
              minHeight: 28,
              display: "flex", alignItems: "flex-end",
              position: "relative",
              overflow: "hidden",
            }}>
              <div style={{ width: "100%", height: `${(v * 100).toFixed(0)}%`, background: color, transition: "height .8s ease" }}></div>
              {s.fault && (
                <div style={{ position: "absolute", top: 4, right: 4, width: 5, height: 5, borderRadius: "50%", background: "var(--danger)", boxShadow: "0 0 6px var(--danger)" }}></div>
              )}
            </div>
          );
        })}
      </div>
      <div className="mono" style={{ marginTop: 14, fontSize: 11, color: "var(--ink-2)", display: "flex", justifyContent: "space-between" }}>
        <span style={{ color: "var(--danger)" }}>● STR-19 · under baseline index</span>
        <span style={{ color: "var(--danger)" }}>● STR-34 · telemetry discontinuity</span>
      </div>
    </div>
  );
}

function SolarEVArt({ accent }) {
  const [t, setT] = useS(0);
  useE(() => {
    let raf, start;
    const tick = (ts) => { if (!start) start = ts; setT((ts - start) / 1000); raf = requestAnimationFrame(tick); };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, []);
  const w = 320, h = 160;
  // surplus curve
  const pts = Array.from({ length: 60 }).map((_, i) => {
    const x = (i / 59) * w;
    const sun = Math.max(0, Math.sin((i / 59) * Math.PI - 0.2));
    const load = 0.35 + Math.sin((i / 59) * Math.PI * 1.2) * 0.05;
    const surplus = Math.max(0, sun - load);
    return { x, sun, load, surplus, y_sun: h - 12 - sun * (h - 30), y_load: h - 12 - load * (h - 30), y_sur: h - 12 - surplus * (h - 30) };
  });
  const path = (key) => pts.map((p, i) => (i ? "L" : "M") + p.x.toFixed(1) + "," + p[key].toFixed(1)).join(" ");
  const cursor = ((t * 8) % 60);
  return (
    <div style={{ padding: 22, height: "100%", display: "flex", flexDirection: "column" }}>
      <div className="mono" style={{ fontSize: 10, color: "var(--ink-3)", letterSpacing: "0.1em", textTransform: "uppercase", marginBottom: 8 }}>
        CHARGE WINDOW · SUN − HOUSE LOAD
      </div>
      <svg viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" style={{ width: "100%", flex: 1 }}>
        <path d={path("y_sun") + ` L ${w},${h} L 0,${h} Z`} fill="rgba(255,194,61,0.08)" />
        <path d={path("y_sun")} stroke="rgba(255,194,61,0.5)" strokeWidth="1" fill="none" />
        <path d={path("y_load")} stroke="rgba(255,255,255,0.25)" strokeWidth="1" strokeDasharray="3 3" fill="none" />
        <path d={path("y_sur") + ` L ${w},${h} L 0,${h} Z`} fill={accent} fillOpacity="0.18" />
        <path d={path("y_sur")} stroke={accent} strokeWidth="1.5" fill="none" />
        <line x1={pts[Math.floor(cursor)].x} y1="0" x2={pts[Math.floor(cursor)].x} y2={h} stroke={accent} strokeOpacity="0.4" />
        <circle cx={pts[Math.floor(cursor)].x} cy={pts[Math.floor(cursor)].y_sur} r="3" fill={accent} />
      </svg>
      <div className="mono" style={{ display: "flex", justifyContent: "space-between", fontSize: 10, color: "var(--ink-3)", marginTop: 6 }}>
        <span style={{ color: "rgba(255,194,61,0.8)" }}>● PV</span>
        <span>● HOUSE LOAD</span>
        <span style={{ color: accent }}>● CHARGE NOW</span>
      </div>
    </div>
  );
}

function APIArt({ accent }) {
  return (
    <div style={{ padding: 22, height: "100%" }} className="mono">
      <div style={{ fontSize: 10, color: "var(--ink-3)", letterSpacing: "0.1em", textTransform: "uppercase", marginBottom: 12 }}>GET /v1/sg/summary</div>
      <pre className="code" style={{ fontSize: 11.5, color: "var(--ink-2)", lineHeight: 1.7, margin: 0 }}>
{`{
  "market": "sg",
  "as_of": "2026-05-06T14:05:00+08:00",
  "coverage": {
    "public_cell_count": 24
  },
  "aggregate": {
    "network_output_index": 76.4,
    "active_cell_share_pct": 86.0,
    "variability_index": 18.6
  }
}`}
      </pre>
    </div>
  );
}

function Products({ accent }) {
  return (
    <section className="sec" id="products" data-screen-label="products">
      <div className="wrap">
        <div className="sec-head">
          <div className="label">05 · PRODUCTS</div>
          <div>
            <h2>Three products on one data spine.</h2>
            <p>One ingestion pipeline, one schema, one API. Built for EPCs, asset owners, energy traders, and developers.</p>
          </div>
        </div>

        <div className="products">
          <div className="product-card product-featured">
            <div className="product-body">
              <span className="tag">01 / FOMONITOR</span>
              <h3>String-level fault detection across any inverter brand.</h3>
              <p>Inverter-agnostic monitoring with detection algorithms tuned to tropical PV. Catches degradation, soiling, and string faults that vendor portals miss — usually days earlier.</p>
              <ul>
                <li>Huawei · Sungrow · SolarEdge · GoodWe · Solis · SMA</li>
                <li>5-minute string-level telemetry, 12-month retention standard</li>
                <li>Alerting via webhook, email, or pushed to your CMMS</li>
              </ul>
              <div className="more">
                <a href="#">View case study →</a>
                <span style={{ color: "var(--ink-4)" }}>·</span>
                <a href="#">Read docs</a>
              </div>
            </div>
            <div className="product-art">
              <FomonitorArt accent={accent} />
            </div>
          </div>

          <div className="product-card small">
            <div className="product-body">
              <span className="tag">02 / SOLAR-TO-EV</span>
              <h3>Charge the EV only when the roof has surplus.</h3>
              <p>Cloud control, no extra hardware. Signs into your charger and your inverter; modulates charge rate to track real-time PV surplus.</p>
              <div className="more">
                <a href="#">How it works →</a>
              </div>
            </div>
            <div className="product-art"><SolarEVArt accent={accent} /></div>
          </div>

          <div className="product-card small">
            <div className="product-body">
              <span className="tag">03 / GRAPH API</span>
              <h3>One time-series API for solar, weather, and grid.</h3>
              <p>Unified schema across sites and sources. Ingest once, query everywhere. Read replicas in SG and JKT.</p>
              <div className="more">
                <a href="#developers">Try the explorer →</a>
              </div>
            </div>
            <div className="product-art"><APIArt accent={accent} /></div>
          </div>
        </div>

        <div className="coming">
          <div className="label">ROADMAP · H2 2026 ONWARD</div>
          <div className="coming-list">
            <div className="ci"><div className="t">Battery dispatch optimiser</div><div className="d">Behind-the-meter & VPP control loops.</div></div>
            <div className="ci"><div className="t">Tropical PV performance dataset</div><div className="d">Module-level field data for ratings teams.</div></div>
            <div className="ci"><div className="t">REC attestation pipeline</div><div className="d">Auditable, meter-grade I-REC issuance.</div></div>
          </div>
        </div>
      </div>
    </section>
  );
}

// ---------------- Dashboard for asset owners ----------------
function Dashboard({ accent }) {
  const [tab, setTab] = useS("overview");
  const cells = [
    { name: "CELL-W-01", region: "WEST", output: "69.3 IDX", status: "ok", activity: "84%" },
    { name: "CELL-W-02", region: "WEST", output: "72.1 IDX", status: "ok", activity: "86%" },
    { name: "CELL-C-01", region: "CENTRAL", output: "76.4 IDX", status: "ok", activity: "89%" },
    { name: "CELL-N-02", region: "NORTH", output: "67.8 IDX", status: "warn", activity: "80%" },
    { name: "CELL-E-01", region: "EAST", output: "78.8 IDX", status: "ok", activity: "91%" },
    { name: "CELL-S-01", region: "SOUTH", output: "71.3 IDX", status: "ok", activity: "85%" },
    { name: "CELL-NE-01", region: "NORTHEAST", output: "74.1 IDX", status: "ok", activity: "87%" },
  ];
  return (
    <section className="sec" id="dashboard" data-screen-label="dashboard">
      <div className="wrap">
        <div className="sec-head">
          <div className="label">08 · OPERATOR VIEW PREVIEW</div>
          <div>
            <h2>Operator dashboards with privacy boundaries built in.</h2>
            <p>Shown here with anonymised sample cells and public-safe indices. Real operator workflows can layer in private telemetry after login, but the public site does not expose it.</p>
          </div>
        </div>

        <div className="dash">
          <div className="dash-head">
            <div className="dash-tabs">
              {["overview", "signals", "quality", "history"].map(k => (
                <div key={k} className={"dash-tab" + (tab === k ? " active" : "")} onClick={() => setTab(k)}>
                  {k[0].toUpperCase() + k.slice(1)}
                </div>
              ))}
            </div>
            <div className="mono" style={{ fontSize: 11, color: "var(--ink-3)", letterSpacing: "0.06em" }}>
              ANONYMISED SAMPLE · 24 PUBLIC CELLS · 5M CADENCE
            </div>
          </div>

          <div className="dash-grid">
            <div className="dash-cell">
              <div className="label">Network output</div>
              <div className="val">76.4 <span style={{ fontSize: 16, color: "var(--ink-3)" }}>IDX</span></div>
              <div className="delta"><span style={{ color: accent }}>+3.8%</span> vs 28d norm</div>
            </div>
            <div className="dash-cell">
              <div className="label">Cell activity</div>
              <div className="val">86%</div>
              <div className="delta">3 cells in watch state</div>
            </div>
            <div className="dash-cell">
              <div className="label">Variance flags</div>
              <div className="val">3</div>
              <div className="delta"><span style={{ color: "var(--warn)" }}>1 elevated</span> · 2 moderate</div>
            </div>

            <div className="dash-cell full">
              <div className="label">Public cells</div>
              <div className="site-list">
                {cells.map((s, i) => (
                  <div className="row" key={i}>
                    <span className={"led " + s.status}></span>
                    <span className="name">{s.name}</span>
                    <span className="meta">{s.region}</span>
                    <span className="meta">{s.output}</span>
                    <span className={"status " + s.status}>ACT {s.activity}</span>
                  </div>
                ))}
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

// ---------------- Customers ----------------
function Customers() {
  return (
    <section className="sec" id="customers" data-screen-label="customers">
      <div className="wrap">
        <div className="sec-head">
          <div className="label">09 · DESIGN PARTNERS</div>
          <div>
            <h2>Built with operator feedback. Public examples stay anonymised.</h2>
            <p>Graph was informed by real operating environments, including collaboration with Fomo Energy. Public-facing examples on this site are reference views only and exclude private portfolio totals or identifiable asset data.</p>
          </div>
        </div>

        <div className="logo-wall">
          <div className="logo-cell featured">
            <div className="fomo-mark">
              <span className="glyph"></span>
              <span className="word">fomo</span>
            </div>
          </div>
          {[1,2,3,4,5,6,7,8,9].map(i => (
            <div className="logo-cell" key={i}>
              <span className="placeholder">PARTNER · {String(i).padStart(2,"0")}</span>
            </div>
          ))}
        </div>

        <div style={{ marginTop: 28, display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 0, border: "1px solid var(--line)", borderRadius: 10, overflow: "hidden" }}>
          {[
            ["24", "PUBLIC CELLS IN SAMPLE GRID"],
            ["5 MIN", "PUBLIC REFRESH CADENCE"],
            ["IDX", "NORMALISED OUTPUT METRIC"],
          ].map(([v, k], i) => (
            <div key={i} style={{ padding: "26px 28px", borderRight: i < 2 ? "1px solid var(--line)" : 0, background: "var(--panel)" }}>
              <div className="mono" style={{ fontSize: 11, color: "var(--ink-3)", letterSpacing: "0.1em" }}>{k}</div>
              <div style={{ fontSize: 32, fontWeight: 500, letterSpacing: "-0.025em", marginTop: 8, fontVariantNumeric: "tabular-nums" }}>{v}</div>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

// ---------------- About ----------------
function About() {
  return (
    <section className="sec" id="about" data-screen-label="about">
      <div className="wrap">
        <div className="sec-head">
          <div className="label">10 · COMPANY</div>
          <div>
            <h2>A small team of operators and engineers.</h2>
            <p>Singapore-based. Long-term oriented. We run real solar assets, not slide decks.</p>
          </div>
        </div>

        <div className="about-grid">
          <div className="about-block">
            <h3>What we're building</h3>
            <p>Distributed energy in Southeast Asia generates more data per dollar of capex than any prior wave of energy infrastructure — and almost none of it reaches the people who could put it to work. Graph is the layer that fixes that, end-to-end, from inverter modbus to a queryable schema to control software that closes the loop.</p>
            <p>We started inside a working EPC. We are deliberately staying close to the metal.</p>
          </div>
          <div className="about-block">
            <h3>Open roles</h3>
            <div style={{ borderTop: "1px solid var(--line)" }}>
              {[
                ["Founding Backend Engineer",     "Time-series · Go/Rust · SG"],
                ["Founding Embedded Engineer",    "Inverter integrations · Modbus · SG"],
                ["Energy Markets Analyst",        "SG/MY wholesale · Data-fluent · SG"],
                ["Product Designer (Tools)",      "Data-dense UIs · SG/Remote"],
              ].map(([title, sub], i) => (
                <a key={i} href="#" style={{
                  display: "grid", gridTemplateColumns: "1fr auto", padding: "16px 0", alignItems: "center",
                  borderBottom: "1px solid var(--line)", color: "var(--ink)"
                }}>
                  <div>
                    <div style={{ fontSize: 16, fontWeight: 500 }}>{title}</div>
                    <div className="role">{sub}</div>
                  </div>
                  <span className="mono" style={{ color: "var(--ink-3)", fontSize: 12 }}>APPLY →</span>
                </a>
              ))}
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

// ---------------- Footer ----------------
function Footer() {
  return (
    <footer data-screen-label="footer">
      <div className="wrap">
        <div className="foot-grid">
          <div>
            <div className="logo" style={{ marginBottom: 14 }}>
              <svg className="logo-mark" viewBox="0 0 24 24" fill="none">
                <path d="M5 4 L4 4 L4 20 L5 20" stroke="#ECEEF0" strokeWidth="1.1" strokeLinecap="square" />
                <path d="M19 4 L20 4 L20 20 L19 20" stroke="#ECEEF0" strokeWidth="1.1" strokeLinecap="square" />
                <line x1="7" y1="14" x2="11" y2="11" stroke="#ECEEF0" strokeWidth="0.9" />
                <line x1="11" y1="11" x2="14" y2="13" stroke="#ECEEF0" strokeWidth="0.9" />
                <line x1="14" y1="13" x2="17" y2="9" stroke="#ECEEF0" strokeWidth="0.9" />
                <circle cx="14" cy="13" r="1.4" fill="var(--accent)" />
              </svg>
              <span className="logo-word">graph</span>
            </div>
            <p style={{ color: "var(--ink-2)", fontSize: 14, maxWidth: 360, margin: 0 }}>
              The data and control layer for Southeast Asia's distributed energy transition.
            </p>
            <div className="social-links">
              <a className="social" href="#" aria-label="LinkedIn">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M4.98 3.5C4.98 4.881 3.87 6 2.5 6S0 4.881 0 3.5C0 2.12 1.12 1 2.5 1s2.48 1.12 2.48 2.5zM.22 8h4.56v14H.22V8zm7.32 0h4.37v1.91h.06c.61-1.15 2.1-2.36 4.32-2.36 4.62 0 5.47 3.04 5.47 7v8.45h-4.56v-7.49c0-1.79-.03-4.09-2.49-4.09-2.49 0-2.87 1.95-2.87 3.96V22H7.54V8z"/></svg>
                <span>LinkedIn</span>
              </a>
              <a className="social" href="#" aria-label="GitHub">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M12 .3a12 12 0 0 0-3.79 23.4c.6.1.82-.26.82-.58v-2c-3.34.72-4.04-1.6-4.04-1.6-.55-1.39-1.34-1.76-1.34-1.76-1.08-.74.08-.73.08-.73 1.2.09 1.83 1.24 1.83 1.24 1.07 1.83 2.81 1.3 3.5.99.1-.78.42-1.3.76-1.6-2.66-.3-5.47-1.34-5.47-5.96 0-1.32.47-2.4 1.24-3.24-.13-.31-.54-1.54.12-3.21 0 0 1-.32 3.3 1.23a11.4 11.4 0 0 1 6 0c2.3-1.55 3.3-1.23 3.3-1.23.66 1.67.25 2.9.12 3.21.77.84 1.24 1.92 1.24 3.24 0 4.63-2.81 5.65-5.49 5.95.43.37.81 1.1.81 2.22v3.29c0 .32.22.7.83.58A12 12 0 0 0 12 .3"/></svg>
                <span>GitHub</span>
              </a>
              <a className="social" href="#" aria-label="X">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
                <span>X</span>
              </a>
            </div>
          </div>
          <div>
            <h5>Products</h5>
            <a href="#">Fomonitor</a>
            <a href="#">Solar-to-EV</a>
            <a href="#">Graph API</a>
            <a href="#">Roadmap</a>
          </div>
          <div>
            <h5>Developers</h5>
            <a href="#">Documentation</a>
            <a href="#">API reference</a>
            <a href="#">Status</a>
            <a href="#">Changelog</a>
          </div>
          <div>
            <h5>Company</h5>
            <a href="#">About</a>
            <a href="#">Careers</a>
            <a href="#">Press kit</a>
            <a href="#">Contact</a>
          </div>
        </div>

        <div className="foot-bottom">
          <div className="legal">
            GRAPH SINGAPORE PRIVATE LIMITED · UEN 202xxxxxxK · 71 AYER RAJAH CRESCENT, #04-12, SINGAPORE 139951 · © 2026
          </div>
          <div>v1.4.2 · BUILD 2026.05.04</div>
        </div>
      </div>
    </footer>
  );
}

window.LiveWidget = LiveWidget;
window.Products = Products;
window.Dashboard = Dashboard;
window.Customers = Customers;
window.About = About;
window.Footer = Footer;
