{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pricing-usage-slider",
  "type": "registry:block",
  "title": "Pricing Usage Slider",
  "description": "A two-plan pricing section where the second card prices itself from a volume slider, the figure spinning with Number Flow as you drag while the track fills behind the handle.",
  "dependencies": [
    "@number-flow/react"
  ],
  "registryDependencies": [
    "https://tween-ui.vercel.app/r/slide-arrow-button.json"
  ],
  "cssVars": {
    "theme": {}
  },
  "files": [
    {
      "path": "components/tweenui/pricing-usage-slider.tsx",
      "type": "registry:component",
      "target": "components/tweenui/pricing-usage-slider.tsx",
      "content": "'use client';\n\nimport { useId, useState, type ComponentPropsWithoutRef, type ReactNode } from 'react';\nimport NumberFlow from '@number-flow/react';\nimport { cn } from '@/lib/utils';\nimport SlideArrowButton from './slide-arrow-button';\n\nfunction BoltIcon({ className }: { className?: string }) {\n  return (\n    <svg viewBox=\"0 0 24 24\" className={className} aria-hidden=\"true\" fill=\"none\">\n      <path\n        d=\"M13 2 4.5 13.5H11l-.5 8.5L19.5 10.5H13V2Z\"\n        stroke=\"currentColor\"\n        strokeWidth=\"1.5\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nfunction CheckIcon({ className }: { className?: string }) {\n  return (\n    <svg viewBox=\"0 0 24 24\" className={className} aria-hidden=\"true\" fill=\"none\">\n      <path d=\"m4 12 5.5 5.5L20 7\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" />\n    </svg>\n  );\n}\n\nexport interface PricingUsageSliderRange {\n  /** Cheapest price on the track. */\n  min: number;\n  /** Most expensive price on the track. */\n  max: number;\n  /** Where the handle starts. */\n  defaultValue: number;\n  /** Caption under the slider, e.g. \"minutes per month\". */\n  label?: string;\n}\n\nexport interface PricingUsageSliderPlan {\n  /** Plan name. */\n  name: string;\n  /** One line under the name. */\n  description: string;\n  /** Fixed monthly price. Omit it and pass `range` for a slider-driven plan. */\n  price?: number;\n  /** Small print under the price. */\n  priceNote?: string;\n  /** What the plan includes. */\n  features: string[];\n  /** Button label. */\n  cta: string;\n  /** Runs when the plan's button is pressed. */\n  onClick?: () => void;\n  /** Highlights the card and adds the badge. */\n  popular?: boolean;\n  /** Turns the price into a slider the visitor scrubs. */\n  range?: PricingUsageSliderRange;\n}\n\nexport interface PricingUsageSliderProps extends Omit<\n  ComponentPropsWithoutRef<'section'>,\n  'title'\n> {\n  /** Section heading. */\n  title?: ReactNode;\n  /** Supporting line under the heading. */\n  description?: string;\n  /** Plans shown side by side. Two is the intended shape. */\n  plans?: PricingUsageSliderPlan[];\n  /** Badge on the popular card. */\n  popularLabel?: string;\n}\n\nconst DEFAULT_TITLE = 'Flexible pricing for every creator';\n\nconst DEFAULT_DESCRIPTION =\n  'Start free and scale as you grow. No contracts, no surprises — just work that ships.';\n\nconst DEFAULT_PLANS: PricingUsageSliderPlan[] = [\n  {\n    name: 'Creator',\n    description: 'Best for solo creators publishing every week',\n    price: 79,\n    priceNote: 'Cancel or pause any time',\n    popular: true,\n    features: ['600+ minutes monthly', 'One custom cloned voice', 'Commercial usage license'],\n    cta: 'Upgrade now',\n  },\n  {\n    name: 'Scale',\n    description: 'Pay for the volume your team actually uses',\n    priceNote: 'Billed monthly, adjust any time',\n    range: { min: 399, max: 1200, defaultValue: 750, label: 'Drag to set your monthly volume' },\n    features: ['Custom SLA and volume', 'Unlimited cloned voices', 'SSO, SAML and a dedicated CSM'],\n    cta: 'Contact sales',\n  },\n];\n\nfunction PlanCard({ plan, popularLabel }: { plan: PricingUsageSliderPlan; popularLabel: string }) {\n  const sliderId = useId();\n  const [value, setValue] = useState(plan.range?.defaultValue ?? 0);\n\n  const range = plan.range;\n  const fill = range\n    ? Math.max(0, Math.min(100, ((value - range.min) / (range.max - range.min || 1)) * 100))\n    : 0;\n\n  return (\n    <div\n      data-pricing-card\n      className={cn(\n        'flex h-full flex-col justify-between rounded-2xl border p-6',\n        'motion-reduce:transform-none motion-reduce:transition-none',\n        plan.popular\n          ? 'border-transparent bg-white shadow-sm dark:bg-white'\n          : 'border-[#18181b]/10 bg-white dark:border-white/10 dark:bg-[#161b22]'\n      )}\n    >\n      <div className=\"space-y-4\">\n        <div className=\"flex items-center justify-between gap-3\">\n          <span\n            className={cn(\n              'grid size-10 shrink-0 place-items-center rounded-full border',\n              plan.popular\n                ? 'border-[#18181b]/15 text-[#12161f]'\n                : 'border-[#18181b]/15 text-[#12161f] dark:border-white/15 dark:text-white'\n            )}\n          >\n            <BoltIcon className=\"size-5\" />\n          </span>\n          {plan.popular && (\n            <span className=\"rounded-full bg-[#c6f56f] px-3 py-1 text-xs text-[#12161f]\">\n              {popularLabel}\n            </span>\n          )}\n        </div>\n\n        <div className=\"space-y-1\">\n          <h3\n            className={cn(\n              'text-lg font-medium',\n              plan.popular ? 'text-[#12161f]' : 'text-[#12161f] dark:text-white'\n            )}\n          >\n            {plan.name}\n          </h3>\n          <p\n            className={cn(\n              'text-xs',\n              plan.popular ? 'text-[#18181b]/60' : 'text-[#18181b]/60 dark:text-white/60'\n            )}\n          >\n            {plan.description}\n          </p>\n        </div>\n\n        <div className=\"space-y-1\">\n          <p\n            className={cn(\n              'text-3xl font-normal',\n              plan.popular ? 'text-[#12161f]' : 'text-[#12161f] dark:text-white'\n            )}\n          >\n            ${range ? <NumberFlow value={value} trend={0} /> : plan.price}\n            <span\n              className={cn(\n                'text-sm',\n                plan.popular ? 'text-[#18181b]/60' : 'text-[#18181b]/60 dark:text-white/60'\n              )}\n            >\n              {' '}\n              /mo\n            </span>\n          </p>\n          {plan.priceNote && (\n            <p\n              className={cn(\n                'text-xs',\n                plan.popular ? 'text-[#18181b]/50' : 'text-[#18181b]/50 dark:text-white/50'\n              )}\n            >\n              {plan.priceNote}\n            </p>\n          )}\n        </div>\n\n        {range && (\n          <div className=\"space-y-2\">\n            <label htmlFor={sliderId} className=\"sr-only\">\n              {range.label ?? 'Adjust your monthly volume'}\n            </label>\n            <input\n              id={sliderId}\n              type=\"range\"\n              min={range.min}\n              max={range.max}\n              step={1}\n              value={value}\n              onChange={(event) => setValue(Number(event.target.value))}\n              // The fill is painted straight onto the track, so the component\n              // needs no stylesheet of its own.\n              style={{\n                background: `linear-gradient(to right, #045f64 0%, #045f64 ${fill}%, rgba(24,24,27,0.12) ${fill}%, rgba(24,24,27,0.12) 100%)`,\n              }}\n              className=\"h-1.5 w-full cursor-pointer appearance-none rounded-full outline-none [&::-moz-range-thumb]:size-4 [&::-moz-range-thumb]:cursor-pointer [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-0 [&::-moz-range-thumb]:bg-[#045f64] [&::-webkit-slider-thumb]:size-4 [&::-webkit-slider-thumb]:cursor-pointer [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-[#045f64]\"\n            />\n            {range.label && <p className=\"text-xs text-[#18181b]/50\">{range.label}</p>}\n          </div>\n        )}\n\n        <ul className=\"space-y-2 pt-1\">\n          {plan.features.map((feature) => (\n            <li\n              key={feature}\n              className={cn(\n                'flex items-start gap-x-2 text-xs',\n                plan.popular ? 'text-[#18181b]/70' : 'text-[#18181b]/70 dark:text-white/70'\n              )}\n            >\n              <CheckIcon className=\"mt-0.5 size-3.5 shrink-0 text-[#045f64] dark:text-[#c6f56f]\" />\n              {feature}\n            </li>\n          ))}\n        </ul>\n      </div>\n\n      <div className=\"mt-6 flex w-full justify-center\">\n        <SlideArrowButton onClick={plan.onClick}>{plan.cta}</SlideArrowButton>\n      </div>\n    </div>\n  );\n}\n\nexport default function PricingUsageSlider({\n  title = DEFAULT_TITLE,\n  description = DEFAULT_DESCRIPTION,\n  plans = DEFAULT_PLANS,\n  popularLabel = 'Most popular',\n  className,\n  ...props\n}: PricingUsageSliderProps) {\n  return (\n    <section\n      data-pricing-usage-slider\n      className={cn('w-full px-5 py-14 md:py-16', className)}\n      {...props}\n    >\n      <div className=\"mx-auto max-w-[1064px] space-y-10\">\n        <div className=\"space-y-3 text-center\">\n          <h2 className=\"text-3xl font-medium tracking-tight text-[#12161f] md:text-4xl dark:text-white\">\n            {title}\n          </h2>\n          <p className=\"mx-auto max-w-[450px] text-sm text-[#18181b]/60 md:text-base dark:text-white/60\">\n            {description}\n          </p>\n        </div>\n\n        <div className=\"mx-auto grid max-w-[860px] items-stretch gap-6 md:grid-cols-2\">\n          {plans.map((plan) => (\n            <PlanCard key={plan.name} plan={plan} popularLabel={popularLabel} />\n          ))}\n        </div>\n      </div>\n    </section>\n  );\n}\n"
    }
  ],
  "docs": "Docs, props and examples: https://tween-ui.vercel.app/block/pricing-usage-slider"
}