{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "draft",
  "type": "registry:ui",
  "title": "Draft",
  "description": "A soft fluid gradient mesh: domain-warped fbm shader swirling slate, indigo, and violet fields in a slow cloud-like drift. Original OGL fragment shader, dithered, offscreen-paused, reduced-motion-safe.",
  "dependencies": [
    "ogl"
  ],
  "registryDependencies": [
    "https://crucibleui.com/r/ogl-canvas.json",
    "https://crucibleui.com/r/use-visibility-pause.json",
    "https://crucibleui.com/r/use-reduced-motion.json"
  ],
  "files": [
    {
      "path": "registry/default/backgrounds/draft/draft.tsx",
      "type": "registry:ui",
      "target": "components/crucible/draft.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport { OglCanvas } from \"@/lib/ogl-canvas\";\n\nexport interface DraftProps extends React.ComponentPropsWithoutRef<\"div\"> {\n  /** Blob colors, any hex color. Defaults to a soft slate / indigo / violet palette. */\n  colors?: [string, string, string, string];\n  /** Animation speed multiplier. 1 = default, 2 = twice as fast. */\n  speed?: number;\n  /** Freeze the animation at its current frame. */\n  paused?: boolean;\n  /**\n   * Idle autoplay: after ~1.5s with no pointer present, a seeded ghost drifts\n   * slowly across the field and leans the mesh, so thumbnails and heroes never\n   * look still; a real pointer takes over instantly. Never runs under reduced\n   * motion. Default true.\n   */\n  idle?: boolean;\n  /**\n   * Softness of the color fields. Kept in the same px scale as the original\n   * CSS version for API compatibility (90 = default); internally it maps to a\n   * shader smoothness uniform — lower values sharpen the mesh edges, higher\n   * values melt them further together.\n   */\n  blur?: number;\n}\n\nconst DEFAULT_COLORS: [string, string, string, string] = [\n  \"#64748b\", // slate — the calm body of the mesh\n  \"#6366f1\", // indigo — cool current\n  \"#1a1a2b\", // deep — the base field everything floats on\n  \"#8b5cf6\", // violet — soft bloom\n];\n\n/** Hex → [r,g,b] in 0–1. Accepts #rgb / #rrggbb. */\nfunction hexToRgb(hex: string): [number, number, number] {\n  let h = hex.replace(\"#\", \"\").trim();\n  if (h.length === 3) {\n    h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];\n  }\n  const int = parseInt(h, 16);\n  if (Number.isNaN(int) || h.length !== 6) return [0.39, 0.45, 0.55];\n  return [((int >> 16) & 255) / 255, ((int >> 8) & 255) / 255, (int & 255) / 255];\n}\n\n/**\n * Original domain-warped fbm \"fluid gradient mesh\" fragment shader.\n * Three chained warp layers (q warps p, r warps p+q, f samples p+r) produce\n * slowly swirling, cloud-soft color fields rather than discrete blobs. Each\n * palette entry keys off a different warp field so the hues drift through one\n * another. Soft exponential tone curve so nothing clips, film-grain dithering\n * to defeat 8-bit banding. GLSL ES 1.00 (WebGL1/2 compatible).\n *\n * No fract() on spatial coordinates (only inside the hash), so there are no\n * wrap seams anywhere in the field.\n */\nconst FRAGMENT = /* glsl */ `\nprecision highp float;\n\nvarying vec2 vUv;\n\nuniform float uTime;\nuniform vec2 uResolution;\nuniform vec2 uPointer;\nuniform float uPointerActive; // eased 0-1 pointer/ghost presence\n\nuniform vec3 uColor1; // slate — mesh body\nuniform vec3 uColor2; // indigo — cool current\nuniform vec3 uColor3; // deep — base field\nuniform vec3 uColor4; // violet — bloom\nuniform float uSpeed;\nuniform float uSoftness; // 1 = default, from the blur prop\n\n// --- compact value noise + fbm (original) ---\nfloat hash21(vec2 p) {\n  p = fract(p * vec2(123.34, 456.21));\n  p += dot(p, p + 45.32);\n  return fract(p.x * p.y);\n}\n\nfloat vnoise(vec2 p) {\n  vec2 i = floor(p);\n  vec2 f = fract(p);\n  float a = hash21(i);\n  float b = hash21(i + vec2(1.0, 0.0));\n  float c = hash21(i + vec2(0.0, 1.0));\n  float d = hash21(i + vec2(1.0, 1.0));\n  vec2 u = f * f * (3.0 - 2.0 * f);\n  return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfloat fbm(vec2 p) {\n  float v = 0.0;\n  float amp = 0.5;\n  // Rotated lacunarity kills the axis-aligned look of raw value noise.\n  mat2 m = mat2(1.6, 1.2, -1.2, 1.6);\n  for (int i = 0; i < 5; i++) {\n    v += amp * vnoise(p);\n    p = m * p;\n    amp *= 0.5;\n  }\n  return v;\n}\n\nvoid main() {\n  vec2 uv = vUv;\n  float aspect = uResolution.x / max(uResolution.y, 1.0);\n  // Slow by design: the whole field should read as a lava-lamp drift.\n  float t = uTime * uSpeed * 0.05;\n\n  // Centered, aspect-corrected field coordinate.\n  vec2 p = (uv - 0.5) * vec2(aspect, 1.0) * 1.35;\n\n  // Gentle pointer influence: the field leans toward the cursor, strongest\n  // nearby and fading out smoothly — a lean, never a spotlight.\n  vec2 pd = vec2((uv.x - uPointer.x) * aspect, uv.y - uPointer.y);\n  float pfall = exp(-dot(pd, pd) * 3.5) * uPointerActive;\n  p -= (uPointer - 0.5) * 0.22 * pfall;\n\n  // --- three-layer domain warp ---\n  // q: first warp field, two decorrelated fbm channels drifting on their own\n  // clocks so the motion never settles into a loop.\n  vec2 q = vec2(\n    fbm(p + t * vec2(0.9, 0.6)),\n    fbm(p + vec2(5.2, 1.3) - t * vec2(0.5, 0.8))\n  );\n\n  // r: second warp, sampled through q — this is what turns soft gradients\n  // into swirling currents.\n  vec2 r = vec2(\n    fbm(p + 1.7 * q + vec2(1.7, 9.2) + t * vec2(0.25, 0.4)),\n    fbm(p + 1.7 * q + vec2(8.3, 2.8) - t * vec2(0.35, 0.15))\n  );\n\n  // f: the final field, warped through both layers.\n  float f = fbm(p + 1.9 * r);\n\n  // Softness maps the blur prop onto the smoothstep transition width: wide\n  // windows melt the hues together, narrow ones cut cleaner shapes.\n  float w = clamp(0.3 * uSoftness, 0.06, 0.85);\n\n  // Each hue keys off a DIFFERENT warp field so the colors drift through one\n  // another instead of moving in lockstep. smoothstep clamps — no clipping.\n  vec3 col = uColor3;\n  col = mix(col, uColor1, smoothstep(0.38 - w, 0.38 + w, f));\n  col = mix(col, uColor2, 0.85 * smoothstep(0.52 - w, 0.52 + w, q.y));\n  col = mix(col, uColor4, 0.75 * smoothstep(0.58 - w, 0.58 + w, r.x));\n\n  // Cloud shading: valleys of the field sit deeper, crests lift — gives the\n  // mesh volume without hard edges.\n  col *= mix(0.72, 1.12, smoothstep(0.18, 0.82, f));\n\n  // Luminous crest bloom where the field peaks, tinted along a second field\n  // so the highlight color itself wanders.\n  float glow = smoothstep(0.62, 0.94, f);\n  col += mix(uColor2, uColor4, r.y) * glow * glow * 0.35;\n\n  // Soft exponential tone curve: compresses highlights asymptotically below\n  // 1.0 so the bloom can never clip to a flat patch.\n  col = 1.0 - exp(-col * 1.7);\n\n  // Faint vignette to seat the field behind content.\n  vec2 vg = (uv - 0.5) * vec2(aspect, 1.0);\n  col *= 1.0 - 0.3 * dot(vg, vg);\n\n  // Film-grain dithering to defeat 8-bit banding in the slow gradients.\n  float grain = hash21(gl_FragCoord.xy + fract(t) * 137.0) - 0.5;\n  col += grain * (1.8 / 255.0);\n\n  gl_FragColor = vec4(max(col, 0.0), 1.0);\n}\n`;\n\n/**\n * Draft — a soft fluid gradient mesh. A domain-warped fbm fragment shader\n * swirls slate, indigo, and violet fields through each other in a slow,\n * cloud-soft drift, with a gentle lean toward the pointer. Built on the\n * `ogl-canvas` harness: GPU-light, offscreen-paused, reduced-motion-safe.\n * Position it inside any `relative` container; it fills and sits behind\n * content.\n */\nexport function Draft({\n  colors = DEFAULT_COLORS,\n  speed = 1,\n  paused = false,\n  idle = true,\n  blur = 90,\n  className,\n  ...props\n}: DraftProps) {\n  const uniforms = React.useMemo(\n    () => ({\n      uColor1: { value: hexToRgb(colors[0]) },\n      uColor2: { value: hexToRgb(colors[1]) },\n      uColor3: { value: hexToRgb(colors[2]) },\n      uColor4: { value: hexToRgb(colors[3]) },\n      uSpeed: { value: Math.max(speed, 0) },\n      // 90px (the CSS-era default) maps to softness 1.\n      uSoftness: { value: Math.min(Math.max(blur / 90, 0.1), 3) },\n    }),\n    [colors, speed, blur]\n  );\n\n  return (\n    <OglCanvas\n      aria-hidden\n      data-crucible=\"draft\"\n      {...props}\n      className={cn(\"absolute inset-0 -z-10\", className)}\n      fragment={FRAGMENT}\n      uniforms={uniforms}\n      paused={paused}\n      ghost={idle}\n      reducedMotionTime={8}\n    />\n  );\n}\n"
    }
  ]
}