/* Practice figures, part three — the four long-form panels.
   These four practices needed the whole chain shown rather than one moment of
   it, so each runs a 20-second cycle in six phases: elements arrive in order
   and stay, then the cycle resets. Phase classes are pf-r1…pf-r6 (reveal) and
   pf-m1…pf-m6 (a packet travelling during that phase, --dx/--dy on the node).
   Helpers resolve at render time; see part two for why. */
const LG = (props) => window.Fig(props);
const LP = (props) => window.Panel(props);
const LM = (...a) => window.MONO(...a);
const kA = "var(--sx-accent)", kF = "var(--sx-field)", kS = "var(--sx-signal)", kAM = "var(--sx-amber)";
const kL = "var(--border-default)", kL2 = "var(--border-strong)";
const kQ = "var(--text-quiet)", kI = "var(--text-strong)", kB = "var(--text-body)";

/* A numbered phase caption — the spine of all four figures. */
function PhaseTag({ x, y, n, label, tone }) {
  const px = Number(x);
  return (
    <g className={`pf-r${n}`}>
      <rect x={px} y={y - 9} width="13" height="13" rx="2" fill="none" stroke={tone || kA} />
      <text x={px + 6.5} y={y + 0.5} textAnchor="middle" fill={tone || kA} style={LM(8, 600)}>{n}</text>
      <text x={px + 20} y={y + 0.5} fill={kI} style={LM(8.5)}>{label}</text>
    </g>
  );
}

/* ── Agri-tech ─────────────────────────────────────────────────────────────
   A greenhouse with the whole loop: probes and air sensors read, a gateway
   ships them, the platform stores them, two models run, setpoints come back
   down to valve, vent and lamp, and the next reading verifies the decision. */
