{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "robot-polar-bear",
  "title": "Polar bear",
  "description": "One body with two ways of holding itself up. Swim is the handover: the plantigrade soles unload as the water takes the weight, the hull settles to a waterline, the hind limbs stop stepping and trail, and the forelimbs paddle on a stroke path the shoulder and elbow are solved to. Long body, long neck, small head — the silhouette does the rest.",
  "registryDependencies": [
    "https://robocn.dev/r/bear-kinematics.json",
    "https://robocn.dev/r/robot-kinematics.json",
    "https://robocn.dev/r/robot-style.json",
    "https://robocn.dev/r/spine-kinematics.json",
    "https://robocn.dev/r/use-pointer-target.json",
    "https://robocn.dev/r/use-robot-motion.json"
  ],
  "files": [
    {
      "path": "src/components/ui/robot-polar-bear.tsx",
      "content": "\"use client\"\n\n/**\n * robot-polar-bear — one body, two ways of holding itself up.\n *\n * `robot-bear` spends the plantigrade base of support on standing up. This one\n * spends it on **giving it away.** `swim` is the handover, 0 on the floor to 1\n * afloat, and it is the same load budget:\n *\n *     legLoad(i) = (1 − swim) · supportLoad(i)\n *     buoyancy   = swim\n *\n * One prop, four consequences, all of them arithmetic. The soles unload and the\n * base of support stops mattering. The body rises to the waterline and levels\n * out. The hind limbs stop stepping and trail, because a swimming bear does not\n * kick. And the forelimbs go from standing on the floor to **paddling** — the\n * paw traces a closed stroke, deep on the pull and shallow on the recovery, and\n * `solveChain2` produces the shoulder and elbow from it, so the articulation is\n * an output of the path rather than a pair of scripted angles.\n *\n * The proportions are the rest of it: long body, long neck, small head carried\n * low, no shoulder hump at all. No markings, no paint — a machine reads as this\n * animal from its silhouette or it does not read as it.\n *\n * Design note: docs/ursine-robots.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  plantigradeStep,\n  solveSole,\n  solveSupport,\n  type SolePose,\n} from \"@/lib/robocn/bear\"\nimport {\n  clamp,\n  lerp,\n  lerp2,\n  rotate2,\n  solveChain2,\n  toDegrees,\n  toRadians,\n  type Vec2,\n} from \"@/lib/robocn/kinematics\"\nimport { solveSpine, spineLimits, type SpinePose } from \"@/lib/robocn/spine\"\nimport {\n  aboutPoint,\n  capsulePath,\n  extrudedPath,\n  px,\n  resolveRobotPalette,\n  resolveRobotSize,\n  robotCamera,\n  robotSurface,\n  roundedFootprint,\n  type RobotPaletteProps,\n  type RobotSize,\n  type RobotVariant,\n  type RobotView,\n} from \"@/lib/robocn/style\"\nimport { cn } from \"@/lib/utils\"\n\nexport type PolarBearBehavior = \"plod\" | \"swim\" | \"stalk\" | \"rear\" | \"static\"\n\n/** Drawn in side elevation; that is the camera it defaults to. */\nconst NATIVE_VIEW: RobotView = \"profile\"\n\nconst ORIGIN = 66\nconst GROUND = 138\nconst HALF_TRACK = 14\n\n/** Croup to withers. Longer and lower than the brown bear's. */\nconst TRUNK = 64\nconst HIND = [21, 19] as const\nconst HIND_SOLE = { heel: 8, toe: 14, ankle: 5 } as const\nconst FORE = [19, 17] as const\n/** The forepaw is the paddle, so it is the biggest foot in the set. */\nconst FORE_SOLE = { heel: 6, toe: 13, ankle: 4.5 } as const\n/** A long cervical chain: the neck is this animal's signature. */\nconst NECK = [13, 11] as const\n\nconst HIND_STANCE = 2\nconst FORE_STANCE = 54\nconst STAND = 39\nconst CROUCH = 29\n/** Reared, the hind legs are nearly straight: this is what they can reach to. */\nconst REARED = 43\n/** A rear takes the spine near vertical, which is what brings the mass back\n *  over the hind soles rather than out past them. */\nconst REAR_PITCH = 76\nconst SHIFT_LIMIT = 22\n\n/** Where the surface sits above the floor, and how deep the hull rides in it. */\nconst WATER = 34\nconst DRAFT = 9\n/** Stroke reach along the body, pull depth, and the shallower recovery. */\nconst STROKE = { reach: 17, pull: 20, recovery: 7 } as const\n/** Units per second the swim eases back at when a drag is released. */\nconst SWIM_RATE = 0.9\n\nconst fits: Record<RobotView, number> = { plan: 0.74, front: 0.88, profile: 1, iso: 0.8 }\nconst framing: Record<RobotView, number> = { plan: -14, front: 0, profile: 0, iso: -8 }\n\nconst viewNames: Record<RobotView, string> = {\n  plan: \"plan view\",\n  front: \"front elevation\",\n  profile: \"side elevation\",\n  iso: \"isometric view\",\n}\n\ntype LegId = \"fore-left\" | \"fore-right\" | \"hind-left\" | \"hind-right\"\n\ninterface PolarLeg extends SolePose {\n  id: LegId\n  side: \"left\" | \"right\"\n  fore: boolean\n  /** Share of the standing weight on this sole, after the water has taken its cut. */\n  load: number\n}\n\n/** The same lateral sequence the brown bear walks on. */\nconst legPlan: { id: LegId; side: \"left\" | \"right\"; fore: boolean; offset: number }[] = [\n  { id: \"hind-left\", side: \"left\", fore: false, offset: 0 },\n  { id: \"fore-left\", side: \"left\", fore: true, offset: 0.12 },\n  { id: \"hind-right\", side: \"right\", fore: false, offset: 0.5 },\n  { id: \"fore-right\", side: \"right\", fore: true, offset: 0.62 },\n]\n\nexport interface RobotPolarBearProps\n  extends Omit<React.ComponentProps<\"svg\">, \"color\">,\n    RobotPaletteProps {\n  /** Where the camera stands. One animal, four projections. */\n  view?: RobotView\n  behavior?: PolarBearBehavior\n  /** Controlled stride, or stroke, fraction. Supplying it stops the internal clock. */\n  phase?: number\n  speed?: number\n  animate?: boolean\n  paused?: boolean\n  offset?: number\n  /** 0 on the floor to 1 afloat. Omit and the behavior works it. */\n  swim?: number\n  /** Wingbeats of the water: forelimb strokes per cycle. */\n  strokes?: number\n  /** 0 on four soles to 1 up on the hind pair. Ignored while swimming. */\n  rear?: number\n  /** How much of the body's carriage the balance rule takes, 0 scripted to 1 derived. */\n  balance?: number\n  arch?: number\n  crouch?: number\n  /** Neck carriage, −1 run down below the shoulder to 1 held high. */\n  neck?: number\n  gaze?: number\n  /** Drag up and down to work the handover; the head tracks the pointer. */\n  interactive?: boolean\n  onSwimChange?: (swim: number) => void\n  size?: RobotSize | number\n  variant?: RobotVariant\n  showGround?: boolean\n  showContacts?: boolean\n  /** Draw the base of support, the centre of mass, and the margin between them. */\n  showSupport?: boolean\n  label?: string\n}\n\nfunction RobotPolarBear({\n  behavior = \"swim\",\n  phase,\n  view = NATIVE_VIEW,\n  speed = 0.45,\n  animate = true,\n  paused = false,\n  offset = 0,\n  swim,\n  strokes = 1,\n  rear,\n  balance,\n  arch,\n  crouch,\n  neck,\n  gaze,\n  interactive = true,\n  onSwimChange,\n  size = \"md\",\n  variant = \"solid\",\n  showGround = true,\n  showContacts = false,\n  showSupport = false,\n  label,\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  ...props\n}: RobotPolarBearProps) {\n  const controlledSwim = swim !== undefined\n  const controlledPhase = phase !== undefined\n  const svgRef = React.useRef<SVGSVGElement>(null)\n  const [held, setHeld] = React.useState<number | null>(null)\n  const hold = controlledSwim ? finiteClamp(swim, 0, 1, 0) : held\n\n  const rate = Number.isFinite(speed) ? speed : 0\n  const goal = React.useCallback(\n    (seconds: number) => polarBearPose(behavior, seconds).stance(seconds * rate).swim,\n    [behavior, rate],\n  )\n  const motion = useRobotScalar(goal, {\n    rate: SWIM_RATE,\n    hold,\n    speed: 1,\n    paused,\n    phase: offset,\n    animate: animate && !controlledPhase && behavior !== \"static\",\n  })\n  const clock = motion.clock\n  const scripted = polarBearPose(behavior, clock)\n  const cycle = controlledPhase ? (Number.isFinite(phase) ? phase : 0) : clock * rate\n  const stance = scripted.stance(cycle)\n  const afloat = finiteClamp(\n    controlledSwim ? swim : controlledPhase ? stance.swim : motion.value,\n    0,\n    1,\n    0,\n  )\n\n  const apply = React.useCallback(\n    (next: number) => {\n      const bounded = Number(clamp(next, 0, 1).toFixed(3))\n      setHeld(bounded)\n      onSwimChange?.(bounded)\n    },\n    [onSwimChange],\n  )\n  const dragging = useRobotDrag(svgRef, {\n    enabled: interactive && !controlledSwim,\n    onDrag: React.useCallback((unit: Vec2) => apply((0.85 - unit.y) / 0.7), [apply]),\n    onDragEnd: React.useCallback(() => setHeld(null), []),\n  })\n\n  const pointer = usePointerTarget(svgRef, {\n    enabled: interactive && !paused,\n    within: \"element\",\n    persist: true,\n    toWorld: React.useCallback(\n      (unit: Vec2) => ({\n        x: clamp((unit.x - 0.5) * 2.2, -1, 1),\n        y: clamp((0.5 - unit.y) * 2, -1, 1),\n      }),\n      [],\n    ),\n  })\n\n  const bow = finiteClamp(arch ?? stance.arch, -1, 1, stance.arch)\n  const fold = finiteClamp(crouch ?? stance.crouch, 0, 1, stance.crouch)\n  // A swimming animal is not standing on anything, so the rear is shut out of\n  // the water rather than blended into it.\n  const rise = finiteClamp(rear ?? stance.rear, 0, 1, stance.rear) * (1 - afloat)\n  const weight = finiteClamp(balance ?? scripted.balance, 0, 1, scripted.balance)\n  const carriage = finiteClamp(neck ?? stance.neck, -1, 1, stance.neck)\n  const aim = finiteClamp(gaze ?? pointer.target?.x ?? scripted.gaze, -1, 1, 0)\n  const beats = Number.isFinite(strokes) ? clamp(strokes, 0.25, 4) : 1\n\n  /* ---- the back: on the floor, or level at the waterline ------------------ */\n\n  // Afloat the hull rides *in* the surface at its own draft, not on top of it.\n  const hipHeight = lerp(lerp(lerp(STAND, CROUCH, fold), REARED, rise), WATER + DRAFT - 6, afloat)\n  const curvature = bow * 0.18\n  const tilt = -(curvature * spineLimits.turn) / 2 + rise * REAR_PITCH + afloat * 4\n  const pitched = tiltPose(\n    solveSpine({\n      segments: 10,\n      length: TRUNK,\n      phase: cycle,\n      amplitude: scripted.flex,\n      waves: 0.8,\n      taper: 0.15,\n      turn: curvature,\n    }),\n    tilt,\n  )\n  const croup = pitched.joints[pitched.joints.length - 1].position\n  const local = (index: number): Vec2 => ({\n    x: pitched.joints[index].position.x - croup.x,\n    y: pitched.joints[index].position.y - croup.y + hipHeight,\n  })\n\n  const massPoints = pitched.joints.map((joint, index) => ({\n    point: local(index),\n    weight: 1 + 0.4 * Math.sin(Math.PI * joint.s),\n  }))\n  const crest = pitched.joints[0].angle\n  const localWithers = local(0)\n  massPoints.push({ point: alongBody(localWithers, crest, 26, 14), weight: 1.3 })\n  const massTotal = massPoints.reduce((sum, entry) => sum + entry.weight, 0)\n  const comLocal = {\n    x: massPoints.reduce((sum, entry) => sum + entry.point.x * entry.weight, 0) / massTotal,\n    y: massPoints.reduce((sum, entry) => sum + entry.point.y * entry.weight, 0) / massTotal,\n  }\n\n  /* ---- where each foot goes: a step, a trail, or a stroke ----------------- */\n\n  // How far from the hip a hind sole can be put and still make the floor.\n  const hindRoom = Math.sqrt(\n    Math.max(0, (HIND[0] + HIND[1]) ** 2 - (hipHeight - HIND_SOLE.ankle) ** 2),\n  )\n\n  const steps = legPlan.map(({ id, fore, offset: legOffset }) => {\n    const walking = stance.stride >= 0 && stance.stride <= 1.5\n    const step = walking\n      ? plantigradeStep(stance.stride + legOffset, { reach: fore ? 12 : 13, clearance: 9 })\n      : { plant: { x: 0, y: 0 }, pivot: \"flat\" as const, pitch: 0, roll: \"flat\" as const, contact: true }\n    // Standing up, the hind soles step in under the centre of mass, as far as\n    // the limb can put them: the first half of the same balance rule.\n    const under = clamp(comLocal.x, -hindRoom, hindRoom)\n    return {\n      id,\n      x:\n        (fore ? FORE_STANCE : lerp(HIND_STANCE, under, rise * weight)) +\n        step.plant.x * (fore ? 1 : 1 - rise),\n      y: step.plant.y,\n      pivot: step.pivot,\n      pitch: step.pitch,\n      airborne: !step.contact,\n    }\n  })\n\n  const preview = solveSupport(\n    steps.map((step, index) => {\n      const sole = legPlan[index].fore ? FORE_SOLE : HIND_SOLE\n      const lifted = step.airborne || (legPlan[index].fore && rise > 0.06)\n      if (lifted) return { id: step.id, span: null }\n      const back =\n        step.pivot === \"heel\" ? step.x : step.x - (step.pivot === \"toe\" ? sole.heel + sole.toe : sole.heel)\n      return { id: step.id, span: [back, back + sole.heel + sole.toe] as [number, number] }\n    }),\n    comLocal.x,\n  )\n  const centre = preview.span ? (preview.span[0] + preview.span[1]) / 2 : comLocal.x\n  // The hip cannot leave its own feet behind, so the slide is bounded by the\n  // hind limb's reach as well as by how far a body slides.\n  const hindFoot = (steps[0].x + steps[2].x) / 2\n  const shift = clamp(\n    clamp(centre - comLocal.x, -SHIFT_LIMIT, SHIFT_LIMIT) * weight * (1 - afloat),\n    hindFoot - hindRoom,\n    hindFoot + hindRoom,\n  )\n  const hip: Vec2 = { x: shift, y: hipHeight }\n  const spinePoint = (index: number): Vec2 => {\n    const point = local(index)\n    return { x: point.x + shift, y: point.y }\n  }\n  const withers = spinePoint(0)\n  const com = { x: comLocal.x + shift, y: comLocal.y }\n\n  const thorax = pitched.joints[1]\n  const under = toRadians(thorax.angle + 90)\n  const shoulder: Vec2 = {\n    x: spinePoint(1).x - Math.cos(under) * 8,\n    y: spinePoint(1).y - Math.sin(under) * 8,\n  }\n\n  const soles = legPlan.map(({ id, fore }, index) => {\n    const step = steps[index]\n    const root = fore ? shoulder : hip\n    const links = fore ? FORE : HIND\n    const sole = fore ? FORE_SOLE : HIND_SOLE\n    // Afloat, a forelimb paddles and a hind limb trails: the stroke is the path\n    // the paw traces, and the two sides run half a cycle apart.\n    const stroke = polarStroke(cycle * beats + (id === \"fore-right\" ? 0.5 : 0))\n    const water: Vec2 = fore\n      ? {\n          x: shoulder.x + stroke.forward * STROKE.reach,\n          y: Math.max(4, shoulder.y - 14 - stroke.depth),\n        }\n      : { x: hip.x - 16, y: Math.max(4, hip.y - 10) }\n    const tuck = fore\n      ? { x: shoulder.x + 9, y: Math.max(8, shoulder.y - 24) }\n      : { x: step.x, y: step.y }\n    const drawIn = fore ? rise : 0\n    const dry: Vec2 = { x: lerp(step.x, tuck.x, drawIn), y: lerp(step.y, tuck.y, drawIn) }\n    const plant = lerp2(dry, water, afloat)\n    const dryPitch = lerp(step.pitch, -52, drawIn)\n    // The paddle is held square to the pull, which is what a paddle is for.\n    const wetPitch = fore ? lerp(-26, 30, (stroke.forward + 1) / 2) : -18\n    return solveSole({\n      hip: root,\n      plant,\n      pivot: step.pivot,\n      pitch: lerp(dryPitch, wetPitch, afloat),\n      femur: links[0],\n      tibia: links[1],\n      heel: sole.heel,\n      toe: sole.toe,\n      ankle: sole.ankle,\n      bend: fore ? \"down\" : \"up\",\n    })\n  })\n\n  const support = solveSupport(\n    soles.map((sole, index) => ({ id: legPlan[index].id, span: sole.span })),\n    com.x,\n  )\n  // The handover, stated once: what the legs carry is what the water has not\n  // taken. `buoyancy` is the rest of the same body.\n  const buoyancy = afloat\n  const legs: PolarLeg[] = soles.map((sole, index) => ({\n    ...sole,\n    id: legPlan[index].id,\n    side: legPlan[index].side,\n    fore: legPlan[index].fore,\n    load: (1 - afloat) * (support.loads[legPlan[index].id] ?? 0),\n  }))\n\n  /* ---- the long neck, and the small head on the end of it ----------------- */\n\n  const nape = alongBody(withers, crest, 2, 5)\n  const target = alongBody(withers, crest, 26, 4 + carriage * 9 - rise * 2)\n  const [, cervical, poll] = solveChain2(nape, target, [...NECK], { bend: \"up\" })\n  const headTilt = toDegrees(Math.atan2(poll.y - cervical.y, poll.x - cervical.x)) + aim * 7 - 8\n\n  /* ---- paint -------------------------------------------------------------- */\n\n  const palette = resolveRobotPalette({ color, accent, metal, dark, glow, grid, palette: paletteOverride })\n  const width = resolveRobotSize(size)\n  const shell = robotSurface(\"shell\", variant, palette)\n  const machined = robotSurface(\"metal\", variant, palette)\n  const cast = robotSurface(\"dark\", variant, palette)\n  const supportColor = support.stable || afloat > 0.5 ? palette.accent : palette.shell\n\n  const camera = robotCamera(view)\n  const offAxis = view !== NATIVE_VIEW\n  const fit = fits[view] ?? 1\n  const slide = framing[view] ?? 0\n  const face = aboutPoint(camera.wall(0, 90), ORIGIN, GROUND, fit)\n  const framed = [slide ? `translate(0 ${px(slide)})` : \"\", face].filter(Boolean).join(\" \")\n  const Frame = (framed ? \"g\" : React.Fragment) as React.FC<{\n    transform?: string\n    children?: React.ReactNode\n  }>\n  const frame = framed ? { transform: framed } : {}\n  const at = (p: Vec2, across = 0) => camera.project(across, p.y, -p.x)\n\n  /** Level topline, no hump at all: this animal's shoulder is flat. */\n  const backline = (s: number) => 12.5 + 2 * s\n  const bellyline = (s: number) =>\n    s < 0.45 ? lerp(16, 13, s / 0.45) : lerp(13, 16, (s - 0.45) / 0.55)\n\n  const readout = Math.round(afloat * 100)\n  const state = dragging\n    ? \"worked by hand\"\n    : afloat > 0.55\n      ? \"swimming\"\n      : rise > 0.55\n        ? \"standing on its hind legs\"\n        : behavior === \"stalk\"\n          ? \"stalking\"\n          : behavior === \"plod\"\n            ? \"plodding\"\n            : \"standing\"\n\n  function legDrawing(leg: PolarLeg) {\n    const far = leg.side === \"right\"\n    const nudge = far ? -7 : 0\n    const move = (p: Vec2): Vec2 => ({ x: p.x + nudge, y: p.y })\n    return (\n      <g key={leg.id} data-leg={leg.id} opacity={far ? 0.52 : 1}>\n        <path d={capsulePath(move(leg.hip), move(leg.knee), leg.fore ? 8 : 9)} {...shell} />\n        <path d={capsulePath(move(leg.knee), move(leg.ankle), leg.fore ? 6.6 : 7)} {...machined} />\n        <g data-sole={leg.id} data-contact-state={leg.contact}>\n          <path d={capsulePath(move(leg.heel), move(leg.toe), leg.fore ? 4.2 : 3.8)} {...cast} />\n          {[0.62, 0.8, 0.98].map((t) => {\n            const claw = lerp2(move(leg.heel), move(leg.toe), t)\n            return <circle key={t} cx={px(claw.x + 1.4)} cy={px(claw.y - 1.2)} r={0.9} fill={palette.metal} />\n          })}\n        </g>\n        <circle\n          data-joint={`${leg.id}-${leg.fore ? \"elbow\" : \"stifle\"}`}\n          cx={px(move(leg.knee).x)}\n          cy={px(move(leg.knee).y)}\n          r={4}\n          {...cast}\n        />\n        <circle data-joint={`${leg.id}-ankle`} cx={px(move(leg.ankle).x)} cy={px(move(leg.ankle).y)} r={3} {...cast} />\n        {leg.fore && (\n          <circle data-joint={`${leg.id}-shoulder`} cx={px(move(leg.hip).x)} cy={px(move(leg.hip).y)} r={3.6} {...cast} />\n        )}\n        {leg.fore && afloat > 0.02 && (\n          <path\n            data-stroke={leg.side}\n            d={strokePath(shoulder, nudge)}\n            fill=\"none\"\n            stroke={palette.accent}\n            strokeWidth={0.7}\n            strokeDasharray=\"2 3\"\n            opacity={px(0.45 * afloat)}\n          />\n        )}\n        {showContacts && leg.span && leg.load > 0.001 && (\n          <rect\n            data-contact\n            x={px(Math.min(leg.span[0], leg.span[1]) + nudge)}\n            y={-2.4}\n            width={px(Math.max(1.6, Math.abs(leg.span[1] - leg.span[0])))}\n            height={2}\n            rx={1}\n            fill={palette.accent}\n            opacity={px(0.3 + 0.6 * leg.load)}\n          />\n        )}\n      </g>\n    )\n  }\n\n  return (\n    <svg\n      ref={svgRef}\n      role={role ?? (interactive ? \"slider\" : \"img\")}\n      aria-label={`Robot polar bear, ${state}, ${viewNames[view] ?? viewNames.profile}`}\n      aria-valuemin={interactive ? 0 : undefined}\n      aria-valuemax={interactive ? 100 : undefined}\n      aria-valuenow={interactive ? readout : undefined}\n      aria-valuetext={interactive ? `${readout} percent afloat` : undefined}\n      tabIndex={tabIndex ?? (interactive ? 0 : undefined)}\n      viewBox=\"0 0 240 168\"\n      width={width}\n      height={px((width * 168) / 240)}\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      onKeyDown={(event) => {\n        onKeyDown?.(event)\n        if (!interactive || controlledSwim || event.defaultPrevented) return\n        const delta = arrowStep(event.key, event.shiftKey ? 0.25 : 0.1, 0.25)\n        if (delta !== 0) apply(afloat + 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      onBlur={(event) => {\n        onBlur?.(event)\n        if (!dragging) setHeld(null)\n      }}\n      {...props}\n    >\n      {variant === \"blueprint\" && (\n        <g fill=\"none\" stroke={palette.grid} strokeWidth={0.5} opacity={0.4}>\n          <path d={`M 10 ${GROUND} H 230 M ${ORIGIN} 14 V ${GROUND + 12}`} strokeDasharray=\"2 3\" />\n          <circle\n            cx={px(ORIGIN + shoulder.x)}\n            cy={px(GROUND - shoulder.y)}\n            r={px(FORE[0] + FORE[1])}\n            strokeDasharray=\"3 4\"\n          />\n        </g>\n      )}\n\n      {showGround && (\n        <g data-ground>\n          <path d={`M 12 ${GROUND} H 228`} stroke={palette.grid} strokeWidth={0.8} fill=\"none\" />\n          <ellipse\n            cx={px(ORIGIN + 26)}\n            cy={GROUND + 3}\n            rx={px(50 - afloat * 16 - rise * 16)}\n            ry={4.4}\n            fill={palette.dark}\n            opacity={px(0.15 * (1 - afloat))}\n          />\n        </g>\n      )}\n\n      {/* The surface. The hull rides in it, so it is drawn over the body. */}\n      {afloat > 0.02 && (\n        <g data-waterline opacity={px(Math.min(1, afloat * 2.2))}>\n          <rect\n            x={10}\n            y={px(GROUND - WATER)}\n            width={220}\n            height={px(WATER)}\n            fill={palette.accent}\n            opacity={0.08}\n          />\n          <path\n            d={`M 10 ${px(GROUND - WATER)} H 230`}\n            stroke={palette.accent}\n            strokeWidth={1}\n            fill=\"none\"\n            opacity={0.6}\n          />\n        </g>\n      )}\n\n      {offAxis && (\n        <g data-solids transform={`translate(${ORIGIN} ${px(GROUND + slide)}) scale(${px(fit)})`}>\n          {pitched.joints.slice(0, -1).map((joint, index) => {\n            const a = spinePoint(index)\n            const b = spinePoint(index + 1)\n            const midX = (a.x + b.x) / 2\n            const midY = (a.y + b.y) / 2\n            const halfLength = Math.hypot(b.x - a.x, b.y - a.y) / 2 + 1.5\n            const footprint = roundedFootprint(HALF_TRACK, halfLength, 5, 5).map((p) => ({\n              x: p.x,\n              y: p.y - midX,\n            }))\n            return (\n              <path\n                key={index}\n                d={extrudedPath(footprint, camera, midY + backline(joint.s) * 0.55, midY - bellyline(joint.s) * 0.55)}\n                {...shell}\n              />\n            )\n          })}\n          {([-HALF_TRACK, HALF_TRACK] as const).map((across) => (\n            <g key={across}>\n              {legs.map((leg) => (\n                <g key={leg.id}>\n                  <path d={capsulePath(at(leg.hip, across * 0.7), at(leg.knee, across), leg.fore ? 8 : 9)} {...shell} />\n                  <path d={capsulePath(at(leg.knee, across), at(leg.ankle, across), leg.fore ? 6.6 : 7)} {...machined} />\n                  {/* The paddle, as a plate rather than a line. */}\n                  <path\n                    d={extrudedPath(\n                      roundedFootprint(\n                        leg.fore ? 6 : 6.5,\n                        (leg.fore ? FORE_SOLE.heel + FORE_SOLE.toe : HIND_SOLE.heel + HIND_SOLE.toe) / 2,\n                        2,\n                        2,\n                      ).map((p) => ({ x: p.x + across, y: p.y - (leg.heel.x + leg.toe.x) / 2 })),\n                      camera,\n                      (leg.heel.y + leg.toe.y) / 2 + 1.6,\n                      (leg.heel.y + leg.toe.y) / 2 - 1.6,\n                    )}\n                    {...cast}\n                  />\n                </g>\n              ))}\n            </g>\n          ))}\n          <path d={capsulePath(at(nape), at(cervical), 6.4)} {...machined} />\n          <path d={capsulePath(at(cervical), at(poll), 5.4)} {...machined} />\n          <path\n            d={extrudedPath(\n              roundedFootprint(6.2, 10, 4, 4).map((p) => ({ x: p.x, y: p.y - poll.x })),\n              camera,\n              poll.y + 6,\n              poll.y - 6,\n            )}\n            {...shell}\n          />\n        </g>\n      )}\n\n      <Frame {...frame}>\n        <g\n          data-polar-bear\n          data-view={view}\n          data-swim={px(afloat)}\n          data-buoyancy={px(buoyancy)}\n          transform={`translate(${ORIGIN} ${GROUND}) scale(1 -1)`}\n        >\n          {legs.filter((leg) => leg.side === \"right\").map(legDrawing)}\n\n          <g data-trunk>\n            {([0, pitched.joints.length - 1] as const).map((index) => {\n              const joint = pitched.joints[index]\n              const place = spinePoint(index)\n              const normal = toRadians(joint.angle + 90)\n              const radius = (backline(joint.s) + bellyline(joint.s)) / 2\n              const bias = (backline(joint.s) - bellyline(joint.s)) / 2\n              return (\n                <circle\n                  key={index}\n                  cx={px(place.x + Math.cos(normal) * bias)}\n                  cy={px(place.y + Math.sin(normal) * bias)}\n                  r={px(radius)}\n                  {...shell}\n                />\n              )\n            })}\n            <path data-spine d={bodyOutline(pitched, spinePoint, backline, bellyline)} {...shell} />\n            <path\n              d={offsetLine(pitched, spinePoint, (t) => -(7 - 3 * Math.sin(Math.PI * t)))}\n              fill=\"none\"\n              stroke={palette.dark}\n              strokeWidth={0.7}\n              opacity={0.22}\n            />\n          </g>\n\n          <path data-neck d={capsulePath(nape, cervical, 6.4)} {...machined} />\n          <path d={capsulePath(cervical, poll, 5.4)} {...machined} />\n\n          <g data-head transform={`translate(${px(poll.x)} ${px(poll.y)}) rotate(${px(-headTilt)})`}>\n            {/* Small ears, set low and close: a smaller head than the brown\n                bear's on a much longer neck is the whole silhouette. */}\n            <g data-ears>\n              {([\n                { id: \"left\" as const, across: 2, opacity: 1 },\n                { id: \"right\" as const, across: -3.5, opacity: 0.55 },\n              ]).map(({ id, across, opacity }) => (\n                <g key={id} opacity={opacity}>\n                  <circle data-ear={id} cx={px(-4.5 + across * 0.4)} cy={px(6.4 + across * 0.5)} r={3.4} {...shell} />\n                  <circle cx={px(-3.8 + across * 0.4)} cy={px(6.2 + across * 0.5)} r={1.7} {...cast} />\n                </g>\n              ))}\n            </g>\n            {/* A long straight skull that runs into the muzzle with no stop. */}\n            <path d=\"M -8.5 -5.5 Q -9.5 6.5 -1 8 Q 7 7.5 10 3.5 Q 12 -1.5 9 -6 Q 2.5 -9 -3 -9 Q -8 -9 -8.5 -5.5 Z\" {...shell} />\n            <path data-muzzle d=\"M 8.5 -5 Q 20 -4.6 21.5 -1.4 L 21.5 2.2 Q 19.5 4.6 8.5 4.8 Z\" {...machined} />\n            <path d=\"M 8.5 1.6 Q 16 2.2 21 2.6 L 21 3.8 Q 15.5 4.2 8.5 4.2 Z\" {...cast} />\n            <ellipse cx={20.8} cy={-1.2} rx={1.9} ry={2.2} fill={palette.dark} />\n            <g data-eyes>\n              <g transform=\"translate(1 2.8)\" opacity={0.5}>\n                <circle r={1.8} {...cast} />\n                <circle cx={px(0.6 + aim * 0.8)} r={0.9} fill={palette.accent} />\n              </g>\n              <g transform=\"translate(4.2 0.2)\">\n                <circle r={2.2} {...cast} />\n                <circle cx={px(0.8 + aim * 1.1)} r={1.15} fill={palette.accent} />\n              </g>\n            </g>\n          </g>\n\n          {legs.filter((leg) => leg.side === \"left\").map(legDrawing)}\n\n          <g data-joints>\n            <circle data-joint=\"hip\" cx={px(hip.x)} cy={px(hip.y)} r={5.2} {...cast} />\n            <circle cx={px(hip.x)} cy={px(hip.y)} r={2} fill={palette.metal} />\n          </g>\n\n          {showSupport && (\n            <g data-support data-stable={support.stable} data-afloat={afloat > 0.5} data-margin={px(support.margin)} data-base={support.span ? px(support.span[1] - support.span[0]) : 0}>\n              {support.span && afloat < 0.98 && (\n                <rect\n                  x={px(support.span[0])}\n                  y={-4.6}\n                  width={px(Math.max(1.5, support.span[1] - support.span[0]))}\n                  height={2.2}\n                  rx={1.1}\n                  fill={supportColor}\n                  opacity={px(0.75 * (1 - afloat))}\n                />\n              )}\n              <path\n                data-com\n                d={`M ${px(com.x)} ${px(com.y)} V ${px(afloat > 0.5 ? WATER : 0)}`}\n                stroke={supportColor}\n                strokeWidth={0.8}\n                strokeDasharray=\"2 2.5\"\n                fill=\"none\"\n                opacity={0.8}\n              />\n              <circle cx={px(com.x)} cy={px(com.y)} r={3} fill=\"none\" stroke={supportColor} strokeWidth={1.2} />\n              <circle cx={px(com.x)} cy={px(com.y)} r={1} fill={supportColor} />\n            </g>\n          )}\n        </g>\n      </Frame>\n\n      {label && (\n        <text x={120} y={162} textAnchor=\"middle\" fontFamily=\"ui-monospace, monospace\" fontSize={6} fill={palette.foreground}>\n          {label}\n        </text>\n      )}\n    </svg>\n  )\n}\n\n/* -------------------------------------------------------------------------- */\n/* geometry                                                                    */\n/* -------------------------------------------------------------------------- */\n\n/**\n * The stroke a forepaw traces, as a fraction of a beat.\n *\n * Forward is a cosine along the body; depth is a sine that pulls deep on the\n * half of the beat that does the work and recovers shallow on the other, which\n * is what makes it a stroke rather than a circle. Pure, so the tests read the\n * path off it rather than off the drawing.\n */\nexport function polarStroke(t: number): { forward: number; depth: number } {\n  const a = 2 * Math.PI * wrap(t)\n  const sweep = Math.sin(a)\n  return {\n    forward: Math.cos(a),\n    depth: sweep >= 0 ? STROKE.pull * sweep : STROKE.recovery * sweep,\n  }\n}\n\n/** The closed stroke, drawn once so a person can see the path the paw is on. */\nfunction strokePath(shoulder: Vec2, nudge: number) {\n  const points: string[] = []\n  for (let step = 0; step <= 24; step += 1) {\n    const stroke = polarStroke(step / 24)\n    const x = shoulder.x + nudge + stroke.forward * STROKE.reach\n    const y = Math.max(4, shoulder.y - 14 - stroke.depth)\n    points.push(`${points.length ? \"L\" : \"M\"} ${px(x)} ${px(y)}`)\n  }\n  return `${points.join(\" \")} Z`\n}\n\nfunction tiltPose(pose: SpinePose, degrees: number): SpinePose {\n  if (!degrees) return pose\n  const radians = toRadians(degrees)\n  const joints = pose.joints.map((joint) => ({\n    ...joint,\n    position: rotate2(joint.position, radians),\n    angle: joint.angle + degrees,\n  }))\n  return { ...pose, joints, head: joints[0], tail: joints[joints.length - 1] }\n}\n\nfunction alongBody(origin: Vec2, degrees: number, forward: number, up: number): Vec2 {\n  const a = toRadians(degrees)\n  return {\n    x: origin.x + Math.cos(a) * forward - Math.sin(a) * up,\n    y: origin.y + Math.sin(a) * forward + Math.cos(a) * up,\n  }\n}\n\nfunction offsetLine(pose: SpinePose, place: (index: number) => Vec2, width: (s: number) => number) {\n  return pose.joints\n    .map((joint, index) => {\n      const normal = toRadians(joint.angle + 90)\n      const at = place(index)\n      const w = width(joint.s)\n      return `${index ? \"L\" : \"M\"} ${px(at.x + Math.cos(normal) * w)} ${px(at.y + Math.sin(normal) * w)}`\n    })\n    .join(\" \")\n}\n\nfunction bodyOutline(\n  pose: SpinePose,\n  place: (index: number) => Vec2,\n  top: (s: number) => number,\n  under: (s: number) => number,\n) {\n  const above: string[] = []\n  const below: string[] = []\n  pose.joints.forEach((joint, index) => {\n    const normal = toRadians(joint.angle + 90)\n    const nx = Math.cos(normal)\n    const ny = Math.sin(normal)\n    const at = place(index)\n    above.push(`${above.length ? \"L\" : \"M\"} ${px(at.x + nx * top(joint.s))} ${px(at.y + ny * top(joint.s))}`)\n    below.unshift(`L ${px(at.x - nx * under(joint.s))} ${px(at.y - ny * under(joint.s))}`)\n  })\n  return [...above, ...below, \"Z\"].join(\" \")\n}\n\nconst finiteClamp = (value: number, min: number, max: number, fallback: number) =>\n  Number.isFinite(value) ? clamp(value, min, max) : fallback\n\nconst wrap = (value: number) => (Number.isFinite(value) ? ((value % 1) + 1) % 1 : 0)\n\n/* -------------------------------------------------------------------------- */\n/* behaviours                                                                  */\n/* -------------------------------------------------------------------------- */\n\nexport interface PolarBearStance {\n  /** 0 on the floor to 1 afloat. */\n  swim: number\n  /** 0 on four soles to 1 up on the hind pair. */\n  rear: number\n  arch: number\n  crouch: number\n  /** Neck carriage, −1 run down below the shoulder to 1 held high. */\n  neck: number\n  /** Where the footfall cycle has got to. Above 1.5 the feet are planted. */\n  stride: number\n}\n\nexport interface PolarBearPose {\n  gaze: number\n  balance: number\n  /** Spine wave amplitude, 0–1. */\n  flex: number\n  stance: (cycle: number) => PolarBearStance\n}\n\n/** What it does with no timeline on it. Pure in the clock. */\nexport function polarBearPose(behavior: PolarBearBehavior, clock: number): PolarBearPose {\n  const time = Number.isFinite(clock) ? clock : 0\n  switch (behavior) {\n    // On the floor: the lateral-sequence plantigrade walk, head carried low.\n    case \"plod\":\n      return {\n        gaze: 0.14 * Math.sin(time * 0.5),\n        balance: 0.8,\n        flex: 0.06,\n        stance: (cycle) => ({\n          swim: 0,\n          rear: 0,\n          arch: 0.06 * Math.sin(2 * Math.PI * wrap(cycle)),\n          crouch: 0.2,\n          neck: -0.2,\n          stride: wrap(cycle),\n        }),\n      }\n    // Long, low and slow, with the neck run right down below the shoulder.\n    case \"stalk\":\n      return {\n        gaze: -0.3 + 0.12 * Math.sin(time * 0.7),\n        balance: 0.9,\n        flex: 0.04,\n        stance: (cycle) => ({\n          swim: 0,\n          rear: 0,\n          arch: -0.18,\n          crouch: 0.68,\n          neck: -0.95,\n          stride: wrap(cycle) * 0.6,\n        }),\n      }\n    case \"rear\":\n      return {\n        gaze: 0.25 * Math.sin(time * 0.7),\n        balance: 1,\n        flex: 0.04,\n        stance: (cycle) => {\n          const up = Math.sin(Math.PI * clamp((wrap(cycle) - 0.12) / 0.76, 0, 1)) ** 0.7\n          return {\n            swim: 0,\n            rear: clamp(up, 0, 1),\n            arch: -0.1 * up,\n            crouch: clamp(0.28 - 0.28 * up, 0, 1),\n            neck: 0.3,\n            stride: 2,\n          }\n        },\n      }\n    case \"static\":\n      return {\n        gaze: 0,\n        balance: 0.5,\n        flex: 0,\n        stance: () => ({ swim: 0, rear: 0, arch: 0, crouch: 0.16, neck: 0, stride: 2 }),\n      }\n    // The signature: afloat at the waterline, forelimbs alternating, the hind\n    // pair trailing. The feet stop stepping, because nothing is on the floor.\n    default:\n      return {\n        gaze: 0.12 * Math.sin(time * 0.6),\n        balance: 0.4,\n        flex: 0.14,\n        stance: () => ({\n          swim: 1,\n          rear: 0,\n          arch: -0.05,\n          crouch: 0.1,\n          neck: 0.55,\n          stride: 2,\n        }),\n      }\n  }\n}\n\nexport { RobotPolarBear }\n",
      "type": "registry:ui",
      "target": "@ui/robot-polar-bear.tsx"
    }
  ],
  "categories": [
    "robotics",
    "animals"
  ],
  "type": "registry:ui"
}