/* eslint-disable */
/* =========================================================
   TEO IoT Dashboard — Digital Twin schematic.
   A 2D top-down floor plan with live overlay for the
   currently selected site. Each device renders as a node
   on the schematic, coloured by state, with a hover card.
   ========================================================= */

// 2D architectural floor plan — projects the SAME zone definitions used
// by Twin3D from above. Each zone becomes a room with thick walls, door
// openings (gaps where the 3D twin places doorways), a light floor tint,
// and a kind-specific furniture overlay.
//
// Reads ZONE_LAYOUTS_3D from window (Twin3D.jsx sets the global).
function getSiteZones(siteId, floor) {
  const layouts = (typeof window !== 'undefined' && window.ZONE_LAYOUTS_3D) || {};
  const all = layouts[siteId] || [];
  return floor != null ? all.filter(z => z.floor === floor) : all;
}

// Per-zone-kind light floor tints (designed to read as architectural
// floor-area shading — much softer than the dark 3D tones).
const FLOOR_TINT_2D = {
  reception:        '#EEF1FA',
  atrium:           '#F6F4EA',
  'atrium-f1':      '#F6F4EA',
  'atrium-f2':      '#F1F3F8',
  'meeting-rooms':  '#F1F4FB',
  'phone-booths':   '#F4F1EA',
  'hot-desks':      '#F5F5F1',
  'private-offices':'#F2F4F9',
  'meeting-pods':   '#F1F4FB',
  wellness:         '#EFF3EE',
  'open-coworking': '#F5F4EE',
  'kitchen-cafe':   '#F4EFE8',
  'event-space':    '#F1F4FB',
  core:             '#EFF0F4',
  plant:            '#F0F1F4',
  // Existing sites
  cnc:              '#F2F1EE',
  assembly:         '#F1F4FB',
  paint:            '#F4EEEE',
  'vrf-outdoor':    '#F0F2F8',
  chiller:          '#EEF3F8',
  'guest-rooms':    '#F4F1EC',
  'ahu-room':       '#F1F4FB',
  lecture:          '#F1F4FB',
  classroom:        '#F5F4EE',
  study:            '#F2F4F0',
  office:           '#F2F4F9',
  logistics:        '#F4F1EC',
  sortation:        '#F1F2F5',
  // Estate / housing
  'public-realm':   '#EFF2EE',
  'estate-block':   '#F2F4F9',
  retail:           '#F4F0E8',
  'car-park':       '#F0F1F4',
  'energy-centre':  '#EEF1F8',
  'roof-terrace':   '#EFF3EE',
  living:           '#F5F2EA',
  'eco-kitchen':    '#F4F1EA',
  bedroom:          '#F1F3F8',
  bathroom:         '#EFF3F8',
  'eco-plant':      '#F0F1F4',
  'solar-roof':     '#EAEEF8',
  'apartment-row':  '#F2F1EC',
  'communal-lobby': '#EFF2F8',
  'communal-room':  '#F4F1EA',
  utility:          '#F0F1F4',
  default:          '#F6F7F9',
};

// Compute the SVG transform that fits the building bounds into 1200×640
// while preserving aspect ratio.
function buildTransform(zones) {
  if (zones.length === 0) return null;
  const pad = 6; // world units around the building
  const minX = Math.min(...zones.map(z => z.x - z.w/2)) - pad;
  const maxX = Math.max(...zones.map(z => z.x + z.w/2)) + pad;
  const minZ = Math.min(...zones.map(z => z.z - z.d/2)) - pad;
  const maxZ = Math.max(...zones.map(z => z.z + z.d/2)) + pad;
  const W = 1200, H = 640;
  const worldW = maxX - minX, worldH = maxZ - minZ;
  const scale = Math.min(W / worldW, H / worldH);
  const offX = (W - worldW * scale) / 2 - minX * scale;
  const offY = (H - worldH * scale) / 2 - minZ * scale;
  return { scale, offX, offY, minX, maxX, minZ, maxZ, pad };
}

// One wall edge — optionally with a door opening in the middle.
const WallEdge = ({ x1, y1, x2, y2, hasOpening }) => {
  const strokeProps = { stroke:'#0A0F1F', strokeWidth:2.5, strokeLinecap:'butt' };
  if (!hasOpening) return <line x1={x1} y1={y1} x2={x2} y2={y2} {...strokeProps} />;
  const dx = x2 - x1, dy = y2 - y1;
  const len = Math.sqrt(dx*dx + dy*dy);
  const gap = Math.min(34, len * 0.28);
  const ux = dx / len, uy = dy / len;
  const mx = (x1 + x2) / 2, my = (y1 + y2) / 2;
  const g1x = mx - ux * gap/2, g1y = my - uy * gap/2;
  const g2x = mx + ux * gap/2, g2y = my + uy * gap/2;
  return (
    <g>
      <line x1={x1} y1={y1} x2={g1x} y2={g1y} {...strokeProps} />
      <line x1={g2x} y1={g2y} x2={x2} y2={y2} {...strokeProps} />
      {/* Door swing arc (thin) */}
      <path d={`M ${g1x} ${g1y} A ${gap} ${gap} 0 0 1 ${g1x + uy*gap} ${g1y - ux*gap}`}
            fill="none" stroke="#878787" strokeWidth="0.6" strokeDasharray="2 2" />
    </g>
  );
};

// Walls around one zone with door openings on the sides listed in `doors`.
const ZoneRoom = ({ zone, T }) => {
  const x1 = T.offX + (zone.x - zone.w/2) * T.scale;
  const x2 = T.offX + (zone.x + zone.w/2) * T.scale;
  const y1 = T.offY + (zone.z - zone.d/2) * T.scale;
  const y2 = T.offY + (zone.z + zone.d/2) * T.scale;
  const doors = zone.doors || [];
  const tint = FLOOR_TINT_2D[zone.kind] || FLOOR_TINT_2D.default;
  return (
    <g>
      {/* Floor tint */}
      <rect x={x1} y={y1} width={x2-x1} height={y2-y1} fill={tint} />
      {/* Walls (with door gaps) */}
      <WallEdge x1={x1} y1={y1} x2={x2} y2={y1} hasOpening={doors.includes('N')} />
      <WallEdge x1={x2} y1={y1} x2={x2} y2={y2} hasOpening={doors.includes('E')} />
      <WallEdge x1={x1} y1={y2} x2={x2} y2={y2} hasOpening={doors.includes('S')} />
      <WallEdge x1={x1} y1={y1} x2={x1} y2={y2} hasOpening={doors.includes('W')} />
    </g>
  );
};

