{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "confirm-slide-shatter",
  "title": "Confirm Slide Shatter",
  "description": "Frosted-glass confirm slider where drag distance drives Voronoi crack density — release early and the cracks heal on a spring; complete the travel and the pane explodes into tumbling glass shards revealing the confirmed state.",
  "dependencies": [],
  "files": [
    {
      "path": "registry/core/confirm-slide-shatter/component.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\n\ntype Pt = { x: number; y: number };\ntype Edge = { ax: number; ay: number; bx: number; by: number; t: number };\ntype Shard = {\n  poly: Pt[];\n  cx: number;\n  cy: number;\n  ox: number;\n  oy: number;\n  vx: number;\n  vy: number;\n  rot: number;\n  vr: number;\n};\n\nconst INSET = 4;\nconst RADIUS = 12;\n// canvas bleeds past the track so shards can tumble out of frame\nconst PAD_X = 160;\nconst PAD_TOP = 120;\nconst PAD_BOTTOM = 320;\nconst COMMIT_AT = 0.98;\nconst SHATTER_MS = 900;\nconst FADE_MS = 700;\nconst KEY_STEP = 0.08;\n\n/** dart-throwing Poisson disc — relaxes min distance if the pane is crowded */\nfunction poissonPoints(w: number, h: number, count: number, minDist: number): Pt[] {\n  const pts: Pt[] = [];\n  let d = minDist;\n  let attempts = 0;\n  while (pts.length < count && attempts < 6000) {\n    attempts++;\n    const c = { x: Math.random() * w, y: Math.random() * h };\n    let ok = true;\n    for (const q of pts) {\n      const dx = q.x - c.x;\n      const dy = q.y - c.y;\n      if (dx * dx + dy * dy < d * d) {\n        ok = false;\n        break;\n      }\n    }\n    if (ok) pts.push(c);\n    if (attempts % 1500 === 0) d *= 0.85;\n  }\n  return pts;\n}\n\n/** keep the part of `poly` on the seed's side of the bisector (dot(p-m, n) <= 0) */\nfunction clipHalfPlane(poly: Pt[], mx: number, my: number, nx: number, ny: number): Pt[] {\n  const out: Pt[] = [];\n  for (let i = 0; i < poly.length; i++) {\n    const a = poly[i];\n    const b = poly[(i + 1) % poly.length];\n    const da = (a.x - mx) * nx + (a.y - my) * ny;\n    const db = (b.x - mx) * nx + (b.y - my) * ny;\n    if (da <= 0) out.push(a);\n    if (da <= 0 !== db <= 0) {\n      const f = da / (da - db);\n      out.push({ x: a.x + (b.x - a.x) * f, y: a.y + (b.y - a.y) * f });\n    }\n  }\n  return out;\n}\n\n/** exact Voronoi cells via half-plane clipping — O(n²), computed once at mount */\nfunction voronoiCells(seeds: Pt[], w: number, h: number): Pt[][] {\n  return seeds\n    .map((s) => {\n      let poly: Pt[] = [\n        { x: 0, y: 0 },\n        { x: w, y: 0 },\n        { x: w, y: h },\n        { x: 0, y: h },\n      ];\n      for (const o of seeds) {\n        if (o === s) continue;\n        poly = clipHalfPlane(poly, (s.x + o.x) / 2, (s.y + o.y) / 2, o.x - s.x, o.y - s.y);\n        if (poly.length === 0) break;\n      }\n      return poly;\n    })\n    .filter((poly) => poly.length >= 3);\n}\n\n/** dedupe shared cell walls into crack polylines, each with a reveal threshold */\nfunction crackEdges(cells: Pt[][], w: number, h: number, ox: number, oy: number): Edge[] {\n  const eps = 0.5;\n  const maxD = Math.hypot(w - ox, Math.max(oy, h - oy));\n  const seen = new Set<string>();\n  const edges: Edge[] = [];\n  for (const poly of cells) {\n    for (let i = 0; i < poly.length; i++) {\n      const a = poly[i];\n      const b = poly[(i + 1) % poly.length];\n      // the pane's own border is a frame, not a crack\n      if (\n        (a.x < eps && b.x < eps) ||\n        (a.x > w - eps && b.x > w - eps) ||\n        (a.y < eps && b.y < eps) ||\n        (a.y > h - eps && b.y > h - eps)\n      )\n        continue;\n      const ka = `${a.x.toFixed(1)},${a.y.toFixed(1)}`;\n      const kb = `${b.x.toFixed(1)},${b.y.toFixed(1)}`;\n      const key = ka < kb ? `${ka}|${kb}` : `${kb}|${ka}`;\n      if (seen.has(key)) continue;\n      seen.add(key);\n      const da = Math.hypot(a.x - ox, a.y - oy);\n      const db = Math.hypot(b.x - ox, b.y - oy);\n      const near = da <= db ? a : b;\n      const far = da <= db ? b : a;\n      // map raw distance into [0.04, 0.86] so every crack finishes growing by p≈0.98\n      const t = 0.04 + 0.82 * (Math.min(da, db) / maxD);\n      edges.push({ ax: near.x, ay: near.y, bx: far.x, by: far.y, t });\n    }\n  }\n  return edges;\n}\n\nfunction roundedRectPath(\n  ctx: CanvasRenderingContext2D,\n  x: number,\n  y: number,\n  w: number,\n  h: number,\n  r: number\n) {\n  ctx.beginPath();\n  ctx.moveTo(x + r, y);\n  ctx.arcTo(x + w, y, x + w, y + h, r);\n  ctx.arcTo(x + w, y + h, x, y + h, r);\n  ctx.arcTo(x, y + h, x, y, r);\n  ctx.arcTo(x, y, x + w, y, r);\n  ctx.closePath();\n}\n\n// Frosted-glass confirm slider: drag distance IS crack density. Voronoi\n// fractures spider from the thumb, heal on a spring if released early, and at\n// full travel the pane explodes into tumbling shards revealing the confirmed\n// state. Progress lives in refs; all drawing is direct-DOM/canvas in one rAF\n// loop that sleeps when settled.\nexport function SlideToShatter({\n  label = \"SLIDE TO CONFIRM\",\n  confirmedLabel = \"CONFIRMED\",\n  width = 320,\n  height = 56,\n  shardCount = 48,\n  onConfirm,\n  resetKey = 0,\n  className = \"\",\n}: {\n  /** mono label etched on the glass */\n  label?: string;\n  /** label revealed once the pane shatters */\n  confirmedLabel?: string;\n  /** px width of the glass pane */\n  width?: number;\n  /** px height of the glass pane */\n  height?: number;\n  /** approximate Voronoi cell count — cracks while dragging, shards on commit */\n  shardCount?: number;\n  /** called once the pane fully shatters */\n  onConfirm?: () => void;\n  /** bump to restore the pane and replay */\n  resetKey?: number;\n  /** extra classes merged onto the rendered root element */\n  className?: string;\n}) {\n  const rootRef = useRef<HTMLDivElement>(null);\n  const glassRef = useRef<HTMLDivElement>(null);\n  const thumbRef = useRef<HTMLButtonElement>(null);\n  const canvasRef = useRef<HTMLCanvasElement>(null);\n  const [confirmed, setConfirmed] = useState(false);\n  const onConfirmRef = useRef(onConfirm);\n  onConfirmRef.current = onConfirm;\n\n  useEffect(() => {\n    const root = rootRef.current;\n    const glass = glassRef.current;\n    const thumb = thumbRef.current;\n    const canvas = canvasRef.current;\n    if (!root || !glass || !thumb || !canvas) return;\n    const ctx = canvas.getContext(\"2d\");\n    if (!ctx) return;\n    const reduced = window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n\n    // reset pane state (also runs on resetKey bump)\n    setConfirmed(false);\n    glass.style.visibility = \"\";\n    thumb.style.visibility = \"\";\n    thumb.style.transform = \"translateX(0px)\";\n    thumb.setAttribute(\"aria-valuenow\", \"0\");\n    root.style.transform = \"\";\n\n    const thumbSize = height - INSET * 2;\n    const maxTravel = width - INSET * 2 - thumbSize;\n    const cw = width + PAD_X * 2;\n    const ch = height + PAD_TOP + PAD_BOTTOM;\n\n    // geometry: one Poisson + Voronoi pass, reused as cracks AND shards\n    const originX = INSET + thumbSize / 2;\n    const originY = height / 2;\n    const seeds = reduced\n      ? []\n      : poissonPoints(\n          width,\n          height,\n          shardCount,\n          Math.max(8, Math.sqrt((width * height) / shardCount) * 0.72)\n        );\n    const cells = voronoiCells(seeds, width, height);\n    const edges = crackEdges(cells, width, height, originX, originY);\n\n    if (!reduced) {\n      const dpr = Math.min(window.devicePixelRatio || 1, 2);\n      canvas.width = Math.round(cw * dpr);\n      canvas.height = Math.round(ch * dpr);\n      ctx.setTransform(dpr, 0, 0, dpr, PAD_X * dpr, PAD_TOP * dpr);\n      ctx.lineCap = \"round\";\n      ctx.lineJoin = \"round\";\n      ctx.clearRect(-PAD_X, -PAD_TOP, cw, ch);\n    }\n\n    const S = {\n      p: 0,\n      v: 0,\n      dragging: false,\n      mode: \"idle\" as \"idle\" | \"drag\" | \"spring\" | \"shatter\",\n      raf: 0,\n      last: 0,\n      startX: 0,\n      startP: 0,\n      shatterStart: 0,\n      shards: [] as Shard[],\n    };\n\n    const setThumb = (p: number) => {\n      thumb.style.transform = `translateX(${p * maxTravel}px)`;\n    };\n\n    const drawCracks = (p: number) => {\n      ctx.clearRect(-PAD_X, -PAD_TOP, cw, ch);\n      if (p <= 0) return;\n      ctx.save();\n      roundedRectPath(ctx, 0.5, 0.5, width - 1, height - 1, RADIUS);\n      ctx.clip();\n      // two passes: hairline + offset ghost stroke for glass depth\n      const passes = [\n        { dx: 0.75, dy: 0.75, w: 0.5, c: \"rgba(255,255,255,0.12)\" },\n        { dx: 0, dy: 0, w: 1, c: \"rgba(255,255,255,0.35)\" },\n      ];\n      for (const pass of passes) {\n        ctx.beginPath();\n        for (const e of edges) {\n          if (p <= e.t) continue;\n          // cracks grow segment-by-segment from the near end outward\n          const f = Math.min(1, (p - e.t) / 0.12);\n          ctx.moveTo(e.ax + pass.dx, e.ay + pass.dy);\n          ctx.lineTo(e.ax + (e.bx - e.ax) * f + pass.dx, e.ay + (e.by - e.ay) * f + pass.dy);\n        }\n        ctx.lineWidth = pass.w;\n        ctx.strokeStyle = pass.c;\n        ctx.stroke();\n      }\n      ctx.restore();\n    };\n\n    const buildShards = (): Shard[] => {\n      const tx = width - INSET - thumbSize / 2;\n      const ty = height / 2;\n      return cells.map((poly) => {\n        let cx = 0;\n        let cy = 0;\n        for (const pt of poly) {\n          cx += pt.x;\n          cy += pt.y;\n        }\n        cx /= poly.length;\n        cy /= poly.length;\n        let dx = cx - tx;\n        let dy = cy - ty;\n        const d = Math.hypot(dx, dy);\n        if (d < 1) {\n          const a = Math.random() * Math.PI * 2;\n          dx = Math.cos(a);\n          dy = Math.sin(a);\n        } else {\n          dx /= d;\n          dy /= d;\n        }\n        const speed = 120 + Math.random() * 300;\n        return {\n          poly: poly.map((pt) => ({ x: pt.x - cx, y: pt.y - cy })),\n          cx,\n          cy,\n          ox: 0,\n          oy: 0,\n          vx: dx * speed,\n          vy: dy * speed,\n          rot: 0,\n          vr: (Math.random() * 2 - 1) * 3,\n        };\n      });\n    };\n\n    const stepShards = (dt: number) => {\n      for (const s of S.shards) {\n        s.vy += 1800 * dt;\n        s.ox += s.vx * dt;\n        s.oy += s.vy * dt;\n        s.rot += s.vr * dt;\n      }\n    };\n\n    const drawShards = (elapsed: number) => {\n      ctx.clearRect(-PAD_X, -PAD_TOP, cw, ch);\n      const alpha = Math.max(0, 1 - elapsed / FADE_MS);\n      if (alpha <= 0) return;\n      for (const s of S.shards) {\n        ctx.save();\n        ctx.translate(s.cx + s.ox, s.cy + s.oy);\n        ctx.rotate(s.rot);\n        ctx.globalAlpha = alpha;\n        ctx.beginPath();\n        for (let i = 0; i < s.poly.length; i++) {\n          const pt = s.poly[i];\n          if (i === 0) ctx.moveTo(pt.x, pt.y);\n          else ctx.lineTo(pt.x, pt.y);\n        }\n        ctx.closePath();\n        ctx.fillStyle = \"rgba(255,255,255,0.10)\";\n        ctx.fill();\n        ctx.lineWidth = 1;\n        ctx.strokeStyle = \"rgba(255,255,255,0.4)\";\n        ctx.stroke();\n        ctx.restore();\n      }\n    };\n\n    const wake = () => {\n      if (!S.raf) {\n        S.last = performance.now();\n        S.raf = requestAnimationFrame(loop);\n      }\n    };\n\n    const commit = () => {\n      S.dragging = false;\n      S.p = 1;\n      setThumb(1);\n      thumb.setAttribute(\"aria-valuenow\", \"100\");\n      // the pane is gone the instant it breaks\n      glass.style.visibility = \"hidden\";\n      thumb.style.visibility = \"hidden\";\n      root.style.transform = \"\";\n      setConfirmed(true);\n      if (!reduced) {\n        S.mode = \"shatter\";\n        S.shatterStart = performance.now();\n        S.shards = buildShards();\n        wake();\n      } else {\n        S.mode = \"idle\";\n      }\n      onConfirmRef.current?.();\n    };\n\n    const loop = (now: number) => {\n      const dt = Math.min((now - S.last) / 1000, 1 / 30);\n      S.last = now;\n      let alive = false;\n      if (S.mode === \"drag\") {\n        if (S.p >= COMMIT_AT) {\n          commit();\n          alive = true;\n        } else {\n          const p = S.p;\n          setThumb(p);\n          drawCracks(p);\n          // structural shudder once the pane is badly cracked\n          root.style.transform =\n            p > 0.6\n              ? `translate(${Math.random() - 0.5}px, ${Math.random() - 0.5}px)`\n              : \"\";\n          alive = true;\n        }\n      } else if (S.mode === \"spring\") {\n        // stiffness 220 / damping 26 — slightly underdamped, tiny overshoot\n        S.v += (-220 * S.p - 26 * S.v) * dt;\n        S.p += S.v * dt;\n        if (Math.abs(S.p) < 0.001 && Math.abs(S.v) < 0.01) {\n          S.p = 0;\n          S.v = 0;\n          S.mode = \"idle\";\n          setThumb(0);\n          drawCracks(0);\n          root.style.transform = \"\";\n        } else {\n          const p = Math.max(0, S.p);\n          setThumb(p);\n          drawCracks(p); // same t-mapping in reverse — the heal is free\n          root.style.transform = \"\";\n          alive = true;\n        }\n      } else if (S.mode === \"shatter\") {\n        const elapsed = now - S.shatterStart;\n        if (elapsed >= SHATTER_MS) {\n          ctx.clearRect(-PAD_X, -PAD_TOP, cw, ch);\n          S.mode = \"idle\";\n        } else {\n          stepShards(dt);\n          drawShards(elapsed);\n          alive = true;\n        }\n      }\n      S.raf = alive ? requestAnimationFrame(loop) : 0;\n    };\n\n    const onDown = (e: PointerEvent) => {\n      if (S.mode === \"shatter\" || glass.style.visibility === \"hidden\") return;\n      thumb.setPointerCapture(e.pointerId);\n      S.dragging = true;\n      S.mode = \"drag\";\n      S.v = 0;\n      S.startX = e.clientX;\n      S.startP = S.p;\n      if (!reduced) wake();\n    };\n\n    const onMove = (e: PointerEvent) => {\n      if (!S.dragging) return;\n      S.p = Math.min(1, Math.max(0, S.startP + (e.clientX - S.startX) / maxTravel));\n      if (reduced) {\n        // no canvas at all — plain slide, instant swap at full travel\n        setThumb(S.p);\n        if (S.p >= COMMIT_AT) commit();\n      }\n    };\n\n    const onUp = () => {\n      if (!S.dragging) return;\n      S.dragging = false;\n      if (S.p >= COMMIT_AT) {\n        if (S.mode !== \"shatter\") commit();\n        return;\n      }\n      thumb.setAttribute(\"aria-valuenow\", \"0\");\n      if (reduced) {\n        S.p = 0;\n        S.mode = \"idle\";\n        setThumb(0);\n      } else {\n        S.mode = \"spring\";\n        wake();\n      }\n    };\n\n    // Arrow keys nudge p by KEY_STEP; Home/End jump to the ends; Enter/Space\n    // confirms outright — mirrors the pointer path without engaging the rAF\n    // loop for a static value (loop only wakes once travel actually commits).\n    const onKeyDown = (e: KeyboardEvent) => {\n      if (S.mode === \"shatter\" || S.mode === \"spring\" || glass.style.visibility === \"hidden\") return;\n      let next: number;\n      switch (e.key) {\n        case \"ArrowRight\":\n        case \"ArrowUp\":\n          next = S.p + KEY_STEP;\n          break;\n        case \"ArrowLeft\":\n        case \"ArrowDown\":\n          next = S.p - KEY_STEP;\n          break;\n        case \"Home\":\n          next = 0;\n          break;\n        case \"End\":\n        case \"Enter\":\n        case \" \":\n          next = 1;\n          break;\n        default:\n          return;\n      }\n      e.preventDefault();\n      next = Math.min(1, Math.max(0, next));\n      S.p = next;\n      S.startP = next;\n      thumb.setAttribute(\"aria-valuenow\", String(Math.round(next * 100)));\n      if (S.p >= COMMIT_AT) {\n        commit();\n      } else {\n        S.mode = \"idle\";\n        setThumb(S.p);\n        if (!reduced) drawCracks(S.p);\n      }\n    };\n\n    thumb.addEventListener(\"pointerdown\", onDown);\n    thumb.addEventListener(\"pointermove\", onMove);\n    thumb.addEventListener(\"pointerup\", onUp);\n    thumb.addEventListener(\"pointercancel\", onUp);\n    thumb.addEventListener(\"keydown\", onKeyDown);\n    return () => {\n      cancelAnimationFrame(S.raf);\n      thumb.removeEventListener(\"pointerdown\", onDown);\n      thumb.removeEventListener(\"pointermove\", onMove);\n      thumb.removeEventListener(\"pointerup\", onUp);\n      thumb.removeEventListener(\"pointercancel\", onUp);\n      thumb.removeEventListener(\"keydown\", onKeyDown);\n      root.style.transform = \"\";\n    };\n  }, [width, height, shardCount, resetKey]);\n\n  const thumbSize = height - INSET * 2;\n\n  return (\n    <div\n      ref={rootRef}\n      className={`relative select-none ${className}`}\n      style={{ width, height }}\n    >\n      {/* revealed state beneath the glass */}\n      <div\n        aria-hidden={!confirmed}\n        className={`absolute inset-0 flex items-center justify-center gap-2 rounded-md border border-border bg-surface ${\n          confirmed ? \"opacity-100\" : \"opacity-0\"\n        }`}\n      >\n        <svg\n          width=\"16\"\n          height=\"16\"\n          viewBox=\"0 0 24 24\"\n          fill=\"none\"\n          stroke=\"currentColor\"\n          strokeWidth=\"2\"\n          strokeLinecap=\"round\"\n          strokeLinejoin=\"round\"\n          aria-hidden\n          className=\"text-foreground\"\n        >\n          <path d=\"M20 6 9 17l-5-5\" />\n        </svg>\n        <span className=\"font-mono text-xs tracking-[0.2em] text-foreground\">\n          {confirmedLabel}\n        </span>\n      </div>\n\n      {/* frosted pane — house glass recipe */}\n      <div\n        ref={glassRef}\n        className={`absolute inset-0 overflow-hidden rounded-md border border-black/15 bg-white/60 shadow-[inset_0_1px_0_0_rgba(255,255,255,0.7),0_8px_24px_-8px_rgba(0,0,0,0.28)] backdrop-blur-xl backdrop-saturate-150 dark:border-white/10 dark:bg-white/[0.06] dark:shadow-[inset_0_1px_0_0_rgba(255,255,255,0.12),0_8px_24px_-8px_rgba(0,0,0,0.5)] ${\n          confirmed ? \"invisible\" : \"\"\n        }`}\n      >\n        <span className=\"absolute inset-0 flex items-center justify-center pl-8 font-mono text-[11px] tracking-[0.25em] text-ns-muted\">\n          {label}\n        </span>\n      </div>\n\n      {/* crack + shard overlay, bleeds past the track so shards can fly */}\n      <canvas\n        ref={canvasRef}\n        aria-hidden\n        className=\"pointer-events-none absolute\"\n        style={{\n          left: -PAD_X,\n          top: -PAD_TOP,\n          width: width + PAD_X * 2,\n          height: height + PAD_TOP + PAD_BOTTOM,\n        }}\n      />\n\n      {/* thumb */}\n      <button\n        ref={thumbRef}\n        type=\"button\"\n        role=\"slider\"\n        aria-label={label}\n        aria-valuemin={0}\n        aria-valuemax={100}\n        aria-valuenow={0}\n        className={`absolute flex cursor-grab touch-none items-center justify-center rounded-sm border border-black/15 bg-black/[0.04] text-ns-muted transition-colors duration-150 hover:border-black/25 hover:bg-black/[0.08] hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ns-accent focus-visible:ring-offset-2 focus-visible:ring-offset-background shadow-[inset_0_1px_0_0_rgba(255,255,255,0.6),0_2px_8px_-2px_rgba(0,0,0,0.25)] will-change-transform active:cursor-grabbing dark:border-white/15 dark:bg-white/10 dark:hover:border-white/30 dark:hover:bg-white/15 dark:shadow-[inset_0_1px_0_0_rgba(255,255,255,0.18),0_2px_8px_-2px_rgba(0,0,0,0.5)] ${\n          confirmed ? \"invisible\" : \"\"\n        }`}\n        style={{ left: INSET, top: INSET, width: thumbSize, height: thumbSize }}\n      >\n        <svg\n          width=\"16\"\n          height=\"16\"\n          viewBox=\"0 0 24 24\"\n          fill=\"none\"\n          stroke=\"currentColor\"\n          strokeWidth=\"2\"\n          strokeLinecap=\"round\"\n          strokeLinejoin=\"round\"\n          aria-hidden\n        >\n          <path d=\"m6 17 5-5-5-5\" />\n          <path d=\"m13 17 5-5-5-5\" />\n        </svg>\n      </button>\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/ui/confirm-slide-shatter.tsx"
    }
  ],
  "cssVars": {
    "theme": {
      "color-ns-muted": "var(--ns-muted)",
      "color-ns-accent": "var(--ns-accent)",
      "color-surface": "var(--surface)"
    },
    "light": {
      "ns-muted": "#4d4d4d",
      "ns-accent": "#006bff",
      "surface": "#fafafa"
    },
    "dark": {
      "ns-muted": "#8f8f8f",
      "surface": "#171717"
    }
  },
  "meta": {
    "collection": "core",
    "tags": [
      "slider",
      "confirm",
      "destructive",
      "glass",
      "canvas",
      "voronoi",
      "shatter",
      "physics"
    ],
    "instruction": "A frosted-glass confirm slider where destruction IS the progress indicator: a 320×56 DOM track in the house glass recipe (light: bg-white/60 with a black/15 border; dark: bg-white/[0.06] with a white/10 border; backdrop-blur-xl, inset top specular, rounded-md) carries a DPR-aware canvas overlay and a 48px grabbable thumb, itself theme-split (black-tinted border/fill on light, white-tinted on dark) so it reads against the pane in both modes. On mount, seed ~48 Poisson-disc points across the track and compute exact Voronoi cells once via half-plane bisector clipping; deduped cell walls become crack polylines, each assigned a reveal threshold t from its distance to the thumb origin, and the same cells are reused later as shard polygons. Dragging (pointer capture, progress in a ref, thumb transform set in a rAF loop, zero React state on the hot path) strokes every polyline with t<p segment-by-segment from its near end — 1px rgba(255,255,255,0.35) hairline plus a 0.75px-offset 0.5px rgba(255,255,255,0.12) ghost for glass depth — and past p=0.6 the whole track shudders ±0.5px per frame. Release early and p springs back to 0 (stiffness 220, damping 26) so the cracks retract along the same t-mapping, healing for free. The thumb is a full role=\"slider\": Arrow/Up/Down keys nudge progress by 0.08, Home/End jump to the ends, and Enter/Space confirm outright, each keyboard step rendering once without waking the rAF loop. At p≥0.98 (by drag or key) the DOM glass hides instantly and the canvas flips to shard mode: each Voronoi cell tumbles outward from the thumb at 120–420px/s under 1800px/s² gravity with ±3rad/s spin, fading over 700ms; after ~900ms the canvas clears, revealing a bg-surface row with a check icon and mono CONFIRMED. The rAF loop sleeps whenever settled, and prefers-reduced-motion drops the canvas entirely for a plain slide with an instant confirmed swap."
  },
  "type": "registry:ui"
}