// plant-primitives.jsx — Isometric 3D drawing primitives for the plant scene.
// Coordinate system: world (x, y, z) where +x = process-flow direction,
// +y = depth (away from viewer-right), +z = up. Cabinet projection.

// ── Iso projection ─────────────────────────────────────────────────────────
const COS30 = 0.8660254037844387;
const SIN30 = 0.38; // flattened cabinet projection

const iso = (x, y, z = 0) => ({
  X: (x - y) * COS30,
  Y: (x + y) * SIN30 - z,
});

const pt = (x, y, z = 0) => {
  const p = iso(x, y, z);
  return `${p.X.toFixed(2)},${p.Y.toFixed(2)}`;
};

const facePts = (pts) => pts.map(([x, y, z = 0]) => pt(x, y, z)).join(' ');

// Shorthand for an SVG line from world point a to world point b.
const isoLine = (a, b, props = {}) => {
  const pa = iso(...a), pb = iso(...b);
  return <line x1={pa.X} y1={pa.Y} x2={pb.X} y2={pb.Y} {...props} />;
};

const SHADE = {
  topLight:  '#252b34',
  rightMid:  '#171c23',
  frontDark: '#0d1218',
  edge:      '#5a6473',
  edgeSoft:  '#3a4250',
  steel:     '#5a6473',
  steelDim:  '#3a4250',
  yellow:    '#fbbf24',
};

// ── IsoBox ─────────────────────────────────────────────────────────────────
function IsoBox({ x, y, z, w, d, h, top = SHADE.topLight, right = SHADE.rightMid, front = SHADE.frontDark, edge = SHADE.edge, edgeWidth = 0.8, label, labelColor = '#5a6473' }) {
  const p001 = [x,     y,     z + h];
  const p101 = [x + w, y,     z + h];
  const p111 = [x + w, y + d, z + h];
  const p011 = [x,     y + d, z + h];
  const p100 = [x + w, y,     z    ];
  const p110 = [x + w, y + d, z    ];
  const p010 = [x,     y + d, z    ];
  return (
    <>
      <polygon points={facePts([p001, p101, p111, p011])} fill={top}   stroke={edge} strokeWidth={edgeWidth} strokeLinejoin="miter" />
      <polygon points={facePts([p100, p110, p111, p101])} fill={right} stroke={edge} strokeWidth={edgeWidth} strokeLinejoin="miter" />
      <polygon points={facePts([p010, p110, p111, p011])} fill={front} stroke={edge} strokeWidth={edgeWidth} strokeLinejoin="miter" />
      {label && (() => {
        const lp = iso(x + w / 2, y + d / 2, z + h + 10);
        return <text x={lp.X} y={lp.Y} textAnchor="middle" fontSize="9" fontFamily="'Source Sans 3', sans-serif" fill={labelColor} letterSpacing="0.06em">{label}</text>;
      })()}
    </>
  );
}

// ── IsoVCylinder — vertical cylinder (axis along +z) ───────────────────────
function IsoVCylinder({ x, y, z, r, h, top = '#2a3140', shell = SHADE.rightMid, edge = SHADE.edge, edgeWidth = 0.9, capRing = true, label, labelOffset = 14 }) {
  const c = iso(x, y, z);
  const ct = iso(x, y, z + h);
  const rx = r * COS30;
  const ry = r * SIN30;
  const shellPath = [
    `M ${(c.X - rx).toFixed(2)} ${c.Y.toFixed(2)}`,
    `A ${rx} ${ry} 0 0 0 ${(c.X + rx).toFixed(2)} ${c.Y.toFixed(2)}`,
    `L ${(ct.X + rx).toFixed(2)} ${ct.Y.toFixed(2)}`,
    `A ${rx} ${ry} 0 0 1 ${(ct.X - rx).toFixed(2)} ${ct.Y.toFixed(2)}`,
    'Z',
  ].join(' ');
  return (
    <>
      <path d={shellPath} fill={shell} stroke={edge} strokeWidth={edgeWidth} />
      <ellipse cx={ct.X} cy={ct.Y} rx={rx} ry={ry} fill={top} stroke={edge} strokeWidth={edgeWidth} />
      {capRing && <ellipse cx={ct.X} cy={ct.Y} rx={rx - 2.5} ry={ry - 1.2} fill="none" stroke={SHADE.edgeSoft} strokeWidth="0.6" />}
      {label && <text x={ct.X} y={ct.Y - labelOffset} textAnchor="middle" fontSize="8.5" fontFamily="'Source Sans 3'" fill="#7a8593">{label}</text>}
    </>
  );
}