// Furniture / equipment overlay per zone kind. Drawn in architectural
// "engineering line" style — light strokes, light fills, no colour.
const ZoneFurniture = ({ zone, T }) => {
  const cx = T.offX + zone.x * T.scale;
  const cy = T.offY + zone.z * T.scale;
  const w = zone.w * T.scale, h = zone.d * T.scale;
  const x = cx - w/2, y = cy - h/2;
  const inset = 12;
  const ix = x + inset, iy = y + inset + 14; // leave room for label
  const iw = w - inset*2, ih = h - inset*2 - 14;
  const stroke = { fill:'#FFF', stroke:'#5A5E6E', strokeWidth:0.7 };
  const ghost  = { fill:'none', stroke:'#878787', strokeWidth:0.6 };
  const chair  = (cx_, cy_, r=3.5) => <circle cx={cx_} cy={cy_} r={r} fill="#FFF" stroke="#5A5E6E" strokeWidth="0.6" />;

  switch (zone.kind) {
    // ---- Workspace (Form Property) ----
    case 'reception': {
      const dx = ix + iw*0.15, dy = iy + 18;
      const dx2 = ix + iw*0.85;
      return (
        <g>
          {/* Curved reception desk */}
          <path d={`M ${dx} ${dy} Q ${cx} ${dy - 18} ${dx2} ${dy}`} fill="#F1ECDD" stroke="#5A5E6E" strokeWidth="0.9" />
          <path d={`M ${dx} ${dy + 10} Q ${cx} ${dy - 8} ${dx2} ${dy + 10}`} fill="none" stroke="#5A5E6E" strokeWidth="0.6" />
          {/* Waiting bench */}
          <rect x={ix + iw*0.2} y={iy + ih - 24} width={iw*0.6} height="10" {...stroke} />
          {/* Plants */}
          <circle cx={ix + 14} cy={iy + ih - 19} r="5" fill="#E5EDE6" stroke="#5A5E6E" strokeWidth="0.5" />
          <circle cx={ix + iw - 14} cy={iy + ih - 19} r="5" fill="#E5EDE6" stroke="#5A5E6E" strokeWidth="0.5" />
        </g>
      );
    }
    case 'meeting-rooms': {
      const rooms = Math.max(2, Math.floor(iw / 70));
      const roomW = iw / rooms;
      return (
        <g>
          {/* Partitions */}
          {Array.from({length: rooms - 1}).map((_, i) => (
            <line key={i} x1={ix + (i + 1) * roomW} y1={iy} x2={ix + (i + 1) * roomW} y2={iy + ih} stroke="#0A0F1F" strokeWidth="1" />
          ))}
          {/* Tables + chairs per room */}
          {Array.from({length: rooms}).map((_, i) => {
            const rx = ix + i * roomW + roomW/2;
            const ry = iy + ih/2;
            const tw = Math.min(roomW * 0.55, 36), tH = Math.min(ih * 0.4, 22);
            return (
              <g key={i}>
                <rect x={rx - tw/2} y={ry - tH/2} width={tw} height={tH} {...stroke} />
                {chair(rx - tw/2 - 5, ry - tH/4)}
                {chair(rx - tw/2 - 5, ry + tH/4)}
                {chair(rx + tw/2 + 5, ry - tH/4)}
                {chair(rx + tw/2 + 5, ry + tH/4)}
              </g>
            );
          })}
        </g>
      );
    }
    case 'phone-booths': {
      const count = Math.max(4, Math.floor(iw / 40));
      const cellW = iw / count;
      return (
        <g>
          {Array.from({length: count}).map((_, i) => {
            const bx = ix + i * cellW;
            return (
              <g key={i}>
                <rect x={bx + 3} y={iy + 6} width={cellW - 6} height={ih - 12} fill="#FFF" stroke="#0A0F1F" strokeWidth="0.9" />
                {/* Door indicator */}
                <line x1={bx + cellW/2 - 4} y1={iy + ih - 6} x2={bx + cellW/2 + 4} y2={iy + ih - 6} stroke="#0A0F1F" strokeWidth="0.9" />
                {/* Stool */}
                {chair(bx + cellW/2, iy + ih*0.55, 2.6)}
              </g>
            );
          })}
        </g>
      );
    }
    case 'hot-desks': {
      // Two long bench rows
      [-1, 1].forEach(() => {});
      const rowH = 14;
      return (
        <g>
          {[-1, 1].map((side, ri) => {
            const ry = cy + side * ih * 0.2 - rowH/2;
            return (
              <g key={ri}>
                <rect x={ix + 6} y={ry} width={iw - 12} height={rowH} {...stroke} />
                {/* Monitor pucks along the bench (small filled circles) */}
                {Array.from({length: Math.floor((iw - 12) / 22)}).map((_, i) => {
                  const mx = ix + 12 + i * 22 + 11;
                  return <rect key={i} x={mx - 5} y={ry + 2} width="10" height="4" fill="#5A5E6E" />;
                })}
                {/* Chairs */}
                {Array.from({length: Math.floor((iw - 12) / 22)}).map((_, i) => {
                  const sx = ix + 12 + i * 22 + 11;
                  return chair(sx, ry + (side < 0 ? rowH + 7 : -7));
                })}
              </g>
            );
          })}
        </g>
      );
    }
    case 'atrium-f1': {
      // Curved staircase + four structural columns
      const r = Math.min(iw, ih) * 0.22;
      return (
        <g>
          {/* Staircase spiral indication */}
          <path d={`M ${cx - r} ${cy} A ${r} ${r} 0 0 1 ${cx + r} ${cy}`} fill="none" stroke="#0A0F1F" strokeWidth="1.2" />
          {/* Stair tread lines */}
          {Array.from({length: 8}).map((_, i) => {
            const a = Math.PI + (i / 8) * Math.PI;
            return <line key={i}
              x1={cx + Math.cos(a) * r * 0.6} y1={cy + Math.sin(a) * r * 0.6}
              x2={cx + Math.cos(a) * r} y2={cy + Math.sin(a) * r}
              stroke="#0A0F1F" strokeWidth="0.6" />;
          })}
          {/* Centre column */}
          <circle cx={cx} cy={cy} r="4" fill="#5A5E6E" />
          {/* Structural columns at the corners */}
          {[[ix + 8, iy + 8], [ix + iw - 8, iy + 8], [ix + 8, iy + ih - 8], [ix + iw - 8, iy + ih - 8]].map(([px, py], i) => (
            <rect key={i} x={px - 3.5} y={py - 3.5} width="7" height="7" fill="#5A5E6E" />
          ))}
        </g>
      );
    }
    case 'atrium-f2': {
      // Void looking down to floor 1 — dashed outline + balustrade indication
      const vw = iw * 0.7, vh = ih * 0.7;
      return (
        <g>
          <rect x={cx - vw/2} y={cy - vh/2} width={vw} height={vh}
                fill="none" stroke="#0A0F1F" strokeWidth="1" strokeDasharray="4 3" />
          <text x={cx} y={cy + 4} textAnchor="middle" style={{ font:"500 9px 'IBM Plex Mono', monospace", fill:'#5A5E6E', letterSpacing:'0.1em' }}>VOID</text>
        </g>
      );
    }
    case 'private-offices': {
      const cols = Math.max(3, Math.floor(iw / 70));
      const cellW = iw / cols;
      return (
        <g>
          {/* Partitions */}
          {Array.from({length: cols - 1}).map((_, i) => (
            <line key={i} x1={ix + (i+1) * cellW} y1={iy} x2={ix + (i+1) * cellW} y2={iy + ih} stroke="#0A0F1F" strokeWidth="1" />
          ))}
          {/* Each office: desk + chair */}
          {Array.from({length: cols}).map((_, i) => {
            const ox = ix + i * cellW + cellW/2;
            const oy = iy + ih * 0.4;
            return (
              <g key={i}>
                <rect x={ox - 14} y={oy - 7} width="28" height="14" {...stroke} />
                {chair(ox, oy + 14)}
              </g>
            );
          })}
        </g>
      );
    }
    case 'meeting-pods': {
      // Four small circular pods
      const positions = [[-1,-1], [1,-1], [-1,1], [1,1]];
      const pr = Math.min(iw, ih) * 0.11;
      return (
        <g>
          {positions.map(([dx, dy], i) => {
            const px = cx + dx * iw * 0.22;
            const py = cy + dy * ih * 0.22;
            return (
              <g key={i}>
                <circle cx={px} cy={py} r={pr} fill="#FFF" stroke="#0A0F1F" strokeWidth="1" />
                <circle cx={px} cy={py} r={pr * 0.5} {...stroke} />
                {/* 3 stools */}
                {[0, 2*Math.PI/3, 4*Math.PI/3].map((a, j) => chair(px + Math.cos(a) * pr * 0.78, py + Math.sin(a) * pr * 0.78, 2.4))}
              </g>
            );
          })}
        </g>
      );
    }
    case 'wellness': {
      // Mirror wall + 6 yoga mats
      return (
        <g>
          {/* Mirror */}
          <line x1={ix + 6} y1={iy + 4} x2={ix + iw - 6} y2={iy + 4} stroke="#0A0F1F" strokeWidth="1.5" />
          <line x1={ix + 6} y1={iy + 6.5} x2={ix + iw - 6} y2={iy + 6.5} stroke="#878787" strokeWidth="0.6" />
          {/* Mats */}
          {Array.from({length: 2}).map((_, r) =>
            Array.from({length: 3}).map((_, c) => (
              <rect key={`${r}-${c}`}
                x={ix + 14 + c * (iw - 28)/3}
                y={iy + 22 + r * (ih - 38)/2}
                width={(iw - 38)/3} height={(ih - 50)/2}
                fill="none" stroke="#5A5E6E" strokeWidth="0.6" strokeDasharray="3 2" />
            ))
          )}
        </g>
      );
    }
    case 'open-coworking': {
      // Cluster of desk groups + a long communal table
      return (
        <g>
          {/* 4-desk clusters in a 2x3 grid */}
          {[-1, 0, 1].map(cx_ =>
            [-1, 1].map(cy_ => {
              const px = cx + cx_ * iw * 0.28;
              const py = cy + cy_ * ih * 0.28;
              return (
                <g key={`${cx_}-${cy_}`}>
                  <rect x={px - 20} y={py - 14} width="40" height="28" {...stroke} />
                  <line x1={px} y1={py - 14} x2={px} y2={py + 14} stroke="#878787" strokeWidth="0.5" />
                  <line x1={px - 20} y1={py} x2={px + 20} y2={py} stroke="#878787" strokeWidth="0.5" />
                </g>
              );
            })
          )}
          {/* Central long table */}
          <rect x={cx - iw * 0.18} y={cy - 8} width={iw * 0.36} height="16" {...stroke} />
        </g>
      );
    }
    case 'kitchen-cafe': {
      // L-shaped counter + small table
      const t = 10;
      return (
        <g>
          <rect x={ix} y={iy} width={iw} height={t} fill="#F1ECDD" stroke="#5A5E6E" strokeWidth="0.7" />
          <rect x={ix} y={iy} width={t} height={ih * 0.55} fill="#F1ECDD" stroke="#5A5E6E" strokeWidth="0.7" />
          {/* Coffee machine pulse */}
          <rect x={ix + iw * 0.5} y={iy + 2} width="10" height="6" fill="#3A6FF8" />
          {/* Small round table */}
          <circle cx={ix + iw * 0.7} cy={iy + ih * 0.7} r="10" {...stroke} />
          {chair(ix + iw * 0.7 - 14, iy + ih * 0.7)}
          {chair(ix + iw * 0.7 + 14, iy + ih * 0.7)}
        </g>
      );
    }
    case 'event-space': {
      // Stage + chair rows
      return (
        <g>
          <rect x={ix + 4} y={iy + 4} width={iw - 8} height="14" {...stroke} />
          {Array.from({length: 3}).map((_, r) => (
            <line key={r}
              x1={ix + 10} y1={iy + 30 + r * 10} x2={ix + iw - 10} y2={iy + 30 + r * 10}
              stroke="#878787" strokeWidth="0.6" strokeDasharray="3 2" />
          ))}
        </g>
      );
    }
    case 'core': {
      return (
        <g>
          {/* Lift shaft */}
          <rect x={ix + 6} y={iy + 8} width="28" height="28" fill="#FFF" stroke="#0A0F1F" strokeWidth="1.2" />
          <line x1={ix + 20} y1={iy + 14} x2={ix + 20} y2={iy + 30} stroke="#0A0F1F" strokeWidth="0.6" strokeDasharray="2 2" />
          {/* Stairs */}
          {Array.from({length: 6}).map((_, i) => (
            <line key={i} x1={ix + iw - 6 - 30} y1={iy + 8 + i * 4} x2={ix + iw - 6} y2={iy + 8 + i * 4} stroke="#0A0F1F" strokeWidth="0.6" />
          ))}
        </g>
      );
    }

    // ---- Energy/manufacturing zones (existing sites) ----
    case 'cnc': {
      // 2x2 grid of CNC machines
      const positions = [[-1,-1], [1,-1], [-1,1], [1,1]];
      return (
        <g>
          {positions.map(([dx, dy], i) => {
            const mx = cx + dx * iw * 0.22, my = cy + dy * ih * 0.22;
            return (
              <g key={i}>
                <rect x={mx - 22} y={my - 14} width="44" height="28" {...stroke} />
                <rect x={mx - 18} y={my - 10} width="36" height="20" fill="none" stroke="#878787" strokeWidth="0.5" />
                <rect x={mx + 18} y={my - 6} width="6" height="12" fill="#3A6FF8" />
              </g>
            );
          })}
        </g>
      );
    }
    case 'assembly': {
      // Long conveyor + 3 robot arm circles
      return (
        <g>
          <rect x={ix + 10} y={cy - 6} width={iw - 20} height="12" {...stroke} />
          {[-1, 0, 1].map((d, i) => {
            const px = cx + d * iw * 0.22;
            return (
              <g key={i}>
                <circle cx={px} cy={iy + ih * 0.3} r="9" fill="#FFF" stroke="#0A0F1F" strokeWidth="1" />
                <line x1={px} y1={iy + ih * 0.3} x2={px + 7} y2={iy + ih * 0.3 + 4} stroke="#0A0F1F" strokeWidth="1.2" />
              </g>
            );
          })}
        </g>
      );
    }
    case 'paint': {
      // Booth outline + spray heads
      return (
        <g>
          <rect x={ix + 8} y={iy + 8} width={iw - 16} height={ih - 16} fill="#FFF" stroke="#0A0F1F" strokeWidth="1.2" />
          {[-1, 1].map((d, i) => (
            <g key={i}>
              <line x1={cx + d * 14} y1={iy + 14} x2={cx + d * 14} y2={iy + 26} stroke="#0A0F1F" strokeWidth="0.8" />
              <circle cx={cx + d * 14} cy={iy + 26} r="3" fill="#3A6FF8" />
            </g>
          ))}
          {/* Car silhouette */}
          <rect x={cx - 26} y={cy - 6} width="52" height="14" rx="3" {...stroke} />
        </g>
      );
    }
    case 'plant': {
      // Tank + AHU + ducts
      return (
        <g>
          <rect x={ix + 8} y={iy + 8} width={iw * 0.4} height="20" {...stroke} />
          <rect x={ix + iw * 0.5} y={iy + 8} width={iw * 0.4 - 6} height={ih * 0.5} {...stroke} />
          {/* Duct */}
          <line x1={ix + 8} y1={iy + ih - 12} x2={ix + iw - 8} y2={iy + ih - 12} stroke="#0A0F1F" strokeWidth="2" />
        </g>
      );
    }
    case 'vrf-outdoor': {
      // 3 condenser units
      return (
        <g>
          {[-1, 0, 1].map((d, i) => {
            const ox = cx + d * iw * 0.22;
            return (
              <g key={i}>
                <rect x={ox - 20} y={iy + 14} width="40" height={ih - 28} {...stroke} />
                <circle cx={ox - 10} cy={iy + 22} r="6" fill="none" stroke="#0A0F1F" strokeWidth="0.7" />
                <circle cx={ox + 10} cy={iy + 22} r="6" fill="none" stroke="#0A0F1F" strokeWidth="0.7" />
              </g>
            );
          })}
        </g>
      );
    }
    case 'chiller': {
      return (
        <g>
          {[-1, 1].map((d, i) => (
            <rect key={i} x={cx + d * iw * 0.22 - 30} y={iy + ih * 0.2} width="60" height={ih * 0.45} {...stroke} />
          ))}
          {/* Buffer tank */}
          <circle cx={cx} cy={iy + ih * 0.75} r="14" {...stroke} />
        </g>
      );
    }
    case 'guest-rooms': {
      // Strip of guest rooms with beds
      const rooms = 5;
      const roomW = iw / rooms;
      return (
        <g>
          {Array.from({length: rooms + 1}).map((_, i) => (
            <line key={`p-${i}`} x1={ix + i * roomW} y1={iy} x2={ix + i * roomW} y2={iy + ih} stroke="#0A0F1F" strokeWidth="1" />
          ))}
          {Array.from({length: rooms}).map((_, i) => {
            const rx = ix + i * roomW + roomW/2;
            return (
              <g key={`r-${i}`}>
                <rect x={rx - roomW * 0.3} y={iy + ih * 0.25} width={roomW * 0.6} height="10" {...stroke} />
                <rect x={rx - roomW * 0.25} y={iy + ih * 0.4} width="6" height="3" fill="#5A5E6E" />
              </g>
            );
          })}
        </g>
      );
    }
    case 'ahu-room': {
      return (
        <g>
          {[-1, 1].map((d, i) => (
            <rect key={i} x={cx + d * iw * 0.22 - 25} y={iy + ih * 0.3} width="50" height={ih * 0.35} {...stroke} />
          ))}
          <rect x={ix + 8} y={iy + 8} width={iw - 16} height="8" fill="none" stroke="#878787" strokeWidth="0.6" />
        </g>
      );
    }
    case 'lecture': {
      // Tiered seating + lectern
      return (
        <g>
          {Array.from({length: 5}).map((_, i) => (
            <line key={i}
              x1={ix + 10 + i * 4} y1={iy + 22 + i * 8} x2={ix + iw - 10 - i * 4} y2={iy + 22 + i * 8}
              stroke="#5A5E6E" strokeWidth="0.8" />
          ))}
          <rect x={ix + iw * 0.5 - 6} y={iy + ih - 18} width="12" height="8" {...stroke} />
        </g>
      );
    }
    case 'classroom': {
      // Grid of desks + whiteboard
      const cols = 4, rows = 3;
      return (
        <g>
          <rect x={ix + iw * 0.25} y={iy + 4} width={iw * 0.5} height="4" fill="#FFF" stroke="#0A0F1F" strokeWidth="0.9" />
          {Array.from({length: rows}).map((_, r) =>
            Array.from({length: cols}).map((_, c) => (
              <g key={`${r}-${c}`}>
                <rect x={ix + 14 + c * (iw - 28)/cols} y={iy + 22 + r * (ih - 30)/rows} width={(iw - 28)/cols - 6} height="10" {...stroke} />
              </g>
            ))
          )}
        </g>
      );
    }
    case 'study': {
      // Cluster of round pods + central long table
      const positions = [[-1,-1],[1,-1],[-1,0],[1,0],[-1,1],[1,1]];
      return (
        <g>
          {positions.map(([dx, dy], i) => (
            <circle key={i} cx={cx + dx * iw * 0.28} cy={cy + dy * ih * 0.25} r="8" {...stroke} />
          ))}
          <rect x={cx - iw * 0.15} y={cy + ih * 0.25 - 4} width={iw * 0.3} height="8" {...stroke} />
        </g>
      );
    }
    case 'office': {
      // Desks + monitors + a round meeting table
      const cols = 3;
      return (
        <g>
          {Array.from({length: 2}).map((_, r) =>
            Array.from({length: cols}).map((_, c) => (
              <g key={`${r}-${c}`}>
                <rect x={ix + 10 + c * (iw * 0.6 / cols)} y={iy + 22 + r * 22} width={(iw * 0.6 / cols) - 6} height="12" {...stroke} />
              </g>
            ))
          )}
          <circle cx={ix + iw * 0.85} cy={cy + ih * 0.15} r="12" {...stroke} />
        </g>
      );
    }
    case 'logistics': {
      // Pallet stacks
      const positions = [[-1,-1],[0,-1],[1,-1],[-1,1],[0,1],[1,1]];
      return (
        <g>
          {positions.map(([dx, dy], i) => (
            <rect key={i} x={cx + dx * iw * 0.25 - 8} y={cy + dy * ih * 0.22 - 8} width="16" height="16" {...stroke} />
          ))}
        </g>
      );
    }
    case 'sortation': {
      return (
        <g>
          {[-1, 1].map((d, i) => (
            <rect key={i} x={ix + 10} y={cy + d * 10 - 6} width={iw - 20} height="6" {...stroke} />
          ))}
        </g>
      );
    }

    // ---- Form Property estate / retail / housing ----
    case 'public-realm': {
      return (
        <g>
          {/* Trees */}
          {[[-1, -1], [1, -1], [-1, 1], [1, 1]].map(([dx, dy], i) => (
            <circle key={`tree-${i}`} cx={cx + dx * iw * 0.25} cy={cy + dy * ih * 0.25} r="7" fill="#E5EDE6" stroke="#5A5E6E" strokeWidth="0.6" />
          ))}
          {/* Benches */}
          <rect x={cx - 18} y={cy - 16} width="36" height="5" {...stroke} />
          <rect x={cx - 18} y={cy + 11} width="36" height="5" {...stroke} />
          {/* Totem */}
          <rect x={cx - 4} y={cy - 4} width="8" height="8" fill="#5A5E6E" />
        </g>
      );
    }
    case 'estate-block': {
      // Window grid representing a multi-storey façade from above
      const cols = 6, rows = 3;
      return (
        <g>
          {Array.from({length: rows}).map((_, r) =>
            Array.from({length: cols}).map((_, c) => (
              <rect key={`${r}-${c}`}
                x={ix + 6 + c * (iw - 12)/cols} y={iy + 6 + r * (ih - 12)/rows}
                width={(iw - 12)/cols - 4} height={(ih - 12)/rows - 4}
                fill="#FFF" stroke="#5A5E6E" strokeWidth="0.6" />
            ))
          )}
        </g>
      );
    }
    case 'retail': {
      const shops = 4;
      const shopW = iw / shops;
      return (
        <g>
          {Array.from({length: shops + 1}).map((_, i) => (
            <line key={i} x1={ix + i * shopW} y1={iy} x2={ix + i * shopW} y2={iy + ih} stroke="#0A0F1F" strokeWidth="1" />
          ))}
          {Array.from({length: shops}).map((_, i) => {
            const sx = ix + i * shopW + shopW/2;
            return (
              <g key={i}>
                <rect x={sx - shopW * 0.3} y={iy + ih - 22} width={shopW * 0.6} height="6" fill="#7A9CFF" stroke="#5A5E6E" strokeWidth="0.5" />
                <rect x={sx - shopW * 0.2} y={iy + 8} width={shopW * 0.4} height="8" {...stroke} />
              </g>
            );
          })}
        </g>
      );
    }
    case 'car-park': {
      const bays = 8;
      const bayW = iw / bays;
      return (
        <g>
          {Array.from({length: bays + 1}).map((_, i) => (
            <line key={i} x1={ix + i * bayW} y1={iy + 8} x2={ix + i * bayW} y2={iy + ih - 8} stroke="#878787" strokeWidth="0.6" strokeDasharray="3 2" />
          ))}
          {/* Cars in alternate bays */}
          {Array.from({length: bays}).map((_, i) => {
            if (i % 2 === 0) return null;
            const bx = ix + i * bayW + bayW/2;
            return <rect key={i} x={bx - bayW*0.3} y={iy + ih * 0.3} width={bayW * 0.6} height={ih * 0.4} {...stroke} />;
          })}
        </g>
      );
    }
    case 'energy-centre': {
      return (
        <g>
          {/* Heat pump banks */}
          {[-1, 1].map((d, i) => (
            <g key={i}>
              <rect x={ix + 8} y={cy + d * 12 - 8} width={iw - 16} height="16" {...stroke} />
              {Array.from({length: 4}).map((_, j) => (
                <circle key={j} cx={ix + 16 + j * (iw - 32)/3} cy={cy + d * 12} r="3" fill="none" stroke="#5A5E6E" strokeWidth="0.6" />
              ))}
            </g>
          ))}
          {/* Buffer tanks */}
          <circle cx={ix + 10} cy={cy} r="5" {...stroke} />
          <circle cx={ix + iw - 10} cy={cy} r="5" {...stroke} />
        </g>
      );
    }
    case 'roof-terrace': {
      return (
        <g>
          {/* Planters */}
          {[-1, 0, 1].map((d, i) => (
            <g key={i}>
              <rect x={cx + d * 18 - 10} y={iy + 6} width="20" height="6" {...stroke} />
              <circle cx={cx + d * 18} cy={iy + 14} r="4" fill="#E5EDE6" stroke="#5A5E6E" strokeWidth="0.5" />
            </g>
          ))}
          {/* Benches */}
          <rect x={cx - 22} y={cy + 4} width="20" height="5" {...stroke} />
          <rect x={cx + 2} y={cy + 4} width="20" height="5" {...stroke} />
          {/* Fire pit */}
          <circle cx={cx} cy={cy + 16} r="7" fill="#F1ECDD" stroke="#5A5E6E" strokeWidth="0.7" />
          <circle cx={cx} cy={cy + 16} r="3" fill="#E0A13B" />
        </g>
      );
    }

    // ---- Housing zones ----
    case 'living': {
      return (
        <g>
          {/* Rug */}
          <rect x={ix + iw * 0.15} y={iy + ih * 0.2} width={iw * 0.7} height={ih * 0.6} fill="#F2EFE6" stroke="#878787" strokeWidth="0.4" strokeDasharray="2 2" />
          {/* Sofa */}
          <rect x={ix + iw * 0.15} y={iy + ih * 0.15} width={iw * 0.7} height="12" {...stroke} />
          {/* Coffee table */}
          <rect x={cx - 15} y={cy - 4} width="30" height="10" {...stroke} />
          {/* TV unit */}
          <rect x={ix + iw * 0.25} y={iy + ih - 18} width={iw * 0.5} height="5" fill="#0A0F1F" />
          {/* Floor lamp */}
          <circle cx={ix + 12} cy={iy + ih - 10} r="3" fill="#E0A13B" />
        </g>
      );
    }
    case 'eco-kitchen': {
      return (
        <g>
          {/* Back counter */}
          <rect x={ix + 6} y={iy + 6} width={iw - 12} height="10" {...stroke} />
          {/* Hob (4 burners) */}
          {[0, 1].map(b => (
            <circle key={b} cx={ix + 24 + b * 8} cy={iy + 11} r="2" fill="none" stroke="#5A5E6E" strokeWidth="0.6" />
          ))}
          {/* Sink */}
          <rect x={ix + iw - 26} y={iy + 8} width="14" height="6" fill="#E0E4ED" stroke="#5A5E6E" strokeWidth="0.5" />
          {/* Fridge */}
          <rect x={ix + iw - 12} y={iy + 6} width="6" height="14" {...stroke} />
          {/* Island */}
          <rect x={cx - 26} y={cy + 8} width="52" height="14" {...stroke} />
          {/* Pendant lights over island */}
          <circle cx={cx - 12} cy={cy + 4} r="2" fill="#E0A13B" />
          <circle cx={cx + 12} cy={cy + 4} r="2" fill="#E0A13B" />
        </g>
      );
    }
    case 'bedroom': {
      return (
        <g>
          {/* Bed */}
          <rect x={cx - 18} y={iy + ih * 0.2} width="36" height={ih * 0.55} {...stroke} />
          {/* Pillows */}
          <rect x={cx - 16} y={iy + ih * 0.22} width="14" height="6" fill="#E0E4ED" stroke="#5A5E6E" strokeWidth="0.4" />
          <rect x={cx + 2} y={iy + ih * 0.22} width="14" height="6" fill="#E0E4ED" stroke="#5A5E6E" strokeWidth="0.4" />
          {/* Bedside tables */}
          <rect x={cx - 26} y={iy + ih * 0.2 - 2} width="6" height="6" {...stroke} />
          <rect x={cx + 20} y={iy + ih * 0.2 - 2} width="6" height="6" {...stroke} />
          {/* Wardrobe */}
          <rect x={ix + iw * 0.2} y={iy + ih - 14} width={iw * 0.6} height="8" {...stroke} />
        </g>
      );
    }
    case 'bathroom': {
      return (
        <g>
          {/* Bath */}
          <rect x={ix + 8} y={cy - 10} width="32" height="20" fill="#E0E4ED" stroke="#5A5E6E" strokeWidth="0.7" />
          {/* Sink */}
          <rect x={ix + iw - 22} y={iy + 6} width="14" height="8" fill="#E0E4ED" stroke="#5A5E6E" strokeWidth="0.6" />
          {/* Toilet */}
          <rect x={ix + iw - 18} y={iy + ih - 18} width="10" height="12" fill="#FFF" stroke="#5A5E6E" strokeWidth="0.7" />
          {/* Mirror */}
          <line x1={ix + iw - 22} y1={iy + 4} x2={ix + iw - 8} y2={iy + 4} stroke="#0A0F1F" strokeWidth="1.5" />
        </g>
      );
    }
    case 'eco-plant': {
      return (
        <g>
          {/* Heat pump indoor unit */}
          <rect x={ix + 8} y={iy + 8} width="14" height="22" fill="#F1ECDD" stroke="#5A5E6E" strokeWidth="0.7" />
          <rect x={ix + 10} y={iy + 26} width="10" height="2" fill="#3A6FF8" />
          {/* Buffer tank */}
          <circle cx={cx} cy={cy} r="8" {...stroke} />
          {/* Inverter */}
          <rect x={ix + iw - 22} y={iy + 10} width="14" height="20" {...stroke} />
          <rect x={ix + iw - 20} y={iy + 22} width="10" height="2" fill="#3A6FF8" />
          {/* Pipework */}
          <line x1={ix + 22} y1={iy + 14} x2={ix + iw - 22} y2={iy + 14} stroke="#5A5E6E" strokeWidth="1" />
        </g>
      );
    }
    case 'solar-roof': {
      // Grid of small tilted panels
      const cols = Math.max(4, Math.floor(iw / 22));
      const rows = Math.max(2, Math.floor(ih / 18));
      const panelW = (iw - 8) / cols - 2;
      const panelH = (ih - 8) / rows - 2;
      return (
        <g>
          {Array.from({length: rows}).map((_, r) =>
            Array.from({length: cols}).map((_, c) => (
              <g key={`${r}-${c}`}>
                <rect
                  x={ix + 4 + c * ((iw - 8)/cols) + 1}
                  y={iy + 4 + r * ((ih - 8)/rows) + 1}
                  width={panelW} height={panelH}
                  fill="#0F162D" stroke="#3A6FF8" strokeWidth="0.5" />
                {/* Cell line */}
                <line
                  x1={ix + 4 + c * ((iw - 8)/cols) + 1}
                  y1={iy + 4 + r * ((ih - 8)/rows) + 1 + panelH/2}
                  x2={ix + 4 + c * ((iw - 8)/cols) + 1 + panelW}
                  y2={iy + 4 + r * ((ih - 8)/rows) + 1 + panelH/2}
                  stroke="#3A6FF8" strokeWidth="0.3" />
              </g>
            ))
          )}
        </g>
      );
    }
    case 'apartment-row': {
      const apts = 4;
      const aptW = iw / apts;
      return (
        <g>
          {/* Partitions */}
          {Array.from({length: apts - 1}).map((_, i) => (
            <line key={i} x1={ix + (i + 1) * aptW} y1={iy} x2={ix + (i + 1) * aptW} y2={iy + ih} stroke="#0A0F1F" strokeWidth="1.2" />
          ))}
          {/* Per-apartment: bed + small kitchen */}
          {Array.from({length: apts}).map((_, i) => {
            const ax = ix + i * aptW + aptW/2;
            return (
              <g key={i}>
                {/* Bed */}
                <rect x={ax - aptW * 0.25} y={iy + 10} width={aptW * 0.5} height="14" {...stroke} />
                <rect x={ax - aptW * 0.2} y={iy + 12} width={aptW * 0.4} height="4" fill="#E0E4ED" stroke="#5A5E6E" strokeWidth="0.4" />
                {/* Kitchen counter */}
                <rect x={ax - aptW * 0.3} y={iy + ih - 14} width={aptW * 0.6} height="6" {...stroke} />
                {/* Door indicator */}
                <line x1={ax - 4} y1={iy + ih - 2} x2={ax + 4} y2={iy + ih - 2} stroke="#3A6FF8" strokeWidth="1.5" />
              </g>
            );
          })}
        </g>
      );
    }
    case 'communal-lobby': {
      return (
        <g>
          {/* Reception desk */}
          <rect x={ix + 8} y={iy + 8} width={iw * 0.4} height="10" {...stroke} />
          {/* Mailbox wall */}
          <rect x={ix + iw - 18} y={iy + 8} width="10" height={ih - 16} {...stroke} />
          {Array.from({length: 4}).map((_, r) =>
            Array.from({length: 2}).map((_, c) => (
              <rect key={`${r}-${c}`} x={ix + iw - 17 + c * 4} y={iy + 10 + r * 6} width="3" height="4" fill="#FFF" stroke="#5A5E6E" strokeWidth="0.3" />
            ))
          )}
          {/* Sofa */}
          <rect x={ix + 10} y={iy + ih - 18} width={iw * 0.4} height="8" {...stroke} />
          {/* Coffee table */}
          <circle cx={ix + iw * 0.3} cy={iy + ih - 6} r="5" {...stroke} />
        </g>
      );
    }
    case 'communal-room': {
      return (
        <g>
          {/* Long table */}
          <rect x={ix + 8} y={cy - 6} width={iw * 0.6} height="12" {...stroke} />
          {/* Chairs both sides */}
          {Array.from({length: 6}).map((_, i) => {
            const x = ix + 12 + i * (iw * 0.55) / 5;
            return (
              <g key={i}>
                <circle cx={x} cy={cy - 10} r="2.5" fill="#5A5E6E" />
                <circle cx={x} cy={cy + 10} r="2.5" fill="#5A5E6E" />
              </g>
            );
          })}
          {/* Kitchenette */}
          <rect x={ix + iw - 22} y={iy + 8} width="14" height="6" {...stroke} />
        </g>
      );
    }
    case 'utility': {
      return (
        <g>
          {/* Bike racks */}
          {Array.from({length: 8}).map((_, i) => {
            const bx = ix + 10 + i * ((iw - 20) / 7);
            return (
              <g key={i}>
                <line x1={bx} y1={iy + 6} x2={bx} y2={iy + 18} stroke="#5A5E6E" strokeWidth="1" />
                {i % 2 === 0 && <circle cx={bx} cy={iy + 14} r="4" fill="none" stroke="#5A5E6E" strokeWidth="0.6" />}
              </g>
            );
          })}
          {/* Storage cages */}
          {[-1, 0, 1].map((d, i) => (
            <rect key={i} x={cx + d * 24 - 10} y={iy + ih - 18} width="20" height="14" {...stroke} />
          ))}
        </g>
      );
    }

    default:
      return null;
  }
};

