// Pick & Place — TOP-DOWN VIEW
// Single perspective: looking straight down on the machine.
// Gantry rails appear as horizontal bars; head is a square carriage.
// The nozzle, viewed from above, is a circle (down the barrel) with crosshairs.
// Component "lifting" is shown by a soft shadow offset + slight scale, not perspective.

// Marigold accent — reads warmly on the ivory blueprint
const ORANGE = '#D9821F';
const ORANGE_DIM = '#B5701F';
const WHITE = '#FFFFFF';
// Component inks — indigo family, read on the indigo board
const INK_900 = '#161C32';
const INK_800 = '#1C2440';
const INK_700 = '#25304F';
const INK_600 = '#313D5E';
const INK_500 = '#455178';
const INK_400 = '#5E6A90';
const INK_300 = '#8B93B2';
const INK_200 = '#C2C8DD';
// Machine surfaces — light paper (fills) → deep indigo (lines)
const M_BG = '#F4EFE4';
const M_1  = '#EBE3D2';
const M_2  = '#E2D9C6';
const M_3  = '#D5CAB2';
const M_4  = '#5E6A90';
const M_5  = '#313C5E';
// Text — indigo inks on ivory
const T_PRI = '#1E2A4A';
const T_SEC = '#5E6A90';
const T_TER = '#98A0BC';

const W = 1920, H = 1080;

// MACHINE BED (the work area visible from above) — fills most of the canvas
const BED = { x: 120, y: 120, w: 1680, h: 840 };

// PCB — center-left of bed
const PCB = { x: 280, y: 280, w: 820, h: 540 };

// FEEDER — right side; the reel itself is a circle, tape extends toward pick point
const FEEDER = {
  reel: { cx: 1560, cy: 720, r: 130 },
  tape: { x: 1380, y: 220, w: 36, h: 520 }, // vertical tape going up to pick point
  pick: { x: 1398, y: 250 } // where the head grabs from the tape
};

// Component slots on PCB (pcb-local coords)
const SLOTS = [
  { x: 80,  y: 90,  w: 160, h: 80,  type: 'ic',    label: 'U1' },
  { x: 280, y: 90,  w: 70,  h: 35,  type: 'res',   label: 'R1' },
  { x: 380, y: 90,  w: 70,  h: 35,  type: 'res',   label: 'R2' },
  { x: 490, y: 90,  w: 100, h: 100, type: 'cap',   label: 'C1' },
  { x: 80,  y: 240, w: 130, h: 130, type: 'qfn',   label: 'U2' },
  { x: 260, y: 260, w: 80,  h: 80,  type: 'led',   label: 'D1' },
  { x: 370, y: 260, w: 80,  h: 80,  type: 'led',   label: 'D2' },
  { x: 490, y: 240, w: 70,  h: 35,  type: 'res',   label: 'R3' },
  { x: 600, y: 240, w: 70,  h: 35,  type: 'res',   label: 'R4' },
  { x: 80,  y: 420, w: 100, h: 50,  type: 'cap',   label: 'C2' },
  { x: 220, y: 420, w: 100, h: 50,  type: 'cap',   label: 'C3' },
  { x: 360, y: 420, w: 240, h: 60,  type: 'conn',  label: 'J1' },
];

// t0 = when nozzle starts descending at PICK point.
// Each cycle needs: prev place-up (~0.18) + travel-to-pick (~0.7) gap before the next t0.
// Spacing minimum ~2.0s per step to avoid teleporting.
const SEQUENCE = [
  { slot: 0,  t0: 1.4  },
  { slot: 4,  t0: 4.0  },
  { slot: 11, t0: 6.6  },
  { slot: 3,  t0: 9.2  },
  { slot: 5,  t0: 11.4 },
  { slot: 6,  t0: 13.4 },
  { slot: 1,  t0: 15.4 },
  { slot: 2,  t0: 17.4 },
  { slot: 7,  t0: 19.4 },
  { slot: 8,  t0: 21.4 },
  { slot: 9,  t0: 23.4 },
  { slot: 10, t0: 25.4 },
];