// ── IsoHCylinder — horizontal cylinder, axis along world-x ─────────────────
function IsoHCylinder({ x, y, z, length, r, top = SHADE.topLight, shell = SHADE.rightMid, end = '#0c1117', edge = SHADE.edge, edgeWidth = 1, label, ring = true }) {
  const rx = r * COS30;
  const ry = r;
  const cBot = iso(x, y, z + r);
  const cTop = iso(x + length, y, z + r);
  const lTop = iso(x, y, z + 2 * r);
  const rTop = iso(x + length, y, z + 2 * r);
  const lBot = iso(x, y, z);
  const rBot = iso(x + length, y, z);
  return (
    <>
      <g transform={`translate(${cTop.X}, ${cTop.Y}) rotate(-30)`}>
        <ellipse cx="0" cy="0" rx={rx} ry={ry} fill={end} stroke={edge} strokeWidth={edgeWidth} />
      </g>
      <polygon
        points={`${lTop.X},${lTop.Y} ${rTop.X},${rTop.Y} ${rBot.X},${rBot.Y} ${lBot.X},${lBot.Y}`}
        fill={shell} stroke={edge} strokeWidth={edgeWidth}
      />
      <g transform={`translate(${cBot.X}, ${cBot.Y}) rotate(-30)`}>
        <ellipse cx="0" cy="0" rx={rx} ry={ry} fill={top} stroke={edge} strokeWidth={edgeWidth} />
        {ring && <ellipse cx="0" cy="0" rx={rx - 3} ry={ry - 3} fill="none" stroke={SHADE.edgeSoft} strokeWidth="0.6" />}
      </g>
      {label && (() => {
        const lp = iso(x + length / 2, y, z - 8);
        return <text x={lp.X} y={lp.Y} textAnchor="middle" fontSize="9" fontFamily="'Source Sans 3'" fill="#7a8593">{label}</text>;
      })()}
    </>
  );
}

// ── IsoCone — apex up by default, or inverted (apex down) ──────────────────
function IsoCone({ x, y, z, r, h, inverted = false, light = '#262d36', dark = '#13181f', edge = SHADE.edge, edgeWidth = 0.9 }) {
  const apexZ = inverted ? z : z + h;
  const baseZ = inverted ? z + h : z;
  const apex = iso(x, y, apexZ);
  const baseCenter = iso(x, y, baseZ);
  const rx = r * COS30;
  const ry = r * SIN30;
  const baseL = { X: baseCenter.X - rx, Y: baseCenter.Y };
  const baseR = { X: baseCenter.X + rx, Y: baseCenter.Y };
  return (
    <>
      <path d={`M ${baseL.X} ${baseL.Y} A ${rx} ${ry} 0 0 1 ${baseR.X} ${baseR.Y}`}
            fill="none" stroke={edge} strokeWidth={edgeWidth} opacity="0.6" />
      <polygon points={`${baseR.X},${baseR.Y} ${apex.X},${apex.Y} ${baseCenter.X},${baseCenter.Y + ry}`}
               fill={light} stroke={edge} strokeWidth={edgeWidth} />
      <polygon points={`${baseL.X},${baseL.Y} ${apex.X},${apex.Y} ${baseCenter.X},${baseCenter.Y + ry}`}
               fill={dark} stroke={edge} strokeWidth={edgeWidth} />
      <path d={`M ${baseL.X} ${baseL.Y} A ${rx} ${ry} 0 0 0 ${baseR.X} ${baseR.Y}`}
            fill="none" stroke={edge} strokeWidth={edgeWidth} />
    </>
  );
}