// Architectural-style zone label — small uppercase title above each room.
const ZoneLabel = ({ zone, T }) => {
  const tx = T.offX + zone.x * T.scale;
  const ty = T.offY + (zone.z - zone.d/2) * T.scale + 14;
  return (
    <text x={tx} y={ty} textAnchor="middle" style={{
      font:"500 10px 'IBM Plex Sans'", fill:'#0A0F1F',
      letterSpacing:'0.12em', textTransform:'uppercase'
    }}>{zone.name}</text>
  );
};

// Device positions within zones — using the 3D zone bounds.
function positionDevicesInZones(siteDevices, zones, T) {
  const groups = {};
  siteDevices.forEach(d => {
    if (!groups[d.zone]) groups[d.zone] = [];
    groups[d.zone].push(d);
  });
  const out = [];
  Object.entries(groups).forEach(([zoneName, list]) => {
    const zone = zones.find(z => z.name === zoneName);
    if (!zone) return;
    const cols = Math.ceil(Math.sqrt(list.length));
    const rows = Math.ceil(list.length / cols);
    list.forEach((d, i) => {
      const col = i % cols, row = Math.floor(i / cols);
      // Place devices toward the front (positive z) of each zone, evenly spaced
      const xWorld = cols === 1
        ? zone.x
        : zone.x - zone.w/2 + zone.w * 0.2 + col * ((zone.w - zone.w*0.4) / Math.max(1, cols-1));
      const zWorld = rows === 1
        ? zone.z + zone.d * 0.3
        : zone.z - zone.d/2 + zone.d * 0.55 + row * ((zone.d * 0.35) / Math.max(1, rows-1));
      out.push({
        ...d,
        x: T.offX + xWorld * T.scale,
        y: T.offY + zWorld * T.scale,
      });
    });
  });
  return out;
}