// ─────────────────────────────────────────────────────────────────────
// Component (top-down flat shape)
// ─────────────────────────────────────────────────────────────────────
function CompTop({ type, w, h, label }) {
  if (type === 'res') {
    return (
      <g>
        <rect x={0} y={0} width={w} height={h} fill="#1C2440" stroke={INK_500} strokeWidth={0.8}/>
        <rect x={w*0.18} y={0} width={w*0.64} height={h} fill="#161C32"/>
        <rect x={0} y={0} width={w*0.18} height={h} fill="#C9874B"/>
        <rect x={w*0.82} y={0} width={w*0.18} height={h} fill="#C9874B"/>
        <text x={w/2} y={h/2 + 3} textAnchor="middle"
              fontFamily="JetBrains Mono" fontSize={Math.min(h*0.5, 10)} fill={INK_300}>10K</text>
      </g>
    );
  }
  if (type === 'cap') {
    return (
      <g>
        <rect x={0} y={0} width={w} height={h} rx={3} fill="#25304F" stroke={INK_500} strokeWidth={0.8}/>
        <rect x={3} y={3} width={w-6} height={h-6} rx={2} fill="#161C32"/>
        <rect x={w*0.05} y={h*0.15} width={w*0.04} height={h*0.7} fill={WHITE} opacity={0.4}/>
      </g>
    );
  }
  if (type === 'led') {
    return (
      <g>
        <rect x={0} y={0} width={w} height={h} rx={4} fill={INK_900} stroke={ORANGE} strokeWidth={1.2}/>
        <rect x={w*0.18} y={h*0.18} width={w*0.64} height={h*0.64} rx={2} fill={ORANGE}/>
        <rect x={w*0.28} y={h*0.28} width={w*0.44} height={h*0.44} rx={1} fill="#FFB088" opacity={0.85}/>
      </g>
    );
  }
  if (type === 'ic') {
    return (
      <g>
        <rect x={0} y={0} width={w} height={h} fill={INK_900} stroke={INK_500} strokeWidth={0.8}/>
        {Array.from({length: 10}).map((_,i) => (
          <g key={i}>
            <rect x={w*0.06 + i*(w*0.88/10)} y={-4} width={w*0.06} height={4} fill={INK_300}/>
            <rect x={w*0.06 + i*(w*0.88/10)} y={h} width={w*0.06} height={4} fill={INK_300}/>
          </g>
        ))}
        <circle cx={w*0.13} cy={h*0.22} r={2} fill={INK_300}/>
        <text x={w/2} y={h/2 + 4} textAnchor="middle"
              fontFamily="JetBrains Mono" fontWeight={600} fontSize={Math.min(h*0.32, 14)}
              fill={INK_200}>{label}</text>
      </g>
    );
  }
  if (type === 'qfn') {
    return (
      <g>
        <rect x={0} y={0} width={w} height={h} fill={INK_900} stroke={INK_500} strokeWidth={1}/>
        {Array.from({length: 7}).map((_,i) => {
          const step = w*0.84/7;
          const o = w*0.08 + i*step + step*0.18;
          const sz = step*0.65;
          return (
            <g key={i}>
              <rect x={o} y={-3} width={sz} height={3} fill={INK_300}/>
              <rect x={o} y={h} width={sz} height={3} fill={INK_300}/>
              <rect x={-3} y={o} width={3} height={sz} fill={INK_300}/>
              <rect x={w} y={o} width={3} height={sz} fill={INK_300}/>
            </g>
          );
        })}
        <circle cx={w*0.16} cy={h*0.18} r={2.5} fill={ORANGE}/>
        <text x={w/2} y={h/2 + 5} textAnchor="middle"
              fontFamily="JetBrains Mono" fontWeight={700} fontSize={Math.min(h*0.22, 16)}
              fill={INK_200}>{label}</text>
      </g>
    );
  }
  if (type === 'conn') {
    return (
      <g>
        <rect x={0} y={0} width={w} height={h} fill="#1C2440" stroke={INK_400} strokeWidth={1}/>
        {Array.from({length: 12}).map((_,i) => (
          <rect key={i} x={w*0.04 + i*(w*0.92/12) + 2} y={h*0.22}
                width={w*0.92/12 - 4} height={h*0.56} fill={INK_700} stroke={INK_500} strokeWidth={0.5}/>
        ))}
      </g>
    );
  }
  return <rect x={0} y={0} width={w} height={h} fill={INK_900}/>;
}

// Solder pad outline
function SolderPad({ slot, highlight }) {
  return (
    <rect
      x={slot.x - 5} y={slot.y - 5}
      width={slot.w + 10} height={slot.h + 10}
      fill="none"
      stroke={highlight ? ORANGE : "#10140d"}
      strokeWidth={highlight ? 1.5 : 1.2}
      strokeDasharray="4 3"
      opacity={highlight ? 1 : 0.9}
    />
  );
}