// ── IsoConveyor — inclined belt with trestles + end pulleys ────────────────
// World-x conveyor (no y change). belt centred at (x, y..y+w, z).
function IsoConveyor({ x1, x2, z1, z2, y = 0, w = 14, trestles = true, color = SHADE.rightMid, edge = SHADE.edge, covered = false }) {
  const halfW = 6;
  const a = iso(x1, y,     z1 + halfW);
  const b = iso(x2, y,     z2 + halfW);
  const c = iso(x2, y + w, z2 + halfW);
  const d = iso(x1, y + w, z1 + halfW);
  const a2 = iso(x1, y,     z1 - halfW);
  const b2 = iso(x2, y,     z2 - halfW);
  const c2 = iso(x2, y + w, z2 - halfW);
  const d2 = iso(x1, y + w, z1 - halfW);
  return (
    <>
      {trestles && (() => {
        const steps = Math.max(2, Math.floor((x2 - x1) / 75));
        const trest = [];
        for (let i = 1; i <= steps; i++) {
          const t = i / (steps + 1);
          const xx = x1 + (x2 - x1) * t;
          const zz = z1 + (z2 - z1) * t;
          // 4 columns under each trestle
          const corners = [
            [xx - 3, y - 4],
            [xx + 3, y - 4],
            [xx - 3, y + w + 4],
            [xx + 3, y + w + 4],
          ];
          trest.push(
            <g key={i}>
              {corners.map(([cx, cy], j) => {
                const a = iso(cx, cy, 0), b = iso(cx, cy, zz);
                return <line key={j} x1={a.X} y1={a.Y} x2={b.X} y2={b.Y} stroke={SHADE.steel} strokeWidth={j < 2 ? 1.1 : 0.8} />;
              })}
              {/* X-bracing on front and back faces */}
              {(() => {
                const fa = iso(xx - 3, y - 4, 0), fb = iso(xx + 3, y - 4, zz);
                const fc = iso(xx + 3, y - 4, 0), fd = iso(xx - 3, y - 4, zz);
                const ba = iso(xx - 3, y + w + 4, 0), bb = iso(xx + 3, y + w + 4, zz);
                return (
                  <>
                    <line x1={fa.X} y1={fa.Y} x2={fb.X} y2={fb.Y} stroke={SHADE.steelDim} strokeWidth="0.5" />
                    <line x1={fc.X} y1={fc.Y} x2={fd.X} y2={fd.Y} stroke={SHADE.steelDim} strokeWidth="0.5" />
                    <line x1={ba.X} y1={ba.Y} x2={bb.X} y2={bb.Y} stroke={SHADE.steelDim} strokeWidth="0.4" opacity="0.5" />
                  </>
                );
              })()}
            </g>
          );
        }
        return trest;
      })()}
      {/* Belt top */}
      <polygon points={`${a.X},${a.Y} ${b.X},${b.Y} ${c.X},${c.Y} ${d.X},${d.Y}`}
               fill="#2a3140" stroke={edge} strokeWidth="0.9" />
      {/* Belt right side */}
      <polygon points={`${a.X},${a.Y} ${b.X},${b.Y} ${b2.X},${b2.Y} ${a2.X},${a2.Y}`}
               fill={color} stroke={edge} strokeWidth="0.9" />
      {/* Belt front */}
      <polygon points={`${d.X},${d.Y} ${c.X},${c.Y} ${c2.X},${c2.Y} ${d2.X},${d2.Y}`}
               fill={SHADE.frontDark} stroke={edge} strokeWidth="0.9" />
      {/* Covered gallery — a low arched roof over the belt */}
      {covered && (() => {
        const galleryH = w * 0.9;
        const ra = iso(x1, y - 1,     z1 + halfW + galleryH);
        const rb = iso(x2, y - 1,     z2 + halfW + galleryH);
        const rc = iso(x2, y + w + 1, z2 + halfW + galleryH);
        const rd = iso(x1, y + w + 1, z1 + halfW + galleryH);
        const ea = iso(x1, y - 1,     z1 + halfW);
        const eb = iso(x2, y - 1,     z2 + halfW);
        return (
          <>
            <polygon points={`${ra.X},${ra.Y} ${rb.X},${rb.Y} ${rc.X},${rc.Y} ${rd.X},${rd.Y}`}
                     fill="#1c2128" stroke={edge} strokeWidth="0.7" opacity="0.85" />
            <polygon points={`${ea.X},${ea.Y} ${eb.X},${eb.Y} ${rb.X},${rb.Y} ${ra.X},${ra.Y}`}
                     fill="#13181f" stroke={edge} strokeWidth="0.7" opacity="0.85" />
            {/* Rib lines along gallery top */}
            {Array.from({ length: 6 }).map((_, i) => {
              const t = (i + 1) / 7;
              const xx = x1 + (x2 - x1) * t;
              const zz = z1 + (z2 - z1) * t;
              const ra = iso(xx, y - 1,     zz + halfW + galleryH);
              const rb = iso(xx, y + w + 1, zz + halfW + galleryH);
              return <line key={i} x1={ra.X} y1={ra.Y} x2={rb.X} y2={rb.Y} stroke={SHADE.steelDim} strokeWidth="0.5" />;
            })}
          </>
        );
      })()}
      {/* End pulleys */}
      {[ [x1, z1], [x2, z2] ].map(([xx, zz], i) => {
        const ep = iso(xx, y + w / 2, zz);
        return <ellipse key={i} cx={ep.X} cy={ep.Y} rx={6 * COS30} ry={3.4} fill="#171c23" stroke={edge} strokeWidth="0.8" />;
      })}
    </>
  );
}