const TwinLegend = () => (
  <div style={{ display:'flex', gap:20, alignItems:'center', flexWrap:'wrap' }}>
    {[['ok','Online'],['warn','Degraded'],['down','Offline'],['idle','Idle']].map(([s,l]) => (
      <div key={s} style={{ display:'flex', alignItems:'center', gap:8, fontSize:12, color:'#5A5E6E' }}>
        <StatusDot state={s} />{l}
      </div>
    ))}
  </div>
);

const TwinNode = ({ d, onClick, selected }) => {
  const color = COLORS[d.state] || COLORS.ok;
  const valuePct = Math.max(0, Math.min(1, (d.metric.value - d.metric.min) / (d.metric.max - d.metric.min)));
  return (
    <g transform={`translate(${d.x}, ${d.y})`} onClick={() => onClick(d)} style={{ cursor:'pointer' }}>
      {selected && <rect x={-28} y={-28} width={56} height={56} fill="none" stroke="#3A6FF8" strokeWidth="1" />}
      {/* outer ring (state) */}
      <circle r="18" fill="#FFF" stroke={color} strokeWidth="1.5" />
      {/* fill arc proportional to metric */}
      <circle r="13" fill="none" stroke={color} strokeOpacity="0.18" strokeWidth="6" />
      <circle r="13" fill="none" stroke={color} strokeWidth="6"
              strokeDasharray={`${valuePct * 81.6} 81.6`} transform="rotate(-90)" />
      {/* center dot */}
      <circle r="3" fill={color} />
      {/* label */}
      <text x={0} y={36} textAnchor="middle" style={{ font:"500 10px 'IBM Plex Mono', monospace", fill:'#0A0F1F' }}>{d.id}</text>
      <text x={0} y={48} textAnchor="middle" style={{ font:"400 9px 'IBM Plex Sans'", fill:'#5A5E6E' }}>
        {d.metric.value}{d.metric.unit}
      </text>
    </g>
  );
};

