{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "animatronic-robot",
  "title": "Animatronic",
  "description": "The whole animatronic, running itself: one routine intent driving gait, breath, gaze, grip and expression together, an attention cascade through the eyes, neck and waist, a solved face on a solved biped, and a centre of mass it keeps over the support polygon its feet actually hold. Four camera angles, every channel posable.",
  "registryDependencies": [
    "https://robocn.dev/r/robot-style.json",
    "https://robocn.dev/r/robot-kinematics.json",
    "https://robocn.dev/r/animatronic-kinematics.json",
    "https://robocn.dev/r/skeleton-kinematics.json",
    "https://robocn.dev/r/face-actuation.json",
    "https://robocn.dev/r/hand-kinematics.json",
    "https://robocn.dev/r/use-pointer-target.json",
    "https://robocn.dev/r/use-robot-motion.json"
  ],
  "files": [
    {
      "path": "src/components/ui/animatronic-robot.tsx",
      "content": "\"use client\"\n\n/**\n * animatronic-robot — the whole animatronic, running itself.\n *\n * The set has the parts already: `robot-skeleton` walks a biped, `robot-torso`\n * breathes a cage, `animatronic-face` drives a head as servo channels,\n * `robot-hand` grips. This is the body they belong to, and two things here are\n * new.\n *\n * **It controls itself as one body.** `routineIntent` returns an\n * `AnimatronicIntent` — gait, stance, lean, twist, reach, gaze, expression,\n * breath, grip, all at once — rather than a scalar, because looking at\n * something is a posture rather than a number. `solveAttention` splits a look\n * across the eyes, the neck and the waist, each taking only what the one before\n * it could not reach, which is what reads as *looking at you*. Every field of\n * the intent is also a prop: supply one and it wins, and nothing below the\n * controller branches on a routine name.\n *\n * **It stands over its own foot.** `centreOfMass` sums the segment masses at\n * their own centres, `supportPolygon` is the hull of the feet actually loaded,\n * and the machine rolls rigidly about its support centroid to bring the plumb\n * line back inside. Rigid: no bone changes length, and `showBalance` draws the\n * polygon, the weight and the plumb line so the claim is checkable.\n *\n * Solved: legs, spine, ribs, arms, hands, the skull's silhouette, every face\n * channel, and the balance. Illustrated: the shell panels, the chest core, the\n * vents and the hip and shoulder cans — they are drawn on the solved frame and\n * drive nothing. There is no dynamics anywhere: the machine corrects its\n * posture toward its support, it does not compute whether it could.\n *\n * One geometry, four projections through `robotCamera`.\n *\n * Design note: `docs/animatronic-robot.md`.\n */\n\nimport * as React from \"react\"\n\nimport { usePointerTarget } from \"@/hooks/use-pointer-target\"\nimport { arrowStep, useRobotDrag, useRobotScalar } from \"@/hooks/use-robot-motion\"\nimport {\n  animatronicRoutines,\n  blendIntent,\n  footHalfWidth,\n  rollAbout,\n  routineIntent,\n  solveAnimatronic,\n  solveAttention,\n  type AnimatronicIntent,\n  type AnimatronicRoutine,\n} from \"@/lib/robocn/animatronic\"\nimport {\n  ellipsoidOutline,\n  faceShape,\n  onFace,\n  rotateHead,\n  type FaceChannels,\n  type FaceExpression,\n  type FaceSide,\n  type HeadGeometry,\n} from \"@/lib/robocn/face\"\nimport { type HandGrasp } from \"@/lib/robocn/hand\"\nimport { clamp, lerp2, type Vec2, type Vec3 } from \"@/lib/robocn/kinematics\"\nimport {\n  defaultProportions,\n  rollPoint,\n  type SkeletonArm,\n  type SkeletonGait,\n  type SkeletonLeg,\n  type SkeletonProportions,\n} from \"@/lib/robocn/skeleton\"\nimport {\n  capsulePath,\n  circleFootprint,\n  px,\n  resolveRobotPalette,\n  resolveRobotSize,\n  robotCamera,\n  robotSurface,\n  slabPath,\n  type RobotPaletteProps,\n  type RobotSize,\n  type RobotVariant,\n  type RobotView,\n} from \"@/lib/robocn/style\"\nimport { cn } from \"@/lib/utils\"\n\n/** What the machine does with nobody driving it. */\nexport type AnimatronicRobotBehavior = AnimatronicRoutine\n\n/** Panels over the frame, or the frame on its own. */\nexport type AnimatronicChassis = \"shell\" | \"frame\"\n\nconst VIEW_WIDTH = 214\nconst VIEW_HEIGHT = 300\n/** World origin on screen: the floor, under the pelvis. */\nconst CENTRE = { x: 107, y: 258 }\nconst SCALE = 1.3\nconst NATIVE_VIEW: RobotView = \"front\"\n\n/**\n * The family's proportions with an animatronic's head: bigger than a person's,\n * because the face is the part of this machine that has to carry at 150px.\n */\nexport const animatronicProportions: SkeletonProportions = {\n  ...defaultProportions,\n  // The family's 80 gives a leg with 86 of bone in it 73 of gap to cover, which\n  // is a permanent half-squat — invisible head-on and a bird leg in profile.\n  // 88 leaves the knee a working bend instead of a crouch.\n  hip: 88,\n  // A wider stance than a person's: at 11 the feet, which are four times longer\n  // than they are apart, cross each other from a three-quarter camera.\n  hipSpan: 14,\n  neck: 12,\n  skull: 34,\n}\n\n/** The face chart's own units; every feature below is drawn in them. */\nconst HEAD_GEOMETRY: HeadGeometry = { radii: { x: 34, y: 43, z: 38 }, gain: 6, travel: 6 }\n/** Chart units to world units, so the skull comes out the height it is spec'd. */\nconst HEAD_SCALE = animatronicProportions.skull / 2 / HEAD_GEOMETRY.radii.y\n\nconst EYE_X = 13\nconst EYE_Y = 12\nconst EYE_R = 7\nconst BROW_Y = 29\nconst MOUTH_Y = -24\n/** The jaw hinges on a real axis through the ear servos. */\nconst HINGE: Vec3 = { x: 0, y: 4, z: 18 }\n/** Where an ear servo sits on the skull's surface, on the machine's right. */\nconst EAR: Vec3 = { x: 33.75, y: 4, z: 3 }\nconst JAW_SWING = 21\n\n/** The frame the foot plates are measured against. */\nconst P = animatronicProportions\n\n/**\n * The sole between the heel and the ball, measured from the ankle joint it\n * pitches about — the same chassis `robot-foot` draws, on the same numbers, so\n * the two machines cannot drift apart. `y` runs up from the ankle, which puts\n * the sole line at `-P.ankle`.\n */\nconst soleChassis: Vec2[] = [\n  { x: -P.heel, y: -P.ankle },\n  { x: -P.heel - 1.5, y: -P.ankle + 3.6 },\n  { x: -P.heel + 1, y: -P.ankle + 8.8 },\n  { x: 3, y: -P.ankle + 11.5 },\n  { x: P.sole - 2, y: -P.ankle + 7.2 },\n  { x: P.sole + 1, y: -P.ankle + 2.6 },\n  { x: P.sole + 1, y: -P.ankle },\n]\n\n/** The toe plate, measured from the ball it hinges on — already on the sole. */\nconst toePlate: Vec2[] = [\n  { x: -1.5, y: 0 },\n  { x: P.toe, y: 0 },\n  { x: P.toe - 1.8, y: 3.2 },\n  { x: -1.5, y: 5.4 },\n]\n\n/** A contact pad lying on a sole line at `base`. */\nconst contactPad = (from: number, to: number, height: number, base: number): Vec2[] => [\n  { x: from, y: base - 0.6 },\n  { x: to, y: base - 0.6 },\n  { x: to, y: base + height },\n  { x: from, y: base + height },\n]\n\n/**\n * The cage. Narrower than the shoulder span, or the ribs overhang the arm rails\n * into open air, and starting above the lumbar so the bottom pair are attached\n * ribs rather than a floating hook ending in nothing.\n */\nconst CAGE = { width: 15.5, depth: 11, front: 5.5, from: 0.46, to: 0.93 }\n\n/**\n * Plan view has no height in it, so a standing machine projects to its own\n * footprint — a head, a pair of shoulders and two feet, and nothing else. That\n * is the correct projection and it is small, so the camera comes in.\n */\nconst fits: Record<RobotView, number> = { plan: 1.7, front: 1, profile: 1, iso: 0.86 }\n\n/**\n * Where the world origin lands on screen. The elevations hang off the floor\n * line, which is what keeps the feet on the ground; plan view has no height in\n * it at all, so a standing machine collapses onto that line and would sit in\n * the bottom of a frame sized for an elevation. It gets the middle instead.\n */\nconst origins: Record<RobotView, { x: number; y: number }> = {\n  plan: { x: CENTRE.x, y: VIEW_HEIGHT / 2 },\n  front: CENTRE,\n  profile: CENTRE,\n  iso: { x: CENTRE.x, y: CENTRE.y - 22 },\n}\n\nconst viewNames: Record<RobotView, string> = {\n  plan: \"plan view\",\n  front: \"front elevation\",\n  profile: \"side elevation\",\n  iso: \"isometric view\",\n}\n\nconst finite = (value: number | undefined, fallback = 0) =>\n  typeof value === \"number\" && Number.isFinite(value) ? value : fallback\n\nconst isRoutine = (value: unknown): value is AnimatronicRoutine =>\n  typeof value === \"string\" && (animatronicRoutines as readonly string[]).includes(value)\n\n/**\n * The self-control loop, re-exported as a pure function of the clock so motion\n * is tested by sampling it rather than by faking animation frames.\n */\nexport function animatronicRobotIntent(\n  behavior: AnimatronicRobotBehavior,\n  clock: number,\n): AnimatronicIntent {\n  return routineIntent(isRoutine(behavior) ? behavior : \"idle\", finite(clock))\n}\n\nexport interface AnimatronicRobotProps\n  extends Omit<React.ComponentProps<\"svg\">, \"color\" | \"height\">,\n    RobotPaletteProps {\n  /** What the machine does with anything you have not supplied. */\n  behavior?: AnimatronicRobotBehavior\n  /** Panels over the frame, or the frame bare. */\n  chassis?: AnimatronicChassis\n\n  /* ---- posing: every one of these wins over the routine ---- */\n  /** Footfall pattern. */\n  gait?: SkeletonGait\n  /** Controlled gait cycle fraction. */\n  gaitPhase?: number\n  /** Hip height, 0 crouched to 1 standing tall. */\n  stance?: number\n  stride?: number\n  lift?: number\n  /** Whole-column pitch in degrees, positive leaning forward. */\n  lean?: number\n  /** Shoulders against the pelvis, in degrees. */\n  twist?: number\n  /** Neck angles in degrees, on top of what the column carries. */\n  neckYaw?: number\n  neckPitch?: number\n  neckRoll?: number\n  /** Pupil aim, −1..1 on both axes. */\n  look?: Vec2 | null\n  /** A point both hands reach for, in the body frame. */\n  reach?: Vec3 | null\n  /** A point one hand reaches for. Wins over `reach` for that side. */\n  reachLeft?: Vec3 | null\n  reachRight?: Vec3 | null\n  /** The face's expression, by name or as a channel vector you blended. */\n  expression?: FaceExpression | FaceChannels\n  /** How far the face rig drives there, 0..1. */\n  intensity?: number\n  /** Lid closure, 0..1. Omit and it blinks on its own. */\n  blink?: number\n  /** Speech level, 0..1: opens the jaw and works the lips. */\n  speech?: number\n  /** Chest expansion, 0 emptied to 1 filled. */\n  breath?: number\n  grasp?: HandGrasp\n  /** Hand closure, 0 open to 1 shut. */\n  grip?: number\n  /** Drive individual face servos. These win over the expression. */\n  channels?: Partial<FaceChannels> & { left?: Partial<FaceSide>; right?: Partial<FaceSide> }\n  /** How hard the machine works to keep its weight over its feet, 0..1. */\n  effort?: number\n\n  /* ---- attention ---- */\n  /**\n   * What the machine is looking at, −1..1 on both axes: `x` to its left on\n   * screen, `y` up. Supplying it stops the pointer tracking.\n   */\n  attend?: Vec2 | null\n  onAttendChange?: (point: Vec2 | null) => void\n  /** Follow the pointer anywhere on the page. */\n  track?: boolean\n  /** Reach for whatever it is attending to, as well as looking at it. */\n  follow?: boolean\n  /** Correct the posture toward the support polygon. */\n  balance?: boolean\n\n  /* ---- the rest of the contract ---- */\n  showGround?: boolean\n  /** Draw the support polygon, the centre of mass and its plumb line. */\n  showBalance?: boolean\n  /** Shade each pad of each sole by what it is carrying. Follows showBalance. */\n  showLoad?: boolean\n  showReadout?: boolean\n  /** Rib hoops in the cage. */\n  ribs?: number\n  proportions?: Partial<SkeletonProportions>\n  /** Where the camera stands. One machine, four projections. */\n  view?: RobotView\n  /** Routine cycles per second. */\n  speed?: number\n  animate?: boolean\n  paused?: boolean\n  /** Seconds of offset, so a crowd of them breaks step. */\n  phase?: number\n  /** Drag to move what it is looking at; arrow keys do the same. */\n  interactive?: boolean\n  label?: string\n  size?: RobotSize | number\n  variant?: RobotVariant\n}\n\nfunction AnimatronicRobot({\n  behavior = \"idle\",\n  chassis = \"shell\",\n  gait,\n  gaitPhase,\n  stance,\n  stride,\n  lift,\n  lean,\n  twist,\n  neckYaw,\n  neckPitch,\n  neckRoll,\n  look,\n  reach,\n  reachLeft,\n  reachRight,\n  expression,\n  intensity,\n  blink,\n  speech,\n  breath,\n  grasp,\n  grip,\n  channels,\n  effort,\n  attend,\n  onAttendChange,\n  track = true,\n  follow = true,\n  balance = true,\n  showGround = true,\n  showBalance = false,\n  showLoad,\n  showReadout = true,\n  ribs = 7,\n  proportions,\n  view = NATIVE_VIEW,\n  speed = 0.5,\n  animate = true,\n  paused = false,\n  phase = 0,\n  interactive = true,\n  label,\n  size = \"md\",\n  variant = \"solid\",\n  color,\n  accent,\n  metal,\n  dark,\n  glow,\n  grid,\n  palette: paletteOverride,\n  className,\n  style,\n  role,\n  tabIndex,\n  onKeyDown,\n  onBlur,\n  \"aria-label\": ariaLabel,\n  ...props\n}: AnimatronicRobotProps) {\n  const palette = resolveRobotPalette({ color, accent, metal, dark, glow, grid, palette: paletteOverride })\n  const width = resolveRobotSize(size)\n  const svgRef = React.useRef<SVGSVGElement>(null)\n  /** Stable across server and client, so two of these on a page cannot collide. */\n  const faceClip = `${React.useId().replace(/:/g, \"\")}-face`\n  const routine: AnimatronicRoutine = isRoutine(behavior) ? behavior : \"idle\"\n\n  /* ------------------------------------------------------------ attention */\n\n  const [held, setHeld] = React.useState<Vec2 | null>(null)\n  const controlledAttention = attend !== undefined\n  const pointer = usePointerTarget(svgRef, {\n    enabled: (track || interactive) && !controlledAttention && held === null,\n    within: \"window\",\n    persist: true,\n    // The page maps to −1..1 with `y` up, clipped to the unit disc so a corner\n    // of the screen is not a harder look than an edge.\n    toWorld: React.useCallback((point: Vec2) => {\n      const x = (point.x - 0.5) * 2\n      const y = -(point.y - 0.5) * 2\n      const distance = Math.hypot(x, y) || 1\n      const limit = Math.min(1, distance) / distance\n      return { x: x * limit, y: y * limit }\n    }, []),\n  })\n\n  const raw = controlledAttention ? attend : (held ?? pointer.target)\n  const target: Vec2 | null =\n    raw && Number.isFinite(raw.x) && Number.isFinite(raw.y)\n      ? { x: clamp(raw.x, -1, 1), y: clamp(raw.y, -1, 1) }\n      : null\n\n  /**\n   * One loop, and one scalar on it: how far the machine has given itself over\n   * to what it is looking at. The routine keeps running underneath, so letting\n   * go eases back into wherever it has moved on to rather than snapping.\n   */\n  const motion = useRobotScalar(target ? 1 : 0, {\n    rate: 2.4,\n    speed,\n    paused,\n    phase,\n    animate: animate && routine !== \"static\",\n  })\n  const clock = motion.clock\n  const engaged = clamp(motion.value, 0, 1)\n\n  /* --------------------------------------------------------- the intent */\n\n  const own = routineIntent(routine, clock)\n  const attention = solveAttention(target, finite(effort, 1))\n\n  // Attending is a whole posture, so it is a second intent crossfaded with the\n  // routine's rather than a set of overrides sprayed over it.\n  const attending: AnimatronicIntent = {\n    ...own,\n    look: attention.look,\n    neckYaw: attention.neckYaw,\n    neckPitch: attention.neckPitch,\n    neckRoll: attention.neckRoll,\n    twist: own.twist * 0.3 + attention.twist,\n    lean: own.lean + attention.lean,\n    grip: follow && target ? Math.max(own.grip, 0.34) : own.grip,\n  }\n  const blended = target ? blendIntent(own, attending, engaged) : own\n  /**\n   * The reach is the one channel the crossfade cannot carry on its own: a\n   * chain either solves to a point or it swings, and there is nothing halfway.\n   * So the *point* is what eases — out from wherever the routine already had\n   * the hands, or from where they hang — and the chain solves to it every\n   * frame. Without this the arms snap out the instant a pointer appears.\n   */\n  const driven: AnimatronicIntent =\n    follow && target\n      ? {\n          ...blended,\n          reach: null,\n          reachLeft: mix3(own.reachLeft ?? own.reach ?? REST_REACH, aimedReach(target, -1), engaged),\n          reachRight: mix3(own.reachRight ?? own.reach ?? REST_REACH, aimedReach(target, 1), engaged),\n        }\n      : blended\n\n  // Props win outright, and only the ones supplied.\n  const posed: AnimatronicIntent = {\n    ...driven,\n    gait: gait ?? driven.gait,\n    cycle: gaitPhase !== undefined ? finite(gaitPhase) : driven.cycle,\n    stance: stance ?? driven.stance,\n    stride: stride ?? driven.stride,\n    lift: lift ?? driven.lift,\n    lean: lean ?? driven.lean,\n    twist: twist ?? driven.twist,\n    neckYaw: neckYaw ?? driven.neckYaw,\n    neckPitch: neckPitch ?? driven.neckPitch,\n    neckRoll: neckRoll ?? driven.neckRoll,\n    look: look ?? driven.look,\n    reach: reach !== undefined ? reach : driven.reach,\n    reachLeft: reachLeft !== undefined ? reachLeft : driven.reachLeft,\n    reachRight: reachRight !== undefined ? reachRight : driven.reachRight,\n    expression: resolveExpression(expression, driven.expression),\n    intensity: intensity ?? driven.intensity,\n    blink: blink ?? driven.blink,\n    speech: speech ?? driven.speech,\n    breath: breath ?? driven.breath,\n    grasp: grasp ?? driven.grasp,\n    grip: grip ?? driven.grip,\n    effort: effort ?? driven.effort,\n  }\n\n  const rig = React.useMemo(\n    () => ({ ...animatronicProportions, ...proportions }),\n    [proportions],\n  )\n\n  const body = solveAnimatronic({\n    ...posed,\n    balance,\n    proportions: rig,\n    head: HEAD_GEOMETRY,\n    ribs,\n    cage: CAGE,\n    handScale: HEAD_SCALE * 0.68,\n  })\n  const pose = body.skeleton\n  // The face channels a caller supplied last, over everything the rig solved.\n  const solution = channels ? withChannels(body.face, channels) : body.face\n\n  /* ---------------------------------------------------------- interaction */\n\n  const apply = React.useCallback(\n    (next: Vec2 | null) => {\n      const bounded = next\n        ? { x: Math.round(clamp(finite(next.x), -1, 1) * 100) / 100, y: Math.round(clamp(finite(next.y), -1, 1) * 100) / 100 }\n        : null\n      setHeld((current) => {\n        const same =\n          current === bounded ||\n          (current !== null && bounded !== null && current.x === bounded.x && current.y === bounded.y)\n        if (!same) onAttendChange?.(bounded)\n        return bounded\n      })\n    },\n    [onAttendChange],\n  )\n  const dragging = useRobotDrag(svgRef, {\n    enabled: interactive,\n    // `onDrag` must stay in a `useCallback` or the listeners rebind every render.\n    onDrag: React.useCallback(\n      (point: Vec2) => apply({ x: (point.x - 0.5) * 2, y: -(point.y - 0.5) * 2 }),\n      [apply],\n    ),\n    // Release hands it straight back: the pointer is where the drag left it, so\n    // the machine keeps watching without the grab becoming a lock.\n    onDragEnd: React.useCallback(() => setHeld(null), []),\n  })\n\n  /* ------------------------------------------------------------- drawing */\n\n  const camera = robotCamera(view)\n  const fit = fits[view] ?? 1\n  const origin = origins[view] ?? CENTRE\n  const to = (point: Vec3): Vec2 => camera.project(point.x, point.y, point.z)\n  const depthOf = (point: Vec3) => camera.depth(point.x, point.y, point.z)\n  const link = (a: Vec3, b: Vec3, radius: number) => capsulePath(to(a), to(b), radius)\n  const trace3 = (points: Vec3[], close = true) => {\n    const path = points\n      .map((point, index) => {\n        const at = to(point)\n        return `${index ? \"L\" : \"M\"} ${px(at.x)} ${px(at.y)}`\n      })\n      .join(\" \")\n    return close ? `${path} Z` : path\n  }\n  const box = (centre: Vec3, hx: number, hy: number, hz: number) =>\n    slabPath(\n      [-1, 1].flatMap((sx) =>\n        [-1, 1].flatMap((sy) =>\n          [-1, 1].map((sz) => ({\n            x: centre.x + sx * hx,\n            y: centre.y + sy * hy,\n            z: centre.z + sz * hz,\n          })),\n        ),\n      ),\n      camera,\n    )\n\n  const shell = robotSurface(\"shell\", variant, palette)\n  const machined = robotSurface(\"metal\", variant, palette)\n  const cast = robotSurface(\"dark\", variant, palette)\n  const panelled = chassis === \"shell\"\n  const showLoadPads = showLoad ?? showBalance\n  /** Line work — brows, lips, seams — follows the variant like everything else. */\n  const line1 = variant === \"solid\" ? palette.dark : palette.grid\n  const hoopPaint = {\n    fill: \"none\",\n    stroke: variant === \"wire\" ? palette.grid : palette.shell,\n    strokeWidth: variant === \"solid\" ? 2.2 : shell.strokeWidth,\n    strokeDasharray: shell.strokeDasharray,\n    strokeLinejoin: \"round\" as const,\n  }\n\n  /* ---- the head: one ellipsoid, every feature pushed onto its surface ---- */\n\n  const headPose = body.headPose\n  const radii = HEAD_GEOMETRY.radii\n  const worldRadii: Vec3 = {\n    x: radii.x * HEAD_SCALE,\n    y: radii.y * HEAD_SCALE,\n    z: radii.z * HEAD_SCALE,\n  }\n  const centreOfHead = body.headCentre\n  /** A point in the head's own chart, out to the world. */\n  const inHead = (point: Vec3): Vec3 => {\n    const turned = rotateHead(point, headPose)\n    return {\n      x: centreOfHead.x + turned.x * HEAD_SCALE,\n      y: centreOfHead.y + turned.y * HEAD_SCALE,\n      z: centreOfHead.z + turned.z * HEAD_SCALE,\n    }\n  }\n  const skin = (u: number, v: number, outset = 0) => onFace(u, v, radii, outset)\n  const chart = (point: Vec2, outset = 0) => to(inHead(skin(point.x, point.y, outset)))\n\n  /** The jaw swings about the ear axis before anything else touches it. */\n  const swing = -(solution.jaw * JAW_SWING * Math.PI) / 180\n  const hinged = (point: Vec3): Vec3 => {\n    const y = point.y - HINGE.y\n    const z = point.z - HINGE.z\n    const c = Math.cos(swing)\n    const s = Math.sin(swing)\n    return { x: point.x, y: HINGE.y + y * c - z * s, z: HINGE.z + y * s + z * c }\n  }\n  const jawChart = (point: Vec2, outset = 0) => to(inHead(hinged(skin(point.x, point.y, outset))))\n\n  const face = (points: Vec2[], project: (p: Vec2, outset?: number) => Vec2, outset = 0, close = true) => {\n    const path = points\n      .map((point, index) => {\n        const at = project(point, outset)\n        return `${index ? \"L\" : \"M\"} ${px(at.x)} ${px(at.y)}`\n      })\n      .join(\" \")\n    return close ? `${path} Z` : path\n  }\n\n  // The camera is linear, so its coefficients are the direction toward it.\n  const toCamera = {\n    x: camera.depth(1, 0, 0),\n    y: camera.depth(0, 1, 0),\n    z: camera.depth(0, 0, 1),\n  }\n  /**\n   * How squarely a patch of the skull meets the camera. The ellipsoid's normal\n   * against the view direction, so the far eye turns away by itself — and so\n   * does anything else on the surface, which is why this takes a point rather\n   * than a pair of face-chart coordinates.\n   */\n  const facingAt = (point: Vec3) => {\n    const normal = { x: point.x / radii.x ** 2, y: point.y / radii.y ** 2, z: point.z / radii.z ** 2 }\n    const length = Math.hypot(normal.x, normal.y, normal.z) || 1\n    const turned = rotateHead(\n      { x: normal.x / length, y: normal.y / length, z: normal.z / length },\n      headPose,\n    )\n    return turned.x * toCamera.x + turned.y * toCamera.y + turned.z * toCamera.z\n  }\n  const facing = (u: number, v: number) => facingAt(skin(u, v))\n  /**\n   * A feature is pushed off the skull by its own outset, so one still being\n   * drawn at the limb projects outside the silhouette and reads as clipped by\n   * the head. It fades out before it gets there rather than at the exact edge.\n   */\n  const seen = (u: number, v: number) => px(clamp((facing(u, v) - 0.14) * 4, 0, 1))\n  const forward = rotateHead({ x: 0, y: 0, z: -1 }, headPose)\n  const ahead = forward.x * toCamera.x + forward.y * toCamera.y + forward.z * toCamera.z\n  /**\n   * Centreline parts sit on the median plane, so the normal test is the wrong\n   * question for them: seen from the side they are not turned away, they are\n   * edge-on, and the projection has already collapsed them to a line. They fade\n   * only once the head has actually turned its back. The floor is what keeps\n   * that line drawn — without it the whole lower face goes blank in profile.\n   */\n  const median = px(clamp(ahead * 2.2 + 0.12, 0, 1))\n\n  const skull = ellipsoidOutline(worldRadii, headPose, camera, centreOfHead)\n  const ring = (cx: number, cy: number, rx: number, ry: number, count = 20, from = 0, sweep = Math.PI * 2) =>\n    Array.from({ length: count }, (_, index) => {\n      const angle = from + (index / (count - 1 || 1)) * sweep\n      return { x: cx + Math.cos(angle) * rx, y: cy + Math.sin(angle) * ry }\n    })\n  const panel = (cx: number, cy: number, halfW: number, halfH: number, corner: number, per = 8) => {\n    const r = Math.max(0, Math.min(corner, halfW, halfH))\n    const centers: [number, number, number][] = [\n      [cx + halfW - r, cy + halfH - r, 0],\n      [cx - halfW + r, cy + halfH - r, Math.PI / 2],\n      [cx - halfW + r, cy - halfH + r, Math.PI],\n      [cx + halfW - r, cy - halfH + r, (Math.PI * 3) / 2],\n    ]\n    return centers.flatMap(([ox, oy, from]) =>\n      Array.from({ length: per }, (_, index) => {\n        const angle = from + (index / (per - 1)) * (Math.PI / 2)\n        return { x: ox + Math.cos(angle) * r, y: oy + Math.sin(angle) * r }\n      }),\n    )\n  }\n\n  const eye = (sign: -1 | 1) => {\n    const channel = sign < 0 ? solution.left : solution.right\n    const cx = sign * EYE_X\n    const lidTop = EYE_Y + EYE_R * 1.05 - channel.lidUpper * (EYE_R * 2.1)\n    const lidBottom = EYE_Y - EYE_R * 1.05 + channel.lidLower * (EYE_R * 1.35)\n    const pupil = { x: cx + solution.gaze.x * 3.2, y: EYE_Y - solution.gaze.y * 2.8 }\n    const lid = (name: string, edge: number, over: boolean) => (\n      <path\n        key={name}\n        data-lid={name}\n        d={face(\n          [\n            { x: cx - EYE_R * 1.16, y: edge },\n            ...ring(cx, EYE_Y, EYE_R * 1.16, EYE_R * 1.3, 12, Math.PI, over ? -Math.PI : Math.PI),\n            { x: cx + EYE_R * 1.16, y: edge },\n          ],\n          chart,\n          1.9,\n        )}\n        fill={variant === \"solid\" ? palette.metal : \"none\"}\n        stroke={variant === \"solid\" ? palette.dark : palette.grid}\n        strokeWidth={0.7}\n      />\n    )\n    return (\n      <g key={sign} data-eye={sign < 0 ? \"left\" : \"right\"} opacity={seen(cx, EYE_Y)}>\n        <path\n          d={face(ring(cx, EYE_Y, EYE_R, EYE_R), chart, 0.9)}\n          fill={palette.metal}\n          stroke={palette.dark}\n          strokeWidth={0.7}\n          opacity={variant === \"solid\" ? 1 : 0.35}\n        />\n        <path d={face(ring(pupil.x, pupil.y, 4.2, 4.2, 18), chart, 1.3)} fill={palette.accent} stroke=\"none\" opacity={0.92} />\n        <path d={face(ring(pupil.x, pupil.y, 2, 2, 14), chart, 1.5)} fill={palette.dark} stroke=\"none\" />\n        {lid(sign < 0 ? \"left-upper\" : \"right-upper\", lidTop, true)}\n        {lid(sign < 0 ? \"left-lower\" : \"right-lower\", lidBottom, false)}\n      </g>\n    )\n  }\n\n  const brow = (sign: -1 | 1) => {\n    const channel = sign < 0 ? solution.left : solution.right\n    const inner = BROW_Y + channel.browInner * 6\n    const outer = BROW_Y + channel.browOuter * 7\n    const points = Array.from({ length: 6 }, (_, index) => {\n      const t = index / 5\n      return {\n        // Kept clear of the limb: a brow drawn out to the temple leaves the\n        // silhouette once its stroke width and outset are added.\n        x: sign * (4.5 + t * 16),\n        // A brow is an arch: the middle rides above the chord between its tips.\n        y: inner + (outer - inner) * t + Math.sin(t * Math.PI) * 2,\n      }\n    })\n    return (\n      <path\n        key={sign}\n        data-brow={sign < 0 ? \"left\" : \"right\"}\n        opacity={seen(sign * 13, BROW_Y)}\n        d={face(points, chart, 1.4, false)}\n        fill=\"none\"\n        stroke={line1}\n        strokeWidth={variant === \"solid\" ? 2.9 : 1.6}\n        strokeLinecap=\"round\"\n      />\n    )\n  }\n\n  const halfMouth = 13 - solution.lipPucker * 4\n  const cornerLeft = { x: -halfMouth, y: MOUTH_Y + solution.left.lipCorner * 5 }\n  const cornerRight = { x: halfMouth, y: MOUTH_Y + solution.right.lipCorner * 5 }\n  const upperLip = [\n    cornerLeft,\n    { x: -halfMouth * 0.45, y: MOUTH_Y + 2.2 - solution.lipPress * 1.3 },\n    { x: 0, y: MOUTH_Y + 1 - solution.lipPress * 1.1 },\n    { x: halfMouth * 0.45, y: MOUTH_Y + 2.2 - solution.lipPress * 1.3 },\n    cornerRight,\n  ]\n  // Deep enough that the plate is still in front of it at full swing, so what\n  // opens is the cavity rather than the back of the head.\n  const cavity = [\n    ...upperLip,\n    { x: halfMouth * 0.92, y: MOUTH_Y - 6 },\n    { x: 0, y: MOUTH_Y - 14 },\n    { x: -halfMouth * 0.92, y: MOUTH_Y - 6 },\n  ]\n  /**\n   * The jaw plate. Kept inside the ellipsoid's own domain: `onFace` clamps a\n   * chart point that is off the surface onto the equator, and an outline with\n   * two of those in it folds over itself and draws as a tangle rather than a\n   * plate. Every point here has real surface under it at every swing.\n   */\n  const jawOutline = [\n    { x: -22, y: -12 },\n    { x: -16, y: -20 },\n    { x: -8, y: -24 },\n    { x: 0, y: -25 },\n    { x: 8, y: -24 },\n    { x: 16, y: -20 },\n    { x: 22, y: -12 },\n    { x: 24.5, y: -16 },\n    { x: 22.5, y: -23 },\n    { x: 17, y: -27.5 },\n    { x: 10, y: -31 },\n    { x: 0, y: -32.5 },\n    { x: -10, y: -31 },\n    { x: -17, y: -27.5 },\n    { x: -22.5, y: -23 },\n    { x: -24.5, y: -16 },\n  ]\n\n  const head = (\n    <g data-head>\n      <g data-neck>\n        <path d={link(pose.shoulders, pose.neck, 3.4)} {...machined} />\n        <path d={link(pose.neck, centreOfHead, 2.8)} {...cast} />\n      </g>\n      <g data-skull>\n        {/*\n          Every feature is a curve pushed off the skull by its own outset, and\n          near the limb that outset projects outside the silhouette — a brow\n          crossing the crown, a lip escaping as a detached bead. Clipping the\n          whole face to the head's own outline makes that impossible at any\n          angle rather than tuning each feature until it stops.\n        */}\n        <clipPath id={faceClip}>\n          <ellipse\n            cx={px(skull.cx)}\n            cy={px(skull.cy)}\n            rx={px(Math.max(skull.rx, 0.1))}\n            ry={px(Math.max(skull.ry, 0.1))}\n            transform={`rotate(${px(skull.angle)} ${px(skull.cx)} ${px(skull.cy)})`}\n          />\n        </clipPath>\n        <ellipse\n          cx={px(skull.cx)}\n          cy={px(skull.cy)}\n          rx={px(Math.max(skull.rx, 0.1))}\n          ry={px(Math.max(skull.ry, 0.1))}\n          transform={`rotate(${px(skull.angle)} ${px(skull.cx)} ${px(skull.cy)})`}\n          {...shell}\n        />\n        {/* The cranium seam: a panel line over the crown, which is what tells\n            the silhouette apart from a ball. */}\n        <path\n          d={face(ring(0, 6, 28, 31, 16, Math.PI * 0.08, Math.PI * 0.84), chart, 0.6, false)}\n          fill=\"none\"\n          stroke={line1}\n          strokeWidth={0.9}\n          opacity={0.35}\n        />\n      </g>\n      <g clipPath={`url(#${faceClip})`}>\n      <path\n        data-band\n        opacity={seen(0, EYE_Y)}\n        d={face(panel(0, EYE_Y, 25, 8.6, 7.5, 9), chart, 0.25)}\n        {...cast}\n      />\n      {([-1, 1] as const).map(eye)}\n      {([-1, 1] as const).map(brow)}\n      {/* The plate and the cavity behind it have volume, so they survive being\n          seen edge-on and are not faded by a test meant for flat detail. The\n          lips drawn on them are flat, and are. */}\n      <g data-mouth>\n        <path data-cavity d={face(cavity, chart, 0.15)} {...cast} opacity={median} />\n        <path data-jaw d={face(jawOutline, jawChart, 0.55)} {...shell} />\n        {/* The seam the plate parts along — its top edge alone, drawn lighter,\n            so the jaw reads as a hinge rather than as a crack. */}\n        <path\n          d={face(jawOutline.slice(0, 7), jawChart, 0.75, false)}\n          fill=\"none\"\n          stroke={line1}\n          strokeWidth={0.9}\n          strokeLinecap=\"round\"\n          opacity={px(median * 0.3)}\n        />\n        <path\n          d={face(upperLip, chart, 1.2, false)}\n          fill=\"none\"\n          stroke={line1}\n          strokeWidth={variant === \"solid\" ? 2 : 1.4}\n          strokeLinecap=\"round\"\n          opacity={px(median * (1 - solution.jaw * 0.85))}\n        />\n      </g>\n      </g>\n      {/* The ear servo cans, on the hinge axis — the one part of the head that\n          only exists off the face's own chart. */}\n      {([-1, 1] as const).map((sign) => {\n        // On the side of the skull, on its surface — not a disc floating on the\n        // cheek. Its own normal points sideways, which is what lets the far one\n        // turn away when the head does.\n        const seat: Vec3 = { x: sign * EAR.x, y: EAR.y, z: EAR.z }\n        const at = to(inHead(seat))\n        return (\n          <circle\n            key={sign}\n            data-servo={sign < 0 ? \"left-ear\" : \"right-ear\"}\n            cx={px(at.x)}\n            cy={px(at.y)}\n            r={2.6}\n            {...machined}\n            /* A can standing off the skull has volume, so unlike the flat\n               detail on the face it survives being seen edge-on — which is\n               exactly the head-on view, where it is the thing breaking the\n               silhouette. It goes only once the head has turned its back. */\n            opacity={px(clamp(facingAt(seat) * 1.8 + 0.72, 0, 1))}\n          />\n        )\n      })}\n    </g>\n  )\n\n  /* ------------------------------------------------------------- the body */\n\n  /**\n   * A limb's own sagittal frame. The balance roll turns the whole machine about\n   * a point on the floor, so `unroll` takes a solved point back into the\n   * upright body the leg was drawn in and `reroll` puts it back — which is what\n   * lets the sole plate and the toe hinge stay flat outlines rather than\n   * becoming special cases of the correction.\n   */\n  const unroll = (point: Vec3) => (body.roll === 0 ? point : rollAbout(point, body.pivot, -body.roll))\n  const reroll = (point: Vec3) => (body.roll === 0 ? point : rollAbout(point, body.pivot, body.roll))\n\n  /**\n   * One leg, with the chassis `robot-leg` and `robot-foot` draw around the same\n   * solver: two strut actuators whose stroke is a consequence of the pose, a\n   * shin plate, a sole turned about the ankle, a toe plate hinged at the ball,\n   * and one contact pad per part of the sole shaded by what it is carrying.\n   */\n  const legPart = (leg: SkeletonLeg) => {\n    const lateral = unroll(leg.hip).x\n    /** Sagittal to world, at a lateral offset across the machine. */\n    const at = (point: Vec2, across = 0): Vec3 =>\n      reroll({ x: lateral + across, y: point.y, z: -point.x })\n    /** A solved world point back into the leg's own sagittal chart. */\n    const sag = (point: Vec3): Vec2 => {\n      const up = unroll(point)\n      return { x: -up.z, y: up.y }\n    }\n    const project = (point: Vec2, across = 0) => to(at(point, across))\n    const solid = (outline: Vec2[], halfWidth = footHalfWidth) =>\n      slabPath(\n        outline.flatMap((point) => [at(point, -halfWidth), at(point, halfWidth)]),\n        camera,\n      )\n    /** The same, for a plate that hinges: turned in the plane it lives in. */\n    const turned = (outline: Vec2[], about: Vec2, degrees: number, halfWidth = footHalfWidth) =>\n      solid(\n        outline.map((point) => rollPoint({ x: point.x + about.x, y: point.y + about.y }, about, degrees)),\n        halfWidth,\n      )\n\n    const hip = sag(leg.hip)\n    const knee = sag(leg.knee)\n    const ankle = sag(leg.ankle)\n    const ball = sag(leg.ball)\n\n    // Struts: one from the pelvis down to the thigh, one across the knee to the\n    // shin. Both run between solved points, so their stroke is the pose.\n    const struts: Array<[string, Vec2, Vec2]> = [\n      // Anchored inside the pelvis block, not above it: a strut that starts\n      // clear of the shell reads as a pillar standing in the machine's belly.\n      [\"hip\", { x: hip.x - 7, y: hip.y + 1 }, lerp2(hip, knee, 0.42)],\n      [\"knee\", lerp2(hip, knee, 0.72), lerp2(knee, ankle, 0.3)],\n    ]\n    const loads: Array<[string, Vec2[], Vec2, number, number]> = [\n      [\"heel\", contactPad(-P.heel - 1, -P.heel + 6, 2.6, -P.ankle), ankle, leg.angle, leg.roll.heelLoad],\n      [\"ball\", contactPad(P.sole - 6, P.sole + 1, 2.6, -P.ankle), ankle, leg.angle, leg.roll.ballLoad],\n      [\"toe\", contactPad(2, P.toe, 2.4, 0), ball, leg.toeAngle, leg.roll.toeLoad],\n    ]\n\n    return (\n      <g key={`leg-${leg.side}`} data-leg={leg.side}>\n        {panelled &&\n          struts.map(([name, from, into]) => {\n            const span = Math.max(Math.hypot(into.x - from.x, into.y - from.y), 1e-3)\n            const rod = lerp2(from, into, Math.min(span * 0.55, 14) / span)\n            return (\n              <g key={name} data-actuator={`${leg.side}-${name}`}>\n                <path d={capsulePath(project(from), project(rod), 2.4)} {...cast} />\n                <path d={capsulePath(project(rod), project(into), 1.1)} {...machined} />\n              </g>\n            )\n          })}\n\n        {panelled && <path d={link(leg.hip, leg.knee, 6.2)} {...shell} />}\n        <path data-femur d={link(leg.hip, leg.knee, 3.4)} {...machined} />\n        <path data-tibia d={link(leg.knee, leg.ankle, 2.6)} {...machined} />\n        {panelled && (\n          <path\n            data-shin\n            d={solid(\n              [\n                lerp2(knee, ankle, 0.16),\n                lerp2(knee, ankle, 0.9),\n                { x: lerp2(knee, ankle, 0.85).x + 4.5, y: lerp2(knee, ankle, 0.85).y },\n                { x: lerp2(knee, ankle, 0.22).x + 5.5, y: lerp2(knee, ankle, 0.22).y },\n              ],\n              4.2,\n            )}\n            {...shell}\n          />\n        )}\n\n        <g data-foot={leg.side}>\n          <path data-sole d={turned(soleChassis, ankle, leg.angle)} {...(panelled ? shell : machined)} />\n          <path data-toe d={turned(toePlate, ball, leg.toeAngle, footHalfWidth - 1)} {...machined} />\n          {panelled && (\n            <path\n              data-heel\n              d={turned(contactPad(-P.heel + 0.5, -P.heel + 6, 3, -P.ankle), ankle, leg.angle, footHalfWidth - 0.5)}\n              {...cast}\n            />\n          )}\n          {/* The loads are geometry — which parts of the sole are still on the\n              floor — and not forces. Nothing here weighs anything, which is why\n              they are shown with the balance rather than by default. */}\n          {showLoadPads && loads.map(([name, outline, about, degrees, amount]) => (\n            <path\n              key={name}\n              data-pad={`${leg.side}-${name}`}\n              d={turned(outline, about, degrees, footHalfWidth - 1.5)}\n              fill={palette.accent}\n              fillOpacity={px(0.08 + amount * 0.8)}\n              stroke=\"none\"\n            />\n          ))}\n        </g>\n\n        <g data-joint={`${leg.side}-knee`}>\n          <circle cx={px(to(leg.knee).x)} cy={px(to(leg.knee).y)} r={4} {...machined} />\n          <circle cx={px(to(leg.knee).x)} cy={px(to(leg.knee).y)} r={1.7} fill={palette.dark} />\n        </g>\n        <g data-joint={`${leg.side}-ankle`}>\n          <circle cx={px(to(leg.ankle).x)} cy={px(to(leg.ankle).y)} r={3.2} {...machined} />\n          <circle cx={px(to(leg.ankle).x)} cy={px(to(leg.ankle).y)} r={1.4} fill={palette.dark} />\n        </g>\n        <circle\n          data-joint={`${leg.side}-toe`}\n          cx={px(to(leg.ball).x)}\n          cy={px(to(leg.ball).y)}\n          r={1.5}\n          {...cast}\n        />\n      </g>\n    )\n  }\n\n  /**\n   * One arm, with `robot-hand`'s own chassis on the end of it: the palm slab\n   * with its thenar plate, a knuckle per finger, alternating phalanx shells,\n   * and a pad on each fingertip shaded by how far that digit has closed. Same\n   * solver, same pose, drawn at the scale the machine wears it.\n   */\n  const armPart = (arm: SkeletonArm) => {\n    const hand = body.hands.find((entry) => entry.side === arm.side)\n    const place = (point: Vec3): Vec3 => {\n      if (!hand) return point\n      const { frame } = hand\n      const s = frame.scale\n      return {\n        x: frame.origin.x + (frame.right.x * point.x + frame.up.x * point.y + frame.out.x * point.z) * s,\n        y: frame.origin.y + (frame.right.y * point.x + frame.up.y * point.y + frame.out.y * point.z) * s,\n        z: frame.origin.z + (frame.right.z * point.x + frame.up.z * point.y + frame.out.z * point.z) * s,\n      }\n    }\n    /** Hand units to drawn radius, so a phalanx keeps its taper at this scale. */\n    const gauge = (radius: number) => Math.max(1.1, radius * (hand?.frame.scale ?? 0.25) * 2)\n    const slab = (outline: Vec2[], front: number, back: number) =>\n      slabPath(\n        outline.flatMap((point) => [\n          place({ x: point.x, y: point.y, z: front }),\n          place({ x: point.x, y: point.y, z: back }),\n        ]),\n        camera,\n      )\n\n    return (\n      <g key={`arm-${arm.side}`} data-arm={arm.side}>\n        {panelled && <path d={link(arm.shoulder, arm.elbow, 5.4)} {...shell} />}\n        <path data-humerus d={link(arm.shoulder, arm.elbow, 2.9)} {...machined} />\n        {panelled && <path d={link(arm.elbow, arm.wrist, 4.2)} {...shell} />}\n        <path data-forearm d={link(arm.elbow, arm.wrist, 2.3)} {...machined} />\n        <g data-joint={`${arm.side}-elbow`}>\n          <circle cx={px(to(arm.elbow).x)} cy={px(to(arm.elbow).y)} r={3.4} {...machined} />\n          <circle cx={px(to(arm.elbow).x)} cy={px(to(arm.elbow).y)} r={1.5} fill={palette.dark} />\n        </g>\n        {hand && (\n          <g data-hand={arm.side}>\n            <path data-palm d={slab(hand.pose.palm, hand.pose.palmFront, hand.pose.palmBack)} {...(panelled ? shell : machined)} />\n            {panelled && (\n              /* Thenar plate: the pad the thumb's saddle joint sits under. */\n              <path\n                d={slab(\n                  hand.pose.palm.map((point) => ({\n                    x: point.x * 0.5 + (arm.side === \"left\" ? -9 : 9),\n                    y: point.y * 0.48 + 12,\n                  })),\n                  hand.pose.palmFront + 1.8,\n                  hand.pose.palmFront,\n                )}\n                {...machined}\n              />\n            )}\n            {hand.pose.digits.slice(1).map((finger) => {\n              const knuckle = to(place(finger.joints[0]))\n              return (\n                <circle\n                  key={finger.name}\n                  data-knuckle={`${arm.side}-${finger.name}`}\n                  cx={px(knuckle.x)}\n                  cy={px(knuckle.y)}\n                  r={px(gauge(finger.radii[0] ?? 4) * 0.78)}\n                  {...machined}\n                />\n              )\n            })}\n            {hand.pose.digits.map((finger) => (\n              <g key={finger.name} data-digit={`${arm.side}-${finger.name}`}>\n                {finger.joints.slice(0, -1).map((joint, i) => (\n                  <path\n                    key={i}\n                    data-phalanx={`${arm.side}-${finger.name}-${i}`}\n                    d={capsulePath(to(place(joint)), to(place(finger.joints[i + 1])), gauge(finger.radii[i] ?? 3))}\n                    {...(panelled && i % 2 ? shell : machined)}\n                  />\n                ))}\n                {/* The pad is the surface that would touch something. It is a\n                    shape the solver produced, not a grip on anything. */}\n                <circle\n                  data-pad={`${arm.side}-${finger.name}`}\n                  cx={px(to(place(finger.pad)).x)}\n                  cy={px(to(place(finger.pad)).y)}\n                  r={px(gauge(finger.radii[finger.radii.length - 1] ?? 3) * 0.7)}\n                  fill={palette.accent}\n                  fillOpacity={px(finger.closure * 0.8)}\n                />\n              </g>\n            ))}\n          </g>\n        )}\n      </g>\n    )\n  }\n\n  /**\n   * The pelvis is drawn before both legs rather than inside the torso: a femur\n   * head sits on the outside of it, so a leg sorted behind the torso would\n   * otherwise disappear into the block on one side and not the other.\n   */\n  const pelvis = (\n    <g data-pelvis>\n      <path d={box({ x: pose.pelvis.x, y: pose.pelvis.y - 4, z: pose.pelvis.z }, 13, 7.5, 8)} {...(panelled ? shell : machined)} />\n      {pose.legs.map((leg) => (\n        <circle\n          key={leg.side}\n          data-joint={`${leg.side}-hip`}\n          cx={px(to(leg.hip).x)}\n          cy={px(to(leg.hip).y)}\n          r={3.6}\n          {...cast}\n        />\n      ))}\n    </g>\n  )\n\n  const torso = (\n    <>\n      <g data-spine>\n        {pose.spine.map((vertebra, index) => (\n          <g key={index} data-vertebra={index}>\n            {index > 0 && <path d={link(pose.spine[index - 1], vertebra, 1.8)} {...cast} />}\n            <path d={box(vertebra, 2.8, 1.4, 2.4)} {...machined} />\n          </g>\n        ))}\n      </g>\n\n      <g data-ribcage>\n        {body.ribs.map((rib) => (\n          <g key={rib.index} data-rib={rib.index}>\n            {[rib.left, rib.right].map((arc, half) => (\n              <path key={half} d={trace3(arc, false)} {...hoopPaint} />\n            ))}\n          </g>\n        ))}\n      </g>\n\n      <g data-chest>\n        <path d={link(body.sternum[0], body.sternum[1], panelled ? 8 : 3.2)} {...(panelled ? shell : machined)} />\n        {/* The core: it opens with the breath, because the cage it sits in does. */}\n        <circle\n          data-core\n          cx={px(to(midpoint(body.sternum[0], body.sternum[1])).x)}\n          cy={px(to(midpoint(body.sternum[0], body.sternum[1])).y)}\n          r={px(3 + posed.breath * 1.6)}\n          fill={palette.accent}\n          fillOpacity={px(0.5 + posed.breath * 0.5)}\n        />\n      </g>\n\n      <g data-shoulders>\n        <path d={link(pose.arms[0].shoulder, pose.arms[1].shoulder, panelled ? 4.4 : 2.8)} {...machined} />\n        {pose.arms.map((arm) => (\n          <circle\n            key={arm.side}\n            data-joint={`${arm.side}-shoulder`}\n            cx={px(to(arm.shoulder).x)}\n            cy={px(to(arm.shoulder).y)}\n            r={panelled ? 4.6 : 3.8}\n            {...cast}\n          />\n        ))}\n      </g>\n    </>\n  )\n\n  /* ---------------------------------------------------------- the balance */\n\n  const onFloor = (point: Vec2): Vec3 => ({ x: point.x, y: 0, z: point.y })\n  /**\n   * The polygon lies on the floor, so it belongs under the feet that hold it;\n   * the weight and its plumb line are a readout about the whole machine, so\n   * they belong over it. Drawing both at one end gets one of them wrong —\n   * either dashes cutting across the boots, or a plumb line sawn in half by\n   * the chest plate.\n   */\n  const supportLayer = showBalance && (\n    <g data-balance=\"support\">\n      {body.balance.support.length > 0 && (\n        /* Mid-stride the polygon narrows to one foot, and at toe-off to a line\n           across the toes. It is drawn as whatever it actually is rather than\n           padded out to look like a base. */\n        <path\n          data-support\n          d={trace3(body.balance.support.map(onFloor), body.balance.support.length > 2)}\n          fill={body.balance.support.length > 2 ? palette.accent : \"none\"}\n          fillOpacity={0.14}\n          stroke={body.balance.stable ? palette.accent : palette.dark}\n          strokeWidth={1.2}\n          strokeDasharray=\"3 3\"\n        />\n      )}\n    </g>\n  )\n\n  const weightLayer = showBalance && (\n    <g data-balance=\"weight\">\n      <path\n        data-plumb\n        d={trace3([body.balance.com, onFloor(body.balance.ground)], false)}\n        fill=\"none\"\n        stroke={body.balance.stable ? palette.accent : palette.dark}\n        strokeWidth={0.9}\n        strokeDasharray=\"2 3\"\n        opacity={0.8}\n      />\n      <circle\n        data-com\n        cx={px(to(body.balance.com).x)}\n        cy={px(to(body.balance.com).y)}\n        r={3.4}\n        fill=\"none\"\n        stroke={body.balance.stable ? palette.accent : palette.dark}\n        strokeWidth={1.6}\n      />\n    </g>\n  )\n\n  /* ------------------------------------------------------------- assembly */\n\n  /**\n   * Draw order is the camera's, not a fixed list. An arm is placed by whichever\n   * of its shoulder and its wrist is nearest, so a hand reaching out in front\n   * passes in front of the chest instead of being painted into the rib cage;\n   * and in plan view, where depth *is* height, the head comes out on top of the\n   * legs by itself. Ties keep the original order, so a pure elevation — where\n   * left and right sit at exactly the same depth — draws as it always did.\n   */\n  const pieces: Array<{ key: string; depth: number; node: React.ReactNode }> = [\n    ...pose.legs.map((leg) => ({\n      key: `leg-${leg.side}`,\n      depth: depthOf(leg.ankle),\n      node: legPart(leg),\n    })),\n    ...pose.arms.map((arm) => ({\n      key: `arm-${arm.side}`,\n      depth: Math.max(depthOf(arm.shoulder), depthOf(arm.wrist)),\n      node: armPart(arm),\n    })),\n    { key: \"torso\", depth: depthOf(body.sternum[1]), node: torso },\n    {\n      key: \"head\",\n      // The skull is a solid, so its centre is the wrong point to sort on: the\n      // camera is linear, so the nearest point of the ellipsoid is its centre\n      // plus the radii projected onto the view direction. Sorting on the centre\n      // puts the chest plate over the jaw.\n      depth:\n        depthOf(centreOfHead) +\n        Math.abs(worldRadii.x * toCamera.x) +\n        Math.abs(worldRadii.y * toCamera.y) +\n        Math.abs(worldRadii.z * toCamera.z),\n      node: head,\n    },\n  ]\n  const ordered = pieces\n    .map((piece, order) => ({ piece, order }))\n    .sort((a, b) => a.piece.depth - b.piece.depth || a.order - b.order)\n    .map(({ piece }) => piece)\n\n  const headYaw = Math.round(headPose.yaw)\n  const margin = Number.isFinite(body.balance.margin) ? Math.round(body.balance.margin) : 0\n  const readout = `${routine.toUpperCase()} / ${headYaw > 0 ? `${headYaw}° LEFT` : headYaw < 0 ? `${-headYaw}° RIGHT` : \"AHEAD\"} / ${body.balance.stable ? `MARGIN ${margin}` : \"OFF SUPPORT\"}`\n\n  return (\n    <svg\n      ref={svgRef}\n      role={role ?? (interactive ? \"slider\" : \"img\")}\n      aria-label={\n        ariaLabel ??\n        `Animatronic robot, ${routine} routine, looking ${headYaw === 0 ? \"straight ahead\" : `${Math.abs(headYaw)} degrees to its ${headYaw > 0 ? \"left\" : \"right\"}`}, ${body.balance.stable ? \"weight over its feet\" : \"weight outside its support\"}, ${viewNames[view] ?? viewNames[NATIVE_VIEW]}`\n      }\n      aria-valuemin={interactive ? -90 : undefined}\n      aria-valuemax={interactive ? 90 : undefined}\n      aria-valuenow={interactive ? headYaw : undefined}\n      aria-valuetext={\n        interactive\n          ? headYaw === 0\n            ? \"looking straight ahead\"\n            : `looking ${Math.abs(headYaw)} degrees to its ${headYaw > 0 ? \"left\" : \"right\"}`\n          : undefined\n      }\n      tabIndex={tabIndex ?? (interactive ? 0 : undefined)}\n      onKeyDown={(event) => {\n        onKeyDown?.(event)\n        if (!interactive || event.defaultPrevented) return\n        const here = target ?? { x: 0, y: 0 }\n        const step = arrowStep(event.key, 0.12, 0.4)\n        if (event.key === \"ArrowLeft\" || event.key === \"ArrowRight\") {\n          apply({ x: here.x + (event.key === \"ArrowLeft\" ? -0.12 : 0.12), y: here.y })\n        } else if (event.key === \"ArrowUp\" || event.key === \"ArrowDown\") {\n          apply({ x: here.x, y: here.y + (event.key === \"ArrowUp\" ? 0.12 : -0.12) })\n        } else if (step !== 0) {\n          apply({ x: here.x + step, y: here.y })\n        } else if (event.key === \"Home\") {\n          apply({ x: 0, y: 0 })\n        } else if (event.key === \"End\") {\n          // Hand it back to the routine.\n          apply(null)\n        } else return\n        event.preventDefault()\n      }}\n      onBlur={(event) => {\n        onBlur?.(event)\n        if (!dragging) apply(null)\n      }}\n      viewBox={`0 0 ${VIEW_WIDTH} ${VIEW_HEIGHT}`}\n      width={width}\n      height={px((width * VIEW_HEIGHT) / VIEW_WIDTH)}\n      className={cn(\n        \"max-w-full select-none\",\n        interactive &&\n          \"cursor-grab touch-none focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-[currentColor]\",\n        dragging && \"cursor-grabbing\",\n        className,\n      )}\n      style={{ color: palette.foreground, ...style }}\n      data-view={view}\n      {...props}\n    >\n      <g\n        data-animatronic\n        transform={`translate(${origin.x} ${origin.y}) scale(${px(SCALE * fit)})`}\n      >\n        {showGround && (\n          /* The floor is a disc in the floor plane, projected — not an ellipse\n             typed to look like one. A fixed ellipse at the origin stops short\n             of a foot whose toe is 30 units out, which reads as the machine\n             standing through its own shadow. */\n          <path\n            data-ground\n            d={trace3(circleFootprint(0, 0, 44, 48).map(onFloor))}\n            fill={palette.dark}\n            opacity={px(pose.grounded ? 0.14 : 0.06)}\n          />\n        )}\n        {supportLayer}\n\n        {pelvis}\n        {ordered.map((piece) => (\n          <React.Fragment key={piece.key}>{piece.node}</React.Fragment>\n        ))}\n\n        {weightLayer}\n\n        {variant === \"blueprint\" && (\n          <path\n            d={trace3(\n              [\n                { x: -52, y: 0, z: 0 },\n                { x: 52, y: 0, z: 0 },\n              ],\n              false,\n            )}\n            fill=\"none\"\n            stroke={palette.grid}\n            strokeWidth={0.4}\n            strokeDasharray=\"2 3\"\n            opacity={0.7}\n          />\n        )}\n      </g>\n\n      <g fontFamily=\"ui-monospace, monospace\" textAnchor=\"middle\" fill={palette.foreground}>\n        {showReadout && (\n          <text x={VIEW_WIDTH / 2} y={281} fontSize={5}>\n            {readout}\n          </text>\n        )}\n        {label && (\n          <text x={VIEW_WIDTH / 2} y={291} fontSize={4.5}>\n            {label}\n          </text>\n        )}\n      </g>\n    </svg>\n  )\n}\n\n/**\n * Where a look in −1..1 sits in the body frame, for a machine that reaches for\n * what it is watching: out in front, up with the look, and across with it.\n */\n/** Where the hands sit with nothing to reach for: below the machine, so both\n *  arms clamp to their own reach and hang nearly straight down. */\nconst REST_REACH: Vec3 = { x: 0, y: -30, z: -6 }\n\nconst mix3 = (a: Vec3, b: Vec3, t: number): Vec3 => {\n  const k = clamp(t, 0, 1)\n  return {\n    x: a.x + (b.x - a.x) * k,\n    y: a.y + (b.y - a.y) * k,\n    z: a.z + (b.z - a.z) * k,\n  }\n}\n\nfunction aimedReach(target: Vec2, side: -1 | 1): Vec3 {\n  // Both hands go out toward the point, but each stays on its own side of the\n  // machine — two hands offered, not two hands meeting in the middle.\n  return {\n    x: side * 19 + clamp(target.x, -1, 1) * 24,\n    y: 116 + clamp(target.y, -1, 1) * 30,\n    z: -44,\n  }\n}\n\nconst midpoint = (a: Vec3, b: Vec3): Vec3 => ({\n  x: (a.x + b.x) / 2,\n  y: (a.y + b.y) / 2,\n  z: (a.z + b.z) / 2,\n})\n\nfunction resolveExpression(\n  supplied: FaceExpression | FaceChannels | undefined,\n  fallback: FaceChannels,\n): FaceChannels {\n  if (!supplied) return fallback\n  if (typeof supplied === \"string\") return faceShape(supplied)\n  return supplied\n}\n\n/** A caller's own servo values, applied last — these win outright. */\nfunction withChannels<T extends FaceChannels>(\n  solution: T,\n  channels: Partial<FaceChannels> & { left?: Partial<FaceSide>; right?: Partial<FaceSide> },\n): T {\n  return {\n    ...solution,\n    ...channels,\n    left: { ...solution.left, ...channels.left },\n    right: { ...solution.right, ...channels.right },\n  }\n}\n\nexport { AnimatronicRobot }\n",
      "type": "registry:ui",
      "target": "@ui/animatronic-robot.tsx"
    }
  ],
  "categories": [
    "robotics",
    "droids"
  ],
  "type": "registry:ui"
}