// ── IsoFrame — open structural-steel frame (4 columns + perimeter beams) ───
// Optionally with X-bracing on the long sides.
function IsoFrame({ x, y, z, w, d, h, edge = SHADE.steel, edgeWidth = 1, bracing = true, intermediateBeams = 0 }) {
  const cornerXY = [[x, y], [x + w, y], [x + w, y + d], [x, y + d]];
  const lines = [];
  // Columns
  cornerXY.forEach(([cx, cy], i) => {
    const a = iso(cx, cy, z), b = iso(cx, cy, z + h);
    lines.push(<line key={`c${i}`} x1={a.X} y1={a.Y} x2={b.X} y2={b.Y} stroke={edge} strokeWidth={edgeWidth} />);
  });
  // Top perimeter
  const top = cornerXY.map(([cx, cy]) => iso(cx, cy, z + h));
  lines.push(
    <polyline key="top" points={top.map((p) => `${p.X},${p.Y}`).join(' ') + ` ${top[0].X},${top[0].Y}`}
              fill="none" stroke={edge} strokeWidth={edgeWidth} strokeLinejoin="miter" />
  );
  // Intermediate horizontal beams (mid-height)
  for (let i = 1; i <= intermediateBeams; i++) {
    const zz = z + (h * i) / (intermediateBeams + 1);
    const beams = cornerXY.map(([cx, cy]) => iso(cx, cy, zz));
    lines.push(
      <polyline key={`mid${i}`} points={beams.map((p) => `${p.X},${p.Y}`).join(' ') + ` ${beams[0].X},${beams[0].Y}`}
                fill="none" stroke={SHADE.steelDim} strokeWidth={edgeWidth * 0.7} />
    );
  }
  // X-bracing on the right side (between columns 1 and 2)
  if (bracing) {
    const a1 = iso(x + w, y,     z),         b1 = iso(x + w, y + d, z + h);
    const a2 = iso(x + w, y + d, z),         b2 = iso(x + w, y,     z + h);
    lines.push(<line key="br1" x1={a1.X} y1={a1.Y} x2={b1.X} y2={b1.Y} stroke={SHADE.steelDim} strokeWidth={edgeWidth * 0.7} />);
    lines.push(<line key="br2" x1={a2.X} y1={a2.Y} x2={b2.X} y2={b2.Y} stroke={SHADE.steelDim} strokeWidth={edgeWidth * 0.7} />);
    // Front face bracing too
    const f1 = iso(x,     y, z),  f2 = iso(x + w, y, z + h);
    const f3 = iso(x + w, y, z),  f4 = iso(x,     y, z + h);
    lines.push(<line key="bf1" x1={f1.X} y1={f1.Y} x2={f2.X} y2={f2.Y} stroke={SHADE.steelDim} strokeWidth={edgeWidth * 0.5} opacity="0.6" />);
    lines.push(<line key="bf2" x1={f3.X} y1={f3.Y} x2={f4.X} y2={f4.Y} stroke={SHADE.steelDim} strokeWidth={edgeWidth * 0.5} opacity="0.6" />);
  }
  return <>{lines}</>;
}

