{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "contour-field",
  "type": "registry:component",
  "title": "ContourField",
  "description": "A topographic map: flowing elevation contours from a procedural field with crisp resolution-independent isolines that glow. Reads instantly as terrain. Zero data, pointer-reactive, palette-driven.",
  "dependencies": [
    "three",
    "@react-three/fiber"
  ],
  "registryDependencies": [
    "https://prism.icglabs.co/r/canvas-perf.json"
  ],
  "tier": "free",
  "files": [
    {
      "path": "components/prism/ContourField.tsx",
      "content": "\"use client\";\n\nimport { useMemo, useRef } from \"react\";\nimport { Canvas, useFrame, useThree } from \"@react-three/fiber\";\nimport * as THREE from \"three\";\nimport { useDeviceTier } from \"@/lib/useDeviceTier\";\nimport { AutoDpr, PauseWhenOffscreen } from \"@/components/showcase/CanvasPerf\";\n\n/**\n * ContourField — a topographic \"map\": flowing elevation contour lines drawn from\n * a procedural noise field with resolution-independent isolines (fwidth), tinted\n * low→high and glowing on each contour. Reads instantly as terrain/elevation.\n * Self-contained, pointer-reactive, palette-driven.\n */\nexport type ContourFieldProps = {\n  /** [low terrain, high terrain, contour-line glow] hex. */\n  palette?: [string, string, string];\n  speed?: number;\n  className?: string;\n};\n\nconst VERT = /* glsl */ `\nvarying vec2 vUv;\nvoid main() { vUv = uv; gl_Position = vec4(position.xy, 0.0, 1.0); }\n`;\n\nconst FRAG = /* glsl */ `\nprecision highp float;\nvarying vec2 vUv;\nuniform float uTime;\nuniform vec2 uMouse;\nuniform float uAspect;\nuniform vec3 uLow;\nuniform vec3 uHigh;\nuniform vec3 uLine;\n\nfloat hash(vec2 p){ p=fract(p*vec2(123.34,345.45)); p+=dot(p,p+34.345); return fract(p.x*p.y); }\nfloat noise(vec2 p){ vec2 i=floor(p),f=fract(p); vec2 u=f*f*(3.0-2.0*f);\n  float a=hash(i),b=hash(i+vec2(1,0)),c=hash(i+vec2(0,1)),d=hash(i+vec2(1,1));\n  return mix(mix(a,b,u.x),mix(c,d,u.x),u.y); }\nfloat fbm(vec2 p){ float v=0.0,a=0.5; for(int i=0;i<5;i++){ v+=a*noise(p); p*=2.0; a*=0.5; } return v; }\n\nvoid main(){\n  vec2 uv = vUv; uv.x *= uAspect;\n  uv += (uMouse - 0.5) * 0.3;\n  float t = uTime * 0.04;\n  float h = fbm(uv * 3.0 + vec2(t, t * 0.5));\n\n  // crisp, resolution-independent isolines\n  float N = 13.0;\n  float d = fwidth(h * N);\n  float lines = abs(fract(h * N - 0.5) - 0.5) / max(d, 1e-4);\n  float c = 1.0 - min(lines, 1.0);\n\n  vec3 terrain = mix(uLow, uHigh, smoothstep(0.15, 0.85, h));\n  vec3 col = terrain * (0.45 + 0.4 * h) + c * uLine * 1.5;\n\n  float vig = smoothstep(1.05, 0.3, length(vUv - 0.5));\n  col *= mix(0.3, 1.0, vig);\n  col += (hash(vUv * 900.0 + t) - 0.5) * 0.02; // dither\n  gl_FragColor = vec4(col, 1.0);\n}\n`;\n\nfunction toV3(hex: string) {\n  const c = new THREE.Color(hex);\n  return new THREE.Vector3(c.r, c.g, c.b);\n}\n\nfunction FieldMesh({ palette, speed }: { palette: [string, string, string]; speed: number }) {\n  const mat = useRef<THREE.ShaderMaterial>(null);\n  const mouse = useRef(new THREE.Vector2(0.5, 0.5));\n  const size = useThree((s) => s.size);\n  const reduced =\n    typeof window !== \"undefined\" &&\n    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n\n  const uniforms = useMemo(\n    () => ({\n      uTime: { value: 0 },\n      uMouse: { value: new THREE.Vector2(0.5, 0.5) },\n      uAspect: { value: 1 },\n      uLow: { value: toV3(palette[0]) },\n      uHigh: { value: toV3(palette[1]) },\n      uLine: { value: toV3(palette[2]) },\n    }),\n    [palette]\n  );\n\n  useFrame((state, delta) => {\n    const m = mat.current;\n    if (!m) return;\n    if (!reduced) m.uniforms.uTime.value += delta * speed;\n    m.uniforms.uAspect.value = size.width / size.height;\n    const target = mouse.current;\n    target.set((state.pointer.x + 1) / 2, (state.pointer.y + 1) / 2);\n    m.uniforms.uMouse.value.lerp(target, 0.05);\n  });\n\n  return (\n    <mesh>\n      <planeGeometry args={[2, 2]} />\n      <shaderMaterial ref={mat} vertexShader={VERT} fragmentShader={FRAG} uniforms={uniforms} />\n    </mesh>\n  );\n}\n\nexport function ContourField({\n  palette = [\"#0c1030\", \"#5b7cff\", \"#1fd4e6\"],\n  speed = 1,\n  className,\n}: ContourFieldProps) {\n  const q = useDeviceTier();\n  return (\n    <div className={className} style={{ width: \"100%\", height: \"100%\" }}>\n      <Canvas dpr={q.dpr} gl={{ antialias: false, powerPreference: \"high-performance\" }}>\n        <FieldMesh palette={palette} speed={speed} />\n        <AutoDpr />\n        <PauseWhenOffscreen />\n      </Canvas>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/prism/ContourField.tsx"
    }
  ]
}