const Twin2D = ({ devices, site, selectedId, onSelect, floor }) => {
  const zones = getSiteZones(site.id, floor);
  const T = buildTransform(zones);

  // Filter devices to current floor + site
  let siteDevices = devices.filter(d => d.site === site.id);
  if (floor != null) siteDevices = siteDevices.filter(d => (d.floor || 1) === floor);
  const positioned = T ? positionDevicesInZones(siteDevices, zones, T) : [];

  return (
    <svg viewBox="0 0 1200 640" preserveAspectRatio="xMidYMid meet" style={{ display:'block', width:'100%', height:'auto', background:'#FAFBFC' }}>
      {/* Architectural grid — fine background */}
      <defs>
        <pattern id="twinGrid2D" width="20" height="20" patternUnits="userSpaceOnUse">
          <path d="M 20 0 L 0 0 0 20" fill="none" stroke="#E6E9EE" strokeWidth="0.3"/>
        </pattern>
        <pattern id="twinGridMaj" width="100" height="100" patternUnits="userSpaceOnUse">
          <path d="M 100 0 L 0 0 0 100" fill="none" stroke="#D8DCE3" strokeWidth="0.4"/>
        </pattern>
      </defs>
      <rect width="1200" height="640" fill="#FAFBFC" />
      <rect width="1200" height="640" fill="url(#twinGrid2D)" />
      <rect width="1200" height="640" fill="url(#twinGridMaj)" />

      {(!T || zones.length === 0) && (
        <text x="600" y="320" textAnchor="middle" style={{ font:"400 16px 'IBM Plex Sans'", fill:'#5A5E6E' }}>
          No zones on this floor.
        </text>
      )}

      {T && (
        <g>
          {/* Building outline — drawn slightly outside the inner walls for a thicker exterior look */}
          {(() => {
            const x = T.offX + (T.minX + T.pad - 1) * T.scale;
            const y = T.offY + (T.minZ + T.pad - 1) * T.scale;
            const w = (T.maxX - T.minX - T.pad * 2 + 2) * T.scale;
            const h = (T.maxZ - T.minZ - T.pad * 2 + 2) * T.scale;
            return <rect x={x} y={y} width={w} height={h} fill="#FFF" stroke="#0A0F1F" strokeWidth="4" />;
          })()}

          {/* Per-zone floor tints */}
          {zones.map((z, i) => <ZoneRoom key={`room-${i}`} zone={z} T={T} />)}

          {/* Furniture / equipment indicators */}
          {zones.map((z, i) => <ZoneFurniture key={`furn-${i}`} zone={z} T={T} />)}

          {/* Zone labels */}
          {zones.map((z, i) => <ZoneLabel key={`label-${i}`} zone={z} T={T} />)}

          {/* North arrow + scale bar (architectural drawing conventions) */}
          <g transform="translate(60, 580)">
            <circle cx="0" cy="0" r="16" fill="#FFF" stroke="#0A0F1F" strokeWidth="1" />
            <path d="M 0 -12 L 5 6 L 0 2 L -5 6 Z" fill="#0A0F1F" />
            <text x="0" y="34" textAnchor="middle" style={{ font:"500 10px 'IBM Plex Sans'", fill:'#0A0F1F', letterSpacing:'0.1em' }}>N</text>
          </g>
        </g>
      )}

      {/* Devices on top */}
      {positioned.map(d => (
        <TwinNode key={d.id} d={d} onClick={onSelect} selected={d.id === selectedId} />
      ))}
    </svg>
  );
};