// ── IsoPlatform — horizontal walkway / platform at a given z ───────────────
// Returns a thin slab plus optional handrails on the perimeter (default: all sides).
function IsoPlatform({ x, y, z, w, d, thick = 4, top = '#1f2630', side = '#0d1218', edge = SHADE.steel, handrail = true, rH = 14, rails = ['front', 'back', 'left', 'right'] }) {
  return (
    <>
      <IsoBox x={x} y={y} z={z} w={w} d={d} h={thick}
              top={top} right={side} front={SHADE.frontDark} edge={edge} edgeWidth={0.6} />
      {handrail && (() => {
        // Posts at corners + intermediates
        const posts = [];
        const railSegs = [];
        // Sample posts every 16 units
        const step = 18;
        const along = (a, b, count, side) => {
          for (let i = 0; i <= count; i++) {
            const t = i / count;
            const px = a[0] + (b[0] - a[0]) * t;
            const py = a[1] + (b[1] - a[1]) * t;
            const ap = iso(px, py, z + thick);
            const bp = iso(px, py, z + thick + rH);
            posts.push(<line key={`p${side}${i}`} x1={ap.X} y1={ap.Y} x2={bp.X} y2={bp.Y} stroke={SHADE.steel} strokeWidth="0.6" />);
          }
        };
        const railLine = (a, b, key) => {
          const pa = iso(a[0], a[1], z + thick + rH);
          const pb = iso(b[0], b[1], z + thick + rH);
          railSegs.push(<line key={`r${key}t`} x1={pa.X} y1={pa.Y} x2={pb.X} y2={pb.Y} stroke={SHADE.steel} strokeWidth="0.8" />);
          const pat = iso(a[0], a[1], z + thick + rH * 0.55);
          const pbt = iso(b[0], b[1], z + thick + rH * 0.55);
          railSegs.push(<line key={`r${key}m`} x1={pat.X} y1={pat.Y} x2={pbt.X} y2={pbt.Y} stroke={SHADE.steelDim} strokeWidth="0.5" />);
        };
        if (rails.includes('front')) {
          along([x, y + d], [x + w, y + d], Math.max(2, Math.floor(w / step)), 'F');
          railLine([x, y + d], [x + w, y + d], 'F');
        }
        if (rails.includes('back')) {
          along([x, y], [x + w, y], Math.max(2, Math.floor(w / step)), 'B');
          railLine([x, y], [x + w, y], 'B');
        }
        if (rails.includes('left')) {
          along([x, y], [x, y + d], Math.max(2, Math.floor(d / step)), 'L');
          railLine([x, y], [x, y + d], 'L');
        }
        if (rails.includes('right')) {
          along([x + w, y], [x + w, y + d], Math.max(2, Math.floor(d / step)), 'R');
          railLine([x + w, y], [x + w, y + d], 'R');
        }
        return <>{posts}{railSegs}</>;
      })()}
    </>
  );
}

// ── IsoStair — flight of stairs between two z levels ───────────────────────
// Treated as a tilted slab going along world-x from (x1,y) at z1 to (x2,y) at z2.
function IsoStair({ x1, x2, z1, z2, y, w = 16, edge = SHADE.steel }) {
  // Stringer slab as a thin sloped band
  const a = iso(x1, y,     z1);
  const b = iso(x2, y,     z2);
  const c = iso(x2, y + w, z2);
  const d = iso(x1, y + w, z1);
  // Step ticks
  const stepCount = 8;
  const ticks = [];
  for (let i = 1; i < stepCount; i++) {
    const t = i / stepCount;
    const xx = x1 + (x2 - x1) * t;
    const zz = z1 + (z2 - z1) * t;
    const ta = iso(xx, y,     zz);
    const tb = iso(xx, y + w, zz);
    ticks.push(<line key={i} x1={ta.X} y1={ta.Y} x2={tb.X} y2={tb.Y} stroke={SHADE.steelDim} strokeWidth="0.5" />);
  }
  // Handrails
  const rH = 14;
  const ha = iso(x1, y,     z1 + rH), hb = iso(x2, y,     z2 + rH);
  const hc = iso(x1, y + w, z1 + rH), hd = iso(x2, y + w, z2 + rH);
  return (
    <>
      <polygon points={`${a.X},${a.Y} ${b.X},${b.Y} ${c.X},${c.Y} ${d.X},${d.Y}`}
               fill="#1c2128" stroke={edge} strokeWidth="0.7" />
      {ticks}
      <line x1={ha.X} y1={ha.Y} x2={hb.X} y2={hb.Y} stroke={SHADE.steel} strokeWidth="0.6" />
      <line x1={hc.X} y1={hc.Y} x2={hd.X} y2={hd.Y} stroke={SHADE.steel} strokeWidth="0.6" />
    </>
  );
}