// ─────────────────────────────────────────────────────────────────────
// PCB (top view) — flat rectangle with copper traces visible
// ─────────────────────────────────────────────────────────────────────
function PCB_Board({ placedSet, activeSlot, placeFlash, targetSlot }) {
  return (
    <g transform={`translate(${PCB.x}, ${PCB.y})`}>
      <rect x={0} y={0} width={PCB.w} height={PCB.h} fill="#26432f"
            stroke="#42664d" strokeWidth={3}/>
      <rect x={0} y={0} width={PCB.w} height={PCB.h} fill="url(#pcbTexture)"/>

      {/* mounting holes */}
      {[[24,24],[PCB.w-24,24],[24,PCB.h-24],[PCB.w-24,PCB.h-24]].map(([cx,cy],i) => (
        <g key={i}>
          <circle cx={cx} cy={cy} r={10} fill="#15281b"/>
          <circle cx={cx} cy={cy} r={6} fill="#26432f"/>
          <circle cx={cx} cy={cy} r={6} fill="none" stroke="#4d7059" strokeWidth={1}/>
        </g>
      ))}

      {/* copper traces */}
      <g stroke="#C9874B" fill="none" opacity={0.55}>
        <path d="M 160,170 L 280,170 L 280,260" strokeWidth={2}/>
        <path d="M 280,170 L 380,170" strokeWidth={1.5}/>
        <path d="M 380,170 L 490,170 L 490,200" strokeWidth={2}/>
        <path d="M 240,90 L 240,50 L 590,50 L 590,140" strokeWidth={3}/>
        <path d="M 130,370 L 130,420" strokeWidth={2}/>
        <path d="M 145,330 L 260,330 L 260,400" strokeWidth={1.5}/>
        <path d="M 340,300 L 370,300" strokeWidth={1.5}/>
        <path d="M 450,300 L 490,300" strokeWidth={1.5}/>
        <path d="M 600,300 L 670,300 L 670,420 L 360,420" strokeWidth={2.5}/>
        <path d="M 720,90 L 760,90 L 760,260" strokeWidth={3}/>
        <path d="M 760,330 L 760,490" strokeWidth={2.5}/>
      </g>

      {/* drill grid */}
      <g fill="#46694f" opacity={0.7}>
        {Array.from({length: 14}).map((_,i) =>
          Array.from({length: 9}).map((_,j) => (
            <circle key={`${i}-${j}`} cx={50 + i*55} cy={40 + j*60} r={0.8}/>
          ))
        )}
      </g>

      {/* solder pads (unplaced) */}
      {SLOTS.map((s, i) => !placedSet.has(i) ? (
        <SolderPad key={`pad-${i}`} slot={s} highlight={i === targetSlot}/>
      ) : null)}

      {/* placed components */}
      {SLOTS.map((s, i) => {
        if (!placedSet.has(i)) return null;
        const flash = i === activeSlot ? placeFlash : 0;
        return (
          <g key={`pl-${i}`} transform={`translate(${s.x}, ${s.y})`}>
            <CompTop type={s.type} w={s.w} h={s.h} label={s.label}/>
            {flash > 0 && (
              <rect x={-7} y={-7} width={s.w+14} height={s.h+14}
                    fill="none" stroke={ORANGE} strokeWidth={2.5} opacity={flash}/>
            )}
          </g>
        );
      })}

      {/* silkscreen */}
      <text x={PCB.w - 16} y={PCB.h - 14} textAnchor="end"
            fontFamily="JetBrains Mono" fontSize={13} fill="#8B93B2" letterSpacing="0.12em">
        APW-PCB-001  REV.A
      </text>
      <text x={16} y={PCB.h - 14}
            fontFamily="Archivo" fontWeight={900} fontStyle="italic"
            fontSize={20} fill="#C9874B" letterSpacing="-0.02em">
        APWAY
      </text>
    </g>
  );
}

// ─────────────────────────────────────────────────────────────────────
// Feeder (top view) — circular reel + vertical tape with components
// ─────────────────────────────────────────────────────────────────────
function FeederTop({ time, tapeOffset }) {
  const r = FEEDER.reel;
  const t = FEEDER.tape;
  const rotation = -time * 22;

  return (
    <g>
      {/* feeder housing */}
      <rect x={t.x - 30} y={t.y - 30} width={t.w + 60} height={t.h + 60 + 200}
            fill={M_2} stroke={M_4} strokeWidth={1.5} rx={4}/>

      {/* tape (vertical) */}
      <rect x={t.x} y={t.y} width={t.w} height={t.h} fill={M_3} stroke={M_5} strokeWidth={1}/>
      {/* tape sprocket holes */}
      {Array.from({length: 18}).map((_,i) => (
        <circle key={`sl-${i}`} cx={t.x + 5} cy={t.y + 12 + i*30 + (tapeOffset%30)} r={1.8} fill={INK_900}/>
      ))}
      {Array.from({length: 18}).map((_,i) => (
        <circle key={`sr-${i}`} cx={t.x + t.w - 5} cy={t.y + 12 + i*30 + (tapeOffset%30)} r={1.8} fill={INK_900}/>
      ))}
      {/* component pockets visible on tape — small dark squares */}
      {Array.from({length: 16}).map((_,i) => {
        const py = t.y + 28 + i*30 + (tapeOffset%30);
        if (py < t.y + 4 || py > t.y + t.h - 16) return null;
        return (
          <rect key={`pk-${i}`} x={t.x + t.w/2 - 8} y={py} width={16} height={16}
                fill="#1E2A4A" stroke={INK_600} strokeWidth={0.5}/>
        );
      })}

      {/* reel */}
      <g transform={`translate(${r.cx}, ${r.cy}) rotate(${rotation})`}>
        <circle r={r.r} fill={M_2} stroke={M_5} strokeWidth={2}/>
        <circle r={r.r - 8} fill="none" stroke={M_3} strokeWidth={1}/>
        {/* spokes */}
        {Array.from({length: 8}).map((_,i) => (
          <line key={i} x1={0} y1={0}
                x2={(r.r - 12) * Math.cos(i * Math.PI / 4)}
                y2={(r.r - 12) * Math.sin(i * Math.PI / 4)}
                stroke={M_4} strokeWidth={2}/>
        ))}
        {/* outer perforations */}
        {Array.from({length: 32}).map((_,i) => (
          <circle key={i}
                  cx={(r.r - 5) * Math.cos(i * Math.PI / 16)}
                  cy={(r.r - 5) * Math.sin(i * Math.PI / 16)}
                  r={2.5} fill={M_3}/>
        ))}
        <circle r={32} fill={M_3} stroke={M_5} strokeWidth={1.5}/>
        <circle r={6} fill={ORANGE}/>
      </g>

      {/* feeder label (top) */}
      <g transform={`translate(${t.x - 20}, ${t.y - 8})`}>
        <text fontFamily="JetBrains Mono" fontWeight={700}
              fontSize={15} fill={T_PRI} letterSpacing="0.12em">
          FEEDER · 08
        </text>
      </g>
      <g transform={`translate(${t.x + t.w + 8}, ${t.y + 4})`}>
        <text fontFamily="JetBrains Mono" fontSize={11} fill={ORANGE} letterSpacing="0.18em">
          ● LIVE
        </text>
      </g>

      {/* pickup point indicator */}
      <g transform={`translate(${FEEDER.pick.x}, ${FEEDER.pick.y})`}>
        <rect x={-14} y={-14} width={28} height={28}
              fill="none" stroke={ORANGE} strokeWidth={1} strokeDasharray="2 2" opacity={0.5}/>
      </g>
    </g>
  );
}