function FigAgriLong({ mobile }) {
  const h = mobile ? 344 : 356;
  const gx = 8, gw = mobile ? 244 : 226, gy = 30, gh = mobile ? 138 : 152;
  const soil = gy + gh - 34;
  const plantsX = [gx + 44, gx + 96, gx + 148, gx + 196].slice(0, mobile ? 3 : 4);
  const px = gx + gw + 12, pw = 400 - px - 4;
  const probe = [["20 cm", 24.7, kF], ["40 cm", 21.4, kA], ["60 cm", 18.9, kQ]];
  return (
    <LG h={h} title="Sense, model, act, verify" subtitle="one greenhouse house, one irrigation cycle, one yield model"
      note="Representative magnitudes for a mid-cycle house at midday. Bounds are hard limits in the deployed system, models are re-fitted per season, and every override is logged with who set it."
      legend={[{ label: "sensor read", color: kF }, { label: "model", color: kA }, { label: "command", color: kAM }]}>
      <defs>
        <linearGradient id="ag-soil" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0" stopColor="var(--surface-raised)" />
          <stop offset="1" stopColor="var(--bg-page)" />
        </linearGradient>
      </defs>

      {/* phase 1 — the house, its plants and its instruments */}
      <g className="pf-r1">
        <path d={`M${gx} ${gy + gh} V${gy + 34} Q${gx + gw / 2} ${gy - 12} ${gx + gw} ${gy + 34} V${gy + gh}`} fill="none" stroke={kL2} strokeWidth="1" />
        <path d={`M${gx + gw / 2} ${gy - 4} V${gy + gh - 34}`} stroke={kL} strokeWidth="0.6" strokeDasharray="3 4" />
        <rect x={gx} y={soil} width={gw} height={gh + gy - soil} fill="url(#ag-soil)" stroke={kL} />
        <text x={gx + 4} y={gy + 8} fill={kQ} style={LM(7)}>house 3 · 1 200 m²</text>
        {plantsX.map((x, i) => (
          <g key={x}>
            <path d={`M${x} ${soil} V${soil - 30 - (i % 2) * 6}`} stroke={kF} strokeWidth="1.2" fill="none" />
            <path d={`M${x} ${soil - 14} q -11 -5 -13 -14 q 11 1 13 14`} fill={kF} opacity="0.32" stroke={kF} strokeWidth="0.6" />
            <path d={`M${x} ${soil - 21} q 11 -5 13 -14 q -11 1 -13 14`} fill={kF} opacity="0.22" stroke={kF} strokeWidth="0.6" />
            <path d={`M${x} ${soil - 28} q -9 -4 -11 -12 q 9 1 11 12`} fill={kF} opacity="0.16" stroke={kF} strokeWidth="0.5" />
          </g>
        ))}
        {probe.map(([d], i) => (
          <g key={d}>
            <line x1={gx + 22} y1={soil + 2} x2={gx + 22} y2={soil + 8 + i * 9} stroke={kL2} strokeWidth="0.8" />
            <circle cx={gx + 22} cy={soil + 8 + i * 9} r="2.2" fill={kF} />
          </g>
        ))}
        <rect x={gx + gw - 54} y={gy + 16} width="46" height="15" rx="2" fill="var(--surface-card)" stroke={kL2} />
        <text x={gx + gw - 31} y={gy + 26} textAnchor="middle" fill={kB} style={LM(7)}>air · par</text>
      </g>

      {/* phase 2 — the readings themselves */}
      <g className="pf-r2">
        {probe.map(([d, v, c], i) => (
          <text key={d} x={gx + 30} y={soil + 11 + i * 9} fill={c} style={LM(7)}>{d} · {v}%</text>
        ))}
        <text x={gx + gw - 54} y={gy + 42} fill={kQ} style={LM(7)}>28.4 °c · 61% rh</text>
        <text x={gx + gw - 54} y={gy + 52} fill={kQ} style={LM(7)}>par 780 µmol</text>
        <text x={gx + 4} y={gy + gh + 14} fill={kQ} style={LM(7)}>14 sensors · 5 min cadence · 4 032 reads/day</text>
      </g>

      {/* phase 3 — gateway and platform */}
      <LP x={px} y={gy - 6} w={pw} h="52" label="gateway" tone={kL2}>
        <g className="pf-r3">
          <text x={px + 8} y={gy + 22} fill={kI} style={LM(9)}>lora · 3 hops</text>
          <text x={px + 8} y={gy + 34} fill={kQ} style={LM(7)}>store and forward · 72 h buffer</text>
        </g>
      </LP>
      <g className="pf-m3">
        <circle cx={gx + gw - 20} cy={gy + 24} r="2.6" fill={kF} style={{ "--dx": `${px - gx - gw + 32}px`, "--dy": "0px" }} />
      </g>
      <LP x={px} y={gy + 54} w={pw} h="64" label="iot platform" tone={kL2}>
        <g className="pf-r4">
          {[["ingest", "4 032/day"], ["series store", "3 seasons"], ["quality", "1 gap filled"]].map((r, i) => (
            <g key={r[0]}>
              <text x={px + 8} y={gy + 80 + i * 12} fill={kQ} style={LM(7)}>{r[0]}</text>
              <text x={px + pw - 8} y={gy + 80 + i * 12} textAnchor="end" fill={kI} style={LM(7.5)}>{r[1]}</text>
            </g>
          ))}
        </g>
      </LP>

      {/* phase 4 — the two models and what they output */}
      <LP x={px} y={gy + 126} w={pw} h="80" label="models" tone={kA}>
        <g className="pf-r5">
          {[["yield · gbm", "6.4 t/ha ±0.3"], ["growth · lstm", "harvest w42"], ["trained", "3 seasons · 1.1M"], ["back-test", "mape 4.1%"]].map((r, i) => (
            <g key={r[0]}>
              <text x={px + 8} y={gy + 148 + i * 13} fill={kQ} style={LM(7)}>{r[0]}</text>
              <text x={px + pw - 8} y={gy + 148 + i * 13} textAnchor="end" fill={i < 2 ? kA : kI} style={LM(7.5)}>{r[1]}</text>
            </g>
          ))}
        </g>
      </LP>
      <g className="pf-m4">
        <circle cx={px + pw / 2} cy={gy + 118} r="2.4" fill={kA} style={{ "--dx": "0px", "--dy": "14px" }} />
      </g>

      {/* phase 5 — setpoints returning to the house */}
      <g className="pf-r5">
        <path d={`M${px + 6} ${gy + 166} H${gx + gw + 4} V${gy + gh + 34} H${gx + 12}`} fill="none" stroke={kAM} strokeWidth="1" strokeDasharray="4 3" />
        <path d={`M${gx + 12} ${gy + gh + 30} l-6 4 l6 4 z`} fill={kAM} />
        <text x={gx + 4} y={gy + gh + 48} fill={kAM} style={LM(8)}>setpoints written back</text>
      </g>
      <g className="pf-m5">
        <circle cx={px + 6} cy={gy + 166} r="2.6" fill={kAM} style={{ "--dx": `${-(px - gx - 16)}px`, "--dy": `${gy + gh + 34 - gy - 166}px` }} />
      </g>
      <g className="pf-r5">
        {[["valve", "9 mm · 22 min"], ["vent", "40% open"], ["lamp", "+2 h to 06:00"]].map((r, i) => (
          <g key={r[0]}>
            <rect x={gx + 8 + i * (mobile ? 80 : 74)} y={gy + gh + 56} width={mobile ? 74 : 68} height="26" rx="3" fill="var(--surface-card)" stroke={kAM} opacity="0.9" />
            <text x={gx + 14 + i * (mobile ? 80 : 74)} y={gy + gh + 68} fill={kI} style={LM(8)}>{r[0]}</text>
            <text x={gx + 14 + i * (mobile ? 80 : 74)} y={gy + gh + 78} fill={kQ} style={LM(6.5)}>{r[1]}</text>
          </g>
        ))}
      </g>

      {/* phase 6 — the verification that closes the loop */}
      <g className="pf-r6">
        <line x1={gx} y1={gy + gh + 96} x2="396" y2={gy + gh + 96} stroke={kL} />
        <text x={gx} y={gy + gh + 112} fill={kQ} style={LM(7)}>next read verifies the decision</text>
        {[["expected", "24.1%"], ["measured", "24.7%"], ["error", "+0.6 pt"], ["correction", "−0.4 mm"]].map((r, i) => (
          <g key={r[0]}>
            <text x={gx + i * (mobile ? 96 : 98)} y={gy + gh + 128} fill={kQ} style={LM(7)}>{r[0]}</text>
            <text x={gx + i * (mobile ? 96 : 98)} y={gy + gh + 140} fill={i === 3 ? kAM : kI} style={LM(9)}>{r[1]}</text>
          </g>
        ))}
        <text x={gx} y={gy + gh + 158} fill={kQ} style={LM(7)}>grower bounds respected · every override logged with who set it</text>
      </g>
      {!mobile && [["read", 1], ["ship", 3], ["store", 4], ["model", 5], ["act", 5], ["verify", 6]].slice(0, 0)}
    </LG>
  );
}

