{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "score-scale",
  "title": "Score Scale",
  "description": "A read-only score meter with endpoint labels and a controlled marker.",
  "registryDependencies": [
    "@jbm/tokens"
  ],
  "files": [
    {
      "path": "registry/jbm/ui/score-scale.tsx",
      "content": "import * as React from \"react\"\nimport { color, font } from \"../lib/tokens\"\n\nexport type ScoreScaleProps = React.HTMLAttributes<HTMLDivElement> & {\n  value: number\n  min?: number\n  max?: number\n  label: string\n  labels?: [string, string]\n  formatValue?: (value: number) => string\n}\n\n/** Read-only score meter, not an input slider. Finite values clamp to the displayed range. */\nexport function ScoreScale({\n  value,\n  min = 0,\n  max = 10,\n  label,\n  labels,\n  formatValue = String,\n  style,\n  ...props\n}: ScoreScaleProps) {\n  if (![value, min, max].every(Number.isFinite) || max <= min)\n    throw new RangeError(\"ScoreScale requires finite values and max > min\")\n  const v = Math.max(min, Math.min(max, value))\n  const p = (v - min) / (max - min)\n  return (\n    <div\n      {...props}\n      style={{ fontFamily: font.sans, color: color.ink, minWidth: 0, ...style }}\n    >\n      <div\n        style={{\n          display: \"flex\",\n          justifyContent: \"space-between\",\n          gap: 16,\n          flexWrap: \"wrap\",\n          marginBottom: 22,\n          overflowWrap: \"anywhere\",\n        }}\n      >\n        <span style={{ fontSize: 16, fontWeight: 600 }}>{label}</span>\n        <strong style={{ fontFamily: font.mono, color: color.accent }}>\n          {formatValue(v)}\n        </strong>\n      </div>\n      <div\n        role=\"meter\"\n        aria-label={label}\n        aria-valuemin={min}\n        aria-valuemax={max}\n        aria-valuenow={v}\n        aria-valuetext={formatValue(v)}\n        style={{ paddingInline: 12 }}\n      >\n        <div style={{ position: \"relative\", height: 24 }}>\n          <div\n            style={{\n              position: \"absolute\",\n              top: 10,\n              width: \"100%\",\n              height: 4,\n              borderRadius: 4,\n              background: color.line,\n            }}\n          />\n          {[0, 0.25, 0.5, 0.75, 1].map((tick) => (\n            <span\n              key={tick}\n              style={{\n                position: \"absolute\",\n                top: 8,\n                left: `${tick * 100}%`,\n                width: 8,\n                height: 8,\n                borderRadius: \"50%\",\n                transform: \"translateX(-50%)\",\n                background: color.dim,\n              }}\n            />\n          ))}\n          <span\n            style={{\n              position: \"absolute\",\n              left: `${p * 100}%`,\n              top: 0,\n              width: 24,\n              height: 24,\n              boxSizing: \"border-box\",\n              border: `3px solid ${color.bg}`,\n              borderRadius: \"50%\",\n              background: color.accent,\n              transform: \"translateX(-50%)\",\n            }}\n          />\n        </div>\n      </div>\n      <div\n        style={{\n          display: \"flex\",\n          justifyContent: \"space-between\",\n          gap: 24,\n          fontFamily: font.mono,\n          fontSize: 12,\n          color: color.dim,\n          marginTop: 10,\n          overflowWrap: \"anywhere\",\n        }}\n      >\n        <span style={{ flex: 1 }}>{labels?.[0] ?? min}</span>\n        <span style={{ flex: 1, textAlign: \"end\" }}>{labels?.[1] ?? max}</span>\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:ui",
      "target": "src/jbm/ui/score-scale.tsx"
    }
  ],
  "type": "registry:ui"
}