{
  "name": "dock",
  "title": "Dock",
  "type": "registry:component",
  "description": "A plank jetty on pilings reaching out over water; walkable deck colliders, side stringers, and seaward mooring posts.",
  "dependencies": [
    "@react-three/rapier@^2.2.0",
    "@runek/core@^0.12.0"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "Dock.tsx",
      "content": "import { CuboidCollider, RigidBody } from '@react-three/rapier'\nimport { rng, useWorld, type WorldComponentProps } from '@runek/core'\nimport { useMemo } from 'react'\n\nexport interface DockProps extends WorldComponentProps {\n  /** How far the jetty reaches out from the shore (local +Z), in units. */\n  length?: number\n  /** Walkway width (local X), in units. */\n  width?: number\n  /** How deep the pilings sink below the deck surface, in units. */\n  depth?: number\n  /** Deck color; defaults to the world palette's `wood`. */\n  color?: string\n}\n\n/** A plank jetty on pilings that reaches out over water. The deck is the gameplay surface\n *  (a few collider slabs, not one per plank, like `Bridge`); the pilings, side stringers, and\n *  the two seaward mooring posts are decorative. The local origin sits at the deck surface on\n *  the shore end, so `position` is where the jetty meets land and it runs out along local +Z. */\nexport function Dock({\n  position = [0, 0, 0],\n  rotation = [0, 0, 0],\n  seed = 1,\n  length = 10,\n  width = 3,\n  depth = 4,\n  color,\n}: DockProps) {\n  const { unit, palette } = useWorld()\n  const deckColor = color ?? palette.wood\n  const postColor = palette.woodDark\n  const L = length * unit\n  const W = width * unit\n  const D = depth * unit\n  const deckT = 0.12 * unit\n  const deckY = -deckT / 2\n  const postX = W / 2 - 0.25 * unit\n  const postR = 0.16 * unit\n\n  const plankCount = Math.max(6, Math.round(length / 0.4))\n  const planks = useMemo(() => {\n    const next = rng(seed)\n    const pd = L / plankCount\n    return Array.from({ length: plankCount }, (_, i) => ({\n      z: (i + 0.5) * pd,\n      d: pd * (0.84 + next() * 0.1),\n    }))\n  }, [L, plankCount, seed])\n\n  const SLABS = 5\n  const slabLen = L / SLABS\n\n  // rows of pilings (\"bents\") along the jetty, including both ends\n  const bents = useMemo(() => {\n    const n = Math.max(2, Math.round(length / 3))\n    return Array.from({ length: n + 1 }, (_, i) => (i / n) * L)\n  }, [length, L])\n\n  return (\n    <RigidBody type=\"fixed\" colliders={false} position={position} rotation={rotation}>\n      {/* deck collider slabs */}\n      {Array.from({ length: SLABS }, (_, i) => (i + 0.5) * slabLen).map((z) => (\n        <CuboidCollider\n          key={`col-${z.toFixed(3)}`}\n          args={[W / 2, deckT / 2, slabLen / 2]}\n          position={[0, deckY, z]}\n        />\n      ))}\n\n      {/* cross planks */}\n      {planks.map((p) => (\n        <mesh key={`plank-${p.z.toFixed(3)}`} castShadow receiveShadow position={[0, deckY, p.z]}>\n          <boxGeometry args={[W, deckT, p.d]} />\n          <meshStandardMaterial color={deckColor} roughness={0.85} />\n        </mesh>\n      ))}\n\n      {/* side stringers under the deck */}\n      {[-1, 1].map((s) => (\n        <mesh key={`stringer-${s}`} position={[s * (W / 2 - 0.1 * unit), deckY - deckT, L / 2]}>\n          <boxGeometry args={[0.12 * unit, 0.2 * unit, L]} />\n          <meshStandardMaterial color={postColor} roughness={0.85} />\n        </mesh>\n      ))}\n\n      {/* pilings, sunk into the water */}\n      {bents.map((z) =>\n        [-1, 1].map((s) => (\n          <mesh key={`pile-${z.toFixed(3)}-${s}`} castShadow position={[s * postX, -D / 2, z]}>\n            <cylinderGeometry args={[postR, postR, D, 8]} />\n            <meshStandardMaterial color={postColor} roughness={0.9} />\n          </mesh>\n        )),\n      )}\n\n      {/* seaward mooring posts */}\n      {[-1, 1].map((s) => (\n        <mesh key={`bollard-${s}`} castShadow position={[s * postX, 0.25 * unit, L - 0.3 * unit]}>\n          <cylinderGeometry args={[0.18 * unit, 0.2 * unit, 0.8 * unit, 8]} />\n          <meshStandardMaterial color={postColor} roughness={0.9} />\n        </mesh>\n      ))}\n    </RigidBody>\n  )\n}\n",
      "type": "registry:component"
    }
  ]
}
