{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "offset-tiers",
  "title": "Offset Tiers",
  "description": "Staggered serif panels on a dot grid, with one entry price and expert CTAs.",
  "dependencies": [
    "lucide-react"
  ],
  "files": [
    {
      "path": "components/spectrumui/blocks/pricing/offset-tiers.tsx",
      "content": "'use client';\n\nimport { ArrowRight, Check } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { REVEAL_KEYFRAMES, revealClass, revealDelay, useReveal } from './reveal';\nimport type { Plan } from './types';\n\nexport type OffsetTiersVariant = 'Staggered' | 'Level';\n\nexport interface OffsetTiersProps {\n  plans: Plan[];\n  eyebrow?: string;\n  heading?: string;\n  subheading?: string;\n  currency?: string;\n  variant?: OffsetTiersVariant;\n  className?: string;\n}\n\nconst OFFSETS = ['@3xl:mt-0', '@3xl:mt-10', '@3xl:mt-20'];\n\nexport function OffsetTiers({\n  plans,\n  eyebrow,\n  heading,\n  subheading,\n  currency = '$',\n  variant = 'Staggered',\n  className,\n}: OffsetTiersProps) {\n  const { ref, shown } = useReveal<HTMLElement>();\n  const staggered = variant === 'Staggered';\n\n  return (\n    <section\n      ref={ref}\n      className={cn(\n        '@container relative w-full bg-white px-6 py-10 sm:px-10 sm:py-14 dark:bg-[#0b0b0d]',\n        '[background-image:radial-gradient(circle,#d9d9d9_1px,transparent_1px)] [background-size:13px_13px]',\n        'dark:[background-image:radial-gradient(circle,#2a2a2e_1px,transparent_1px)]',\n        className,\n      )}\n    >\n      <style dangerouslySetInnerHTML={{ __html: REVEAL_KEYFRAMES }} />\n\n      {heading && (\n        <div className={cn('mb-12 max-w-[52ch]', revealClass(shown))}>\n          {eyebrow && (\n            <span className=\"flex items-center gap-2.5\">\n              <span aria-hidden className=\"size-2 rounded-full bg-[#6f88f7]\" />\n              <span className=\"text-[12px] font-medium uppercase tracking-[0.14em] text-[#6b6b70] dark:text-[#9d9da5]\">\n                {eyebrow}\n              </span>\n            </span>\n          )}\n          <h2 className=\"mt-4 text-balance [font-family:var(--font-spectral,Georgia,serif)] text-[40px] font-normal leading-[1.05] tracking-[-0.5px] text-[#111113] dark:text-[#f4f4f5]\">\n            {heading}\n          </h2>\n          {subheading && (\n            <p className=\"mt-4 text-pretty text-[16px] leading-[1.55] text-[#6b6b70] dark:text-[#9d9da5]\">\n              {subheading}\n            </p>\n          )}\n        </div>\n      )}\n\n      <div className=\"grid gap-8 @3xl:grid-cols-3 @3xl:gap-9\">\n        {plans.map((plan, position) => (\n          <div\n            key={plan.id}\n            style={revealDelay(shown, position)}\n            className={cn(\n              'flex flex-col border border-[#e4e4e4] bg-white transition-[border-color,box-shadow] duration-200 dark:border-[#28282d] dark:bg-[#111113]',\n              '[@media(hover:hover)]:hover:border-[#c9c9cc] [@media(hover:hover)]:hover:shadow-[0_16px_40px_-28px_rgba(0,0,0,0.25)] dark:[@media(hover:hover)]:hover:border-[#3a3a41]',\n              revealClass(shown),\n              staggered && OFFSETS[position % OFFSETS.length],\n            )}\n          >\n            <div className=\"px-8 pb-9 pt-10 @lg:px-9\">\n              <h3 className=\"[font-family:var(--font-spectral,Georgia,serif)] text-[40px] font-normal leading-none tracking-[-0.5px] text-[#111113] dark:text-[#f4f4f5]\">\n                {plan.name}\n              </h3>\n              <p className=\"mt-5 text-pretty text-[16px] leading-[1.55] text-[#6b6b70] dark:text-[#9d9da5]\">\n                {plan.tagline}\n              </p>\n            </div>\n\n            <div className=\"flex-1 border-t border-[#e4e4e4] dark:border-[#28282d] px-8 py-9 @lg:px-9\">\n              {plan.inherits && (\n                <p className=\"mb-7 text-[16.5px] font-medium leading-snug text-[#26262a] dark:text-[#e7e7ec]\">\n                  Includes everything in {plan.inherits}, plus:\n                </p>\n              )}\n              <ul className=\"space-y-5\">\n                {plan.features.map((feature) => (\n                  <li key={feature} className=\"flex gap-3.5\">\n                    <span\n                      aria-hidden\n                      className=\"mt-0.5 grid size-[21px] shrink-0 place-items-center rounded-full bg-[#6f88f7]\"\n                    >\n                      <Check className=\"size-3 text-white\" strokeWidth={3} />\n                    </span>\n                    <span className=\"text-[15.5px] leading-[1.5] text-[#3d3d42] dark:text-[#c6c6cd]\">\n                      {feature}\n                    </span>\n                  </li>\n                ))}\n              </ul>\n            </div>\n\n            <div className=\"border-t border-[#e4e4e4] dark:border-[#28282d] px-8 pb-9 pt-7 @lg:px-9\">\n              <p className=\"text-center text-[17px] text-[#26262a] dark:text-[#e7e7ec]\">\n                {plan.price ? (\n                  <>\n                    From{' '}\n                    <span className=\"font-semibold\">\n                      {currency}\n                      {plan.price.monthly.toLocaleString('en-US')} /month\n                    </span>\n                  </>\n                ) : (\n                  'Talk to us to find out more'\n                )}\n              </p>\n              <button\n                type=\"button\"\n                className={cn(\n                  'group mt-5 flex h-[52px] w-full items-center justify-center gap-2 rounded-[4px] bg-[#111113] text-[15px] font-medium text-white dark:bg-[#f4f4f5] dark:text-[#0b0b0d]',\n                  'transition-[transform,background-color] duration-150 hover:bg-black active:scale-[0.98] dark:hover:bg-white',\n                  'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-[#6f88f7] focus-visible:ring-offset-2 dark:focus-visible:ring-offset-[#111113]',\n                )}\n              >\n                {plan.cta}\n                <ArrowRight\n                  aria-hidden\n                  className=\"size-4 transition-transform duration-[180ms] ease-[cubic-bezier(0.23,1,0.32,1)] group-hover:translate-x-0.5 motion-reduce:transition-none\"\n                />\n              </button>\n            </div>\n          </div>\n        ))}\n      </div>\n    </section>\n  );\n}\n\nexport default OffsetTiers;\n",
      "type": "registry:block",
      "target": "components/spectrumui/blocks/pricing/offset-tiers.tsx"
    },
    {
      "path": "components/spectrumui/blocks/pricing/types.ts",
      "content": "export type BillingPeriod = 'monthly' | 'annual';\n\nexport interface PlanPrice {\n  monthly: number;\n  annual: number;\n}\n\nexport interface Plan {\n  id: string;\n  name: string;\n  tagline: string;\n  price: PlanPrice | null;\n  unit?: string;\n  cta: string;\n  featured?: boolean;\n  badge?: string;\n  inherits?: string;\n  features: string[];\n  footnote?: string;\n}\n\nexport type FeatureValue = string | boolean;\n\nexport interface FeatureRow {\n  label: string;\n  hint?: string;\n  values: FeatureValue[];\n}\n\nexport interface FeatureGroup {\n  label: string;\n  rows: FeatureRow[];\n}\n\nexport interface RateTier {\n  from: number;\n  to?: number;\n  unitPrice: number;\n}\n\nexport interface MeteredProduct {\n  id: string;\n  name: string;\n  description?: string;\n  unit: string;\n  unitPlural: string;\n  included: number;\n  tiers: RateTier[];\n}\n\nexport interface CreditPack {\n  id: string;\n  name: string;\n  credits: number;\n  bonus: number;\n  price: number;\n  featured?: boolean;\n  note?: string;\n}\n\nexport interface CreditRate {\n  id: string;\n  label: string;\n  credits: number;\n  per: number;\n  unitLabel: string;\n}\n\nexport interface AddOn {\n  id: string;\n  name: string;\n  description: string;\n  price: number;\n  unit?: string;\n  perSeat?: boolean;\n  defaultOn?: boolean;\n}\n\nexport interface ModelRate {\n  id: string;\n  name: string;\n  context: string;\n  input: number;\n  output: number;\n  cacheWrite: number;\n  cacheRead: number;\n  badge?: string;\n}\n\nexport interface UsageDimension {\n  id: string;\n  label: string;\n  unit: string;\n  min: number;\n  max: number;\n  step: number;\n  defaultValue: number;\n}\n\nexport interface ComplianceItem {\n  id: string;\n  label: string;\n  detail: string;\n}\n\nexport interface RecommenderOption {\n  id: string;\n  label: string;\n  scores: Record<string, number>;\n  reason?: string;\n}\n\nexport interface RecommenderQuestion {\n  id: string;\n  label: string;\n  hint?: string;\n  options: RecommenderOption[];\n}\n\nexport interface PlanFamily {\n  id: string;\n  label: string;\n  description: string;\n  plans: Plan[];\n  footnote?: string;\n}\n",
      "type": "registry:file",
      "target": "components/spectrumui/blocks/pricing/types.ts"
    },
    {
      "path": "components/spectrumui/blocks/pricing/reveal.ts",
      "content": "'use client';\n\nimport { useEffect, useRef, useState } from 'react';\n\nexport const REVEAL_KEYFRAMES = `\n@keyframes su-reveal { from { opacity: 0; transform: translateY(14px) } to { opacity: 1; transform: none } }\n@keyframes su-draw { from { stroke-dashoffset: 1 } to { stroke-dashoffset: 0 } }\n@keyframes su-sheen { 0%, 62% { transform: translateX(-110%) } 100% { transform: translateX(110%) } }\n`;\n\nexport function useReveal<T extends HTMLElement>() {\n  const ref = useRef<T>(null);\n  const [shown, setShown] = useState(false);\n\n  useEffect(() => {\n    const node = ref.current;\n    if (!node) return;\n    if (typeof IntersectionObserver === 'undefined') {\n      const frame = requestAnimationFrame(() => setShown(true));\n      return () => cancelAnimationFrame(frame);\n    }\n    const observer = new IntersectionObserver(\n      (entries) => {\n        if (entries.some((entry) => entry.isIntersecting)) {\n          setShown(true);\n          observer.disconnect();\n        }\n      },\n      { threshold: 0.18, rootMargin: '0px 0px -8% 0px' },\n    );\n    observer.observe(node);\n    return () => observer.disconnect();\n  }, []);\n\n  return { ref, shown };\n}\n\nexport function revealClass(shown: boolean) {\n  return shown\n    ? '[animation:su-reveal_480ms_cubic-bezier(0.23,1,0.32,1)_both] motion-reduce:animate-none'\n    : '';\n}\n\nexport function revealDelay(shown: boolean, index: number) {\n  return shown ? { animationDelay: `${index * 70}ms` } : undefined;\n}\n",
      "type": "registry:file",
      "target": "components/spectrumui/blocks/pricing/reveal.ts"
    }
  ],
  "type": "registry:block"
}