const TwinInspector = ({ device, onOpen, onCommand }) => {
  if (!device) {
    return (
      <Card pad={32}>
        <Eyebrow>Inspector</Eyebrow>
        <div style={{ fontSize:14, color:'#5A5E6E', marginTop:16 }}>Select a device on the schematic to view live state.</div>
      </Card>
    );
  }
  return (
    <Card pad={0}>
      <div style={{ padding:'24px', borderBottom:'1px solid #EDEDED' }}>
        <Eyebrow accent>{device.id}</Eyebrow>
        <div style={{ fontSize:21, fontWeight:400, color:'#0A0F1F', margin:'6px 0 10px' }}>{device.name}</div>
        <div style={{ display:'flex', alignItems:'center', gap:12, color:'#5A5E6E', fontSize:13 }}>
          <StatusPill state={device.state} />
          <Hairline vertical />
          <span>{device.zone}</span>
        </div>
      </div>
      <div style={{ padding:'24px', borderBottom:'1px solid #EDEDED' }}>
        <Eyebrow>{device.metric.label}</Eyebrow>
        <div style={{ display:'flex', alignItems:'baseline', gap:6, margin:'10px 0 16px', fontVariantNumeric:'tabular-nums' }}>
          <span style={{ fontSize:40, fontWeight:300, letterSpacing:'-0.06em', color:'#0A0F1F' }}>{device.metric.value}</span>
          <span style={{ fontSize:14, color:'#5A5E6E' }}>{device.metric.unit}</span>
        </div>
        <ThresholdBar value={device.metric.value} min={device.metric.min} max={device.metric.max} warn={device.metric.warn} crit={device.metric.crit} unit={device.metric.unit} />
      </div>
      <div style={{ padding:'24px', borderBottom:'1px solid #EDEDED' }}>
        <Eyebrow>Metadata</Eyebrow>
        <div style={{ marginTop:12, display:'grid', gridTemplateColumns:'80px 1fr', gap:'8px 16px', fontSize:13, color:'#0A0F1F' }}>
          <span style={{ color:'#5A5E6E' }}>Kind</span><span style={{ textTransform:'capitalize' }}>{device.kind}</span>
          <span style={{ color:'#5A5E6E' }}>Model</span><span style={{ fontFamily:'IBM Plex Mono, monospace', fontSize:12 }}>{device.model}</span>
          <span style={{ color:'#5A5E6E' }}>Firmware</span><span style={{ fontFamily:'IBM Plex Mono, monospace', fontSize:12 }}>{device.firmware}</span>
          <span style={{ color:'#5A5E6E' }}>Site</span><span style={{ fontFamily:'IBM Plex Mono, monospace', fontSize:12 }}>{device.site}</span>
        </div>
      </div>
      <div style={{ padding:'24px', display:'flex', gap:8 }}>
        <Button kind="ghost" onClick={() => onOpen(device)}>Open detail</Button>
        <Button kind="primary" onClick={() => onCommand(device, 'restart')}>Restart</Button>
      </div>
    </Card>
  );
};