/* ── Applied ML ────────────────────────────────────────────────────────────
   The whole modelling job, not just its output: acquisition, processing,
   training and selection, back-test, the forecast, then what serving costs. */
function FigForecastLong({ mobile }) {
  const h = mobile ? 450 : 472;
  const x0 = 30, x1 = 392;
  const acqY = 34, prcY = mobile ? 116 : 112, trnY = mobile ? 196 : 190;
  const top = mobile ? 288 : 282, base = top + (mobile ? 84 : 96);
  const sources = [["scada · feeders", "15 min", "4.2M rows"], ["weather api", "hourly", "61k rows"], ["calendar", "daily", "holidays, ramadan"]];
  const steps = [["gap fill", "1.2k"], ["outlier clamp", "340"], ["resample", "15m → 1h"], ["features", "lags, roll, temp²"]];
  const N = 26, actual = Array.from({ length: N }, (_, i) => 210 + 28 * Math.sin(i / 3.1) + i * 1.9);
  const X = (i) => x0 + (i / (N - 1 + 8)) * (x1 - x0);
  const Y = (v) => base - ((v - 170) / 150) * (base - top);
  const fc = Array.from({ length: 9 }, (_, i) => actual[N - 1] + i * 3.4 + 6 * Math.sin(i / 1.6));
  const fX = (i) => x0 + ((N - 1 + i) / (N - 1 + 8)) * (x1 - x0);
  return (
    <LG h={h} title="From meter to forecast" subtitle="acquisition, processing, training, back-test, serving"
      note="Representative rows and errors from a distribution-utility deployment. The band is the 80% interval from the back-test, and it widens with horizon because the honest one does."
      legend={[{ label: "actual", color: kA }, { label: "forecast", color: kS }, { label: "80% band", color: kL2 }]}>

      <PhaseTag x="4" y={acqY - 12} n="1" label="acquisition" />
      <g className="pf-r1">
        {sources.map(([n, cad, rows], i) => (
          <g key={n}>
            <rect x="4" y={acqY + i * 20} width={mobile ? 168 : 150} height="17" rx="2" fill="var(--surface-card)" stroke={kL2} />
            <text x="10" y={acqY + 12 + i * 20} fill={kI} style={LM(8)}>{n}</text>
            <text x={(mobile ? 168 : 150) - 2} y={acqY + 12 + i * 20} textAnchor="end" fill={kQ} style={LM(7)}>{cad}</text>
            <text x={(mobile ? 180 : 162)} y={acqY + 12 + i * 20} fill={kF} style={LM(7)}>{rows}</text>
            <path d={`M${mobile ? 246 : 236} ${acqY + 8 + i * 20} H${mobile ? 268 : 262}`} stroke={kL} strokeWidth="0.8" />
          </g>
        ))}
        <LP x={mobile ? 272 : 266} y={acqY - 2} w={mobile ? 124 : 130} h="52" label="landing" tone={kL2}>
          <text x={(mobile ? 272 : 266) + 8} y={acqY + 24} fill={kI} style={LM(9)}>4.3M rows/day</text>
          <text x={(mobile ? 272 : 266) + 8} y={acqY + 36} fill={kQ} style={LM(7)}>by feeder and hour</text>
        </LP>
      </g>
      <g className="pf-m1">
        <circle cx={mobile ? 246 : 236} cy={acqY + 28} r="2.6" fill={kF} style={{ "--dx": "30px", "--dy": "0px" }} />
      </g>

      <PhaseTag x="4" y={prcY - 12} n="2" label="processing" />
      <g className="pf-r2">
        {steps.map(([n, v], i) => {
          const w = mobile ? 90 : 92, gap = mobile ? 96 : 98, x = 4 + (i % (mobile ? 4 : 4)) * gap;
          return (
            <g key={n}>
              <rect x={x} y={prcY} width={w} height="30" rx="3" fill="var(--surface-card)" stroke={kL2} />
              <text x={x + 6} y={prcY + 13} fill={kI} style={LM(8)}>{n}</text>
              <text x={x + 6} y={prcY + 24} fill={kQ} style={LM(6.5)}>{v}</text>
              {i > 0 && <path d={`M${x - 6} ${prcY + 15} H${x}`} stroke={kL2} strokeWidth="0.8" />}
            </g>
          );
        })}
        <text x="4" y={prcY + 46} fill={kQ} style={LM(7)}>every transform versioned, so a forecast can be rebuilt from the raw reads</text>
      </g>

      <PhaseTag x="4" y={trnY - 12} n="3" label="training and selection" />
      <g className="pf-r3">
        {[["train", 0.62, "2019–2023", kL2], ["val", 0.2, "2024", kA], ["test", 0.18, "2025", kS]].map((s, i, arr) => {
          const w = 388 * s[1], x = 4 + arr.slice(0, i).reduce((a, b) => a + 388 * b[1], 0);
          return (
            <g key={s[0]}>
              <rect x={x} y={trnY} width={w - 2} height="14" rx="2" fill="none" stroke={s[3]} />
              <text x={x + 5} y={trnY + 10} fill={kI} style={LM(7.5)}>{s[0]}</text>
              <text x={x + w - 7} y={trnY + 10} textAnchor="end" fill={kQ} style={LM(6.5)}>{s[2]}</text>
            </g>
          );
        })}
        {[["5-fold cv", "rolling origin"], ["sweep", "48 runs"], ["chosen", "gbm + ar residual"], ["fit time", "6 min"]].map((r, i) => (
          <g key={r[0]}>
            <text x={4 + i * 98} y={trnY + 32} fill={kQ} style={LM(7)}>{r[0]}</text>
            <text x={4 + i * 98} y={trnY + 44} fill={i === 2 ? kA : kI} style={LM(8)}>{r[1]}</text>
          </g>
        ))}
      </g>

      <PhaseTag x="4" y={top - 24} n="4" label="back-test and forecast" />
      <g className="pf-r4">
        {[0, 0.5, 1].map((t) => (
          <g key={t}>
            <line x1={x0} y1={base - t * (base - top)} x2={x1} y2={base - t * (base - top)} stroke={kL} strokeWidth="0.6" />
            <text x={x0 - 4} y={base - t * (base - top) + 3} textAnchor="end" fill={kQ} style={LM(7)}>{Math.round(170 + t * 150)}</text>
          </g>
        ))}
        <text x={x0 - 26} y={top - 6} fill={kQ} style={LM(6.5)}>mw</text>
        <path className="pf-draw-a" d={actual.map((v, i) => `${i ? "L" : "M"}${X(i)} ${Y(v)}`).join(" ")} fill="none" stroke={kA} strokeWidth="1.6" />
      </g>
      <g className="pf-r5">
        <path d={`${fc.map((v, i) => `${i ? "L" : "M"}${fX(i)} ${Y(v + 5 + i * 2.4)}`).join(" ")} ${fc.map((v, i) => `L${fX(fc.length - 1 - i)} ${Y(fc[fc.length - 1 - i] - 5 - (fc.length - 1 - i) * 2.4)}`).join(" ")} Z`} fill={kS} opacity="0.14" />
        <path d={fc.map((v, i) => `${i ? "L" : "M"}${fX(i)} ${Y(v)}`).join(" ")} fill="none" stroke={kS} strokeWidth="1.6" strokeDasharray="5 3" />
        <line x1={fX(0)} y1={top - 2} x2={fX(0)} y2={base} stroke={kL2} strokeDasharray="2 3" />
        <text x={fX(0) + 4} y={base - 6} fill={kQ} style={LM(6.5)}>now</text>
        <circle cx={fX(fc.length - 1)} cy={Y(fc[fc.length - 1])} r="3" fill={kS} />
        <text x={x1} y={Y(fc[fc.length - 1]) - 8} textAnchor="end" fill={kS} style={LM(8)}>287 mw ±14 · 24 h</text>
      </g>

      <PhaseTag x="4" y={base + 26} n="5" label="serving" />
      <g className="pf-r6">
        {!mobile && (
          <g>
            <text x="4" y={base + 44} fill={kQ} style={LM(7)}>residuals, test year</text>
            {[0.2, 0.45, 0.8, 1, 0.72, 0.34, 0.14].map((v, i) => (
              <rect key={i} x={4 + i * 12} y={base + 74 - v * 24} width="9" height={v * 24} fill={kA} opacity="0.55" />
            ))}
            <line x1="4" y1={base + 74} x2="94" y2={base + 74} stroke={kL} />
          </g>
        )}
        {[["mape", "2.8%"], ["re-forecast", "hourly"], ["drift check", "weekly"], ["alert at", "mape > 4%"]].map((r, i) => (
          <g key={r[0]}>
            <text x={(mobile ? 4 : 108) + i * (mobile ? 98 : 72)} y={base + (mobile ? 44 : 48)} fill={kQ} style={LM(7)}>{r[0]}</text>
            <text x={(mobile ? 4 : 108) + i * (mobile ? 98 : 72)} y={base + (mobile ? 56 : 62)} fill={i === 0 ? kF : kI} style={LM(9)}>{r[1]}</text>
          </g>
        ))}
        <text x={mobile ? 4 : 108} y={base + (mobile ? 74 : 80)} fill={kQ} style={LM(7)}>re-fitted on schedule, and whenever drift crosses the bound</text>
      </g>
    </LG>
  );
}

