{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "bore-construct",
  "title": "Bore construct",
  "description": "A construct-light tunnelling head: a stepped rotary bit forward, a pair of treaded drive wheels on one transverse axle aft that roll it into the face, and a right-angle gear train, thrust rams, grippers and a flushing pump between them, boring through a wall that cracks, spalls and heaps its own spoil.",
  "registryDependencies": [
    "https://robocn.dev/r/robot-style.json",
    "https://robocn.dev/r/robot-kinematics.json",
    "https://robocn.dev/r/use-robot-motion.json",
    "https://robocn.dev/r/bore-geometry.json",
    "https://robocn.dev/r/linkage-geometry.json",
    "https://robocn.dev/r/transmission-geometry.json"
  ],
  "files": [
    {
      "path": "src/components/ui/bore-construct.tsx",
      "content": "\"use client\"\n\n/**\n * bore-construct — a tunnelling head forged out of light, and the wall it takes\n * with it.\n *\n * Every other machine in the set moves in front of a background it never\n * touches. This one's whole job is to change that background: the hole is the\n * real intersection of the bit's swept envelope with the slab, the spoil heaped\n * at the collar is the volume that came out of it, and the rubble off the kerf\n * is integrated ballistically. All of that is `src/lib/robocn/boring.ts` —\n * pure, no React, tested on its own.\n *\n * **Solved:** the penetration rate, from an energy balance at the face; the\n * cavity and its clipping at both faces; the excavated volume and the heap that\n * conserves it; the spall trajectories; the reduction — a fixed-ring planetary\n * set whose `ratio` is why the bit turns slower than the drum; the transfer\n * pair, meshed through `meshAngle`; the flushing pump, a real slider-crank; and\n * the main bearing's cage ratio.\n *\n * **Illustrated:** the fracture pattern around the bore (deterministic, but no\n * fracture mechanics), the bit's flights, the wheel's tread, the hoses and fins,\n * and the glow. Nothing collides — the machine is not stopped by the wall, it is\n * driven through it by `depth`.\n *\n * The wall is drawn as a **cutaway**: an opaque slab whose near half is washed\n * back, so the machine inside the bore can be seen. The bit is drawn inscribed\n * in the envelope the cavity is cut from, so it never stands outside its own\n * hole.\n *\n * Drawn once in the profile elevation and pushed through `robotCamera`, so all\n * four views are the same geometry rather than four drawings that drift apart.\n *\n * Original archetype: a generic boring construct, named for its job. Design\n * note: docs/bore-construct.md.\n */\n\nimport * as React from \"react\"\n\nimport { arrowStep, useRobotDrag, useRobotScalar } from \"@/hooks/use-robot-motion\"\nimport {\n  BORE_TAPER,\n  boreCavity,\n  boreDuty,\n  boreEnvelope,\n  boreFractures,\n  boreSpall,\n  cageRatio,\n  rollAngle,\n  spoilHeap,\n  type BoreWall,\n} from \"@/lib/robocn/boring\"\nimport { clamp, type Vec2 } from \"@/lib/robocn/kinematics\"\nimport { solveSliderCrank } from \"@/lib/robocn/linkage\"\nimport {\n  elevationPoint,\n  boxCorners,\n  elevationDraft,\n  fitTransform,\n  px,\n  resolveRobotPalette,\n  resolveRobotSize,\n  robotCamera,\n  robotSurface,\n  type RobotPaletteProps,\n  type RobotSize,\n  type RobotSurface,\n  type RobotVariant,\n  type RobotView,\n} from \"@/lib/robocn/style\"\nimport { gearPath, meshAngle, planetaryPose, planetaryTrain } from \"@/lib/robocn/transmission\"\nimport { cn } from \"@/lib/utils\"\n\n/** What the machine does with nobody driving it. Always includes `static`. */\nexport type BoreConstructBehavior = \"bore\" | \"surge\" | \"idle\" | \"static\"\n\nconst VIEW_WIDTH = 316\nconst VIEW_HEIGHT = 132\nconst NATIVE_VIEW: RobotView = \"profile\"\n\n/* -------------------------------------------------------------------------- */\n/* the machine, in world units: x along the drawing, y up, depth out of it      */\n/* -------------------------------------------------------------------------- */\n\n/** Centreline of the machine, and of the bore. Low, because the drive wheel\n *  behind it has to reach the floor. */\nconst AXIS = 30\n/** Gauge radius of the cutterhead — the largest thing on the machine, so\n *  everything behind it passes through the hole it cuts. */\nconst BIT_R = 24\nconst BODY_R = 16\n/** Nose-cone length — the same one `boreCavity` cuts with. */\nconst TAPER = BIT_R * BORE_TAPER\n\n/** Where the machine sits at `depth = 0`: crown on the face, tail at `TAIL`. */\nconst TAIL = -194\n/** How far the crown travels between `depth` 0 and 1. */\nconst TRAVEL = 96\n\nconst WALL: BoreWall = { face: 0, thickness: 46, axis: AXIS, base: 0, top: 88 }\nconst WALL_DEPTH = 52\n/** The share of the muck that reaches the collar; the rest packs the bore\n *  behind the machine, which is what a real head leaves behind it. */\nconst SPOIL_SHARE = 0.35\n\nconst ENVELOPE = boxCorners(\n  { x: -WALL_DEPTH, y: 0, z: -(TRAVEL + 8) },\n  { x: WALL_DEPTH, y: WALL.top, z: -TAIL },\n)\n\n/** The stepped bit, each cylinder inscribed in the cone the cavity is cut from. */\nconst BIT_STEPS: { from: number; to: number; radius: number }[] = [\n  { from: 0, to: 0.2, radius: 0.08 },\n  { from: 0.2, to: 0.4, radius: 0.2 },\n  { from: 0.4, to: 0.6, radius: 0.4 },\n  { from: 0.6, to: 0.8, radius: 0.6 },\n  { from: 0.8, to: 1, radius: 0.8 },\n]\n\n/** The reduction between the drum and the bit. Tooth counts that assemble. */\nconst TRAIN = planetaryTrain(16, 12, 3)\n/** Module of the planetary set, so the ring fits the spindle housing. */\nconst PLANET_MODULE = 0.7\nconst SUN_R = (PLANET_MODULE * TRAIN.sun) / 2\nconst PLANET_R = (PLANET_MODULE * TRAIN.planet) / 2\nconst RING_R = (PLANET_MODULE * TRAIN.ring) / 2\nconst CARRIER_R = SUN_R + PLANET_R\n\n/** Right-angle tap off the main shaft, out to the layshaft. */\nconst CROWN: Vec2 = { x: -96, y: AXIS }\nconst CROWN_TEETH = 34\nconst CROWN_R = 17\nconst PINION_TEETH = 13\nconst PINION_R = 6.5\n/** The layshaft gear, up and aft of the crown so it clears the bore. */\nconst LAYSHAFT_BEARING = -135\nconst LAY_TEETH = 10\nconst LAY_R = 5\nconst LAY: Vec2 = {\n  x: CROWN.x - (CROWN_R + LAY_R) * Math.SQRT1_2,\n  y: CROWN.y + (CROWN_R + LAY_R) * Math.SQRT1_2,\n}\n\n/** The flushing pump, on the front end of the layshaft. */\nconst PUMP_DEPTH = 12\nconst PUMP_CRANK = 4\nconst PUMP_ROD = 13\n\n/** Thrust rams, four about the axis, and the gripper shoes they react against. */\nconst RAM_RING = 19\nconst RAM_ANGLES = [45, 135, 225, 315]\nconst RAM_ROOT = -126\nconst RAM_HEAD = -62\nconst GRIP_X = -74\nconst GRIP_ANGLES = [0, 90, 180, 270]\n\n/** Main bearing: rollers on a pitch circle, orbiting at the real cage ratio. */\nconst ROLLERS = 9\nconst ROLLER_R = 2.1\nconst BEARING_PITCH = 11\nconst CAGE = cageRatio(ROLLER_R * 2, BEARING_PITCH * 2)\n\n/**\n * The drive wheel: a pair on one transverse axle behind the body, rolling on\n * the floor. Its radius is its ride height, so the hub sits exactly on the\n * machine's axis and the tyre meets the ground.\n */\nconst AXLE: Vec2 = { x: -164, y: AXIS }\n/** One unit shy of the ride height, so the tread tips meet the floor. */\nconst WHEEL_R = AXIS - 1\nconst RIM_R = WHEEL_R - 6.5\nconst HUB_R = 8\n/** Half the track: one wheel each side of the body, on the same axle. */\nconst WHEEL_TRACK = 19\nconst WHEEL_WIDTH = 5.5\nconst TREAD_BLOCKS = 20\nconst SPOKES = 8\n/** Longitudinal ribs down the body, the way the reference is built. */\nconst RIBS = 12\n\nconst viewNames: Record<RobotView, string> = {\n  plan: \"plan view\",\n  front: \"front elevation\",\n  profile: \"side elevation\",\n  iso: \"isometric view\",\n}\n\n/* -------------------------------------------------------------------------- */\n/* motion                                                                      */\n/* -------------------------------------------------------------------------- */\n\n/**\n * How far through the wall the head is at `clock`, 0 to 1, where `cycles` is\n * the share of the travel the solved penetration rate covers per clock unit.\n *\n * `bore` drives through and backs out to re-enter; `surge` takes the same\n * ground in four bites, with the grippers re-setting between them; `idle` turns\n * at the face without advancing.\n */\nexport function boreConstructAdvance(\n  behavior: BoreConstructBehavior,\n  clock: number,\n  cycles = 0.25,\n): number {\n  if (!Number.isFinite(clock)) return 0.45\n  const per = Number.isFinite(cycles) ? clamp(cycles, 0, 4) : 0.25\n  const t = ((((clock * per) % 1) + 1) % 1)\n  switch (behavior) {\n    case \"bore\":\n      // In over the first 82% of the cycle, withdrawn quickly to re-enter.\n      return t < 0.82 ? t / 0.82 : clamp(1 - (t - 0.82) / 0.18, 0, 1)\n    case \"surge\": {\n      // Four bites: push, hold while the grippers re-set, push again.\n      const bites = 4\n      const step = Math.floor(t * bites)\n      const within = t * bites - step\n      const pushed = (step + clamp(within / 0.62, 0, 1)) / bites\n      return clamp(pushed, 0, 1)\n    }\n    case \"idle\":\n      return 0.015 + (Math.sin(clock * Math.PI * 2) + 1) * 0.01\n    default:\n      return 0.45\n  }\n}\n\n/** A point on a ring about the machine axis, in the drawing and out of it. */\nfunction aboutAxis(radius: number, degrees: number) {\n  const a = (degrees * Math.PI) / 180\n  return { across: Math.cos(a) * radius, depth: Math.sin(a) * radius }\n}\n\n/* -------------------------------------------------------------------------- */\n/* the component                                                               */\n/* -------------------------------------------------------------------------- */\n\nexport interface BoreConstructProps\n  extends Omit<React.ComponentProps<\"svg\">, \"color\">,\n    RobotPaletteProps {\n  /**\n   * Controlled bore progress: 0 crown on the near face, 1 crown clear of the\n   * far one. Supplying it pins the advance exactly — the spindle keeps turning,\n   * because a drill held at depth is still a drill that is turning.\n   */\n  depth?: number\n  onDepthChange?: (depth: number) => void\n  behavior?: BoreConstructBehavior\n  /** Spindle speed, bit revolutions per clock unit. The drum turns faster. */\n  rev?: number\n  /** Torque at the bit, 0 free to 1 the drive's rating. */\n  thrust?: number\n  /** The material: 0 spoil, 1 hard rock. Too hard for the thrust and it stalls. */\n  hardness?: number\n  /** How solidly the construct stands, 0 to 1. Paint only — nothing moves. */\n  forge?: number\n  showWall?: boolean\n  /** The rubble off the kerf and the heap at the collar. */\n  showSpoil?: boolean\n  showGround?: boolean\n  /** Where the camera stands. Defaults to the view the machine was drawn in. */\n  view?: RobotView\n  speed?: number\n  phase?: number\n  paused?: boolean\n  animate?: boolean\n  interactive?: boolean\n  label?: string\n  size?: RobotSize | number\n  variant?: RobotVariant\n}\n\nfunction BoreConstruct({\n  depth,\n  onDepthChange,\n  behavior = \"bore\",\n  rev = 1.1,\n  thrust = 0.72,\n  hardness = 0.45,\n  forge = 1,\n  showWall = true,\n  showSpoil = true,\n  showGround = true,\n  view = NATIVE_VIEW,\n  speed = 0.5,\n  phase = 0,\n  paused = false,\n  animate = true,\n  interactive = false,\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}: BoreConstructProps) {\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  const [held, setHeld] = React.useState<number | null>(null)\n  const controlled = depth !== undefined\n\n  const spin = Number.isFinite(rev) ? clamp(rev, 0, 12) : 1.1\n  const torque = Number.isFinite(thrust) ? clamp(thrust, 0, 1) : 0.72\n  const rock = Number.isFinite(hardness) ? clamp(hardness, 0, 1) : 0.45\n  const solidity = Number.isFinite(forge) ? clamp(forge, 0, 1) : 1\n\n  // The energy balance at the face. Everything the readout shows, and the rate\n  // the behaviour advances at, comes out of this one call.\n  const duty = boreDuty({ radius: BIT_R, cutters: 8, rev: spin, torque, hardness: rock })\n\n  // Controlled wins and pins the advance; the clock keeps running underneath,\n  // so the spindle turns and release reads as a machine resuming.\n  const hold = controlled ? (Number.isFinite(depth) ? clamp(depth as number, 0, 1) : 0) : held\n  // A stalled head advances at zero, and the behaviour eases back to the face.\n  const cycles = clamp(duty.rate / TRAVEL, 0, 4)\n  const goal = React.useCallback(\n    (clock: number) => boreConstructAdvance(behavior, clock, cycles),\n    [behavior, cycles],\n  )\n  const motion = useRobotScalar(goal, {\n    rate: 2.4,\n    hold,\n    speed,\n    paused,\n    phase,\n    animate: animate && behavior !== \"static\",\n  })\n  const progress = clamp(motion.value, 0, 1)\n  const advance = progress * TRAVEL\n\n  const camera = robotCamera(view)\n  const frame = fitTransform(ENVELOPE, camera, VIEW_WIDTH, VIEW_HEIGHT)\n  const { point: to, path: line, solid, box, bar, disc } = elevationDraft(camera, \"profile\")\n\n  const apply = React.useCallback(\n    (next: number) => {\n      const bounded = Math.round(clamp(next, 0, 1) * 100) / 100\n      setHeld(bounded)\n      onDepthChange?.(bounded)\n    },\n    [onDepthChange],\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((unit: Vec2) => apply(unit.x), [apply]),\n    onDragEnd: React.useCallback(() => setHeld(null), []),\n  })\n\n  /* --- paint ------------------------------------------------------------- */\n\n  const base = {\n    shell: robotSurface(\"shell\", variant, palette),\n    metal: robotSurface(\"metal\", variant, palette),\n    dark: robotSurface(\"dark\", variant, palette),\n    accent: robotSurface(\"accent\", variant, palette),\n  }\n  /**\n   * A construct is solid light: in `solid` the panels wash out and every edge\n   * lights up. The other three variants are the set's own, untouched — this\n   * only ever changes paint.\n   */\n  const forged = (surface: RobotSurface, fill = 0.26): RobotSurface =>\n    variant === \"solid\"\n      ? { ...surface, stroke: palette.glow, strokeWidth: surface.strokeWidth + 0.55, fillOpacity: fill }\n      : surface\n  const shell = forged(base.shell)\n  const machined = forged(base.metal, 0.3)\n  const cast = forged(base.dark, 0.5)\n  const lit = forged(base.accent, 0.72)\n  // The wall is not a construct: it stays opaque.\n  const rockFace = base.dark\n  const rockCut = base.metal\n\n  /* --- the cut ------------------------------------------------------------ */\n\n  const cavity = boreCavity(advance, { radius: BIT_R }, WALL)\n  const envelope = boreEnvelope(advance, { radius: BIT_R }, WALL)\n  const crown: Vec2 = { x: advance, y: AXIS }\n  const inWall = showWall && advance > 0 && advance < WALL.thickness + TAPER\n  const cutting = duty.turning ? clamp(duty.rate / 30, 0, 1) : 0\n  const spall =\n    showWall && showSpoil && inWall\n      ? boreSpall(motion.clock, cutting, { radius: BIT_R }, { at: crown, count: 16, life: 0.8 })\n      : []\n  const heap = spoilHeap(cavity.volume * SPOIL_SHARE, 27, { x: WALL.face - 26, y: 0 })\n  const fractures = showWall ? boreFractures(cavity, { radius: BIT_R, hardness: rock }, WALL, 14) : []\n  // Courses through the wall, interrupted exactly where the bore has eaten them.\n  const courses = [16, 34, 52, 70, 88, 106].map((y) => {\n    const half = Math.abs(y - AXIS)\n    // The envelope reaches half-width `half` at this x; ahead of it is rock.\n    const eaten = half <= BIT_R ? clamp(advance - (half / BIT_R) * TAPER, 0, WALL.thickness) : 0\n    return { y, from: WALL.face + eaten, to: WALL.face + WALL.thickness }\n  })\n\n  /* --- the drive train ---------------------------------------------------- */\n\n  const turns = motion.clock * spin\n  const bitAngle = turns * 360\n  // The cutterhead drive: the spindle turns `ratio` times for every turn of\n  // the bit, and the bevel branch off it drives the flushing pump.\n  const spindleAngle = bitAngle * TRAIN.ratio\n  const planets = planetaryPose(TRAIN, spindleAngle)\n  // The drive wheel rolls: it turns exactly as far as the machine has moved.\n  const wheelAngle = rollAngle(advance, WHEEL_R)\n  // A bevel, so the ratio is the tooth counts and the drawing is two cones.\n  const crownAngle = -spindleAngle * (PINION_TEETH / CROWN_TEETH)\n  const layAngle = meshAngle(CROWN_TEETH, crownAngle, LAY_TEETH, LAYSHAFT_BEARING)\n  const pump = solveSliderCrank(layAngle, { crank: PUMP_CRANK, rod: PUMP_ROD })\n  const cageAngle = spindleAngle * CAGE\n  const ramStroke = torque * 9\n\n  /* --- draw order --------------------------------------------------------- */\n\n  /** How far toward the camera a part riding a ring about the axis sits. */\n  const axialDepth = (x: number, across: number, out: number) => {\n    const p = elevationPoint({ x, y: AXIS + across }, out, \"profile\")\n    return camera.depth(p.x, p.y, p.z)\n  }\n  /** Sorts anything riding that ring back to front, so it paints in order. */\n  const sortAxial = <T extends { x: number; across: number; depth: number }>(items: T[]) =>\n    [...items].sort(\n      (a, b) => axialDepth(a.x, a.across, a.depth) - axialDepth(b.x, b.across, b.depth),\n    )\n\n  const cutters = sortAxial(\n    Array.from({ length: 6 }, (_, i) => {\n      const { across, depth: d } = aboutAxis(BIT_R * 0.72, bitAngle + (i * 360) / 6)\n      return { x: -TAPER * 0.55, across, depth: d, index: i }\n    }),\n  )\n  const gauge = sortAxial(\n    Array.from({ length: 8 }, (_, i) => {\n      const { across, depth: d } = aboutAxis(BIT_R - 2.6, bitAngle * -1 + (i * 360) / 8)\n      return { x: -TAPER - 7, across, depth: d, index: i }\n    }),\n  )\n  const rollers = sortAxial(\n    Array.from({ length: ROLLERS }, (_, i) => {\n      const { across, depth: d } = aboutAxis(BEARING_PITCH, cageAngle + (i * 360) / ROLLERS)\n      return { x: -50, across, depth: d, index: i }\n    }),\n  )\n  const carriers = sortAxial(\n    planets.planets.map((planet, i) => {\n      const { across, depth: d } = aboutAxis(CARRIER_R, planet.bearing)\n      return { x: -68, across, depth: d, index: i }\n    }),\n  )\n  /** A point on the wheel's own rim, in drawing coordinates. */\n  const onRim = (radius: number, degrees: number): Vec2 => {\n    const a = ((degrees + wheelAngle) * Math.PI) / 180\n    return { x: AXLE.x + Math.cos(a) * radius, y: AXLE.y + Math.sin(a) * radius }\n  }\n  const spokeAngles = Array.from({ length: SPOKES }, (_, i) => (i * 360) / SPOKES)\n  const treadAngles = Array.from({ length: TREAD_BLOCKS }, (_, i) => (i * 360) / TREAD_BLOCKS)\n  const ribs = sortAxial(\n    Array.from({ length: RIBS }, (_, i) => {\n      const { across, depth: d } = aboutAxis(BODY_R + 0.8, (i * 360) / RIBS)\n      return { x: -150, across, depth: d, index: i }\n    }),\n  )\n  const blades = sortAxial(\n    Array.from({ length: 3 }, (_, i) => {\n      const angle = bitAngle + 90 + (i * 360) / 3\n      const { across, depth: d } = aboutAxis(BIT_R + 5, angle)\n      return { x: -34, across, depth: d, index: i, angle }\n    }),\n  )\n  const rams = sortAxial(\n    RAM_ANGLES.map((angle, i) => {\n      const { across, depth: d } = aboutAxis(RAM_RING, angle)\n      return { x: RAM_ROOT, across, depth: d, index: i }\n    }),\n  )\n  const grips = sortAxial(\n    GRIP_ANGLES.map((angle, i) => {\n      const { across, depth: d } = aboutAxis(1, angle)\n      return { x: GRIP_X, across, depth: d, index: i, angle }\n    }),\n  )\n\n  const percent = Math.round(progress * 100)\n  const readout = `${percent} percent through the wall`\n  // The gear artwork lives in the elevation plane: x right, y down.\n  const inPlane = (depthOut = 0) => camera.wall(depthOut, 90) || undefined\n\n  return (\n    <svg\n      ref={svgRef}\n      role={role ?? (interactive ? \"slider\" : \"img\")}\n      aria-label={\n        ariaLabel ??\n        `Bore construct, ${readout}, ${duty.turning ? \"cutting\" : \"stalled against the face\"}, ${viewNames[view] ?? viewNames.profile}`\n      }\n      aria-valuemin={interactive ? 0 : undefined}\n      aria-valuemax={interactive ? 1 : undefined}\n      aria-valuenow={interactive ? px(progress) : undefined}\n      aria-valuetext={interactive ? readout : undefined}\n      tabIndex={tabIndex ?? (interactive ? 0 : undefined)}\n      onKeyDown={(event) => {\n        onKeyDown?.(event)\n        if (!interactive || event.defaultPrevented) return\n        const delta = arrowStep(event.key, 0.04, 0.2)\n        if (delta !== 0) apply(progress + delta)\n        else if (event.key === \"Home\") apply(0)\n        else if (event.key === \"End\") apply(1)\n        else return\n        event.preventDefault()\n      }}\n      viewBox={`0 0 ${VIEW_WIDTH} ${VIEW_HEIGHT}`}\n      width={width}\n      height={px((width * VIEW_HEIGHT) / VIEW_WIDTH)}\n      onBlur={(event) => {\n        onBlur?.(event)\n        if (!dragging) setHeld(null)\n      }}\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      {...props}\n    >\n      {variant === \"blueprint\" && (\n        <path\n          d={`M 10 ${VIEW_HEIGHT - 26} H ${VIEW_WIDTH - 10}`}\n          fill=\"none\"\n          stroke={palette.grid}\n          strokeWidth={0.5}\n          strokeDasharray=\"3 4\"\n          opacity={0.4}\n        />\n      )}\n\n      <g data-view={view} transform={frame || undefined}>\n        {showGround && (\n          <>\n            <path\n              data-ground\n              d={solid([{ x: TAIL - 6, y: 0 }, { x: TRAVEL + 6, y: 0 }], WALL_DEPTH)}\n              fill={palette.dark}\n              opacity={0.12}\n            />\n            <path\n              d={line([{ x: TAIL - 6, y: 0 }, { x: TRAVEL + 6, y: 0 }])}\n              fill=\"none\"\n              stroke={palette.dark}\n              strokeWidth={1}\n              opacity={0.5}\n            />\n          </>\n        )}\n\n        {/* The spoil that reached the collar: the volume the bit removed. */}\n        {showWall && showSpoil && heap.radius > 0.5 && (\n          <g data-spoil data-volume={px(cavity.volume)}>\n            <path\n              d={solid(heap.outline, heap.radius * 0.72)}\n              fill={palette.dark}\n              opacity={0.18}\n            />\n            <path\n              d={line(heap.outline)}\n              fill=\"none\"\n              stroke={palette.dark}\n              strokeWidth={0.7}\n              strokeLinejoin=\"round\"\n              opacity={0.42}\n            />\n            {/* A few boulders shed off the toe, so the heap reads as loose. */}\n            {[-0.82, -0.44, 0.5, 0.86].map((along, index) => (\n              <path\n                key={along}\n                d={disc(\n                  { x: WALL.face - 26 + along * heap.radius, y: 1.4 + index * 0.4 },\n                  1.4 + index * 0.35,\n                  1.2,\n                  (index - 1.5) * heap.radius * 0.3,\n                )}\n                fill={palette.dark}\n                opacity={0.3}\n              />\n            ))}\n          </g>\n        )}\n\n        {/* The wall, in cutaway: the slab's mass, then the sectioned face with\n            the bore cut out of it under `evenodd`. */}\n        {showWall && (\n          <g data-wall>\n            <path d={solid(cavity.outline, WALL_DEPTH)} {...rockFace} opacity={0.28} />\n            <path\n              data-cavity\n              data-progress={px(cavity.progress)}\n              d={`${line(cavity.outline, 0, true)} ${cavity.hole.length ? line(cavity.hole, 0, true) : \"\"}`}\n              fillRule=\"evenodd\"\n              fill={variant === \"solid\" || variant === \"blueprint\" ? palette.metal : \"none\"}\n              stroke={palette.dark}\n              strokeWidth={0.9}\n              opacity={variant === \"wire\" ? 0.25 : 0.42}\n            />\n            {courses.map((course) =>\n              course.to - course.from > 0.5 ? (\n                <path\n                  key={course.y}\n                  d={line([\n                    { x: course.from, y: course.y },\n                    { x: course.to, y: course.y },\n                  ])}\n                  fill=\"none\"\n                  stroke={palette.dark}\n                  strokeWidth={0.6}\n                  opacity={0.35}\n                />\n              ) : null,\n            )}\n            <g data-fracture>\n              {fractures.map((crack, index) => (\n                <path\n                  key={index}\n                  d={line(crack.points)}\n                  fill=\"none\"\n                  stroke={palette.dark}\n                  strokeWidth={px(0.3 + crack.weight * 0.6)}\n                  strokeLinecap=\"round\"\n                  strokeLinejoin=\"round\"\n                  opacity={px(0.18 + crack.weight * 0.3)}\n                />\n              ))}\n            </g>\n            {/* The void itself: what the bit took out, so the machine inside it\n                reads as a machine inside a hole. */}\n            {cavity.hole.length > 0 && (\n              <>\n                <path\n                  data-void\n                  d={line(cavity.hole, 0, true)}\n                  fill={palette.dark}\n                  opacity={variant === \"wire\" ? 0.12 : 0.55}\n                />\n                <path\n                  d={line(cavity.hole, 0, true)}\n                  fill=\"none\"\n                  stroke={palette.dark}\n                  strokeWidth={1.1}\n                  opacity={0.75}\n                />\n              </>\n            )}\n            {/* The bore's mouth, and the far face once it starts to give way. */}\n            {cavity.hole.length > 0 && (\n              <path\n                d={line([\n                  { x: WALL.face, y: AXIS + envelope(WALL.face) },\n                  { x: WALL.face, y: AXIS - envelope(WALL.face) },\n                ])}\n                fill=\"none\"\n                stroke={palette.accent}\n                strokeWidth={1.4}\n                opacity={0.5}\n              />\n            )}\n            {cavity.breakthrough > 0.01 && (\n              <path\n                data-breakthrough\n                d={box(\n                  WALL.face + WALL.thickness - 1.6,\n                  AXIS - Math.max(1, cavity.exitRadius),\n                  WALL.face + WALL.thickness + 1.6,\n                  AXIS + Math.max(1, cavity.exitRadius),\n                  Math.max(1, cavity.exitRadius),\n                )}\n                fill={palette.glow}\n                opacity={px(0.12 + cavity.breakthrough * 0.34)}\n              />\n            )}\n          </g>\n        )}\n\n        {/* ------------------------------------------------------------------ */}\n        {/* the machine                                                        */}\n        {/* ------------------------------------------------------------------ */}\n        <g\n          data-machine\n          data-advance={px(advance)}\n          transform={`translate(${px(to({ x: advance, y: 0 }).x - to({ x: 0, y: 0 }).x)} ${px(to({ x: advance, y: 0 }).y - to({ x: 0, y: 0 }).y)})`}\n          opacity={px(0.58 + solidity * 0.42)}\n        >\n          {/* The construct's own bloom, hugging the silhouette it is made of.\n              Solid only: the other three variants are line drawings. */}\n          {variant === \"solid\" && (\n            <path\n              // A polyline, not a swept solid: a hull would bridge the waist\n              // and the head into one wedge instead of following the shape.\n              d={line(\n                [\n                  { x: -152, y: AXIS + BODY_R + 3 },\n                  { x: -50, y: AXIS + BODY_R + 3 },\n                  { x: -44, y: AXIS + BIT_R + 3 },\n                  { x: -TAPER, y: AXIS + BIT_R + 3 },\n                  { x: 3, y: AXIS },\n                  { x: -TAPER, y: AXIS - BIT_R - 3 },\n                  { x: -44, y: AXIS - BIT_R - 3 },\n                  { x: -50, y: AXIS - BODY_R - 3 },\n                  { x: -152, y: AXIS - BODY_R - 3 },\n                ],\n                0,\n                true,\n              )}\n              fill={palette.glow}\n              opacity={px(0.04 + solidity * 0.09)}\n            />\n          )}\n\n          {/*\n            --- the drive wheel -------------------------------------------\n            A pair on one transverse axle behind the body, rolling on the\n            floor. `rollAngle` ties the turn to the travel: one revolution per\n            2πr of advance, nothing at all while the machine is held. Every\n            wheel part is drawn in the elevation plane, so the wheel is a wheel\n            here and foreshortens to its own width seen from the front.\n          */}\n          <g data-wheel data-angle={px(wheelAngle % 360)}>\n            {/* The trailing arms carrying the axle, one each side. */}\n            {[-WHEEL_TRACK, WHEEL_TRACK].map((side) => (\n              <path\n                key={`arm${side}`}\n                d={bar({ x: -142, y: AXIS + 4 }, AXLE, 4, 3.4, side)}\n                {...cast}\n              />\n            ))}\n            {[-WHEEL_TRACK, WHEEL_TRACK].map((side) => (\n              <g key={side} data-side={side < 0 ? \"port\" : \"starboard\"}>\n                {/* Tyre, rim well, and the spokes that make the turn legible. */}\n                <path d={disc(AXLE, WHEEL_R, WHEEL_WIDTH, side, 28)} {...shell} />\n                <path d={disc(AXLE, RIM_R, WHEEL_WIDTH * 0.75, side, 24)} {...cast} />\n                {spokeAngles.map((angle, index) => (\n                  <path\n                    key={angle}\n                    data-spoke={index}\n                    d={bar(\n                      onRim(HUB_R * 0.7, angle),\n                      onRim(RIM_R - 1, angle),\n                      2.3,\n                      WHEEL_WIDTH * 0.5,\n                      side,\n                    )}\n                    {...machined}\n                  />\n                ))}\n                {/* The tread: lugs round the rim, standing proud of the tyre. */}\n                {treadAngles.map((angle, index) => (\n                  <path\n                    key={angle}\n                    data-tread={index}\n                    d={solid(\n                      [\n                        onRim(WHEEL_R - 2.4, angle - 7),\n                        onRim(WHEEL_R + 0.9, angle - 5),\n                        onRim(WHEEL_R + 0.9, angle + 5),\n                        onRim(WHEEL_R - 2.4, angle + 7),\n                      ],\n                      WHEEL_WIDTH * 1.1,\n                      side,\n                    )}\n                    {...cast}\n                  />\n                ))}\n                {/* Hub, and the drive that lives inside it. */}\n                <path d={disc(AXLE, HUB_R, WHEEL_WIDTH * 1.5, side, 16)} {...machined} />\n                <path\n                  data-core\n                  d={disc(AXLE, HUB_R * 0.5, WHEEL_WIDTH * 1.7, side, 12)}\n                  fill={palette.accent}\n                  opacity={px(0.4 + solidity * 0.5)}\n                />\n              </g>\n            ))}\n            {/* The axle itself, through the body. */}\n            <path\n              d={bar(\n                { x: AXLE.x, y: AXIS },\n                { x: AXLE.x, y: AXIS },\n                2.6,\n                WHEEL_TRACK + WHEEL_WIDTH,\n              )}\n              {...machined}\n            />\n          </g>\n\n          {/* --- the machinery bay ----------------------------------------- */}\n          <g data-bay>\n            <path d={box(-150, AXIS - BODY_R, -112, AXIS + BODY_R, BODY_R)} {...shell} />\n            {/* The rib cage down the body: longitudinal staves over the shell. */}\n            {ribs.map((rib) => (\n              <path\n                key={rib.index}\n                data-rib={rib.index}\n                d={bar(\n                  { x: -146, y: AXIS + rib.across },\n                  { x: -80, y: AXIS + rib.across },\n                  1.5,\n                  1.5,\n                  rib.depth,\n                )}\n                {...machined}\n              />\n            ))}\n            {/* Bands round the cage, the way the reference is hooped. */}\n            {[-142, -128, -114, -100, -86].map((x) => (\n              <path\n                key={x}\n                d={box(x - 1.8, AXIS - BODY_R - 1.6, x + 1.8, AXIS + BODY_R + 1.6, BODY_R + 1.6)}\n                {...cast}\n              />\n            ))}\n            {/* Aft bulkhead, and the bolts round it. */}\n            <path d={box(-152, AXIS - BODY_R - 2.5, -146, AXIS + BODY_R + 2.5, BODY_R + 2.5)} {...cast} />\n            {[0, 60, 120, 180, 240, 300].map((angle) => {\n              const { across, depth: d } = aboutAxis(BODY_R + 1.2, angle)\n              return (\n                <path\n                  key={angle}\n                  d={bar({ x: -153, y: AXIS + across }, { x: -145, y: AXIS + across }, 1.5, 1.5, d)}\n                  {...machined}\n                />\n              )\n            })}\n            {/* Heat exchanger fins on the crown of the body. */}\n            <g data-fins>\n              {[-144, -138, -132, -126, -120].map((x) => (\n                <path\n                  key={x}\n                  d={solid(\n                    [\n                      { x: x - 1, y: AXIS + BODY_R - 1 },\n                      { x: x + 1, y: AXIS + BODY_R - 1 },\n                      { x: x + 1, y: AXIS + BODY_R + 5.5 },\n                      { x: x - 1, y: AXIS + BODY_R + 5.5 },\n                    ],\n                    11,\n                  )}\n                  {...machined}\n                />\n              ))}\n            </g>\n          </g>\n\n          {/* --- thrust rams: four about the axis, extending with torque ---- */}\n          <g data-thrust>\n            {rams.map((ram) => {\n              const y = AXIS + ram.across\n              const rodEnd = RAM_HEAD + ramStroke\n              return (\n                <g key={ram.index} data-ram={ram.index}>\n                  <path\n                    d={bar({ x: RAM_ROOT, y }, { x: -92, y }, 3.4, 3.4, ram.depth)}\n                    {...machined}\n                  />\n                  <path\n                    d={bar({ x: -94, y }, { x: rodEnd, y }, 1.5, 1.5, ram.depth)}\n                    {...cast}\n                  />\n                  <path d={bar({ x: -93.4, y }, { x: -90.6, y }, 3.9, 3.9, ram.depth)} {...cast} />\n                  <path d={bar({ x: rodEnd - 1.6, y }, { x: rodEnd + 1.6, y }, 2.6, 2.6, ram.depth)} {...machined} />\n                </g>\n              )\n            })}\n          </g>\n\n          {/* --- grippers: the shoes the thrust reacts against -------------- */}\n          <g data-gripper data-reach={px(ramStroke)}>\n            {grips.map((grip) => {\n              const reach = BODY_R + 1 + (BIT_R - BODY_R - 3) * (progress > 0.01 ? torque : 0)\n              const seat = aboutAxis(BODY_R - 2, grip.angle)\n              const pad = aboutAxis(reach, grip.angle)\n              const shoe = aboutAxis(reach - 2.6, grip.angle)\n              return (\n                <g key={grip.index} data-shoe={grip.index}>\n                  <path\n                    d={bar(\n                      { x: GRIP_X, y: AXIS + seat.across },\n                      { x: GRIP_X, y: AXIS + shoe.across },\n                      2.2,\n                      2.2,\n                      (seat.depth + shoe.depth) / 2,\n                    )}\n                    {...cast}\n                  />\n                  <path\n                    d={solid(\n                      [\n                        { x: GRIP_X - 8, y: AXIS + shoe.across },\n                        { x: GRIP_X + 8, y: AXIS + shoe.across },\n                        { x: GRIP_X + 6, y: AXIS + pad.across },\n                        { x: GRIP_X - 6, y: AXIS + pad.across },\n                      ],\n                      4,\n                      (pad.depth + shoe.depth) / 2,\n                    )}\n                    {...machined}\n                  />\n                </g>\n              )\n            })}\n          </g>\n\n          {/* --- the gearbox: a right-angle tap, a layshaft gear, a pump ---- */}\n          <g data-gearbox data-ratio={px(TRAIN.ratio)}>\n            <path d={box(-114, AXIS - BODY_R, -78, AXIS + BODY_R, BODY_R)} {...shell} />\n            {/* The hump that houses the layshaft. */}\n            <path d={box(-116, AXIS + BODY_R - 3, -84, AXIS + 22, 11)} {...cast} />\n            {/* Main shaft, drum to spindle. */}\n            <path d={bar({ x: -148, y: AXIS }, { x: -80, y: AXIS }, 3.2, 3.2)} {...machined} />\n\n            <g transform={inPlane(0)}>\n              <g\n                data-gear=\"crown\"\n                transform={`translate(${px(CROWN.x)} ${px(-CROWN.y)}) rotate(${px(crownAngle)})`}\n              >\n                <path d={gearPath(CROWN_TEETH, CROWN_R)} {...machined} />\n                <circle r={4.5} fill={palette.dark} opacity={0.8} />\n              </g>\n              <g\n                data-gear=\"layshaft\"\n                transform={`translate(${px(LAY.x)} ${px(-LAY.y)}) rotate(${px(layAngle)})`}\n              >\n                <path d={gearPath(LAY_TEETH, LAY_R)} {...machined} />\n                <circle r={1.6} fill={palette.dark} opacity={0.8} />\n              </g>\n            </g>\n\n            {/* The bevel pinion on the main shaft, meshing with the crown's rim. */}\n            <path\n              data-gear=\"pinion\"\n              d={solid(\n                [\n                  { x: CROWN.x + CROWN_R - 1, y: AXIS - PINION_R },\n                  { x: CROWN.x + CROWN_R + 7, y: AXIS - PINION_R * 0.45 },\n                  { x: CROWN.x + CROWN_R + 7, y: AXIS + PINION_R * 0.45 },\n                  { x: CROWN.x + CROWN_R - 1, y: AXIS + PINION_R },\n                ],\n                PINION_R,\n              )}\n              {...cast}\n            />\n\n            {/* The flushing pump, on the front end of the layshaft. */}\n            <g data-pump transform={inPlane(PUMP_DEPTH)}>\n              <path\n                d={`M ${px(LAY.x)} ${px(-LAY.y)} L ${px(LAY.x + pump.pin.x)} ${px(-(LAY.y + pump.pin.y))}`}\n                stroke={palette.metal}\n                strokeWidth={2.4}\n                strokeLinecap=\"round\"\n                fill=\"none\"\n              />\n              <path\n                d={`M ${px(LAY.x + pump.pin.x)} ${px(-(LAY.y + pump.pin.y))} L ${px(LAY.x + pump.wrist.x)} ${px(-(LAY.y + pump.wrist.y))}`}\n                stroke={palette.dark}\n                strokeWidth={1.7}\n                strokeLinecap=\"round\"\n                fill=\"none\"\n              />\n              <rect\n                x={px(LAY.x + pump.wrist.x - 1)}\n                y={px(-LAY.y - 3.2)}\n                width={7}\n                height={6.4}\n                rx={1}\n                {...machined}\n              />\n              <rect\n                x={px(LAY.x + PUMP_CRANK + PUMP_ROD - 5)}\n                y={px(-LAY.y - 4.6)}\n                width={14}\n                height={9.2}\n                rx={1.6}\n                fill=\"none\"\n                stroke={palette.metal}\n                strokeWidth={1}\n                opacity={0.85}\n              />\n            </g>\n\n            {/* Flushing line, pump forward to the bit. */}\n            <path\n              data-wiring\n              d={line(\n                [\n                  { x: LAY.x + 14, y: LAY.y },\n                  { x: -74, y: AXIS + BODY_R - 3 },\n                  { x: -50, y: AXIS + BODY_R - 6 },\n                  { x: -38, y: AXIS + 8 },\n                ],\n                PUMP_DEPTH,\n              )}\n              fill=\"none\"\n              stroke={palette.accent}\n              strokeWidth={1.5}\n              strokeLinecap=\"round\"\n              opacity={0.75}\n            />\n          </g>\n\n          {/* --- the planetary reduction ----------------------------------- */}\n          <g data-reduction data-carrier={px(planets.carrier % 360)}>\n            <path d={box(-80, AXIS - 17, -58, AXIS + 17, 17)} {...shell} />\n            <path d={box(-81.6, AXIS - RING_R - 1.6, -78.4, AXIS + RING_R + 1.6, RING_R + 1.6)} {...cast} />\n            <path d={box(-59.6, AXIS - RING_R - 1.6, -56.4, AXIS + RING_R + 1.6, RING_R + 1.6)} {...cast} />\n            {carriers.map((planet) => (\n              <g key={planet.index} data-planet={planet.index}>\n                <path\n                  d={bar(\n                    { x: -78, y: AXIS + planet.across },\n                    { x: -60, y: AXIS + planet.across },\n                    PLANET_R,\n                    PLANET_R,\n                    planet.depth,\n                  )}\n                  {...machined}\n                />\n                <path\n                  d={bar(\n                    { x: -60, y: AXIS + planet.across },\n                    { x: -56, y: AXIS + planet.across },\n                    PLANET_R * 0.35,\n                    PLANET_R * 0.35,\n                    planet.depth,\n                  )}\n                  {...cast}\n                />\n              </g>\n            ))}\n            <path d={bar({ x: -80, y: AXIS }, { x: -44, y: AXIS }, SUN_R, SUN_R)} {...cast} />\n          </g>\n\n          {/* --- the main bearing ------------------------------------------ */}\n          <g data-bearing data-cage={px(cageAngle % 360)}>\n            <path d={box(-58, AXIS - 14, -44, AXIS + 14, 14)} {...machined} />\n            {rollers.map((roller) => (\n              <path\n                key={roller.index}\n                data-roller={roller.index}\n                d={bar(\n                  { x: -56, y: AXIS + roller.across },\n                  { x: -46, y: AXIS + roller.across },\n                  ROLLER_R,\n                  ROLLER_R,\n                  roller.depth,\n                )}\n                {...cast}\n              />\n            ))}\n            {/* Spindle collar, out to the head. */}\n            <path d={box(-46, AXIS - 11, -38, AXIS + 11, 11)} {...cast} />\n          </g>\n\n          {/* Plough blades: a cowl of three claws off the gauge, hooking over\n              the cone to the nose, turning with the head. */}\n          <g data-cowl>\n            {blades.map((blade) => {\n              // One ribbon at constant radial thickness: outer edge forward,\n              // inner edge back, both on the same ray so it never fattens.\n              const ribs2 = Array.from({ length: 10 }, (_, i) => {\n                const t = i / 9\n                const outer = (BIT_R + 6) * (1 - t * t * 0.84)\n                const inner = Math.max(1.6, outer - 5)\n                const at = aboutAxis(1, blade.angle)\n                return {\n                  x: -48 + t * 54,\n                  outer: at.across * outer,\n                  inner: at.across * inner,\n                  depth: at.depth * ((outer + inner) / 2),\n                }\n              })\n              return (\n                <path\n                  key={blade.index}\n                  data-blade={blade.index}\n                  d={solid(\n                    [\n                      ...ribs2.map((r) => ({ x: r.x, y: AXIS + r.outer })),\n                      ...ribs2\n                        .slice()\n                        .reverse()\n                        .map((r) => ({ x: r.x - 4, y: AXIS + r.inner })),\n                    ],\n                    2.6,\n                    ribs2[4].depth,\n                  )}\n                  {...shell}\n                />\n              )\n            })}\n          </g>\n\n          {/* --- the cutterhead -------------------------------------------- */}\n          <g data-bit data-angle={px(bitAngle % 360)}>\n            {/* Gauge barrel: the widest thing on the machine, so everything\n                behind it passes through the hole it cuts. */}\n            <path d={box(-44, AXIS - BIT_R, -TAPER, AXIS + BIT_R, BIT_R)} {...shell} />\n            {/* The stepped cone, inscribed in the envelope the cavity is cut from. */}\n            {BIT_STEPS.map((step) => (\n              <path\n                key={step.from}\n                d={box(\n                  -TAPER * step.to,\n                  AXIS - BIT_R * step.radius,\n                  -TAPER * step.from,\n                  AXIS + BIT_R * step.radius,\n                  BIT_R * step.radius,\n                )}\n                {...machined}\n              />\n            ))}\n            {/* Helical flights up the cone: drawing, phased by the bit's angle. */}\n            <g data-flights opacity={0.8}>\n              {[0, 1, 2].map((strand) => {\n                const points = Array.from({ length: 11 }, (_, i) => {\n                  const t = i / 10\n                  const x = -TAPER * t\n                  const turn = bitAngle + strand * 120 + t * 260\n                  return { x, y: AXIS + aboutAxis(BIT_R * (t * 0.94 + 0.08), turn).across }\n                })\n                return (\n                  <path\n                    key={strand}\n                    d={line(points)}\n                    fill=\"none\"\n                    stroke={palette.glow}\n                    strokeWidth={1.5}\n                    strokeLinecap=\"round\"\n                    opacity={0.65}\n                  />\n                )\n              })}\n            </g>\n            {/* Disc cutters on the cone, and gauge cutters on the barrel. */}\n            {cutters.map((cutter) => (\n              <path\n                key={cutter.index}\n                data-cutter={cutter.index}\n                d={disc({ x: cutter.x, y: AXIS + cutter.across }, 3.6, 1.4, cutter.depth)}\n                {...cast}\n              />\n            ))}\n            {gauge.map((tooth) => (\n              <path\n                key={tooth.index}\n                data-cutter={`gauge-${tooth.index}`}\n                d={disc({ x: tooth.x, y: AXIS + tooth.across }, 2.8, 2.8, tooth.depth)}\n                {...lit}\n              />\n            ))}\n            {/* The crown ring, and the kerf it is standing in. */}\n            <path d={box(-TAPER - 0.9, AXIS - BIT_R - 1, -TAPER + 0.9, AXIS + BIT_R + 1, BIT_R + 1)} {...machined} />\n            {cutting > 0.02 && inWall && (\n              <path\n                data-kerf\n                d={disc({ x: -1.5, y: AXIS }, px(3 + cutting * 5), 1)}\n                fill={palette.glow}\n                opacity={px(0.3 + cutting * 0.5)}\n              />\n            )}\n          </g>\n        </g>\n\n        {/* Rubble off the kerf: ballistic, and thrown by the head's own flow. */}\n        {spall.length > 0 && (\n          <g data-spall>\n            {spall.map((fragment, index) => (\n              <path\n                key={index}\n                d={disc({ x: fragment.x, y: fragment.y }, px(fragment.size), px(fragment.size * 0.6), fragment.depth)}\n                fill={palette.dark}\n                opacity={px(clamp(0.75 - fragment.age * 0.7, 0, 1))}\n              />\n            ))}\n          </g>\n        )}\n\n        {variant === \"blueprint\" && (\n          <g data-diagnostic fontFamily=\"ui-monospace, monospace\" fontSize={5.4} fill={palette.foreground}>\n            <text x={px(to({ x: TAIL + 4, y: WALL.top - 6 }).x)} y={px(to({ x: TAIL + 4, y: WALL.top - 6 }).y)}>\n              {`ROP ${duty.rate.toFixed(1)} u/s · ${duty.advancePerRev.toFixed(2)} u/rev`}\n            </text>\n            <text x={px(to({ x: TAIL + 4, y: WALL.top - 14 }).x)} y={px(to({ x: TAIL + 4, y: WALL.top - 14 }).y)}>\n              {`Es ${duty.specificEnergy.toFixed(2)} · i ${TRAIN.ratio.toFixed(2)}:1 · ${duty.turning ? \"CUT\" : \"STALL\"}`}\n            </text>\n          </g>\n        )}\n      </g>\n\n      {label && (\n        <text\n          x={VIEW_WIDTH / 2}\n          y={VIEW_HEIGHT - 6}\n          textAnchor=\"middle\"\n          fontFamily=\"ui-monospace, monospace\"\n          fontSize={6}\n          fill={palette.foreground}\n        >\n          {label}\n        </text>\n      )}\n    </svg>\n  )\n}\n\nexport { BoreConstruct }\n",
      "type": "registry:ui",
      "target": "@ui/bore-construct.tsx"
    }
  ],
  "categories": [
    "robotics",
    "energy"
  ],
  "type": "registry:ui"
}