// ─────────────────────────────────────────────────────────────────────
// Gantry (top view)
// X-rail = horizontal bar across top of bed
// Y-rail = vertical bar perpendicular, sliding along X-rail
// Head/carriage = square that slides along Y-rail
// Nozzle = circle with crosshair, looking down the barrel
// "lift" of nozzle from work surface = scale of nozzle circle (bigger when lifted, smaller when down)
// ─────────────────────────────────────────────────────────────────────
function Gantry({ headX, headY, nozzleZ, holding, time, showLaser, gantryColor }) {
  const railW = BED.w;
  const xRailY = BED.y; // top of bed - X rail spans bed top
  const xRailH = 60;

  // Y-rail is a vertical bar that follows headX, spans bed height
  const yRailW = 60;

  // Head sits at intersection of Y-rail and current Y position
  // nozzleZ: 0 = up (high), 1 = down (touching surface)
  // Visually: when up, the nozzle circle has a wider outer ring (perspective of barrel further)
  //           when down, it shrinks to the actual contact circle.
  const nozzleOuterR = 28 - nozzleZ * 8;  // outer ring (barrel) shrinks as we descend
  const nozzleInnerR = 10 + nozzleZ * 6;  // contact tip grows as we touch
  const nozzleBlur = (1 - nozzleZ) * 0.4; // when up, slight motion blur halo

  return (
    <g>
      {/* X-rail (horizontal, at top of bed) */}
      <g>
        <rect x={BED.x} y={xRailY - xRailH/2} width={railW} height={xRailH}
              fill={gantryColor} stroke={M_5} strokeWidth={1}/>
        <rect x={BED.x} y={xRailY - xRailH/2} width={railW} height={6} fill={M_5}/>
        <rect x={BED.x} y={xRailY + xRailH/2 - 6} width={railW} height={6} fill={M_5}/>
        {/* tick marks */}
        {Array.from({length: Math.floor(railW/40) + 1}).map((_,i) => (
          <line key={i} x1={BED.x + i*40} y1={xRailY - 4} x2={BED.x + i*40} y2={xRailY + 4}
                stroke={M_5} strokeWidth={0.6}/>
        ))}
        {/* end caps */}
        <rect x={BED.x - 30} y={xRailY - xRailH/2 - 8} width={36} height={xRailH + 16}
              fill={M_3} stroke={M_5} strokeWidth={1}/>
        <rect x={BED.x + railW - 6} y={xRailY - xRailH/2 - 8} width={36} height={xRailH + 16}
              fill={M_3} stroke={M_5} strokeWidth={1}/>
      </g>

      {/* Y-rail — perpendicular bar that rides on X-rail at headX */}
      <g transform={`translate(${headX}, 0)`}>
        {/* X-carriage block (where Y-rail meets X-rail) */}
        <rect x={-50} y={xRailY - xRailH/2 - 12} width={100} height={xRailH + 24}
              fill={M_2} stroke={M_5} strokeWidth={1.5} rx={3}/>
        <rect x={-50} y={xRailY - xRailH/2 - 12} width={100} height={6} fill={M_4}/>
        {/* ID stripe */}
        <rect x={-46} y={xRailY - 4} width={92} height={8} fill={ORANGE} opacity={0.85}/>

        {/* Y-rail bar going down across bed */}
        <rect x={-yRailW/2} y={xRailY + xRailH/2}
              width={yRailW} height={BED.y + BED.h - (xRailY + xRailH/2)}
              fill={gantryColor} stroke={M_5} strokeWidth={1}/>
        <rect x={-yRailW/2} y={xRailY + xRailH/2}
              width={6} height={BED.y + BED.h - (xRailY + xRailH/2)}
              fill={M_5}/>
        <rect x={yRailW/2 - 6} y={xRailY + xRailH/2}
              width={6} height={BED.y + BED.h - (xRailY + xRailH/2)}
              fill={M_5}/>
        {/* Y-rail tick marks down its center */}
        {Array.from({length: Math.floor((BED.h)/40)}).map((_,i) => (
          <line key={i} x1={-4} y1={xRailY + xRailH/2 + i*40 + 20}
                x2={4} y2={xRailY + xRailH/2 + i*40 + 20}
                stroke={M_5} strokeWidth={0.5}/>
        ))}

        {/* HEAD CARRIAGE — slides along Y-rail at headY */}
        <g transform={`translate(0, ${headY})`}>
          {/* carriage outer body */}
          <rect x={-70} y={-50} width={140} height={100}
                fill={M_2} stroke={M_5} strokeWidth={1.5} rx={4}/>
          {/* top stripe */}
          <rect x={-70} y={-50} width={140} height={8} fill={M_4}/>
          {/* status lamps */}
          <circle cx={-56} cy={-38} r={4} fill={ORANGE}
                  opacity={0.7 + 0.3*Math.sin(time*8)}/>
          <circle cx={-44} cy={-38} r={4} fill={M_5}/>
          <circle cx={-32} cy={-38} r={4} fill={M_5}/>
          {/* head label */}
          <text x={-20} y={-32}
                fontFamily="JetBrains Mono" fontWeight={700}
                fontSize={11} fill={T_PRI} letterSpacing="0.1em">
            HEAD·01
          </text>
          {/* corner bolts */}
          {[[-62,-42],[62,-42],[-62,42],[62,42]].map(([cx,cy],i) => (
            <g key={i}>
              <circle cx={cx} cy={cy} r={3.5} fill={M_3} stroke={M_5} strokeWidth={0.8}/>
              <line x1={cx-2.2} y1={cy} x2={cx+2.2} y2={cy} stroke={M_5} strokeWidth={0.6}/>
            </g>
          ))}
          {/* mount plate around nozzle */}
          <rect x={-36} y={-5} width={72} height={45}
                fill={M_3} stroke={M_5} strokeWidth={1} rx={2}/>

          {/* NOZZLE (top view — concentric circles) */}
          <g>
            {/* outer barrel ring */}
            <circle cx={0} cy={20} r={nozzleOuterR}
                    fill={M_2} stroke={M_5} strokeWidth={1.5}/>
            {/* mid ring */}
            <circle cx={0} cy={20} r={nozzleOuterR - 6}
                    fill="none" stroke={M_4} strokeWidth={0.8}/>
            {/* held component (tiny, centered on nozzle, slightly visible from above) */}
            {holding && (
              <g transform={`translate(${-holding.w*0.25/2}, ${20 - holding.h*0.25/2})`}>
                <g transform={`scale(0.25)`}>
                  <CompTop type={holding.type} w={holding.w} h={holding.h} label={holding.label}/>
                </g>
              </g>
            )}
            {/* inner contact tip */}
            <circle cx={0} cy={20} r={nozzleInnerR}
                    fill={nozzleZ > 0.4 ? ORANGE : M_3}
                    stroke={ORANGE} strokeWidth={1}
                    opacity={nozzleZ > 0.4 ? 0.85 : 1}/>
            {/* center pinhole */}
            <circle cx={0} cy={20} r={2} fill={INK_900}/>
            {/* crosshair */}
            <line x1={-nozzleOuterR-4} y1={20} x2={-nozzleOuterR+nozzleInnerR+1} y2={20}
                  stroke={ORANGE} strokeWidth={0.6} opacity={0.7}/>
            <line x1={nozzleOuterR-nozzleInnerR-1} y1={20} x2={nozzleOuterR+4} y2={20}
                  stroke={ORANGE} strokeWidth={0.6} opacity={0.7}/>
            <line x1={0} y1={20-nozzleOuterR-4} x2={0} y2={20-nozzleInnerR-1}
                  stroke={ORANGE} strokeWidth={0.6} opacity={0.7}/>
            <line x1={0} y1={20+nozzleInnerR+1} x2={0} y2={20+nozzleOuterR+4}
                  stroke={ORANGE} strokeWidth={0.6} opacity={0.7}/>

            {/* halo when lifted (suggests barrel sticking up) */}
            {nozzleBlur > 0 && (
              <circle cx={0} cy={20} r={nozzleOuterR + 4}
                      fill="none" stroke={M_5}
                      strokeWidth={1} opacity={nozzleBlur * 0.6}/>
            )}
          </g>
        </g>
      </g>
    </g>
  );
}