// Sites that support multi-floor layouts — driven by zone.floor in mockData.
const MULTI_FLOOR_SITES = {
  'FORM-LCF-01': [1, 2],
  'FORM-RIV-01': [1, 2],
};

// Group sites by their cohort (Commercial / Eco housing) so the building
// strip reads as a portfolio rather than a flat list. Renders an eyebrow
// per cohort then the building tabs for that cohort.
const BuildingTabs = ({ sites, currentSite, onSiteChange }) => {
  // Stable cohort ordering
  const cohortOrder = ['Commercial','Eco housing'];
  const grouped = {};
  sites.forEach(s => {
    const c = s.cohort || 'Other';
    if (!grouped[c]) grouped[c] = [];
    grouped[c].push(s);
  });
  const cohorts = cohortOrder.filter(c => grouped[c]).concat(Object.keys(grouped).filter(c => !cohortOrder.includes(c)));
  return (
    <div style={{ borderBottom:'1px solid #EDEDED', background:'#FAFBFC' }}>
      <div style={{ display:'flex', alignItems:'stretch', overflowX:'auto', WebkitOverflowScrolling:'touch' }}>
        {cohorts.map((c, ci) => (
          <div key={c} style={{
            display:'flex', alignItems:'stretch',
            borderLeft: ci > 0 ? '1px solid #EDEDED' : 'none'
          }}>
            <div style={{
              padding:'14px 18px 12px',
              fontSize:10, letterSpacing:'0.12em', textTransform:'uppercase',
              color:'#5A5E6E', alignSelf:'center', whiteSpace:'nowrap',
              borderRight:'1px solid #EDEDED', background:'#F2F4F7'
            }}>
              {c}
            </div>
            {grouped[c].map(s => {
              const active = s.id === currentSite.id;
              return (
                <button key={s.id} onClick={() => onSiteChange(s)} style={{
                  flex:'0 0 auto',
                  padding:'14px 16px',
                  background: active ? '#FFF' : 'transparent',
                  color: active ? '#0A0F1F' : '#5A5E6E',
                  border:'none',
                  borderBottom: active ? '2px solid #3A6FF8' : '2px solid transparent',
                  borderRight:'1px solid #EDEDED',
                  cursor:'pointer',
                  font: active ? "500 14px 'IBM Plex Sans'" : "400 14px 'IBM Plex Sans'",
                  whiteSpace:'nowrap'
                }}>
                  {s.name.replace(/^.+? · /, '')}
                </button>
              );
            })}
          </div>
        ))}
      </div>
    </div>
  );
};