// ── IsoPipe — thick stroke between two world points (with edge "shadow") ──
function IsoPipe({ a, b, color = '#3a4250', width = 5, edge = SHADE.steel }) {
  const pa = iso(...a), pb = iso(...b);
  return (
    <>
      <line x1={pa.X} y1={pa.Y} x2={pb.X} y2={pb.Y} stroke={edge} strokeWidth={width + 1.5} strokeLinecap="round" />
      <line x1={pa.X} y1={pa.Y} x2={pb.X} y2={pb.Y} stroke={color} strokeWidth={width} strokeLinecap="round" />
    </>
  );
}

// ── IsoPipeRun — series of segments forming a piping route ────────────────
function IsoPipeRun({ pts, color = '#3a4250', width = 5, edge = SHADE.steel }) {
  return pts.slice(1).map((p, i) => (
    <IsoPipe key={i} a={pts[i]} b={p} color={color} width={width} edge={edge} />
  ));
}

// ── IsoLatticeMast — tall lattice column with diagonal bracing ────────────
function IsoLatticeMast({ x, y, z = 0, h, w = 8, head = true, edge = SHADE.steel, glow }) {
  // 4 corner columns, horizontal cross-ties every 30 units, diagonal X bracing
  const segs = Math.max(3, Math.floor(h / 28));
  const corners = [[x - w/2, y - w/2], [x + w/2, y - w/2], [x + w/2, y + w/2], [x - w/2, y + w/2]];
  const lines = [];
  corners.forEach(([cx, cy], i) => {
    const a = iso(cx, cy, z), b = iso(cx, cy, z + h);
    lines.push(<line key={`c${i}`} x1={a.X} y1={a.Y} x2={b.X} y2={b.Y} stroke={edge} strokeWidth="1" />);
  });
  for (let i = 1; i <= segs; i++) {
    const zz = z + (h * i) / segs;
    // Ring at top of each segment (only visible front-right faces)
    const ring = corners.map(([cx, cy]) => iso(cx, cy, zz));
    lines.push(
      <polyline key={`ring${i}`} points={ring.map((p) => `${p.X},${p.Y}`).join(' ') + ` ${ring[0].X},${ring[0].Y}`}
                fill="none" stroke={SHADE.steelDim} strokeWidth="0.5" />
    );
    // Diagonal bracing on front face only
    const zzPrev = z + (h * (i - 1)) / segs;
    const a1 = iso(corners[0][0], corners[0][1], zzPrev);
    const a2 = iso(corners[1][0], corners[1][1], zz);
    const a3 = iso(corners[1][0], corners[1][1], zzPrev);
    const a4 = iso(corners[0][0], corners[0][1], zz);
    lines.push(<line key={`x1-${i}`} x1={a1.X} y1={a1.Y} x2={a2.X} y2={a2.Y} stroke={SHADE.steelDim} strokeWidth="0.5" />);
    lines.push(<line key={`x2-${i}`} x1={a3.X} y1={a3.Y} x2={a4.X} y2={a4.Y} stroke={SHADE.steelDim} strokeWidth="0.5" />);
  }
  if (head) {
    const a = iso(x - w, y - 4, z + h);
    const b = iso(x + w, y - 4, z + h);
    const c = iso(x + w, y + 4, z + h);
    const d = iso(x - w, y + 4, z + h);
    lines.push(
      <polygon key="head" points={`${a.X},${a.Y} ${b.X},${b.Y} ${c.X},${c.Y} ${d.X},${d.Y}`}
               fill="#2a313b" stroke={glow || SHADE.yellow} strokeWidth="0.6" />
    );
  }
  return <>{lines}</>;
}