// ─────────────────────────────────────────────────────────────────────
// Component shadow (drops on bed when component is held - shows it's lifted)
// ─────────────────────────────────────────────────────────────────────
function HeldShadow({ headX, headY, holding, nozzleZ }) {
  if (!holding || nozzleZ > 0.5) return null;
  // shadow offset proportional to lift (1 - nozzleZ): higher = bigger offset
  const lift = 1 - nozzleZ;
  const offset = lift * 6;
  return (
    <g transform={`translate(${headX + offset}, ${headY + 20 + offset})`}>
      <ellipse cx={0} cy={0} rx={20 + lift*4} ry={10 + lift*2}
               fill="#2C3656" opacity={0.28 - lift*0.08}/>
    </g>
  );
}

// ─────────────────────────────────────────────────────────────────────
// Telemetry HUD (top view — overlay corners)
// ─────────────────────────────────────────────────────────────────────
function Telemetry({ time, placedCount, total, headX, headY, currentLabel, phase }) {
  return (
    <g>
      {/* Top-left brand */}
      <g transform="translate(48, 56)">
        <text fontFamily="Rozha One" fontWeight={400}
              fontSize={46} fill={T_PRI} letterSpacing="0.01em">
          PICK · PLACE
        </text>
        <text y={28} fontFamily="JetBrains Mono" fontSize={12}
              fill={T_SEC} letterSpacing="0.18em">
          AP-PNP · TOP VIEW · LINE 04
        </text>
      </g>

      {/* Top-right coords */}
      <g transform={`translate(${W - 48}, 56)`}>
        <text textAnchor="end" fontFamily="JetBrains Mono" fontSize={11}
              fill={T_SEC} letterSpacing="0.18em">
          GANTRY POSITION
        </text>
        <text textAnchor="end" y={26} fontFamily="JetBrains Mono"
              fontWeight={500} fontSize={20} fill={T_PRI}>
          X {String(Math.round(headX)).padStart(4,'0')}
        </text>
        <text textAnchor="end" y={50} fontFamily="JetBrains Mono"
              fontWeight={500} fontSize={20} fill={T_PRI}>
          Y {String(Math.round(headY)).padStart(4,'0')}
        </text>
        <text textAnchor="end" y={72} fontFamily="JetBrains Mono"
              fontSize={11} fill={ORANGE} letterSpacing="0.18em">
          ● {phase}
        </text>
      </g>

      {/* Bottom-left stats */}
      <g transform={`translate(48, ${H - 110})`}>
        <line x1={0} y1={-16} x2={620} y2={-16} stroke={M_4} strokeWidth={1}/>
        <g>
          <text fontFamily="JetBrains Mono" fontSize={11} fill={T_SEC} letterSpacing="0.18em">
            PLACED
          </text>
          <text y={42} fontFamily="JetBrains Mono" fontWeight={700} fontSize={42} fill={T_PRI}>
            {String(placedCount).padStart(2,'0')}
            <tspan fill={T_TER} fontSize={26}>/{String(total).padStart(2,'0')}</tspan>
          </text>
        </g>
        <g transform="translate(190, 0)">
          <text fontFamily="JetBrains Mono" fontSize={11} fill={T_SEC} letterSpacing="0.18em">
            T+
          </text>
          <text y={42} fontFamily="JetBrains Mono" fontWeight={700} fontSize={42} fill={T_PRI}>
            {time.toFixed(1)}<tspan fontSize={20} fill={T_TER}>s</tspan>
          </text>
        </g>
        <g transform="translate(360, 0)">
          <text fontFamily="JetBrains Mono" fontSize={11} fill={T_SEC} letterSpacing="0.18em">
            CURRENT
          </text>
          <text y={42} fontFamily="JetBrains Mono" fontWeight={700}
                fontSize={40} fill={ORANGE} letterSpacing="0.01em">
            {currentLabel || '—'}
          </text>
        </g>
      </g>

      {/* Bottom-right logo */}
      <g transform={`translate(${W - 48}, ${H - 68})`}>
        <text textAnchor="end" fontFamily="Archivo" fontWeight={900} fontStyle="italic"
              fontSize={34} fill={T_PRI} letterSpacing="-0.02em">
          AP<tspan fill={ORANGE}>WAY</tspan>
        </text>
        <text textAnchor="end" y={24} fontFamily="JetBrains Mono"
              fontSize={11} fill={T_TER} letterSpacing="0.18em">
          APWAY · ASSEMBLY
        </text>
      </g>
    </g>
  );
}