/* ── ERP and billing ───────────────────────────────────────────────────────
   The cycle from validated read to reconciled payment: rating against the
   tariff, a bill produced and delivered, money arriving on four channels,
   matched and posted, with what is still outstanding shown honestly. */
function FigLedgerLong({ mobile }) {
  const h = mobile ? 482 : 496;
  const readY = 32, rateY = mobile ? 104 : 100, billY = mobile ? 208 : 200, payY = mobile ? 292 : 284, recY = mobile ? 366 : 360;
  const slabs = [["0–100 units", "rs 13.48"], ["101–300", "rs 22.14"], ["301+", "rs 35.22"]];
  const charges = [["energy", "4 812.60"], ["fixed", "200.00"], ["fpa", "318.40"], ["tv fee", "35.00"], ["gst 18%", "965.52"], ["subsidy", "−420.00"]];
  const channels = [["bank", 0.41], ["wallet", 0.27], ["counter", 0.22], ["autopay", 0.10]];
  return (
    <LG h={h} title="Read to reconciled" subtitle="one billing cycle, end to end"
      note="Representative magnitudes for a cycle of this size. Tariff figures are illustrative — the deployed rating engine is driven by the gazetted schedule and re-runs a cycle without re-keying it."
      legend={[{ label: "posted", color: kF }, { label: "held", color: kAM }, { label: "money in", color: kA }]}>

      <PhaseTag x="4" y={readY - 12} n="1" label="reads in, validated" />
      <g className="pf-r1">
        {[["reads taken", "7.5M"], ["intervals", "96/day"], ["rules run", "6"], ["held", "1 204"]].map((r, i) => (
          <g key={r[0]}>
            <rect x={4 + i * 98} y={readY} width="92" height="30" rx="3" fill="var(--surface-card)" stroke={i === 3 ? kAM : kL2} />
            <text x={10 + i * 98} y={readY + 13} fill={kQ} style={LM(7)}>{r[0]}</text>
            <text x={10 + i * 98} y={readY + 25} fill={i === 3 ? kAM : kI} style={LM(9)}>{r[1]}</text>
          </g>
        ))}
        <text x="4" y={readY + 46} fill={kQ} style={LM(7)}>a held read is worked at the circle desk — it never reaches a bill silently</text>
      </g>

      <PhaseTag x="4" y={rateY - 12} n="2" label="rating engine" />
      <g className="pf-r2">
        {slabs.map(([s, r], i) => (
          <g key={s}>
            <rect x="4" y={rateY + i * 17} width={mobile ? 150 : 140} height="14" rx="2" fill="none" stroke={kL2} />
            <text x="10" y={rateY + 10 + i * 17} fill={kB} style={LM(7)}>{s}</text>
            <text x={(mobile ? 150 : 140) - 4} y={rateY + 10 + i * 17} textAnchor="end" fill={kI} style={LM(7)}>{r}</text>
          </g>
        ))}
        <text x="4" y={rateY + 62} fill={kQ} style={LM(6.5)}>gazetted schedule</text>
      </g>
      <g className="pf-r3">
        <LP x={mobile ? 162 : 152} y={rateY - 6} w={mobile ? 234 : 244} h="80" label="sample account · 412 units">
          {charges.map(([n, v], i) => (
            <g key={n}>
              <text x={(mobile ? 162 : 152) + 8 + (i % 2) * (mobile ? 116 : 122)} y={rateY + 22 + Math.floor(i / 2) * 15} fill={kQ} style={LM(7)}>{n}</text>
              <text x={(mobile ? 162 : 152) + (i % 2 ? (mobile ? 226 : 236) : (mobile ? 110 : 116))} y={rateY + 22 + Math.floor(i / 2) * 15} textAnchor="end" fill={n === "subsidy" ? kF : kI} style={LM(7.5)}>{v}</text>
            </g>
          ))}
          <line x1={(mobile ? 162 : 152) + 8} y1={rateY + 56} x2={(mobile ? 162 : 152) + (mobile ? 226 : 236)} y2={rateY + 56} stroke={kL} />
          <text x={(mobile ? 162 : 152) + 8} y={rateY + 70} fill={kI} style={LM(8)}>payable by 14 sept</text>
          <text x={(mobile ? 162 : 152) + (mobile ? 226 : 236)} y={rateY + 70} textAnchor="end" fill={kA} style={LM(11, 600)}>5 911.52</text>
        </LP>
      </g>

      <PhaseTag x="4" y={billY - 12} n="3" label="bills produced and delivered" />
      <g className="pf-r4">
        {[["bills", "7.5M"], ["print batch", "18 runs"], ["qr + iban", "on every bill"], ["duplicate rate", "0.02%"]].map((r, i) => (
          <g key={r[0]}>
            <text x={4 + i * 98} y={billY + 10} fill={kQ} style={LM(7)}>{r[0]}</text>
            <text x={4 + i * 98} y={billY + 22} fill={kI} style={LM(8.5)}>{r[1]}</text>
          </g>
        ))}
        {[["print", kL2], ["sms", kL2], ["email", kL2], ["portal", kA]].map((c, i) => (
          <g key={c[0]}>
            <rect x={4 + i * (mobile ? 96 : 74)} y={billY + 32} width={mobile ? 88 : 68} height="18" rx="9" fill="none" stroke={c[1]} opacity="0.85" />
            <text x={4 + (mobile ? 44 : 34) + i * (mobile ? 96 : 74)} y={billY + 44} textAnchor="middle" fill={i === 3 ? kA : kB} style={LM(7.5)}>{c[0]}</text>
          </g>
        ))}
        <text x="4" y={billY + 60} fill={kQ} style={LM(7)}>same bill on every channel — one document of record</text>
      </g>

      <PhaseTag x="4" y={payY - 12} n="4" label="payments in" />
      <g className="pf-r5">
        {channels.map(([n, share], i) => (
          <g key={n}>
            <text x={4 + i * 98} y={payY + 10} fill={kQ} style={LM(7)}>{n}</text>
            <rect x={4 + i * 98} y={payY + 16} width="86" height="7" rx="1.5" fill={kL2} opacity="0.5" />
            <rect x={4 + i * 98} y={payY + 16} width={86 * share} height="7" rx="1.5" fill={kA} />
            <text x={4 + i * 98} y={payY + 36} fill={kI} style={LM(8)}>{Math.round(share * 100)}%</text>
          </g>
        ))}
        <text x="4" y={payY + 52} fill={kQ} style={LM(7)}>matched three ways: bill, receipt, bank statement line</text>
      </g>
      <g className="pf-m5">
        <circle cx="60" cy={payY + 20} r="2.6" fill={kA} style={{ "--dx": "300px", "--dy": "0px" }} />
      </g>

      <PhaseTag x="4" y={recY - 12} n="5" label="reconciled and posted" />
      <g className="pf-r6">
        {[["matched", "96.1%", kF], ["unmatched queue", "2 118", kAM], ["gl posting", "nightly", kI], ["collected", "90.6%", kA]].map((r, i) => (
          <g key={r[0]}>
            <text x={4 + i * 98} y={recY + 10} fill={kQ} style={LM(7)}>{r[0]}</text>
            <text x={4 + i * 98} y={recY + 24} fill={r[2]} style={LM(10, 600)}>{r[1]}</text>
          </g>
        ))}
        <line x1="4" y1={recY + 34} x2="396" y2={recY + 34} stroke={kL} />
        <text x="4" y={recY + 48} fill={kQ} style={LM(7)}>arrears ageing, share of billed value</text>
        {[["0–30", 0.62, kF], ["31–60", 0.28, kF], ["61–90", 0.14, kAM], ["90+", 0.08, kS]].map((b, i) => (
          <g key={b[0]}>
            <rect x={4 + i * 68} y={recY + 56} width="56" height="7" rx="1.5" fill={kL2} opacity="0.5" />
            <rect x={4 + i * 68} y={recY + 56} width={56 * b[1]} height="7" rx="1.5" fill={b[2]} />
            <text x={4 + i * 68} y={recY + 74} fill={kQ} style={LM(6.5)}>{b[0]}</text>
          </g>
        ))}
        <text x="4" y={recY + 88} fill={kQ} style={LM(7)}>reported per circle, division and tariff class</text>
      </g>
    </LG>
  );
}