const Twin = ({ devices, site, sites, onSite, onOpenDevice, onCommand }) => {
  const [selected, setSelected] = React.useState(null);
  const [view, setView] = React.useState('3D');
  const floors = MULTI_FLOOR_SITES[site.id];
  const [floor, setFloor] = React.useState(floors ? floors[0] : null);

  // Reset floor when switching sites
  React.useEffect(() => {
    setFloor(MULTI_FLOOR_SITES[site.id] ? MULTI_FLOOR_SITES[site.id][0] : null);
    setSelected(null);
  }, [site.id]);

  const selectedDevice = selected ? devices.find(d => d.id === selected) : null;
  const onSelectFromCanvas = (d) => setSelected(d.id);

  // Form Property building tabs — show when current site has customer 'Form Property'
  const formSites = site.customer === 'Form Property'
    ? sites.filter(s => s.customer === 'Form Property')
    : null;

  return (
    <div data-pad-md data-collapse-md style={{ padding:32, background:'#F6F7F9', display:'grid', gridTemplateColumns:'1fr 360px', gap:24, alignItems:'start' }}>
      <div style={{ background:'#FFF', border:'1px solid #EDEDED', position:'relative' }}>
        {/* Building tabs (Form Property only) */}
        {formSites && formSites.length > 1 && (
          <BuildingTabs sites={formSites} currentSite={site} onSiteChange={onSite} />
        )}

        {/* Header */}
        <div style={{ padding:'18px 24px', borderBottom:'1px solid #EDEDED', display:'flex', justifyContent:'space-between', alignItems:'center', flexWrap:'wrap', gap:12 }}>
          <div>
            <Eyebrow accent>{site.customer ? `${site.customer} · ${site.id}` : site.id}</Eyebrow>
            <div style={{ fontSize:21, fontWeight:400, color:'#0A0F1F', marginTop:6 }}>{site.name}</div>
          </div>
          <div style={{ display:'flex', alignItems:'center', gap:16, flexWrap:'wrap' }}>
            {floors && (
              <SegmentedControl value={`Floor ${floor}`} onChange={v => setFloor(Number(v.split(' ')[1]))} options={floors.map(f => `Floor ${f}`)} />
            )}
            {view === '2D' && <TwinLegend />}
            <SegmentedControl value={view} onChange={setView} options={['2D','3D']} />
          </div>
        </div>

        {view === '2D' && (
          <Twin2D devices={devices} site={site} selectedId={selected} onSelect={onSelectFromCanvas} floor={floor} />
        )}
        {view === '3D' && (
          <Twin3D devices={devices} site={site} selectedId={selected} onSelect={onSelectFromCanvas} floor={floor} />
        )}
      </div>
      <TwinInspector device={selectedDevice} onOpen={onOpenDevice} onCommand={onCommand} />
    </div>
  );
};

Object.assign(window, { Twin });