// ─────────────────────────────────────────────────────────────────────
// MAIN SCENE — computes head state + renders
// ─────────────────────────────────────────────────────────────────────
function PickPlaceScene({ tweaks }) {
  const rawTime = useTime();
  const time = rawTime * tweaks.speed;

  const HEAD_PARK = { x: 960, y: 540 };

  let headX = HEAD_PARK.x;
  let headY = HEAD_PARK.y;
  let nozzleZ = 0;
  let holding = null;
  let activeSlot = null;
  let placeFlash = 0;
  let targetSlot = null;
  let phase = 'IDLE';
  const placedSet = new Set();
  let currentLabel = '';

  if (time < 0.6) {
    const t = Easing.easeOutCubic(clamp(time / 0.6, 0, 1));
    headX = -200 + (HEAD_PARK.x + 200) * t;
    headY = HEAD_PARK.y;
    phase = 'INIT';
  }

  for (let i = 0; i < SEQUENCE.length; i++) {
    const step = SEQUENCE[i];
    const slot = SLOTS[step.slot];
    const slotWorld = {
      x: PCB.x + slot.x + slot.w/2,
      y: PCB.y + slot.y + slot.h/2,
    };

    const T_PICK_DOWN = 0.18;
    const T_PICK_HOLD = 0.10;
    const T_PICK_UP   = 0.18;
    const T_TRAVEL_TO_PLACE = 0.55;
    const T_PLACE_DOWN = 0.20;
    const T_PLACE_HOLD = 0.12;
    const T_PLACE_UP   = 0.18;

    const tPickStart = step.t0;
    const tPickDownEnd = tPickStart + T_PICK_DOWN;
    const tPickHoldEnd = tPickDownEnd + T_PICK_HOLD;
    const tPickUpEnd = tPickHoldEnd + T_PICK_UP;
    const tTravelEnd = tPickUpEnd + T_TRAVEL_TO_PLACE;
    const tPlaceDownEnd = tTravelEnd + T_PLACE_DOWN;
    const tPlaceHoldEnd = tPlaceDownEnd + T_PLACE_HOLD;
    const tPlaceUpEnd = tPlaceHoldEnd + T_PLACE_UP;
    // travel-to-pick starts exactly when previous place-up ends (or at 0 for first step)
    let tTravelToPickStart;
    if (i === 0) {
      tTravelToPickStart = Math.max(0, tPickStart - 0.8);
    } else {
      const prev = SEQUENCE[i-1];
      tTravelToPickStart = prev.t0 + T_PICK_DOWN + T_PICK_HOLD + T_PICK_UP +
                            T_TRAVEL_TO_PLACE + T_PLACE_DOWN + T_PLACE_HOLD + T_PLACE_UP;
    }
    const T_TRAVEL_TO_PICK = tPickStart - tTravelToPickStart;

    // mark earlier as placed
    for (let j = 0; j < i; j++) {
      const prev = SEQUENCE[j];
      const prevPlaceUpEnd = prev.t0 + T_PICK_DOWN + T_PICK_HOLD + T_PICK_UP +
                              T_TRAVEL_TO_PLACE + T_PLACE_DOWN + T_PLACE_HOLD;
      if (time >= prevPlaceUpEnd - 0.05) placedSet.add(prev.slot);
    }

    if (time >= tTravelToPickStart && time < tPickStart) {
      const t = Easing.easeInOutCubic(clamp((time - tTravelToPickStart) / T_TRAVEL_TO_PICK, 0, 1));
      let startX = HEAD_PARK.x, startY = HEAD_PARK.y;
      if (i > 0) {
        const prev = SLOTS[SEQUENCE[i-1].slot];
        startX = PCB.x + prev.x + prev.w/2;
        startY = PCB.y + prev.y + prev.h/2;
      }
      headX = startX + (FEEDER.pick.x - startX) * t;
      headY = startY + (FEEDER.pick.y - startY) * t;
      nozzleZ = 0;
      currentLabel = slot.label;
      targetSlot = step.slot;
      phase = 'TRAVEL → PICK';
      break;
    } else if (time >= tPickStart && time < tPickDownEnd) {
      const t = Easing.easeOutCubic(clamp((time - tPickStart) / T_PICK_DOWN, 0, 1));
      headX = FEEDER.pick.x; headY = FEEDER.pick.y;
      nozzleZ = t;
      currentLabel = slot.label;
      targetSlot = step.slot;
      phase = 'PICKING';
      break;
    } else if (time >= tPickDownEnd && time < tPickHoldEnd) {
      headX = FEEDER.pick.x; headY = FEEDER.pick.y;
      nozzleZ = 1;
      holding = { ...slot };
      currentLabel = slot.label;
      targetSlot = step.slot;
      phase = 'VACUUM ON';
      break;
    } else if (time >= tPickHoldEnd && time < tPickUpEnd) {
      const t = Easing.easeOutCubic(clamp((time - tPickHoldEnd) / T_PICK_UP, 0, 1));
      headX = FEEDER.pick.x; headY = FEEDER.pick.y;
      nozzleZ = 1 - t;
      holding = { ...slot };
      currentLabel = slot.label;
      targetSlot = step.slot;
      phase = 'LIFTING';
      break;
    } else if (time >= tPickUpEnd && time < tTravelEnd) {
      const t = Easing.easeInOutCubic(clamp((time - tPickUpEnd) / T_TRAVEL_TO_PLACE, 0, 1));
      headX = FEEDER.pick.x + (slotWorld.x - FEEDER.pick.x) * t;
      headY = FEEDER.pick.y + (slotWorld.y - FEEDER.pick.y) * t;
      nozzleZ = 0;
      holding = { ...slot };
      currentLabel = slot.label;
      targetSlot = step.slot;
      phase = 'TRAVEL → PLACE';
      break;
    } else if (time >= tTravelEnd && time < tPlaceDownEnd) {
      const t = Easing.easeOutCubic(clamp((time - tTravelEnd) / T_PLACE_DOWN, 0, 1));
      headX = slotWorld.x; headY = slotWorld.y;
      nozzleZ = t;
      holding = { ...slot };
      activeSlot = step.slot;
      placeFlash = 0;
      currentLabel = slot.label;
      targetSlot = step.slot;
      phase = 'PLACING';
      break;
    } else if (time >= tPlaceDownEnd && time < tPlaceHoldEnd) {
      headX = slotWorld.x; headY = slotWorld.y;
      nozzleZ = 1;
      holding = null;
      placedSet.add(step.slot);
      activeSlot = step.slot;
      placeFlash = 1 - (time - tPlaceDownEnd) / T_PLACE_HOLD;
      currentLabel = slot.label;
      phase = 'PLACED';
      break;
    } else if (time >= tPlaceHoldEnd && time < tPlaceUpEnd) {
      const t = Easing.easeOutCubic(clamp((time - tPlaceHoldEnd) / T_PLACE_UP, 0, 1));
      headX = slotWorld.x; headY = slotWorld.y;
      nozzleZ = 1 - t;
      holding = null;
      placedSet.add(step.slot);
      activeSlot = step.slot;
      placeFlash = (1 - t) * 0.4;
      currentLabel = slot.label;
      phase = 'RETRACT';
      break;
    } else if (time >= tPlaceUpEnd) {
      placedSet.add(step.slot);
      headX = slotWorld.x; headY = slotWorld.y;
      nozzleZ = 0;
      holding = null;
      currentLabel = slot.label;
      phase = 'IDLE';
    }
  }

  // tape moves down (reel feeds tape) when picking
  const tapeOffset = -time * 18;

  return (
    <g>
      {/* PCB sits on bed, beneath gantry */}
      <PCB_Board placedSet={placedSet} activeSlot={activeSlot} placeFlash={placeFlash} targetSlot={targetSlot}/>
      <FeederTop time={time} tapeOffset={tapeOffset}/>

      {/* laser sight from head down to surface (only visible when nozzleZ < 0.6) */}
      {tweaks.showLaser && nozzleZ < 0.6 && (
        <g>
          <circle cx={headX} cy={headY + 20} r={3 + (1-nozzleZ)*4}
                  fill="none" stroke={ORANGE} strokeWidth={1} opacity={0.6}/>
          <circle cx={headX} cy={headY + 20} r={6 + (1-nozzleZ)*8}
                  fill="none" stroke={ORANGE} strokeWidth={0.6} opacity={0.3}/>
        </g>
      )}

      {/* shadow of held component on bed below nozzle when lifted */}
      <HeldShadow headX={headX} headY={headY} holding={holding} nozzleZ={nozzleZ}/>

      {/* gantry on top */}
      <Gantry headX={headX} headY={headY} nozzleZ={nozzleZ}
              holding={holding} time={time}
              showLaser={tweaks.showLaser} gantryColor={tweaks.gantryColor}/>

      {tweaks.showHud && (
        <Telemetry time={time} placedCount={placedSet.size} total={SEQUENCE.length}
                   headX={headX} headY={headY} currentLabel={currentLabel} phase={phase}/>
      )}
    </g>
  );
}