// ── IsoChevronRoof — pitched roof for a building (parallelogram peak) ──────
function IsoChevronRoof({ x, y, z, w, d, peakH, top = '#2a313b', side = '#171c23', edge = SHADE.edge }) {
  // Peak runs along world-x direction (mid of y); two slopes facing front+back.
  const p1 = [x,     y,         z];           // base front-left
  const p2 = [x + w, y,         z];           // base front-right
  const p3 = [x + w, y + d,     z];           // base back-right
  const p4 = [x,     y + d,     z];           // base back-left
  const peakL = [x,     y + d/2, z + peakH];  // peak left end
  const peakR = [x + w, y + d/2, z + peakH];  // peak right end
  return (
    <>
      {/* Back slope (visible) */}
      <polygon points={facePts([p4, p3, peakR, peakL])} fill={top} stroke={edge} strokeWidth="0.8" />
      {/* Front slope */}
      <polygon points={facePts([p1, p2, peakR, peakL])} fill={side} stroke={edge} strokeWidth="0.8" />
      {/* Gable end (right) */}
      <polygon points={facePts([p2, p3, peakR])} fill="#11161d" stroke={edge} strokeWidth="0.8" />
      {/* Vent ridge */}
      {(() => {
        const a = iso(...peakL), b = iso(...peakR);
        return <line x1={a.X} y1={a.Y} x2={b.X} y2={b.Y} stroke={SHADE.edge} strokeWidth="0.9" />;
      })()}
    </>
  );
}

// ── IsoTank — vertical cylinder with sloped bottom-cone (process tank) ────
function IsoTank({ x, y, z, r, h, coneH = 0, shell = SHADE.rightMid, top = '#2a3140', edge = SHADE.edge, ladder = true, label }) {
  return (
    <>
      <IsoVCylinder x={x} y={y} z={z + coneH} r={r} h={h} shell={shell} top={top} edge={edge} label={label} />
      {coneH > 0 && <IsoCone x={x} y={y} z={z} r={r} h={coneH} inverted light={shell} dark="#10141a" edge={edge} />}
      {ladder && (() => {
        // A simple ladder on the front face — series of rungs.
        const rungs = [];
        const rungCount = Math.max(3, Math.floor(h / 10));
        for (let i = 0; i < rungCount; i++) {
          const zz = z + coneH + (h * i) / rungCount;
          const a = iso(x, y - r * 0.55, zz);
          const b = iso(x, y - r * 0.55, zz + 3);
          rungs.push(<line key={i} x1={a.X} y1={a.Y} x2={b.X} y2={b.Y} stroke={SHADE.steelDim} strokeWidth="0.6" />);
        }
        // Two stiles
        const sa = iso(x - 2, y - r * 0.55, z + coneH), sb = iso(x - 2, y - r * 0.55, z + coneH + h);
        const sc = iso(x + 2, y - r * 0.55, z + coneH), sd = iso(x + 2, y - r * 0.55, z + coneH + h);
        return (
          <>
            <line x1={sa.X} y1={sa.Y} x2={sb.X} y2={sb.Y} stroke={SHADE.steelDim} strokeWidth="0.7" />
            <line x1={sc.X} y1={sc.Y} x2={sd.X} y2={sd.Y} stroke={SHADE.steelDim} strokeWidth="0.7" />
            {rungs}
          </>
        );
      })()}
    </>
  );
}

Object.assign(window, {
  iso, pt, facePts, isoLine, SHADE, COS30, SIN30,
  IsoBox, IsoVCylinder, IsoHCylinder, IsoCone, IsoConveyor,
  IsoFrame, IsoPlatform, IsoStair, IsoPipe, IsoPipeRun,
  IsoLatticeMast, IsoChevronRoof, IsoTank,
});