/* ── Cloud and DevOps ──────────────────────────────────────────────────────
   The estate, not just the pipeline: where it runs, what stores state, how a
   change reaches production, how a model is deployed, and what watches it. */
function FigDevopsLong({ mobile }) {
  const h = mobile ? 430 : 444;
  const ciY = 34, infraY = mobile ? 112 : 108, dataY = mobile ? 196 : 188, mlY = mobile ? 282 : 270, obsY = mobile ? 360 : 352;
  const ci = [["commit", "trunk"], ["build", "1m 40s"], ["tests", "1 284"], ["scan", "sbom, cve"], ["image", "signed"]];
  const clouds = [["aws", "eks · rds · s3"], ["azure", "aks · blob · entra"], ["in-country", "own datacentre"]];
  const stores = [["postgres", "primary + replica"], ["redis", "cache, queues"], ["object store", "documents, images"], ["backups", "pitr · 35 d"]];
  return (
    <LG h={h} title="How it runs, and how it ships" subtitle="pipeline, platform, state, models, watch"
      note="Cloud names are the platforms we deploy on, not vendor endorsements; the same delivery shape runs on a client's own datacentre when the data cannot leave it. Provider marks can replace these labels once you send the artwork."
      legend={[{ label: "pipeline", color: kA }, { label: "state", color: kF }, { label: "held", color: kAM }]}>

      <PhaseTag x="4" y={ciY - 12} n="1" label="ci — every commit" />
      <g className="pf-r1">
        {ci.map(([n, v], i) => {
          const w = mobile ? 74 : 74, gap = mobile ? 78 : 78, x = 4 + i * gap;
          return (
            <g key={n}>
              {i > 0 && <path d={`M${x - 4} ${ciY + 15} H${x}`} stroke={kL2} strokeWidth="0.8" />}
              <rect className="pf-stage" x={x} y={ciY} width={w} height="30" rx="3" fill="var(--surface-card)" stroke={kL2} style={{ animationDelay: `${i * 0.5}s` }} />
              <text x={x + 6} y={ciY + 13} fill={kI} style={LM(8)}>{n}</text>
              <text x={x + 6} y={ciY + 24} fill={kQ} style={LM(6.5)}>{v}</text>
            </g>
          );
        })}
        <text x="4" y={ciY + 46} fill={kQ} style={LM(7)}>merge blocked until the whole row is green — no manual override path</text>
      </g>

      <PhaseTag x="4" y={infraY - 12} n="2" label="platform" />
      <g className="pf-r2">
        {clouds.map(([n, v], i) => (
          <g key={n}>
            <rect x={4 + i * (mobile ? 130 : 100)} y={infraY} width={mobile ? 124 : 94} height="34" rx="3" fill="var(--surface-card)" stroke={i === 2 ? kL2 : kA} opacity="0.95" />
            <path d={`M${12 + i * (mobile ? 130 : 100)} ${infraY + 14} a5 5 0 0 1 9 -3 a4 4 0 0 1 4 5 h-13 a4 4 0 0 1 0 -2 z`} fill="none" stroke={i === 2 ? kQ : kA} strokeWidth="1" />
            <text x={30 + i * (mobile ? 130 : 100)} y={infraY + 15} fill={kI} style={LM(8.5)}>{n}</text>
            <text x={12 + i * (mobile ? 130 : 100)} y={infraY + 28} fill={kQ} style={LM(6.5)}>{v}</text>
          </g>
        ))}
        {!mobile && (
          <g>
            <rect x="304" y={infraY} width="92" height="34" rx="3" fill="none" stroke={kL2} />
            <text x="310" y={infraY + 14} fill={kI} style={LM(8.5)}>terraform</text>
            <text x="310" y={infraY + 27} fill={kQ} style={LM(6.5)}>plan · apply · drift</text>
          </g>
        )}
        <text x="4" y={infraY + 50} fill={kQ} style={LM(7)}>containers on managed kubernetes · autoscaling by queue depth, not by guess</text>
      </g>

      <PhaseTag x="4" y={dataY - 12} n="3" label="state" />
      <g className="pf-r3">
        {stores.map(([n, v], i) => (
          <g key={n}>
            <rect x={4 + i * 98} y={dataY} width="92" height="32" rx="3" fill="var(--surface-card)" stroke={kF} opacity="0.9" />
            <ellipse cx={14 + i * 98} cy={dataY + 12} rx="5" ry="2.4" fill="none" stroke={kF} strokeWidth="0.9" />
            <path d={`M9 ${dataY + 12} v6 a5 2.4 0 0 0 10 0 v-6`} transform={`translate(${i * 98 + 0},0)`} fill="none" stroke={kF} strokeWidth="0.9" />
            <text x={24 + i * 98} y={dataY + 14} fill={kI} style={LM(8)}>{n}</text>
            <text x={10 + i * 98} y={dataY + 27} fill={kQ} style={LM(6.5)}>{v}</text>
          </g>
        ))}
        <text x="4" y={dataY + 48} fill={kQ} style={LM(7)}>restores rehearsed quarterly — a backup nobody has restored is not a backup</text>
      </g>

      <PhaseTag x="4" y={mlY - 12} n="4" label="model deployment" />
      <g className="pf-r4">
        {[["registry", "v14 promoted"], ["inference", "gpu · 40 rps"], ["canary", "10% traffic"], ["rollback", "one command"]].map((r, i) => (
          <g key={r[0]}>
            <rect x={4 + i * 98} y={mlY} width="92" height="30" rx="3" fill="none" stroke={i === 2 ? kAM : kA} opacity="0.85" />
            <text x={10 + i * 98} y={mlY + 13} fill={kI} style={LM(8)}>{r[0]}</text>
            <text x={10 + i * 98} y={mlY + 24} fill={kQ} style={LM(6.5)}>{r[1]}</text>
          </g>
        ))}
        <text x="4" y={mlY + 46} fill={kQ} style={LM(7)}>model versions are artefacts like any other — promoted, canaried, reversible</text>
      </g>
      <g className="pf-m4">
        <circle cx="46" cy={mlY + 15} r="2.6" fill={kA} style={{ "--dx": "196px", "--dy": "0px" }} />
      </g>

      <PhaseTag x="4" y={obsY - 12} n="5" label="delivery and watch" />
      <g className="pf-r5">
        {["dev", "staging", "prod", "dr"].map((e, i) => (
          <g key={e}>
            {i > 0 && <path d={`M${4 + i * 62 - 6} ${obsY + 9} H${4 + i * 62}`} stroke={kL2} strokeWidth="0.8" />}
            <rect x={4 + i * 62} y={obsY} width="56" height="18" rx="3" fill="none" stroke={i === 2 ? kA : kL2} />
            <text x={32 + i * 62} y={obsY + 12} textAnchor="middle" fill={i === 2 ? kA : kB} style={LM(7.5)}>{e}</text>
          </g>
        ))}
      </g>
      <g className="pf-r6">
        <g className="pf-rollback">
          <path d={`M${4 + 2 * 62 + 28} ${obsY + 22} V${obsY + 34} H${32} V${obsY + 22}`} fill="none" stroke={kAM} strokeWidth="1.1" strokeDasharray="4 3" />
          <path d={`M28 ${obsY + 26} l4 -6 l4 6 z`} fill={kAM} />
        </g>
        <text x={mobile ? 4 : 256} y={mobile ? obsY + 48 : obsY + 12} fill={kAM} style={LM(7.5)}>gate held, rolled back in 40s</text>
        <line x1="4" y1={obsY + (mobile ? 56 : 40)} x2="396" y2={obsY + (mobile ? 56 : 40)} stroke={kL} />
        {[["uptime 90 d", "99.95%"], ["p95 latency", "180 ms"], ["alerts", "paged 24×7"], ["logs · traces", "30 d"]].map((r, i) => (
          <g key={r[0]}>
            <text x={4 + i * 98} y={obsY + (mobile ? 70 : 54)} fill={kQ} style={LM(7)}>{r[0]}</text>
            <text x={4 + i * 98} y={obsY + (mobile ? 82 : 66)} fill={i === 0 ? kF : kI} style={LM(8.5)}>{r[1]}</text>
          </g>
        ))}
      </g>
    </LG>
  );
}

Object.assign(window, { PF_LONG: { agri: (m) => <FigAgriLong mobile={m} />, forecast: (m) => <FigForecastLong mobile={m} />, ledger: (m) => <FigLedgerLong mobile={m} />, devops: (m) => <FigDevopsLong mobile={m} /> }, FigAgriLong, FigForecastLong, FigLedgerLong, FigDevopsLong });