// ─────────────────────────────────────────────────────────────────────
// Background — black bed with technical grid
// ─────────────────────────────────────────────────────────────────────
function Background() {
  return (
    <g>
      {/* subtle dot grid only — no solid fill, no frame */}
      <rect x={0} y={0} width={W} height={H} fill="url(#techGrid)" opacity={0.35}/>
      {/* bed grid */}
      <rect x={BED.x} y={BED.y} width={BED.w} height={BED.h} fill="url(#bedGrid)" opacity={0.5}/>
      {/* bed corner markers */}
      {[[BED.x, BED.y],[BED.x + BED.w, BED.y],[BED.x, BED.y+BED.h],[BED.x+BED.w, BED.y+BED.h]].map(([cx,cy],i) => (
        <g key={i}>
          <line x1={cx-12} y1={cy} x2={cx+12} y2={cy} stroke={M_5} strokeWidth={1}/>
          <line x1={cx} y1={cy-12} x2={cx} y2={cy+12} stroke={M_5} strokeWidth={1}/>
        </g>
      ))}
    </g>
  );
}

// ─────────────────────────────────────────────────────────────────────
// Time labeller — sets data-screen-label per second on root
// ─────────────────────────────────────────────────────────────────────
function TimeLabeler() {
  const time = useTime();
  const sec = Math.floor(time);
  React.useEffect(() => {
    const root = document.querySelector('[data-screen-label]');
    if (root) root.setAttribute('data-screen-label', `t=${sec}s`);
  }, [sec]);
  return null;
}

const TWEAK_DEFAULTS = {
  "speed": 1.0,
  "showHud": true,
  "showLaser": true,
  "gantryColor": "#2C3656",
  "bedScale": 1.0
};

// Export scene components for website.jsx
Object.assign(window, {
  PickPlaceScene, Background, W, H, BED, FEEDER, PCB_Board,
  FeederTop, Gantry, Telemetry, HeldShadow, CompTop,
  SLOTS, SEQUENCE, TWEAK_DEFAULTS, TimeLabeler, SolderPad,
  M_BG, M_1, M_2, M_3, M_4, M_5, T_PRI, T_SEC, T_TER,
  ORANGE, ORANGE_DIM,
});

// Only mount standalone App if website.jsx hasn't claimed the root
window.__SCENE_APP = function() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);

  return (
    <div data-screen-label="t=0s" style={{ position: 'absolute', inset: 0 }}>
      <Stage width={W} height={H} duration={28} background={M_BG} loop={true} showControls={false}>
        <svg viewBox={`0 0 ${W} ${H}`} width={W} height={H}
             style={{ position: 'absolute', inset: 0 }}>
          <defs>
            <pattern id="techGrid" width="40" height="40" patternUnits="userSpaceOnUse">
              <circle cx="20" cy="20" r="0.7" fill="#E0E0E0"/>
            </pattern>
            <pattern id="bedGrid" width="40" height="40" patternUnits="userSpaceOnUse">
              <path d="M 40 0 L 0 0 0 40" fill="none" stroke="#E8E8E8" strokeWidth="0.5"/>
            </pattern>
            <pattern id="pcbTexture" width="6" height="6" patternUnits="userSpaceOnUse">
              <rect width="6" height="6" fill="transparent"/>
              <circle cx="3" cy="3" r="0.4" fill="#0f2a18"/>
            </pattern>
          </defs>
          <Background/>
          <PickPlaceScene tweaks={t}/>
          <TimeLabeler/>
        </svg>
      </Stage>
    </div>
  );
};

// Auto-mount only if website.jsx hasn't set a flag
setTimeout(() => {
  if (!window.__WEBSITE_MOUNTED) {
    const App = window.__SCENE_APP;
    ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
  }
}, 0);

