{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "cta-section",
  "title": "Cta Section",
  "author": "AlgorizeTech",
  "description": "Ask the user to pick one of 4 cta section variants: https://avaraui.com/view/cta-section. This item holds 4 of them as looks of one component (offer-showcase, mobile-app, concentric-portal, brand-pillars). Skip the question if the user already named one. An interconnected Brand Pillars call-to-action component featuring interactive service cards, a synchronized scorecard with animated progress meters, and a highlight popover bar.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "https://avaraui.com/r/mobile-mockup.json",
    "utils"
  ],
  "files": [
    {
      "path": "src/components/custom/blocks/CtaSectionBlock.tsx",
      "content": "\"use client\";\n\nimport React, { useState, useRef, useEffect, useLayoutEffect, useMemo } from \"react\";\nimport {\n  Palette,\n  Monitor,\n  MessageSquare,\n  TrendingUp,\n  ArrowRight,\n  ChevronDown,\n  Sparkles,\n  Check,\n  Star,\n  Play,\n  Signal,\n  Wifi,\n  Battery,\n} from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport { prefersReducedMotion, springTiming, usePrefersReducedMotion } from \"@/lib/animate\";\nimport { Iphone16ProMockup } from \"./MobileMockupBlock\";\n\n/** How a card travels: quick into a slot, slow and weighty back to the deck. */\nconst TO_SLOT = springTiming({ stiffness: 240, damping: 25 });\nconst TO_DECK = springTiming({ stiffness: 75, damping: 16, mass: 1.1 });\n\n/** A card's centre, size and tilt as drawn right now, even part-way through a flight. */\nfunction measureCard(el: HTMLElement) {\n  const rect = el.getBoundingClientRect();\n  const matrix = new DOMMatrixReadOnly(getComputedStyle(el).transform);\n  return {\n    x: rect.left + rect.width / 2,\n    y: rect.top + rect.height / 2,\n    width: el.offsetWidth,\n    height: el.offsetHeight,\n    rotate: (Math.atan2(matrix.b, matrix.a) * 180) / Math.PI,\n  };\n}\n\n/** The \"drag the cards\" pill drops in after the cards have landed. */\nfunction enterPill(el: HTMLDivElement | null) {\n  if (!el || prefersReducedMotion()) return;\n  el.animate(\n    [\n      { opacity: 0, transform: \"translateY(-10px)\" },\n      { opacity: 1, transform: \"none\" },\n    ],\n    { duration: 500, delay: 1500, easing: \"ease-in-out\", fill: \"backwards\" },\n  );\n}\n\n/** Rises in when `show` turns true, and stays mounted until it has fallen back out. */\nfunction RisePresence({\n  show,\n  className,\n  children,\n}: {\n  show: boolean;\n  className?: string;\n  children: React.ReactNode;\n}) {\n  const [present, setPresent] = useState(show);\n  if (show && !present) setPresent(true);\n  const ref = useRef<HTMLDivElement>(null);\n\n  useLayoutEffect(() => {\n    const el = ref.current;\n    if (!el) return;\n    if (prefersReducedMotion()) {\n      if (!show) setPresent(false);\n      return;\n    }\n    const shown = { opacity: 1, transform: \"none\" };\n    const animation = el.animate(\n      show\n        ? [{ opacity: 0, transform: \"translateY(18px) scale(0.95)\" }, shown]\n        : [shown, { opacity: 0, transform: \"translateY(12px) scale(0.95)\" }],\n      { duration: 350, easing: \"ease-out\", fill: \"both\" },\n    );\n    if (!show) animation.onfinish = () => setPresent(false);\n    return () => animation.cancel();\n  }, [show, present]);\n\n  if (!present) return null;\n  return (\n    <div ref={ref} className={className}>\n      {children}\n    </div>\n  );\n}\n\n// Animated counter for scores and summary percentage\nfunction AnimatedNumber({\n  value,\n  duration = 0.8,\n  suffix = \"\",\n}: {\n  value: number;\n  duration?: number;\n  suffix?: string;\n}) {\n  const [displayValue, setDisplayValue] = useState(value);\n  const prevValueRef = useRef(value);\n\n  useEffect(() => {\n    let startTimestamp: number | null = null;\n    const startVal = prevValueRef.current;\n    const endVal = value;\n    prevValueRef.current = value;\n\n    if (startVal === endVal) {\n      setDisplayValue(endVal);\n      return;\n    }\n\n    const step = (timestamp: number) => {\n      if (!startTimestamp) startTimestamp = timestamp;\n      const progress = Math.min((timestamp - startTimestamp) / (duration * 1000), 1);\n      const easeProgress = 1 - Math.pow(1 - progress, 3);\n      setDisplayValue(Math.round(startVal + (endVal - startVal) * easeProgress));\n      if (progress < 1) {\n        window.requestAnimationFrame(step);\n      }\n    };\n    const reqId = window.requestAnimationFrame(step);\n    return () => window.cancelAnimationFrame(reqId);\n  }, [value, duration]);\n\n  return (\n    <span>\n      {displayValue}\n      {suffix}\n    </span>\n  );\n}\n\n// ==========================================\n// TYPES & INTERFACES\n// ==========================================\nexport interface PillarItem {\n  id: string;\n  eyebrow: string;\n  title: string;\n  description: string;\n  baselineScore?: number;\n  score?: number;\n  highlight?: boolean;\n  icon?: React.ReactNode;\n}\n\nexport type CtaSectionVariant =\n  | \"brand-pillars\"\n  | \"minimal\"\n  | \"split\"\n  | \"concentric-portal\"\n  | \"orbital-lens\"\n  | \"offer-showcase\"\n  | \"cta-offer-showcase\"\n  | \"mobile-app\"\n  | \"app-showcase\"\n  | \"mobile-showcase\"\n  | \"mobile-mockup\";\n\nexport interface CtaSectionProps extends React.HTMLAttributes<HTMLElement> {\n  variant?: CtaSectionVariant;\n  color?: string;\n  heading?: string;\n  headingAccent?: string;\n  subheading?: string;\n  statement?: string;\n  pillars?: PillarItem[];\n  summaryText?: string;\n  summaryScore?: string | number;\n  ctaText?: string;\n  ctaHref?: string;\n  onCtaClick?: () => void;\n  activePillarId?: string;\n  onPillarChange?: (id: string) => void;\n  rounded?: string | number;\n  className?: string;\n  // Mobile App Showcase props\n  appleStoreHref?: string;\n  googlePlayHref?: string;\n  showAppStoreBadges?: boolean;\n  userName?: string;\n  userAvatar?: string;\n  featuredWorkoutTitle?: string;\n  featuredWorkoutImage?: string;\n  workoutDuration?: string;\n  workoutCalories?: string;\n  phonePosition?: \"left\" | \"right\";\n  cardBackground?: string;\n  // Concentric Portal / Orbital Lens props\n  phrase?: string;\n  scrollProgress?: number;\n  autoRotate?: boolean;\n  // Offer Showcase props\n  badge?: string;\n  price?: number | string;\n  compareAtPrice?: number | string;\n  currency?: string;\n  period?: string;\n  savingsText?: string;\n  footnote?: string;\n  mediaItems?: string[];\n  images?: string[];\n  enableHoverSlowdown?: boolean;\n}\n\n// ==========================================\n// DEFAULT PILLARS (Matching Framer Brand Pillars)\n// ==========================================\nexport const DEFAULT_PILLARS: PillarItem[] = [\n  {\n    id: \"branding\",\n    eyebrow: \"Branding\",\n    title: \"A brand that sticks\",\n    description: \"Identity, visual language and positioning.\",\n    baselineScore: 26,\n    score: 100,\n    icon: <Palette className=\"size-5 sm:size-6 stroke-[2]\" />,\n  },\n  {\n    id: \"website\",\n    eyebrow: \"Website\",\n    title: 'An \"on-brand\" website',\n    description: \"Your digital storefront, built to convert.\",\n    baselineScore: 34,\n    score: 100,\n    icon: <Monitor className=\"size-5 sm:size-6 stroke-[2]\" />,\n  },\n  {\n    id: \"social\",\n    eyebrow: \"Social content\",\n    title: \"Content that stays consistent\",\n    description: \"Social media that looks and sounds like your brand.\",\n    baselineScore: 19,\n    score: 100,\n    icon: <MessageSquare className=\"size-5 sm:size-6 stroke-[2]\" />,\n  },\n  {\n    id: \"data\",\n    eyebrow: \"Data & insights\",\n    title: \"Knowing what works\",\n    description: \"Insights that show what's working and what needs improvement.\",\n    baselineScore: 41,\n    score: 100,\n    highlight: true,\n    icon: <TrendingUp className=\"size-5 sm:size-6 stroke-[2]\" />,\n  },\n];\n\n// Preset color map for quick accents\nconst COLOR_MAP: Record<string, { bg: string; text: string; gradient: string }> = {\n  orange: {\n    bg: \"#f95716\",\n    text: \"#f95716\",\n    gradient: \"from-[#ff6426] to-[#e64708]\",\n  },\n  primary: {\n    bg: \"var(--primary)\",\n    text: \"var(--primary)\",\n    gradient: \"from-primary to-primary/80\",\n  },\n  default: {\n    bg: \"var(--primary)\",\n    text: \"var(--primary)\",\n    gradient: \"from-primary to-primary/80\",\n  },\n  lavender: {\n    bg: \"#9b8afb\",\n    text: \"#9b8afb\",\n    gradient: \"from-[#a99bfb] to-[#8d7bf9]\",\n  },\n  indigo: {\n    bg: \"#6366f1\",\n    text: \"#6366f1\",\n    gradient: \"from-indigo-500 to-indigo-600\",\n  },\n  rose: {\n    bg: \"#f43f5e\",\n    text: \"#f43f5e\",\n    gradient: \"from-rose-500 to-rose-600\",\n  },\n  emerald: {\n    bg: \"#10b981\",\n    text: \"#10b981\",\n    gradient: \"from-emerald-500 to-emerald-600\",\n  },\n  zinc: {\n    bg: \"#18181b\",\n    text: \"#18181b\",\n    gradient: \"from-zinc-800 to-zinc-950\",\n  },\n};\n\n/**\n * Universal theme color resolver:\n * Safely resolves 'default', 'primary', theme swatch names, or hex strings into concrete CSS values\n */\nexport function resolveThemeColor(color?: string, fallback = \"var(--primary)\"): string {\n  if (!color || color === \"default\" || color === \"primary\") return fallback;\n  const PRESET_HEX_MAP: Record<string, string> = {\n    lavender: \"#9b8afb\",\n    indigo: \"#6366f1\",\n    rose: \"#f43f5e\",\n    emerald: \"#10b981\",\n    orange: \"#f95716\",\n    zinc: \"#18181b\",\n    primary: fallback,\n    default: fallback,\n  };\n  return PRESET_HEX_MAP[color] || COLOR_MAP[color]?.bg || color;\n}\n\n// Deck card placement & rotation offsets:\n// 1 line absolute at bottom, spanning across the component\n// Alternate rotation & translation (-5 to +5) as requested by user\n// Deck card placement & rotation offsets:\n// Perfectly distributed across the component from left edge (0%) to right edge\n// Responsive offsets based on container width\nconst DECK_CONFIG: Record<\n  string,\n  {\n    rotate: number;\n    pctOffset: number;\n    mobilePctOffset: number;\n    offsetY: number;\n    zIndex: number;\n    alignRight?: boolean;\n    isOrange?: boolean;\n  }\n> = {\n  branding: { rotate: -5, pctOffset: 0, mobilePctOffset: 0, offsetY: 5, zIndex: 40 },\n  website: { rotate: 5, pctOffset: 21.7, mobilePctOffset: 12, offsetY: -5, zIndex: 30 },\n  social: { rotate: -5, pctOffset: 43.3, mobilePctOffset: 24, offsetY: 5, zIndex: 20 },\n  data: { rotate: 3, pctOffset: 65, mobilePctOffset: 36, offsetY: -4, zIndex: 10, isOrange: true, alignRight: true },\n};\n\n// Alternating rotations on mobile (-5, +5, -5, +5)\nconst MOBILE_ROTATIONS: Record<string, number> = {\n  branding: -5,\n  website: 5,\n  social: -5,\n  data: 5,\n};\n\n// Initial cascade / rain drop entrance delays (right-to-left: Data -> Social -> Website -> Branding)\nconst RAIN_DELAYS: Record<string, number> = {\n  data: 0.15,\n  social: 0.45,\n  website: 0.75,\n  branding: 1.05,\n};\n\n// ==========================================\n// BRAND PILLARS CTA VARIANT (PIXEL-PERFECT FRAMER REPLICA)\n// ==========================================\nfunction BrandPillarsCta({\n  color,\n  heading,\n  subheading,\n  statement = \"A strong brand isn't built from a single element. Branding, website, social, and data reinforce each other — together creating a brand that's recognizable, consistent, and effective.\",\n  pillars = DEFAULT_PILLARS,\n  summaryText = 'One complete, consistent brand\" — 100%',\n  summaryScore,\n  ctaText = \"Boek a demo\",\n  ctaHref = \"#\",\n  onCtaClick,\n  activePillarId: _controlledActiveId,\n  onPillarChange,\n  rounded,\n  className,\n  style,\n  ...props\n}: CtaSectionProps) {\n  // Starts with 0 cards placed (initial state matching the user's screenshot)\n  const [placedCardIds, setPlacedCardIds] = useState<string[]>([]);\n\n  // Use CSS theme variable var(--primary) by default so theme switcher changes it across the entire component\n  const activeAccent = resolveThemeColor(color, \"var(--primary)\");\n  // Dynamic radius bound to global --radius token from the theme customizer sidebar\n  const dynamicRadius =\n    rounded !== undefined\n      ? typeof rounded === \"number\"\n        ? `${rounded}px`\n        : rounded\n      : \"var(--radius, 0.625rem)\";\n  const containerRadius =\n    rounded !== undefined\n      ? dynamicRadius\n      : \"calc(var(--radius, 0.625rem) * 1.6)\";\n  const iconRadius =\n    rounded !== undefined\n      ? dynamicRadius\n      : \"calc(var(--radius, 0.625rem) * 0.75)\";\n\n  // Track responsive screen size for mobile deck spacing\n  const [isMobile, setIsMobile] = useState(false);\n  const [hasMounted, setHasMounted] = useState(false);\n\n  useEffect(() => {\n    setHasMounted(true);\n    const checkMobile = () => setIsMobile(window.innerWidth < 640);\n    checkMobile();\n    window.addEventListener(\"resize\", checkMobile);\n    return () => window.removeEventListener(\"resize\", checkMobile);\n  }, []);\n\n  const reduceMotion = usePrefersReducedMotion();\n  const sectionRef = useRef<HTMLElement>(null);\n  const flightFrom = useRef<({ id: string } & ReturnType<typeof measureCard>) | null>(null);\n  const findCard = (id: string) =>\n    [...(sectionRef.current?.querySelectorAll<HTMLElement>(\"[data-pillar-card]\") ?? [])].find(\n      (el) => el.dataset.pillarCard === id,\n    );\n\n  // Cards rain into the deck on load, right to left.\n  useLayoutEffect(() => {\n    if (!hasMounted || prefersReducedMotion()) return;\n    sectionRef.current?.querySelectorAll<HTMLElement>(\"[data-pillar-card]\").forEach((el) => {\n      const y = Number(el.dataset.restY);\n      const rotate = Number(el.dataset.restRotate);\n      el.animate(\n        [\n          { opacity: 0, transform: `translateY(-750px) rotate(${rotate * 2}deg) scale(0.9)` },\n          { opacity: 1, transform: `translateY(${y}px) rotate(${rotate}deg) scale(1)` },\n        ],\n        { ...TO_DECK, delay: (RAIN_DELAYS[el.dataset.pillarCard ?? \"\"] ?? 0) * 1000, fill: \"backwards\" },\n      );\n    });\n  }, [hasMounted]);\n\n  // A placed or returned card is a new element in a new spot. It starts at the\n  // old one's position and size and flies to its own. Width and height animate\n  // rather than scale, so the text on the card never stretches.\n  useLayoutEffect(() => {\n    const from = flightFrom.current;\n    flightFrom.current = null;\n    if (!from || prefersReducedMotion()) return;\n    const el = findCard(from.id);\n    if (!el) return;\n    const to = measureCard(el);\n    const restY = Number(el.dataset.restY);\n    const left = el.offsetLeft;\n    const dx = from.x - (to.x - to.width / 2) - from.width / 2;\n    const dy = from.y - (to.y - restY - to.height / 2) - from.height / 2;\n    el.animate(\n      [\n        {\n          left: `${left}px`,\n          right: \"auto\",\n          width: `${from.width}px`,\n          height: `${from.height}px`,\n          transform: `translate(${dx}px, ${dy}px) rotate(${from.rotate}deg)`,\n        },\n        {\n          left: `${left}px`,\n          right: \"auto\",\n          width: `${to.width}px`,\n          height: `${to.height}px`,\n          transform: `translate(0px, ${restY}px) rotate(${to.rotate}deg)`,\n        },\n      ],\n      el.dataset.restSlot ? TO_SLOT : TO_DECK,\n    );\n  }, [placedCardIds]);\n\n  // Active flying animations to ensure moving cards are always on top of EVERYTHING\n  const [activeFlyingCards, setActiveFlyingCards] = useState<Record<string, \"toSlot\" | \"toDeck\">>({});\n  const flyingTimersRef = useRef<Record<string, ReturnType<typeof setTimeout>>>({});\n\n  // Toggle or place card\n  const handlePlaceCard = (id: string) => {\n    const isCurrentlyPlaced = placedCardIds.includes(id);\n    const direction: \"toSlot\" | \"toDeck\" = isCurrentlyPlaced ? \"toDeck\" : \"toSlot\";\n\n    const card = findCard(id);\n    if (card) flightFrom.current = { id, ...measureCard(card) };\n\n    // Elevate moving card above all other elements\n    setActiveFlyingCards((prev) => ({ ...prev, [id]: direction }));\n\n    if (flyingTimersRef.current[id]) {\n      clearTimeout(flyingTimersRef.current[id]);\n    }\n    flyingTimersRef.current[id] = setTimeout(() => {\n      setActiveFlyingCards((prev) => {\n        const next = { ...prev };\n        delete next[id];\n        return next;\n      });\n    }, 700);\n\n    setPlacedCardIds((prev) => {\n      if (prev.includes(id)) {\n        return prev.filter((item) => item !== id);\n      }\n      return [...prev, id];\n    });\n    onPillarChange?.(id);\n  };\n\n\n  const isAllPlaced = placedCardIds.length === pillars.length;\n  const progressPercent = Math.round((placedCardIds.length / pillars.length) * 100);\n\n  // Unplaced cards remaining in the floating deck\n  const unplacedPillars = pillars.filter((p) => !placedCardIds.includes(p.id));\n\n  return (\n    <section\n      ref={sectionRef}\n      className={cn(\n        \"relative w-full overflow-x-clip overflow-y-visible bg-muted/40 dark:bg-muted/20 pt-8 sm:pt-12 pb-24 sm:pb-32 text-foreground transition-colors\",\n        className\n      )}\n      style={{ ...style }}\n      {...props}\n    >\n      <div className=\"mx-auto w-full max-w-6xl px-4 sm:px-6 lg:px-8 overflow-visible\">\n        {/* Optional Header (hidden if not passed, as in standard Framer component) */}\n        {(heading || subheading) && (\n          <div className=\"mb-8 text-center sm:mb-12\">\n            {subheading && (\n              <span\n                className=\"inline-block text-xs font-bold uppercase tracking-widest\"\n                style={{ color: activeAccent }}\n              >\n                {subheading}\n              </span>\n            )}\n            {heading && (\n              <h2 className=\"mt-2 text-2xl font-black tracking-tight text-neutral-900 dark:text-neutral-100 sm:text-4xl\">\n                {heading}\n              </h2>\n            )}\n          </div>\n        )}\n\n        {/* Relative wrapper holding both columns and the overlapping floating deck */}\n        <div className=\"relative overflow-visible pb-12 sm:pb-16\">\n          {/* Main 2-Column Split Layout */}\n          <div className=\"grid grid-cols-1 gap-5 lg:grid-cols-12 lg:gap-7 items-stretch overflow-visible\">\n            {/* ======================================================== */}\n            {/* LEFT COLUMN: 4 Vertical Card Slots */}\n            {/* ======================================================== */}\n            <div\n              className=\"lg:col-span-5 flex flex-col justify-between space-y-3 sm:space-y-3.5 relative overflow-visible z-10\"\n            >\n              {pillars.map((pillar) => {\n                const isPlaced = placedCardIds.includes(pillar.id);\n                const isDataSlot = pillar.id === \"data\";\n                const isCardFlying = Boolean(activeFlyingCards[pillar.id]);\n\n                return (\n                  <div\n                    key={pillar.id}\n                    className={cn(\n                      \"relative overflow-visible\",\n                      isCardFlying ? \"z-50\" : \"z-10\"\n                    )}\n                    style={{ minHeight: \"96px\" }}\n                  >\n                    {/* Permanent Background Placeholder Slot - Always visible (\"agula harai jabe na\") */}\n                    <div\n                      onClick={() => !isPlaced && handlePlaceCard(pillar.id)}\n                      className={cn(\n                        \"p-5 sm:p-5.5 select-none border w-full h-full transition-all duration-300\",\n                        isPlaced ? \"cursor-default\" : \"cursor-pointer hover:border-foreground/10\",\n                        isDataSlot\n                          ? \"bg-muted-foreground/35 dark:bg-muted-foreground/25 text-white border-transparent\"\n                          : \"bg-card/70 dark:bg-card/40 text-muted-foreground border-border/60\"\n                      )}\n                      style={{ borderRadius: dynamicRadius }}\n                      title={!isPlaced ? \"Click to place card in slot\" : undefined}\n                    >\n                      <div className=\"flex items-start gap-4\">\n                        <div\n                          className={cn(\n                            \"size-11 shrink-0 flex items-center justify-center\",\n                            isDataSlot\n                              ? \"bg-white/20 text-white\"\n                              : \"bg-muted text-muted-foreground\"\n                          )}\n                          style={{ borderRadius: iconRadius }}\n                        >\n                          {pillar.icon}\n                        </div>\n                        <div className=\"min-w-0 flex-1\">\n                          <span\n                            className={cn(\n                              \"block text-[10px] sm:text-[11px] font-bold tracking-widest uppercase\",\n                              isDataSlot ? \"text-white/80\" : \"text-muted-foreground/80\"\n                            )}\n                          >\n                            {pillar.eyebrow}\n                          </span>\n                          <h3\n                            className={cn(\n                              \"mt-0.5 text-base sm:text-lg font-bold tracking-tight\",\n                              isDataSlot ? \"text-white\" : \"text-foreground/80\"\n                            )}\n                          >\n                            {pillar.title}\n                          </h3>\n                          <p\n                            className={cn(\n                              \"mt-0.5 text-xs sm:text-[13px] leading-relaxed\",\n                              isDataSlot ? \"text-white/80\" : \"text-neutral-400 dark:text-neutral-500\"\n                            )}\n                          >\n                            {pillar.description}\n                          </p>\n                        </div>\n                      </div>\n                    </div>\n\n                    {/* Shared Layout Card when placed - Sits directly ON TOP (\"agular uporer jaiya boshbe\") */}\n                    {isPlaced && (\n                      <div\n                        data-pillar-card={pillar.id}\n                        data-rest-y={0}\n                        data-rest-slot=\"\"\n                        onClick={() => handlePlaceCard(pillar.id)}\n                        className={cn(\n                          \"absolute inset-0 cursor-pointer p-5 sm:p-5.5 select-none border w-full h-full\",\n                          isDataSlot\n                            ? \"text-white border-transparent\"\n                            : \"bg-card text-card-foreground border-border\"\n                        )}\n                        style={{\n                          backgroundColor: isDataSlot ? activeAccent : undefined,\n                          borderRadius: dynamicRadius,\n                          zIndex: isCardFlying ? 9999 : 20,\n                        }}\n                        title=\"Click to return card to deck\"\n                      >\n                        <div className=\"flex items-start gap-4\">\n                          <div\n                            className={cn(\n                              \"size-11 shrink-0 flex items-center justify-center\",\n                              isDataSlot\n                                ? \"bg-white/20 text-white\"\n                                : \"bg-primary/10 text-primary\"\n                            )}\n                            style={{\n                              borderRadius: iconRadius,\n                              color: !isDataSlot ? activeAccent : undefined,\n                              backgroundColor: !isDataSlot ? `color-mix(in srgb, ${activeAccent} 12%, transparent)` : undefined,\n                            }}\n                          >\n                            {pillar.icon}\n                          </div>\n                          <div className=\"min-w-0 flex-1\">\n                            <span\n                              className=\"block text-[10px] sm:text-[11px] font-bold tracking-widest uppercase\"\n                              style={{ color: isDataSlot ? \"rgba(255,255,255,0.9)\" : activeAccent }}\n                            >\n                              {pillar.eyebrow}\n                            </span>\n                            <h3\n                              className={cn(\n                                \"mt-0.5 text-base sm:text-lg font-bold tracking-tight\",\n                                isDataSlot ? \"text-white\" : \"text-foreground\"\n                              )}\n                            >\n                              {pillar.title}\n                            </h3>\n                            <p\n                              className={cn(\n                                \"mt-0.5 text-xs sm:text-[13px] leading-relaxed\",\n                                isDataSlot ? \"text-white/90\" : \"text-muted-foreground\"\n                              )}\n                            >\n                              {pillar.description}\n                            </p>\n                          </div>\n                        </div>\n                      </div>\n                    )}\n                  </div>\n                );\n              })}\n            </div>\n\n            {/* ======================================================== */}\n            {/* RIGHT COLUMN: White Scorecard Panel */}\n            {/* ======================================================== */}\n            <div className=\"lg:col-span-7 flex flex-col relative z-10\">\n              <div\n                className=\"relative flex flex-col justify-between overflow-hidden border border-border/80 bg-card p-6 sm:p-8 lg:p-9 transition-all h-full\"\n                style={{\n                  borderRadius: containerRadius,\n                }}\n              >\n                {/* Top Philosophy Statement */}\n                <div>\n                  <p\n                    className=\"text-[16px] sm:text-[18px] font-semibold leading-snug text-foreground tracking-tight transition-all duration-500\"\n                    style={{\n                      filter: isAllPlaced ? \"grayscale(0)\" : \"grayscale(1)\",\n                      opacity: isAllPlaced ? 1 : 0.45,\n                    }}\n                  >\n                    {statement}\n                  </p>\n\n                  {/* 4 Scorecards (2x2 on mobile matching screenshot, 4 across on desktop) */}\n                  <div className=\"grid grid-cols-2 sm:grid-cols-4 gap-2.5 sm:gap-3.5 my-7 sm:my-8\">\n                    {pillars.map((pillar) => {\n                      const isPlaced = placedCardIds.includes(pillar.id);\n                      const isDataSlot = pillar.id === \"data\";\n                      const targetScore = isPlaced\n                        ? (pillar.score ?? 100)\n                        : (pillar.baselineScore ?? 25);\n\n                      return (\n                        <div\n                          key={pillar.id}\n                          onClick={() => handlePlaceCard(pillar.id)}\n                          className={cn(\n                            \"cursor-pointer flex flex-col justify-between items-center p-3 sm:p-4.5 h-44 sm:h-52 border transition-all duration-300 select-none\",\n                            isPlaced\n                              ? isDataSlot\n                                ? \"text-white border-transparent\"\n                                : \"bg-card text-card-foreground border-border\"\n                              : isDataSlot\n                                ? \"bg-muted-foreground/40 dark:bg-neutral-700 text-white border-transparent\"\n                                : \"bg-card text-card-foreground border-border\"\n                          )}\n                          style={{\n                            borderRadius: dynamicRadius,\n                            backgroundColor: isPlaced && isDataSlot ? activeAccent : undefined,\n                            filter: isPlaced ? \"grayscale(0)\" : \"grayscale(1)\",\n                            opacity: isPlaced ? 1 : 0.55,\n                            transform: isPlaced ? \"scale(1)\" : \"scale(0.98)\",\n                          }}\n                        >\n                          {/* Short Label */}\n                          <span\n                            className={cn(\n                              \"text-xs sm:text-sm font-semibold tracking-tight transition-colors\",\n                              (isPlaced && isDataSlot) || (!isPlaced && isDataSlot)\n                                ? \"text-white\"\n                                : \"text-neutral-600 dark:text-neutral-400\"\n                            )}\n                          >\n                            {pillar.eyebrow.split(\" \")[0]}\n                          </span>\n\n                          {/* Large Score with dynamic count-up */}\n                          <div className=\"my-auto\">\n                            <span\n                              className={cn(\n                                \"text-3xl sm:text-4xl lg:text-5xl font-black tabular-nums tracking-tight transition-colors\",\n                                (isPlaced && isDataSlot) || (!isPlaced && isDataSlot)\n                                  ? \"text-white\"\n                                  : \"text-neutral-900 dark:text-neutral-100\"\n                              )}\n                            >\n                              <AnimatedNumber value={targetScore} duration={0.8} />\n                            </span>\n                          </div>\n\n                          {/* Bottom Full-Width Progress Track (Framer exact) */}\n                          <div\n                            className=\"w-full h-1.5 rounded-full overflow-hidden\"\n                            style={{\n                              backgroundColor:\n                                isDataSlot && isPlaced\n                                  ? \"rgba(255, 255, 255, 0.25)\"\n                                  : isDataSlot\n                                    ? \"rgba(255, 255, 255, 0.2)\"\n                                    : \"rgba(28, 26, 23, 0.08)\",\n                            }}\n                          >\n                            <div\n                              className=\"h-full rounded-full transition-[width] duration-[800ms] ease-out motion-reduce:transition-none\"\n                              style={{\n                                width: isPlaced ? \"100%\" : `${pillar.baselineScore ?? 25}%`,\n                                backgroundColor: isDataSlot ? \"#FFFFFF\" : activeAccent,\n                              }}\n                            />\n                          </div>\n                        </div>\n                      );\n                    })}\n                  </div>\n                </div>\n\n                {/* Bottom Summary Bar */}\n                <div\n                  className=\"relative z-10 flex flex-wrap items-center justify-between gap-4 p-4 sm:p-4.5 transition-all duration-500\"\n                  style={{\n                    borderRadius: dynamicRadius,\n                    backgroundColor: activeAccent,\n                    filter: isAllPlaced ? \"grayscale(0)\" : \"grayscale(1)\",\n                    opacity: isAllPlaced ? 1 : 0.55,\n                  }}\n                >\n                  <span className=\"text-xs sm:text-sm font-bold tracking-tight text-white\">\n                    {summaryText}\n                  </span>\n\n                  <span className=\"text-sm sm:text-base font-extrabold text-white tabular-nums\">\n                    {summaryScore !== undefined ? (\n                      summaryScore\n                    ) : (\n                      <AnimatedNumber value={isAllPlaced ? 100 : progressPercent} suffix=\"%\" duration={0.8} />\n                    )}\n                  </span>\n                </div>\n              </div>\n            </div>\n          </div>\n\n          {/* ======================================================== */}\n          {/* ABSOLUTE CARDS DECK AT COMPONENT BOTTOM (Matches user screenshot 1:1) */}\n          {/* ======================================================== */}\n          {unplacedPillars.length > 0 && (\n            <div\n              className=\"absolute -bottom-8 sm:-bottom-10 left-0 right-0 w-full flex flex-col items-center overflow-visible z-30 pointer-events-none\"\n            >\n              {/* Floating Orange Instruction Pill with Down Arrow - disappears once any card is picked */}\n              {placedCardIds.length === 0 && (\n                <div\n                  ref={enterPill}\n                  style={hasMounted ? undefined : { opacity: 0 }}\n                  className=\"flex flex-col items-center mb-1 select-none pointer-events-auto transition-opacity duration-300\"\n                >\n                  <div\n                    className=\"inline-flex items-center gap-1.5 px-3.5 py-1 rounded-full text-white text-[11px] font-bold tracking-wide select-none\"\n                    style={{ backgroundColor: activeAccent }}\n                  >\n                    <span className=\"text-xs\">✋</span>\n                    <span>Drag the cards into place</span>\n                  </div>\n                  <span\n                    className=\"text-xs font-black -mt-0.5 leading-none\"\n                    style={{ color: activeAccent }}\n                  >\n                    ↓\n                  </span>\n                </div>\n              )}\n\n              {/* Cards Container: Stacked According to Index (Desktop: Horizontal line, Mobile: Stacked underneath each other) */}\n              <div className=\"relative w-full h-[115px] sm:h-[110px] overflow-visible pointer-events-auto\">\n                {unplacedPillars.map((pillar, index) => {\n                  const cfg = DECK_CONFIG[pillar.id] || {\n                    rotate: 0,\n                    pctOffset: 0,\n                    mobilePctOffset: 0,\n                    offsetY: 0,\n                    zIndex: 1,\n                  };\n                  const isOrangeCard = pillar.id === \"data\";\n                  const isCardFlying = Boolean(activeFlyingCards[pillar.id]);\n                  const pct = cfg.pctOffset;\n                  const currentRotate = isMobile\n                    ? (MOBILE_ROTATIONS[pillar.id] ?? (index % 2 === 0 ? -5 : 5))\n                    : cfg.rotate;\n                  // On mobile, slight vertical offset (index * 5) so cards peek out according to index\n                  const currentY = isMobile ? (index * 5) : cfg.offsetY;\n\n                  return (\n                    <div\n                      key={pillar.id}\n                      data-pillar-card={pillar.id}\n                      data-rest-y={currentY}\n                      data-rest-rotate={currentRotate}\n                      onClick={() => handlePlaceCard(pillar.id)}\n                      className={cn(\n                        \"absolute top-0 cursor-pointer select-none\",\n                        \"w-[calc(100%-16px)] sm:w-[35%] p-4 sm:p-5 select-none min-h-[96px] sm:min-h-[104px] flex items-center\",\n                        isOrangeCard\n                          ? \"text-white border border-transparent\"\n                          : \"bg-card text-card-foreground border border-border\"\n                      )}\n                      style={{\n                        left: isMobile ? \"8px\" : (cfg.alignRight ? undefined : `${pct}%`),\n                        right: isMobile ? \"8px\" : (cfg.alignRight ? 0 : undefined),\n                        backgroundColor: isOrangeCard ? activeAccent : undefined,\n                        borderRadius: dynamicRadius,\n                        zIndex: isCardFlying ? 9999 : cfg.zIndex,\n                        opacity: hasMounted ? 1 : 0,\n                        transform: hasMounted\n                          ? `translateY(${currentY}px) rotate(${currentRotate}deg)`\n                          : `translateY(-750px) rotate(${currentRotate * 2}deg) scale(0.9)`,\n                        transition: reduceMotion ? undefined : `transform ${TO_DECK.duration}ms ${TO_DECK.easing}`,\n                      }}\n                      title=\"Click to place card in slot\"\n                    >\n                      <div className=\"flex items-start gap-3.5 sm:gap-4 w-full\">\n                        <div\n                          className={cn(\n                            \"size-10 sm:size-11 shrink-0 flex items-center justify-center\",\n                            isOrangeCard\n                              ? \"bg-white/20 text-white\"\n                              : \"bg-primary/10 text-primary\"\n                          )}\n                          style={{\n                            borderRadius: iconRadius,\n                            color: !isOrangeCard ? activeAccent : undefined,\n                            backgroundColor: !isOrangeCard ? `color-mix(in srgb, ${activeAccent} 12%, transparent)` : undefined,\n                          }}\n                        >\n                          {pillar.icon}\n                        </div>\n                        <div className=\"min-w-0 flex-1 overflow-hidden\">\n                          <span\n                            className=\"block text-[10px] sm:text-[11px] font-bold tracking-wider uppercase truncate\"\n                            style={{ color: isOrangeCard ? \"rgba(255,255,255,0.9)\" : activeAccent }}\n                          >\n                            {pillar.eyebrow}\n                          </span>\n                          <h4\n                            className={cn(\n                              \"mt-0.5 text-sm sm:text-base font-extrabold tracking-tight leading-snug truncate\",\n                              isOrangeCard ? \"text-white\" : \"text-foreground\"\n                            )}\n                          >\n                            {pillar.title}\n                          </h4>\n                          <p\n                            className={cn(\n                              \"mt-0.5 text-[11px] sm:text-xs leading-snug truncate\",\n                              isOrangeCard ? \"text-white/90\" : \"text-muted-foreground\"\n                            )}\n                          >\n                            {pillar.description}\n                          </p>\n                        </div>\n                      </div>\n                    </div>\n                  );\n                })}\n              </div>\n            </div>\n          )}\n        </div>\n\n        {/* ======================================================== */}\n        {/* BOTTOM CTA BUTTON (Matches Framer when 100% complete) */}\n        {/* ======================================================== */}\n        <RisePresence show={isAllPlaced} className=\"mt-8 flex justify-center\">\n            <a\n              href={ctaHref}\n              onClick={(e) => {\n                if (ctaHref === \"#\") e.preventDefault();\n                onCtaClick?.();\n              }}\n              className=\"inline-flex items-center gap-3 pl-6 pr-2.5 py-2.5 rounded-full text-white font-semibold text-sm sm:text-base transition-transform duration-200 hover:scale-[1.03] active:scale-[0.98]\"\n              style={{ backgroundColor: activeAccent }}\n            >\n              <span>{ctaText}</span>\n              <span className=\"size-8 rounded-full bg-white flex items-center justify-center shrink-0\">\n                <ArrowRight className=\"size-4 stroke-[2.5]\" style={{ color: activeAccent }} />\n              </span>\n            </a>\n        </RisePresence>\n      </div>\n    </section>\n  );\n}\n\n// ==========================================\n// MINIMAL CTA VARIANT\n// ==========================================\nfunction MinimalCta({\n  color,\n  heading = \"Transform your brand into a growth engine\",\n  statement = \"Start building unified identity, conversion-driven websites, and data-backed marketing systems today.\",\n  ctaText = \"Get Started\",\n  ctaHref = \"#\",\n  onCtaClick,\n  rounded,\n  className,\n  style,\n  ...props\n}: CtaSectionProps) {\n  const activeAccent = resolveThemeColor(color, \"var(--primary)\");\n  const dynamicRadius =\n    rounded !== undefined\n      ? typeof rounded === \"number\"\n        ? `${rounded}px`\n        : rounded\n      : \"var(--radius, 1rem)\";\n\n  return (\n    <section\n      className={cn(\n        \"relative w-full overflow-hidden bg-background py-16 sm:py-24 text-center text-foreground\",\n        className\n      )}\n      style={{ ...style }}\n      {...props}\n    >\n      <div className=\"mx-auto max-w-4xl px-4 sm:px-6\">\n        <h2 className=\"text-3xl sm:text-5xl font-extrabold tracking-tight text-balance text-foreground\">\n          {heading}\n        </h2>\n        <p className=\"mt-4 text-base sm:text-lg text-muted-foreground max-w-2xl mx-auto leading-relaxed\">\n          {statement}\n        </p>\n        <div className=\"mt-8 flex flex-wrap justify-center gap-4\">\n          <a\n            href={ctaHref}\n            onClick={(e) => {\n              if (ctaHref === \"#\") e.preventDefault();\n              onCtaClick?.();\n            }}\n            className=\"inline-flex items-center gap-2 px-6 py-3.5 text-sm sm:text-base font-bold text-white shadow-lg transition-all hover:opacity-95 active:scale-95\"\n            style={{\n              backgroundColor: activeAccent,\n              borderRadius: dynamicRadius,\n            }}\n          >\n            <span>{ctaText}</span>\n            <ArrowRight className=\"size-4\" />\n          </a>\n        </div>\n      </div>\n    </section>\n  );\n}\n\n// ==========================================\n// SPLIT CTA VARIANT\n// ==========================================\nfunction SplitCta({\n  color,\n  heading = \"One team for your complete brand ecosystem\",\n  statement = \"From brand strategy to custom web development and growth content, everything connects seamlessly.\",\n  ctaText = \"Book a Call\",\n  ctaHref = \"#\",\n  onCtaClick,\n  pillars = DEFAULT_PILLARS,\n  rounded,\n  className,\n  style,\n  ...props\n}: CtaSectionProps) {\n  const activeAccent = resolveThemeColor(color, \"var(--primary)\");\n  const dynamicRadius =\n    rounded !== undefined\n      ? typeof rounded === \"number\"\n        ? `${rounded}px`\n        : rounded\n      : \"var(--radius, 1rem)\";\n\n  return (\n    <section\n      className={cn(\n        \"relative w-full overflow-hidden bg-background py-12 sm:py-16 text-foreground\",\n        className\n      )}\n      style={{ ...style }}\n      {...props}\n    >\n      <div className=\"mx-auto max-w-6xl px-4 sm:px-6\">\n        <div\n          className=\"relative overflow-hidden border border-border/70 bg-card p-8 sm:p-12 shadow-sm grid grid-cols-1 lg:grid-cols-12 gap-8 items-center\"\n          style={{ borderRadius: dynamicRadius }}\n        >\n          <div className=\"lg:col-span-7 space-y-4\">\n            <span\n              className=\"text-xs font-bold uppercase tracking-widest\"\n              style={{ color: activeAccent }}\n            >\n              Unified Brand Growth\n            </span>\n            <h2 className=\"text-2xl sm:text-4xl font-extrabold tracking-tight text-foreground\">\n              {heading}\n            </h2>\n            <p className=\"text-sm sm:text-base text-muted-foreground leading-relaxed\">\n              {statement}\n            </p>\n            <div className=\"pt-2\">\n              <a\n                href={ctaHref}\n                onClick={(e) => {\n                  if (ctaHref === \"#\") e.preventDefault();\n                  onCtaClick?.();\n                }}\n                className=\"inline-flex items-center gap-2 px-6 py-3 text-sm font-bold text-white shadow-md transition-all hover:opacity-95 active:scale-95\"\n                style={{\n                  backgroundColor: activeAccent,\n                  borderRadius: dynamicRadius,\n                }}\n              >\n                <span>{ctaText}</span>\n                <ArrowRight className=\"size-4\" />\n              </a>\n            </div>\n          </div>\n\n          <div className=\"lg:col-span-5 grid grid-cols-2 gap-3\">\n            {pillars.map((pillar) => (\n              <div\n                key={pillar.id}\n                className=\"p-4 rounded-xl border border-border/60 bg-muted/30 flex flex-col justify-between space-y-3\"\n              >\n                <div\n                  className=\"size-8 rounded-lg flex items-center justify-center\"\n                  style={{\n                    backgroundColor: `${activeAccent}18`,\n                    color: activeAccent,\n                  }}\n                >\n                  {pillar.icon}\n                </div>\n                <div>\n                  <h4 className=\"text-sm font-bold text-foreground\">{pillar.title}</h4>\n                  <p className=\"text-[11px] text-muted-foreground line-clamp-2 mt-0.5\">\n                    {pillar.description}\n                  </p>\n                </div>\n              </div>\n            ))}\n          </div>\n        </div>\n      </div>\n    </section>\n  );\n}\n\n// ==========================================\n// CONCENTRIC PORTAL / ORBITAL LENS CTA (Reference: contentarchitecture.dev)\n// ==========================================\n// Performance model:\n//  - All orbital text + dotted orbits are drawn on ONE <canvas> from pre-rendered glyph\n//    sprites (no SVG <textPath>, no per-ring components, no per-frame React work).\n//  - A single rAF loop smooths scroll progress, repaints the canvas and writes the portal's\n//    transform/opacity straight to the DOM, so scrubbing never triggers a React re-render.\n//  - The loop sleeps when the section is off-screen or nothing is animating.\nconst PORTAL_RINGS = [\n  { id: \"ring-1\", radius: 75, direction: \"cw\" as const, duration: 55, scrollAngle: 45 },\n  { id: \"ring-2\", radius: 130, direction: \"ccw\" as const, duration: 70, scrollAngle: -60 },\n  { id: \"ring-3\", radius: 190, direction: \"cw\" as const, duration: 85, scrollAngle: 75 },\n  { id: \"ring-4\", radius: 255, direction: \"ccw\" as const, duration: 105, scrollAngle: -95 },\n  { id: \"ring-5\", radius: 325, direction: \"cw\" as const, duration: 125, scrollAngle: 115 },\n  { id: \"ring-6\", radius: 400, direction: \"ccw\" as const, duration: 150, scrollAngle: -135 },\n  { id: \"ring-7\", radius: 480, direction: \"cw\" as const, duration: 175, scrollAngle: 155 },\n  { id: \"ring-8\", radius: 565, direction: \"ccw\" as const, duration: 205, scrollAngle: -175 },\n  { id: \"ring-9\", radius: 655, direction: \"cw\" as const, duration: 235, scrollAngle: 195 },\n  { id: \"ring-10\", radius: 750, direction: \"ccw\" as const, duration: 270, scrollAngle: -215 },\n  { id: \"ring-11\", radius: 850, direction: \"cw\" as const, duration: 310, scrollAngle: 235 },\n  { id: \"ring-12\", radius: 955, direction: \"ccw\" as const, duration: 350, scrollAngle: -255 },\n  { id: \"ring-13\", radius: 1065, direction: \"cw\" as const, duration: 395, scrollAngle: 275 },\n  { id: \"ring-14\", radius: 1180, direction: \"ccw\" as const, duration: 445, scrollAngle: -295 },\n  { id: \"ring-15\", radius: 1300, direction: \"cw\" as const, duration: 500, scrollAngle: 315 },\n];\n\nconst PORTAL_DOTTED_ORBITS = [\n  45, 102, 160, 222, 290, 362, 440, 522, 610, 702, 800, 902, 1010, 1122, 1240, 1365,\n];\n\nconst PORTAL_FONT_FAMILY = \"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace\";\nconst PORTAL_TEXT_COLOR = \"rgba(255, 255, 255, 0.94)\";\nconst PORTAL_DOT_COLOR = \"rgba(255, 255, 255, 0.22)\";\nconst PORTAL_GLYPH_ADVANCE = 0.74; // em: 0.6em monospace glyph + 0.14em letter-spacing\nconst PORTAL_MIN_SCALE = 0.94;\nconst PORTAL_MAX_SCALE = 1.22;\nconst PORTAL_SMOOTHING = 14; // higher = snappier follow of the scroll target (1/s)\n\nconst clamp01 = (v: number) => (v < 0 ? 0 : v > 1 ? 1 : v);\nconst portalLinesScale = (p: number) =>\n  PORTAL_MIN_SCALE + (PORTAL_MAX_SCALE - PORTAL_MIN_SCALE) * clamp01(p / 0.85);\nconst portalCircleScale = (p: number) =>\n  p <= 0.03 ? 0 : 1 - Math.pow(1 - clamp01((p - 0.03) / 0.72), 2.4);\n\ninterface PortalRingLayout {\n  radius: number;\n  fontSize: number;\n  step: number; // radians between neighbouring glyphs\n  chars: string[];\n  sprites: Int16Array; // atlas index per glyph (-1 = blank)\n  direction: 1 | -1;\n  periodMs: number;\n  scrollRad: number;\n}\n\ninterface GlyphAtlas {\n  canvas: HTMLCanvasElement;\n  cellW: number;\n  cellH: number;\n  baseline: number;\n  index: Map<string, number>;\n}\n\nfunction buildRingLayouts(phrase: string): PortalRingLayout[] {\n  const unit = `${phrase.trim() || \"THE CONTENT ARCHITECTURE\"} · · · `;\n  return PORTAL_RINGS.map((ring) => {\n    const fontSize = ring.radius < 180 ? 16 : 18.5;\n    // Whole number of phrase units per ring so the text wraps seamlessly (no cut-off at the seam)\n    const natural = (2 * Math.PI * ring.radius) / (fontSize * PORTAL_GLYPH_ADVANCE);\n    const count = unit.length * Math.max(1, Math.round(natural / unit.length));\n    return {\n      radius: ring.radius,\n      fontSize,\n      step: (2 * Math.PI) / count,\n      chars: Array.from({ length: count }, (_, i) => unit[i % unit.length]),\n      sprites: new Int16Array(count),\n      direction: ring.direction === \"cw\" ? 1 : -1,\n      periodMs: ring.duration * 1000,\n      scrollRad: (ring.scrollAngle * Math.PI) / 180,\n    };\n  });\n}\n\n// Renders each distinct character once; frames then blit + rotate sprites (GPU textured quads).\nfunction buildGlyphAtlas(chars: string[], fontSize: number, pxScale: number): GlyphAtlas {\n  const unique = Array.from(new Set(chars)).filter((c) => c.trim() !== \"\");\n  const cellW = Math.ceil(fontSize * 0.9 * pxScale);\n  const cellH = Math.ceil(fontSize * 1.4 * pxScale);\n  const baseline = Math.ceil(fontSize * 1.08 * pxScale);\n  const canvas = document.createElement(\"canvas\");\n  canvas.width = cellW * Math.max(1, unique.length);\n  canvas.height = cellH;\n  const index = new Map<string, number>();\n  const ctx = canvas.getContext(\"2d\");\n  if (ctx) {\n    ctx.font = `700 ${fontSize * pxScale}px ${PORTAL_FONT_FAMILY}`;\n    ctx.fillStyle = PORTAL_TEXT_COLOR;\n    ctx.textAlign = \"center\";\n    ctx.textBaseline = \"alphabetic\";\n    unique.forEach((c, i) => {\n      ctx.fillText(c, i * cellW + cellW / 2, baseline);\n      index.set(c, i);\n    });\n  }\n  return { canvas, cellW, cellH, baseline, index };\n}\n\nexport function ConcentricPortalCta({\n  heading = \"The next 5 days are yours.\",\n  subheading,\n  statement,\n  phrase = \"THE CONTENT ARCHITECTURE\",\n  ctaText = \"GET ACCESS\",\n  ctaHref = \"#\",\n  onCtaClick,\n  color,\n  scrollProgress,\n  autoRotate = true,\n  className,\n  style,\n  ...props\n}: CtaSectionProps) {\n  const containerRef = useRef<HTMLDivElement>(null);\n  const canvasRef = useRef<HTMLCanvasElement>(null);\n  const circleRef = useRef<HTMLDivElement>(null);\n  const contentRef = useRef<HTMLDivElement>(null);\n  const hintRef = useRef<HTMLDivElement>(null);\n\n  // Scroll target (0..1). The render loop eases the displayed progress toward it.\n  const targetRef = useRef(scrollProgress !== undefined ? clamp01(scrollProgress) : 0);\n  const autoRotateRef = useRef(autoRotate);\n  const maxDiameterRef = useRef(520);\n  const loopRef = useRef<{ wake: () => void } | null>(null);\n\n  // Screen-responsive max diameter\n  const [maxDiameter, setMaxDiameter] = useState(520);\n  useEffect(() => {\n    const updateMax = () => {\n      const w = typeof window !== \"undefined\" ? window.innerWidth : 1200;\n      if (w < 480) {\n        setMaxDiameter(Math.min(320, w - 32));\n      } else if (w < 640) {\n        setMaxDiameter(Math.min(420, w - 48));\n      } else {\n        setMaxDiameter(520);\n      }\n    };\n    updateMax();\n    window.addEventListener(\"resize\", updateMax);\n    return () => window.removeEventListener(\"resize\", updateMax);\n  }, []);\n\n  useEffect(() => {\n    maxDiameterRef.current = maxDiameter;\n    loopRef.current?.wake();\n  }, [maxDiameter]);\n\n  useEffect(() => {\n    autoRotateRef.current = autoRotate;\n    loopRef.current?.wake();\n  }, [autoRotate]);\n\n  useEffect(() => {\n    if (scrollProgress !== undefined) {\n      targetRef.current = clamp01(scrollProgress);\n      loopRef.current?.wake();\n    }\n  }, [scrollProgress]);\n\n  // Render loop: smoothing + canvas painting + direct DOM writes (no React re-renders)\n  useEffect(() => {\n    const section = containerRef.current;\n    const canvas = canvasRef.current;\n    const circle = circleRef.current;\n    const content = contentRef.current;\n    const hint = hintRef.current;\n    if (!section || !canvas || !circle || !content || !hint) return;\n    const ctx = canvas.getContext(\"2d\");\n    if (!ctx) return;\n\n    const layouts = buildRingLayouts(phrase);\n    const dotsCanvas = document.createElement(\"canvas\");\n    const dotsCtx = dotsCanvas.getContext(\"2d\");\n    let atlases = new Map<number, GlyphAtlas>();\n    let dotsKey = \"\";\n\n    let width = 0;\n    let height = 0;\n    let dpr = 1;\n\n    let p = targetRef.current;\n    let appliedP = -1;\n    let ambientMs = 0;\n    let lastTs = 0;\n    let raf = 0;\n    let inView = true;\n\n    const rebuildAtlases = () => {\n      atlases = new Map();\n      for (const layout of layouts) {\n        let atlas = atlases.get(layout.fontSize);\n        if (!atlas) {\n          const chars = layouts\n            .filter((l) => l.fontSize === layout.fontSize)\n            .flatMap((l) => l.chars);\n          atlas = buildGlyphAtlas(chars, layout.fontSize, dpr * PORTAL_MAX_SCALE);\n          atlases.set(layout.fontSize, atlas);\n        }\n        for (let i = 0; i < layout.chars.length; i++) {\n          layout.sprites[i] = atlas.index.get(layout.chars[i]) ?? -1;\n        }\n      }\n    };\n\n    const applyProgress = (value: number) => {\n      const c = clamp01((value - 0.32) / 0.28);\n      circle.style.transform = `scale(${portalCircleScale(value)})`;\n      circle.style.opacity = String(clamp01((value - 0.03) / 0.05));\n      circle.style.visibility = value <= 0.03 ? \"hidden\" : \"visible\";\n      content.style.opacity = String(c);\n      content.style.transform = `scale(${0.88 + 0.12 * c})`;\n      hint.style.opacity = String(1 - clamp01(value / 0.08));\n    };\n\n    // Dotted orbits only change with the zoom level, so cache them between frames.\n    const drawDots = (scale: number, reach: number) => {\n      if (!dotsCtx) return;\n      const key = `${canvas.width}x${canvas.height}:${scale.toFixed(4)}`;\n      if (key !== dotsKey) {\n        if (dotsCanvas.width !== canvas.width || dotsCanvas.height !== canvas.height) {\n          dotsCanvas.width = canvas.width;\n          dotsCanvas.height = canvas.height;\n        }\n        dotsCtx.setTransform(1, 0, 0, 1, 0, 0);\n        dotsCtx.clearRect(0, 0, dotsCanvas.width, dotsCanvas.height);\n        dotsCtx.setTransform(dpr * scale, 0, 0, dpr * scale, (width / 2) * dpr, (height / 2) * dpr);\n        dotsCtx.strokeStyle = PORTAL_DOT_COLOR;\n        dotsCtx.lineWidth = 1;\n        dotsCtx.setLineDash([2, 7]);\n        for (const r of PORTAL_DOTTED_ORBITS) {\n          if (r * scale > reach) break;\n          dotsCtx.beginPath();\n          dotsCtx.arc(0, 0, r, 0, Math.PI * 2);\n          dotsCtx.stroke();\n        }\n        dotsKey = key;\n      }\n      ctx.drawImage(dotsCanvas, 0, 0);\n    };\n\n    const draw = (value: number) => {\n      if (!width || !height) return;\n      const scale = portalLinesScale(value);\n      const cx = width / 2;\n      const cy = height / 2;\n      const reach = Math.hypot(cx, cy) + 30; // farthest visible distance from center\n      const circleR = value > 0.08 ? (maxDiameterRef.current / 2) * portalCircleScale(value) : 0;\n      const k = scale * dpr;\n      const margin = 24;\n\n      ctx.setTransform(1, 0, 0, 1, 0, 0);\n      ctx.clearRect(0, 0, canvas.width, canvas.height);\n      drawDots(scale, reach);\n\n      for (const layout of layouts) {\n        const rs = layout.radius * scale;\n        if (rs > reach) continue; // ring is fully outside the section\n        if (rs + layout.fontSize * 1.1 * scale < circleR) continue; // hidden under the portal\n        const atlas = atlases.get(layout.fontSize);\n        if (!atlas) continue;\n\n        const ambient = autoRotateRef.current\n          ? layout.direction * ((ambientMs / layout.periodMs) % 1) * Math.PI * 2\n          : 0;\n        const base = ambient + layout.scrollRad * value;\n        const dw = atlas.cellW / (dpr * PORTAL_MAX_SCALE);\n        const dh = atlas.cellH / (dpr * PORTAL_MAX_SCALE);\n        const dy = -atlas.baseline / (dpr * PORTAL_MAX_SCALE);\n\n        for (let i = 0; i < layout.sprites.length; i++) {\n          const sprite = layout.sprites[i];\n          if (sprite < 0) continue;\n          const a = base + i * layout.step;\n          const sn = Math.sin(a);\n          const cs = Math.cos(a);\n          const x = cx + sn * rs;\n          const y = cy - cs * rs;\n          if (x < -margin || x > width + margin || y < -margin || y > height + margin) continue;\n          ctx.setTransform(cs * k, sn * k, -sn * k, cs * k, x * dpr, y * dpr);\n          ctx.drawImage(\n            atlas.canvas,\n            sprite * atlas.cellW,\n            0,\n            atlas.cellW,\n            atlas.cellH,\n            -dw / 2,\n            dy,\n            dw,\n            dh\n          );\n        }\n      }\n    };\n\n    // Returns true while another frame is needed\n    const step = (now: number) => {\n      const dt = lastTs ? Math.min(0.064, (now - lastTs) / 1000) : 0;\n      lastTs = now;\n\n      const target = targetRef.current;\n      if (p !== target) {\n        p += (target - p) * (1 - Math.exp(-dt * PORTAL_SMOOTHING));\n        if (Math.abs(target - p) < 0.0004) p = target;\n      }\n      if (autoRotateRef.current) ambientMs += dt * 1000;\n\n      if (p !== appliedP) {\n        applyProgress(p);\n        appliedP = p;\n      }\n      draw(p);\n      return p !== target || autoRotateRef.current;\n    };\n\n    const tick = (now: number) => {\n      raf = 0;\n      if (step(now) && inView) {\n        raf = requestAnimationFrame(tick);\n      } else {\n        lastTs = 0;\n      }\n    };\n\n    const wake = () => {\n      if (!raf && inView) raf = requestAnimationFrame(tick);\n    };\n    loopRef.current = { wake };\n\n    const resize = () => {\n      const nextDpr = Math.min(window.devicePixelRatio || 1, 2);\n      width = section.clientWidth;\n      height = section.clientHeight;\n      canvas.width = Math.max(1, Math.round(width * nextDpr));\n      canvas.height = Math.max(1, Math.round(height * nextDpr));\n      dotsKey = \"\";\n      if (nextDpr !== dpr || atlases.size === 0) {\n        dpr = nextDpr;\n        rebuildAtlases();\n      }\n      draw(p); // resizing clears the canvas; repaint immediately to avoid a flash\n      wake();\n    };\n\n    const resizeObserver = new ResizeObserver(resize);\n    resizeObserver.observe(section);\n    const intersectionObserver = new IntersectionObserver(([entry]) => {\n      inView = entry.isIntersecting;\n      if (inView) wake();\n    });\n    intersectionObserver.observe(section);\n\n    dpr = Math.min(window.devicePixelRatio || 1, 2);\n    rebuildAtlases();\n    resize();\n\n    return () => {\n      loopRef.current = null;\n      if (raf) cancelAnimationFrame(raf);\n      resizeObserver.disconnect();\n      intersectionObserver.disconnect();\n    };\n  }, [phrase]);\n\n  // Wheel scrubbing: scroll over the section smoothly scrubs 0 -> 1 without React re-renders\n  useEffect(() => {\n    const el = containerRef.current;\n    if (!el) return;\n\n    const onWheel = (e: WheelEvent) => {\n      const cur = targetRef.current;\n      const next = clamp01(cur + e.deltaY * 0.0014);\n\n      // Intercept wheel while expanding (down < 0.99) or collapsing (up > 0.01)\n      if ((e.deltaY > 0 && cur < 0.99) || (e.deltaY < 0 && cur > 0.01)) {\n        e.preventDefault();\n        targetRef.current = next;\n        loopRef.current?.wake();\n      }\n    };\n\n    el.addEventListener(\"wheel\", onWheel, { passive: false });\n    return () => el.removeEventListener(\"wheel\", onWheel);\n  }, []);\n\n  // Touch swipe support for mobile\n  useEffect(() => {\n    const el = containerRef.current;\n    if (!el) return;\n\n    let touchStartY = 0;\n\n    const onTouchStart = (e: TouchEvent) => {\n      touchStartY = e.touches[0].clientY;\n    };\n\n    const onTouchMove = (e: TouchEvent) => {\n      const currentY = e.touches[0].clientY;\n      const diff = touchStartY - currentY;\n      const cur = targetRef.current;\n      const next = clamp01(cur + diff * 0.0035);\n\n      if ((diff > 0 && cur < 0.99) || (diff < 0 && cur > 0.01)) {\n        if (e.cancelable) e.preventDefault();\n        targetRef.current = next;\n        loopRef.current?.wake();\n      }\n      touchStartY = currentY;\n    };\n\n    el.addEventListener(\"touchstart\", onTouchStart, { passive: true });\n    el.addEventListener(\"touchmove\", onTouchMove, { passive: false });\n    return () => {\n      el.removeEventListener(\"touchstart\", onTouchStart);\n      el.removeEventListener(\"touchmove\", onTouchMove);\n    };\n  }, []);\n\n  // Split CTA text into [ Left ] [ Right • ] segments\n  const [leftLabel, rightLabel] = useMemo(() => {\n    if (ctaText && ctaText.includes(\" \")) {\n      const parts = ctaText.trim().split(/\\s+/);\n      return [parts[0], parts.slice(1).join(\" \")];\n    }\n    return [ctaText || \"GET\", \"ACCESS\"];\n  }, [ctaText]);\n\n  const activeAccent = resolveThemeColor(color, \"var(--primary)\");\n\n  return (\n    <section\n      ref={containerRef}\n      className={cn(\n        \"relative w-full h-[600px] sm:h-[680px] bg-[#08080a] overflow-hidden select-none\",\n        className\n      )}\n      style={{ ...style }}\n      {...props}\n    >\n      <div className=\"relative w-full h-full flex items-center justify-center overflow-hidden bg-[#08080a] text-white\">\n        {/* Top Monospace Code Matrix Texture */}\n        <div className=\"absolute inset-x-0 top-0 h-44 sm:h-56 pointer-events-none overflow-hidden select-none opacity-20 font-mono text-[9px] sm:text-[11px] text-neutral-400 z-10\">\n          <div className=\"p-3 sm:p-5 space-y-1.5 uppercase tracking-[0.22em] leading-relaxed\">\n            <p className=\"truncate\">\n              NEXT 16.X · ASTRO 7.X · SANITY V3 · TS: STRICT · AGENTS.MD: LOADED · AGENT-BROWSER: HEADLESS · DRIFT: 0 · LLMS.TXT · SKILLS · PORTABLE TEXT · DRAFT · PUBLISHED · REVALIDATE\n            </p>\n            <p className=\"truncate\">\n              THE CONTENT ARCHITECTURE · SCHEMA · GROQ · PAGE BUILDER · SECTIONS · TYPEGEN · NEXT 16.X · ASTRO 7.X · SANITY V3 · TS: STRICT · AGENTS.MD: LOADED · AGENT-BROWSER: HEADLESS\n            </p>\n            <p className=\"truncate\">\n              DRIFT: 0 · LLMS.TXT · SKILLS · PORTABLE TEXT · DRAFT · PUBLISHED · REVALIDATE · THE CONTENT ARCHITECTURE · SCHEMA · GROQ · PAGE BUILDER · SECTIONS · TYPEGEN\n            </p>\n            <p className=\"truncate\">\n              AGENTS.MD: LOADED · AGENT-BROWSER: HEADLESS · DRIFT: 0 · LLMS.TXT · SKILLS · PORTABLE TEXT · DRAFT · PUBLISHED · REVALIDATE · THE CONTENT ARCHITECTURE\n            </p>\n          </div>\n          <div className=\"absolute inset-0 bg-gradient-to-b from-transparent via-[#08080a]/50 to-[#08080a]\" />\n        </div>\n\n        {/* Concentric orbital rings & monospace text (single canvas, zoom handled in the draw loop) */}\n        <canvas\n          ref={canvasRef}\n          aria-hidden=\"true\"\n          className=\"absolute inset-0 w-full h-full pointer-events-none select-none\"\n        />\n\n        {/* Expanding White/Cream Circle Portal (\"The Eye\") */}\n        <div className=\"absolute inset-0 flex items-center justify-center pointer-events-none z-20\">\n          <div\n            ref={circleRef}\n            style={{\n              width: maxDiameter,\n              height: maxDiameter,\n              transform: \"scale(0)\",\n              opacity: 0,\n              visibility: \"hidden\",\n              willChange: \"transform, opacity\",\n            }}\n            className=\"shrink-0 rounded-full bg-[#f1eee7] text-neutral-900 flex flex-col items-center justify-center text-center shadow-[0_0_80px_rgba(0,0,0,0.85)] pointer-events-auto select-none overflow-hidden\"\n          >\n            <div\n              ref={contentRef}\n              style={{ opacity: 0, transform: \"scale(0.88)\" }}\n              className=\"flex flex-col items-center justify-center max-w-[360px] sm:max-w-[440px] w-full p-6 sm:p-10\"\n            >\n              {/* Elegant Eyebrow Pill with Sparkles Icon */}\n              <div className=\"inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-neutral-900/[0.06] border border-neutral-900/10 mb-3 shadow-[0_1px_2px_rgba(0,0,0,0.04)]\">\n                <Sparkles className=\"size-3 text-neutral-800 animate-pulse\" />\n                <span className=\"text-[10px] sm:text-xs font-mono font-medium tracking-wider text-neutral-700 uppercase\">\n                  {subheading || \"Access Portal\"}\n                </span>\n              </div>\n\n              {/* Core Heading */}\n              <h2 className=\"text-2xl sm:text-4xl md:text-[42px] font-bold text-neutral-950 tracking-tight leading-[1.12]\">\n                {heading}\n              </h2>\n\n              {/* Optional Statement / Subtext */}\n              {statement ? (\n                <p className=\"mt-2 text-xs sm:text-sm text-neutral-600 line-clamp-2 max-w-[320px] font-normal leading-relaxed\">\n                  {statement}\n                </p>\n              ) : null}\n\n              {/* High-Tech Action Button & Micro-Trust Footnote */}\n              <div className=\"mt-5 sm:mt-7 flex flex-col items-center gap-2.5\">\n                <a\n                  href={ctaHref}\n                  onClick={(e) => {\n                    if (ctaHref === \"#\") e.preventDefault();\n                    onCtaClick?.();\n                  }}\n                  className=\"inline-flex items-center gap-2 rounded-full bg-neutral-900 hover:bg-neutral-800 text-white pl-4 pr-1.5 py-1.5 shadow-[0_4px_16px_rgba(0,0,0,0.2)] border border-neutral-700/50 transition-all duration-200 active:scale-95 group cursor-pointer\"\n                >\n                  <div className=\"flex items-center gap-2 font-mono text-xs sm:text-sm font-semibold tracking-wider uppercase\">\n                    <span>{leftLabel}</span>\n                    <span className=\"h-3 w-px bg-neutral-700\" />\n                    <span className=\"flex items-center gap-1.5\">\n                      <span>{rightLabel}</span>\n                      <span\n                        className=\"size-1.5 rounded-full animate-pulse\"\n                        style={{ backgroundColor: activeAccent }}\n                      />\n                    </span>\n                  </div>\n                  <div className=\"size-7 sm:size-8 rounded-full bg-neutral-800 group-hover:bg-neutral-700 flex items-center justify-center transition-colors\">\n                    <ArrowRight className=\"size-3.5 sm:size-4 stroke-[2.5] text-neutral-200 group-hover:text-white group-hover:translate-x-0.5 transition-transform\" />\n                  </div>\n                </a>\n\n                <div className=\"flex items-center gap-1.5 text-[10px] sm:text-[11px] font-mono text-neutral-500 font-medium\">\n                  <Check className=\"size-3 text-emerald-600 stroke-[3]\" />\n                  <span>No credit card required · Instant setup</span>\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n\n        {/* Bottom Scroll Cue */}\n        <div\n          ref={hintRef}\n          className=\"absolute bottom-6 left-1/2 -translate-x-1/2 flex flex-col items-center gap-1.5 text-neutral-500 text-[10px] tracking-widest font-mono uppercase pointer-events-none select-none z-10\"\n        >\n          <span>Scroll to expand</span>\n          <ChevronDown className=\"size-3.5 animate-bounce\" />\n        </div>\n      </div>\n    </section>\n  );\n}\n\n// Alias for ConcentricPortalCta\nexport const OrbitalLensCta = ConcentricPortalCta;\n\n// ==========================================\n// CTA OFFER SHOWCASE VARIANT (FRAMER REPLICA)\n// Central high-impact offer card flanked by moving nature reels\n// ==========================================\nexport const DEFAULT_OFFER_NATURE_IMAGES: string[] = [\n  \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_800,c_limit/v1790146209/photo-1470071459604-3b5ec3a7fe05-1790146212205.jpg\",\n  \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_800,c_limit/v1790145140/photo-1426604966848-d7adac402bff-1790145143490.jpg\",\n  \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_800,c_limit/v1790146211/photo-1441974231531-c6227db76b6e-1790146212212.jpg\",\n  \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_800,c_limit/v1790146209/photo-1469474968028-56623f02e42e-1790146212205.jpg\",\n  \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_800,c_limit/v1790146210/photo-1506744038136-46273834b3fb-1790146212208.jpg\",\n  \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_800,c_limit/v1790145142/photo-1511671782779-c97d3d27a1d4-1790145143493.jpg\",\n  \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_800,c_limit/v1790145140/photo-1550684848-fac1c5b4e853-1790145143489.jpg\",\n  \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_800,c_limit/v1790148864/photo-1618005182384-a83a8bd57fbe-1790148865111.jpg\",\n  \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_800,c_limit/v1790166049/pexels-photo-7688336-1790166052227.jpg\",\n  \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_800,c_limit/v1790166049/pexels-photo-8867434-1790166052229.jpg\",\n  \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_800,c_limit/v1790166049/pexels-photo-7658387-1790166052226.jpg\",\n  \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_800,c_limit/v1790166049/pexels-photo-7709086-1790166052228.jpg\",\n  \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_800,c_limit/v1790145139/pexels-photo-7709208-1790145143489.jpg\",\n  \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_800,c_limit/v1790145139/pexels-photo-1036623-1790145143487.jpg\",\n  \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_800,c_limit/v1790145142/pexels-photo-2043590-1790145143488.jpg\",\n  \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_800,c_limit/v1790145139/pexels-photo-1587009-1790145143488.jpg\",\n];\n\nfunction ReelLane({\n  images,\n  direction = \"up\",\n  duration = 32,\n  isSlowed = false,\n  className,\n}: {\n  images: string[];\n  direction?: \"up\" | \"down\";\n  duration?: number;\n  isSlowed?: boolean;\n  className?: string;\n}) {\n  const laneRef = useRef<HTMLDivElement>(null);\n\n  useEffect(() => {\n    if (!laneRef.current) return;\n    const anims = laneRef.current.getAnimations({ subtree: true });\n    anims.forEach((a) => {\n      a.playbackRate = isSlowed ? 0.25 : 1;\n    });\n  }, [isSlowed]);\n\n  // Ensure minimum 8 images in a set so that the set height comfortably exceeds the entire viewport\n  const setImages = useMemo(() => {\n    if (!images || images.length === 0) return DEFAULT_OFFER_NATURE_IMAGES.slice(0, 8);\n    let list = [...images];\n    while (list.length < 8) {\n      list = [...list, ...images];\n    }\n    return list;\n  }, [images]);\n\n  return (\n    <div\n      ref={laneRef}\n      className={cn(\n        \"relative overflow-hidden h-full min-h-[720px] xs:min-h-[820px] sm:min-h-[960px] md:min-h-[1100px] shrink-0 select-none pointer-events-auto\",\n        className,\n      )}\n    >\n      <div\n        className={cn(\n          \"flex flex-col will-change-transform\",\n          direction === \"up\" ? \"animate-cta-reel-up\" : \"animate-cta-reel-down\",\n        )}\n        style={{\n          animationDuration: `${duration}s`,\n          animationTimingFunction: \"linear\",\n          animationIterationCount: \"infinite\",\n        }}\n      >\n        {/* Set 1: Full set with trailing gap via padding-bottom */}\n        <div className=\"flex flex-col gap-3 sm:gap-3.5 pb-3 sm:pb-3.5 shrink-0\">\n          {setImages.map((src, i) => (\n            <div\n              key={`s1-${i}`}\n              className=\"w-full aspect-[3/4] rounded-2xl overflow-hidden border border-border/40 dark:border-white/10 bg-muted/40 shadow-sm shrink-0 select-none\"\n            >\n              <img\n                src={src}\n                alt=\"\"\n                loading=\"lazy\"\n                decoding=\"async\"\n                className=\"size-full object-cover select-none pointer-events-none\"\n              />\n            </div>\n          ))}\n        </div>\n\n        {/* Set 2: Exact identical duplicate set with matching trailing gap so loop is 100% seamless with zero jumps */}\n        <div className=\"flex flex-col gap-3 sm:gap-3.5 pb-3 sm:pb-3.5 shrink-0\" aria-hidden=\"true\">\n          {setImages.map((src, i) => (\n            <div\n              key={`s2-${i}`}\n              className=\"w-full aspect-[3/4] rounded-2xl overflow-hidden border border-border/40 dark:border-white/10 bg-muted/40 shadow-sm shrink-0 select-none\"\n            >\n              <img\n                src={src}\n                alt=\"\"\n                loading=\"lazy\"\n                decoding=\"async\"\n                className=\"size-full object-cover select-none pointer-events-none\"\n              />\n            </div>\n          ))}\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport function OfferShowcaseCta({\n  badge = \"Limited launch offer\",\n  heading = \"Everything you need, one payment\",\n  subheading = \"Lifetime access to every current and future release. No subscription, no renewals.\",\n  statement,\n  price = 149,\n  compareAtPrice = 349,\n  currency = \"$\",\n  period = \"one-time\",\n  savingsText = \"Save 57%\",\n  ctaText = \"Claim the offer\",\n  ctaHref = \"#\",\n  onCtaClick,\n  footnote = \"Secure checkout · 14-day refund\",\n  mediaItems,\n  images,\n  color,\n  rounded,\n  className,\n  style,\n  ...props\n}: CtaSectionProps) {\n  const [leftHovered, setLeftHovered] = useState(false);\n  const [rightHovered, setRightHovered] = useState(false);\n\n  const activeAccent = resolveThemeColor(color, \"var(--primary)\");\n  const dynamicRadius =\n    rounded !== undefined\n      ? typeof rounded === \"number\"\n        ? `${rounded}px`\n        : rounded\n      : \"var(--radius, 1rem)\";\n\n  const numPrice = typeof price === \"number\" ? price : parseFloat(String(price)) || 149;\n  const descriptionText = statement || subheading;\n\n  // Prefer images, then mediaItems, falling back to default Cloudinary nature collection\n  const rawList = images && images.length > 0 ? images : (mediaItems && mediaItems.length > 0 ? mediaItems : DEFAULT_OFFER_NATURE_IMAGES);\n  const items = rawList.length >= 4 ? rawList : DEFAULT_OFFER_NATURE_IMAGES;\n\n  // Distribute items across the 4 vertical lanes evenly\n  const lane1: string[] = [];\n  const lane2: string[] = [];\n  const lane3: string[] = [];\n  const lane4: string[] = [];\n\n  items.forEach((src, idx) => {\n    const laneIdx = idx % 4;\n    if (laneIdx === 0) lane1.push(src);\n    else if (laneIdx === 1) lane2.push(src);\n    else if (laneIdx === 2) lane3.push(src);\n    else lane4.push(src);\n  });\n\n  const safeLane1 = lane1.length > 0 ? lane1 : DEFAULT_OFFER_NATURE_IMAGES.slice(0, 4);\n  const safeLane2 = lane2.length > 0 ? lane2 : DEFAULT_OFFER_NATURE_IMAGES.slice(4, 8);\n  const safeLane3 = lane3.length > 0 ? lane3 : DEFAULT_OFFER_NATURE_IMAGES.slice(8, 12);\n  const safeLane4 = lane4.length > 0 ? lane4 : DEFAULT_OFFER_NATURE_IMAGES.slice(12, 16);\n\n  return (\n    <section\n      className={cn(\n        \"group/cta-showcase relative w-full min-h-[560px] xs:min-h-[620px] sm:min-h-[720px] lg:min-h-[780px] overflow-hidden bg-[#F3F1EC] dark:bg-[#0E0E10] text-[#141414] dark:text-[#F4F4F2] flex items-center justify-center py-12 sm:py-20 px-3 sm:px-6 select-none\",\n        className,\n      )}\n      style={style}\n      {...props}\n    >\n      <style>{`\n        @keyframes cta-reel-up {\n          0% { transform: translateY(0); }\n          100% { transform: translateY(-50%); }\n        }\n        @keyframes cta-reel-down {\n          0% { transform: translateY(-50%); }\n          100% { transform: translateY(0); }\n        }\n        .animate-cta-reel-up {\n          animation: cta-reel-up 54s linear infinite;\n        }\n        .animate-cta-reel-down {\n          animation: cta-reel-down 62s linear infinite;\n        }\n        @keyframes cta-dot-ping {\n          0% { box-shadow: 0 0 0 0 rgba(232, 84, 46, 0.55); }\n          75%, 100% { box-shadow: 0 0 0 7px transparent; }\n        }\n        .animate-cta-dot-ping {\n          animation: cta-dot-ping 2.4s cubic-bezier(0.3, 0, 0.2, 1) infinite;\n        }\n      `}</style>\n\n      {/* 3D Flanking Media Reels Container */}\n      <div className=\"absolute inset-0 pointer-events-none overflow-hidden select-none\">\n        {/* Left Side (3D Perspective tilt with rotateY & rotate, exactly matching Framer) */}\n        <div\n          className=\"absolute top-0 bottom-0 left-0 w-1/2 pointer-events-auto\"\n          style={{\n            perspective: \"1100px\",\n            perspectiveOrigin: \"0% 50%\",\n            WebkitMaskImage: \"linear-gradient(to right, #000 22%, transparent 82%), linear-gradient(to bottom, transparent 0%, #000 14%, #000 86%, transparent 100%)\",\n            maskImage: \"linear-gradient(to right, #000 22%, transparent 82%), linear-gradient(to bottom, transparent 0%, #000 14%, #000 86%, transparent 100%)\",\n            WebkitMaskComposite: \"source-in\",\n            maskComposite: \"intersect\",\n          }}\n          onMouseEnter={() => setLeftHovered(true)}\n          onMouseLeave={() => setLeftHovered(false)}\n        >\n          <div\n            className=\"absolute -top-[25%] h-[150%] flex gap-2.5 sm:gap-3.5 left-2.5 xs:left-4 sm:left-7 md:left-9 origin-[0%_50%]\"\n            style={{\n              transform: \"rotate(-6deg) rotateY(14deg)\",\n            }}\n          >\n            {/* Lane 1: Primary visible lane on mobile & desktop */}\n            <ReelLane\n              images={safeLane1}\n              direction=\"up\"\n              duration={52}\n              isSlowed={leftHovered}\n              className=\"w-[84px] xs:w-[94px] sm:w-[130px] md:w-[160px] lg:w-[190px]\"\n            />\n            {/* Lane 2: Outer lane on desktop (hidden on small mobile screens <=720px exactly per Framer spec) */}\n            <ReelLane\n              images={safeLane2}\n              direction=\"down\"\n              duration={62}\n              isSlowed={leftHovered}\n              className=\"hidden sm:block sm:w-[130px] md:w-[160px] lg:w-[190px]\"\n            />\n          </div>\n        </div>\n\n        {/* Right Side (3D Perspective tilt with rotateY & rotate, exactly matching Framer) */}\n        <div\n          className=\"absolute top-0 bottom-0 right-0 w-1/2 pointer-events-auto\"\n          style={{\n            perspective: \"1100px\",\n            perspectiveOrigin: \"100% 50%\",\n            WebkitMaskImage: \"linear-gradient(to left, #000 22%, transparent 82%), linear-gradient(to bottom, transparent 0%, #000 14%, #000 86%, transparent 100%)\",\n            maskImage: \"linear-gradient(to left, #000 22%, transparent 82%), linear-gradient(to bottom, transparent 0%, #000 14%, #000 86%, transparent 100%)\",\n            WebkitMaskComposite: \"source-in\",\n            maskComposite: \"intersect\",\n          }}\n          onMouseEnter={() => setRightHovered(true)}\n          onMouseLeave={() => setRightHovered(false)}\n        >\n          <div\n            className=\"absolute -top-[25%] h-[150%] flex flex-row-reverse gap-2.5 sm:gap-3.5 right-2.5 xs:right-4 sm:right-7 md:right-9 origin-[100%_50%]\"\n            style={{\n              transform: \"rotate(6deg) rotateY(-14deg)\",\n            }}\n          >\n            {/* Lane 3: Primary visible lane on mobile & desktop */}\n            <ReelLane\n              images={safeLane3}\n              direction=\"down\"\n              duration={56}\n              isSlowed={rightHovered}\n              className=\"w-[84px] xs:w-[94px] sm:w-[130px] md:w-[160px] lg:w-[190px]\"\n            />\n            {/* Lane 4: Outer lane on desktop (hidden on small mobile screens <=720px exactly per Framer spec) */}\n            <ReelLane\n              images={safeLane4}\n              direction=\"up\"\n              duration={66}\n              isSlowed={rightHovered}\n              className=\"hidden sm:block sm:w-[130px] md:w-[160px] lg:w-[190px]\"\n            />\n          </div>\n        </div>\n      </div>\n\n      {/* Edge gradient vignettes (top & bottom smooth fade to section background) */}\n      <div\n        className=\"pointer-events-none absolute inset-x-0 top-0 h-24 sm:h-36 bg-gradient-to-b from-[#F3F1EC] dark:from-[#0E0E10] via-[#F3F1EC]/90 dark:via-[#0E0E10]/90 to-transparent z-10\"\n        aria-hidden=\"true\"\n      />\n      <div\n        className=\"pointer-events-none absolute inset-x-0 bottom-0 h-24 sm:h-36 bg-gradient-to-t from-[#F3F1EC] dark:from-[#0E0E10] via-[#F3F1EC]/90 dark:via-[#0E0E10]/90 to-transparent z-10\"\n        aria-hidden=\"true\"\n      />\n\n      {/* Vertical center wash running top-to-bottom behind the card across all screens (ComponentWall 2nd variant style) */}\n      <div\n        className=\"pointer-events-none absolute inset-y-0 left-1/2 w-full max-w-[42rem] sm:max-w-[52rem] -translate-x-1/2 bg-[radial-gradient(closest-side,#F3F1EC_60%,transparent)] dark:bg-[radial-gradient(closest-side,#0E0E10_60%,transparent)] z-10\"\n        aria-hidden=\"true\"\n      />\n\n      {/* Central High-Impact Ticket Offer Card (Clean Minimalist Ticket with real radial-gradient bite notches) */}\n      <div className=\"relative z-20 w-full max-w-[min(468px,calc(100%-20px))] sm:max-w-[420px] md:max-w-[468px] mx-auto filter drop-shadow-[0_28px_56px_rgba(20,20,20,0.08)] dark:drop-shadow-[0_28px_56px_rgba(0,0,0,0.6)] flex flex-col items-center text-center\">\n        {/* Top Half of Card: Badge, Heading, Subtitle */}\n        <div\n          className=\"w-full bg-white dark:bg-[#18181B] border border-[rgba(20,20,20,0.10)] dark:border-[rgba(255,255,255,0.10)] border-b-0 p-5 xs:p-6 sm:p-8 pb-3.5 sm:pb-5 flex flex-col items-center\"\n          style={{\n            borderTopLeftRadius: dynamicRadius,\n            borderTopRightRadius: dynamicRadius,\n            WebkitMaskImage: \"radial-gradient(circle at 0% 100%, transparent 12px, #000 12.5px), radial-gradient(circle at 100% 100%, transparent 12px, #000 12.5px)\",\n            maskImage: \"radial-gradient(circle at 0% 100%, transparent 12px, #000 12.5px), radial-gradient(circle at 100% 100%, transparent 12px, #000 12.5px)\",\n            WebkitMaskComposite: \"source-in\",\n            maskComposite: \"intersect\",\n          }}\n        >\n          {badge && (\n            <span\n              style={{ borderRadius: dynamicRadius }}\n              className=\"inline-flex items-center gap-2 px-3 py-1 sm:px-3.5 sm:py-1.5 border border-[rgba(20,20,20,0.10)] dark:border-[rgba(255,255,255,0.12)] bg-neutral-50/60 dark:bg-white/5 text-[11px] sm:text-[13px] font-medium text-[#141414] dark:text-[#F4F4F2] mb-3 sm:mb-4\"\n            >\n              <span className=\"size-[7px] rounded-full shrink-0 animate-cta-dot-ping\" style={{ backgroundColor: activeAccent }} />\n              <span>{badge}</span>\n            </span>\n          )}\n\n          <h2 className=\"text-xl xs:text-2xl sm:text-[34px] md:text-[36px] font-semibold tracking-[-0.02em] text-[#141414] dark:text-[#F4F4F2] leading-[1.12]\">\n            {heading}\n          </h2>\n\n          {descriptionText && (\n            <p className=\"mt-2 text-xs xs:text-[13px] sm:text-[15px] text-[#6E6A64] dark:text-[#9C9CA3] leading-[1.5] max-w-[340px] sm:max-w-[380px]\">\n              {descriptionText}\n            </p>\n          )}\n        </div>\n\n        {/* Bottom Half of Card: Price, Savings Chip, CTA Button, Footnote */}\n        <div\n          className=\"relative w-full bg-white dark:bg-[#18181B] border border-[rgba(20,20,20,0.10)] dark:border-[rgba(255,255,255,0.10)] border-t-0 p-5 xs:p-6 sm:p-8 pt-3.5 sm:pt-5 flex flex-col items-center\"\n          style={{\n            borderBottomLeftRadius: dynamicRadius,\n            borderBottomRightRadius: dynamicRadius,\n            WebkitMaskImage: \"radial-gradient(circle at 0% 0%, transparent 12px, #000 12.5px), radial-gradient(circle at 100% 0%, transparent 12px, #000 12.5px)\",\n            maskImage: \"radial-gradient(circle at 0% 0%, transparent 12px, #000 12.5px), radial-gradient(circle at 100% 0%, transparent 12px, #000 12.5px)\",\n            WebkitMaskComposite: \"source-in\",\n            maskComposite: \"intersect\",\n          }}\n        >\n          {/* Dashed Perforation Line Connecting the Left & Right Notches */}\n          <div\n            className=\"absolute top-0 left-5 right-5 border-t-[1.5px] border-dashed border-[rgba(20,20,20,0.15)] dark:border-[rgba(255,255,255,0.15)] pointer-events-none\"\n            aria-hidden=\"true\"\n          />\n\n          {/* Price display with superscript currency */}\n          <div className=\"flex items-baseline justify-center text-[#141414] dark:text-[#F4F4F2]\">\n            <span className=\"text-xl xs:text-2xl sm:text-3xl font-semibold -translate-y-1 sm:-translate-y-2 mr-0.5 font-sans\">\n              {currency}\n            </span>\n            <span className=\"text-4xl xs:text-5xl sm:text-[62px] font-semibold tracking-[-0.035em] leading-none tabular-nums font-sans\">\n              <AnimatedNumber value={numPrice} duration={1} />\n            </span>\n            {period && (\n              <span className=\"text-xs sm:text-[14px] font-medium text-[#6E6A64] dark:text-[#9C9CA3] ml-2 font-sans\">\n                {period}\n              </span>\n            )}\n          </div>\n\n          {/* Strikethrough & Savings Tag */}\n          {(compareAtPrice || savingsText) && (\n            <div className=\"mt-2.5 flex items-center justify-center gap-2.5\">\n              {compareAtPrice && (\n                <s className=\"text-sm sm:text-[16px] text-[#6E6A64] dark:text-[#9C9CA3] font-normal decoration-[1.5px]\">\n                  {currency}{compareAtPrice}\n                </s>\n              )}\n              {savingsText && (\n                <span\n                  style={{ borderRadius: dynamicRadius }}\n                  className=\"inline-flex items-center px-2.5 py-1 text-[11px] sm:text-[12px] font-semibold text-[#E8542E] dark:text-[#FF7A4D] bg-[color-mix(in_srgb,#E8542E_14%,transparent)] dark:bg-[color-mix(in_srgb,#FF7A4D_18%,transparent)]\"\n                >\n                  {savingsText}\n                </span>\n              )}\n            </div>\n          )}\n\n          {/* CTA Button */}\n          <a\n            href={ctaHref}\n            onClick={(e) => {\n              if (ctaHref === \"#\") e.preventDefault();\n              onCtaClick?.();\n            }}\n            style={{ borderRadius: dynamicRadius }}\n            className=\"mt-4 sm:mt-5 w-full py-3 sm:py-3.5 px-6 font-semibold text-xs sm:text-[15px] bg-[#141414] hover:bg-black dark:bg-[#F4F4F2] dark:hover:bg-white text-white dark:text-[#0E0E10] transition-transform duration-300 hover:-translate-y-0.5 active:scale-[0.985] flex items-center justify-center gap-2.5 group cursor-pointer\"\n          >\n            <span>{ctaText}</span>\n            <svg\n              width=\"16\"\n              height=\"16\"\n              viewBox=\"0 0 16 16\"\n              fill=\"none\"\n              className=\"size-3.5 sm:size-4 transition-transform duration-300 group-hover:translate-x-1\"\n            >\n              <path\n                d=\"M3 8h10M9 4l4 4-4 4\"\n                stroke=\"currentColor\"\n                strokeWidth=\"1.6\"\n                strokeLinecap=\"round\"\n                strokeLinejoin=\"round\"\n              />\n            </svg>\n          </a>\n\n          {/* Footnote */}\n          {footnote && (\n            <p className=\"mt-3 text-[11px] sm:text-[12px] text-[#6E6A64] dark:text-[#9C9CA3] font-normal\">\n              {footnote}\n            </p>\n          )}\n        </div>\n      </div>\n    </section>\n  );\n}\n\n// ==========================================\n// MOBILE APP SHOWCASE CTA (IPHONE MOCKUP)\n// Two-Section Transition: Section 1 (Center Phone) -> Section 2 (Left Phone + Right Content)\n// Driven by Parent Scroll / Wheel / Touch / Stepper\n// ==========================================\nexport function MobileAppCta({\n  heading = \"Aura Clearmind..\\nYour partner in wellness.\",\n  headingAccent = \"Your partner\",\n  subheading = \"Find and book your favorite yoga classes, guided meditation, and daily mindful wellness sessions from anywhere with our all-in-one mobile app, designed to bring balance, clarity, and peace to your life.\",\n  statement,\n  badge = \"Mobile App Available\",\n  appleStoreHref = \"#\",\n  googlePlayHref = \"#\",\n  showAppStoreBadges = true,\n  userName = \"Alicia Regis\",\n  userAvatar = \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_120,c_limit/v1790145142/pexels-photo-2043590-1790145143488.jpg\",\n  featuredWorkoutTitle = \"Wellness Workshops\",\n  featuredWorkoutImage = \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_600,c_limit/v1790146210/photo-1506744038136-46273834b3fb-1790146212208.jpg\",\n  workoutDuration = \"10 Min\",\n  workoutCalories = \"300 Kcal\",\n  color,\n  scrollProgress,\n  phonePosition = \"left\",\n  cardBackground,\n  rounded,\n  className,\n  style,\n  ...props\n}: CtaSectionProps) {\n  const containerRef = useRef<HTMLDivElement>(null);\n  const stageRef = useRef<HTMLDivElement>(null);\n  const phoneColRef = useRef<HTMLDivElement>(null);\n\n  const [progress, setProgress] = useState(\n    scrollProgress !== undefined ? Math.max(0, Math.min(1, scrollProgress)) : 0,\n  );\n  const targetProgressRef = useRef(\n    scrollProgress !== undefined ? Math.max(0, Math.min(1, scrollProgress)) : 0,\n  );\n  const [shiftDeltaX, setShiftDeltaX] = useState(290);\n\n  const activeAccent = resolveThemeColor(color, \"var(--primary)\");\n  const descriptionText = subheading || statement;\n\n  // Dynamic radius variables bound directly to global --radius token from the theme customizer sidebar\n  const dynamicRadius =\n    rounded !== undefined\n      ? typeof rounded === \"number\"\n        ? `${rounded}px`\n        : rounded\n      : \"var(--radius, 1rem)\";\n  const containerRadius = dynamicRadius;\n  const cardRadius = dynamicRadius;\n  const buttonRadius = dynamicRadius;\n  const pillRadius = dynamicRadius;\n  const iconRadius = dynamicRadius;\n\n  // Sync if external scrollProgress prop is passed\n  useEffect(() => {\n    if (scrollProgress !== undefined) {\n      targetProgressRef.current = Math.max(0, Math.min(1, scrollProgress));\n    }\n  }, [scrollProgress]);\n\n  // Dynamically calculate exact pixel distance from side column to center of stage\n  useLayoutEffect(() => {\n    const updateDelta = () => {\n      if (stageRef.current && phoneColRef.current) {\n        const stageRect = stageRef.current.getBoundingClientRect();\n        const colRect = phoneColRef.current.getBoundingClientRect();\n        const stageCenter = stageRect.left + stageRect.width / 2;\n        const colCenter = colRect.left + colRect.width / 2;\n        const isDesktop = window.innerWidth >= 1024;\n        setShiftDeltaX(isDesktop ? Math.round(stageCenter - colCenter) : 0);\n      }\n    };\n\n    updateDelta();\n    const timer1 = setTimeout(updateDelta, 60);\n    const timer2 = setTimeout(updateDelta, 240);\n    window.addEventListener(\"resize\", updateDelta);\n    return () => {\n      clearTimeout(timer1);\n      clearTimeout(timer2);\n      window.removeEventListener(\"resize\", updateDelta);\n    };\n  }, [phonePosition]);\n\n  // 60FPS RAF Spring Lerp loop for buttery-smooth transition\n  useEffect(() => {\n    let current = targetProgressRef.current;\n    let active = true;\n    let rafId: number | null = null;\n\n    const tick = () => {\n      if (!active) return;\n      const target = targetProgressRef.current;\n      const diff = target - current;\n      if (Math.abs(diff) > 0.0006) {\n        current += diff * 0.18;\n        setProgress(current);\n      } else if (current !== target) {\n        current = target;\n        setProgress(target);\n      }\n      rafId = requestAnimationFrame(tick);\n    };\n    rafId = requestAnimationFrame(tick);\n\n    return () => {\n      active = false;\n      if (rafId) cancelAnimationFrame(rafId);\n    };\n  }, []);\n\n  // 1. Direct Mouse Wheel listener with passive: false to drive the animation immediately\n  useEffect(() => {\n    const container = containerRef.current;\n    if (!container) return;\n\n    const onWheel = (e: WheelEvent) => {\n      if (Math.abs(e.deltaY) < 1) return;\n\n      const current = targetProgressRef.current;\n      // Smooth scrubbing sensitivity: ~3 gentle wheel notches transition fully\n      const step = e.deltaY * 0.0022;\n      const next = Math.max(0, Math.min(1, current + step));\n\n      const isScrollingDown = step > 0;\n      const isScrollingUp = step < 0;\n\n      // While between 0 and 1, intercept wheel to animate transition smoothly\n      if (\n        (isScrollingDown && current < 0.99) ||\n        (isScrollingUp && current > 0.01)\n      ) {\n        e.preventDefault();\n        e.stopPropagation();\n        targetProgressRef.current = next;\n      }\n    };\n\n    container.addEventListener(\"wheel\", onWheel, { passive: false });\n    return () => {\n      container.removeEventListener(\"wheel\", onWheel);\n    };\n  }, []);\n\n  // 2. Touch gesture listener for mobile and tablets\n  useEffect(() => {\n    const container = containerRef.current;\n    if (!container) return;\n\n    let touchStartY = 0;\n\n    const onTouchStart = (e: TouchEvent) => {\n      if (e.touches.length > 0) {\n        touchStartY = e.touches[0].clientY;\n      }\n    };\n\n    const onTouchMove = (e: TouchEvent) => {\n      if (e.touches.length === 0) return;\n      const currentY = e.touches[0].clientY;\n      const deltaY = touchStartY - currentY;\n      if (Math.abs(deltaY) < 4) return;\n\n      const current = targetProgressRef.current;\n      const step = deltaY * 0.0035;\n      const next = Math.max(0, Math.min(1, current + step));\n\n      const isScrollingDown = step > 0;\n      const isScrollingUp = step < 0;\n\n      if (\n        (isScrollingDown && current < 0.99) ||\n        (isScrollingUp && current > 0.01)\n      ) {\n        if (e.cancelable) e.preventDefault();\n        targetProgressRef.current = next;\n        touchStartY = currentY;\n      }\n    };\n\n    container.addEventListener(\"touchstart\", onTouchStart, { passive: true });\n    container.addEventListener(\"touchmove\", onTouchMove, { passive: false });\n    return () => {\n      container.removeEventListener(\"touchstart\", onTouchStart);\n      container.removeEventListener(\"touchmove\", onTouchMove);\n    };\n  }, []);\n\n  // 3. Fallback: Page scroll observer when placed inside a standard tall website page\n  useEffect(() => {\n    const container = containerRef.current;\n    if (!container) return;\n\n    const onScroll = () => {\n      if (scrollProgress !== undefined) return;\n      const rect = container.getBoundingClientRect();\n      const vh = window.innerHeight;\n      const triggerStart = vh * 0.8;\n      const triggerEnd = vh * 0.2;\n      const dist = triggerStart - triggerEnd;\n      if (dist > 0 && rect.top <= triggerStart && rect.bottom >= triggerEnd) {\n        const p = Math.max(0, Math.min(1, (triggerStart - rect.top) / dist));\n        targetProgressRef.current = p;\n      }\n    };\n\n    window.addEventListener(\"scroll\", onScroll, { passive: true });\n    return () => {\n      window.removeEventListener(\"scroll\", onScroll);\n    };\n  }, [scrollProgress]);\n\n  // Easing computations\n  const ease = 1 - Math.pow(1 - progress, 3);\n  const phoneShiftX = Math.round(shiftDeltaX * (1 - ease));\n  const phoneShiftY = 0; // Strictly vertically centered at all stages of motion\n\n  // Section 2 card background & right content reveal\n  const sec2Opacity = Math.max(0, Math.min(1, (progress - 0.12) / 0.88));\n  const sec2Ease = 1 - Math.pow(1 - sec2Opacity, 3);\n  const contentShiftX = Math.round((1 - sec2Ease) * (phonePosition === \"left\" ? 35 : -35));\n\n  // Split heading into 2 clean lines: Upper line shorter width, Lower line wider width\n  const heading2Lines = useMemo(() => {\n    if (heading.includes(\"\\n\")) {\n      const [l1, ...rest] = heading.split(\"\\n\");\n      return { line1: l1.trim(), line2: rest.join(\" \").trim() };\n    }\n    if (heading.includes(\",\")) {\n      const idx = heading.indexOf(\",\");\n      return {\n        line1: heading.slice(0, idx + 1).trim(),\n        line2: heading.slice(idx + 1).trim(),\n      };\n    }\n    const words = heading.split(\" \");\n    const splitIndex = Math.max(1, Math.floor(words.length * 0.35));\n    return {\n      line1: words.slice(0, splitIndex).join(\" \"),\n      line2: words.slice(splitIndex).join(\" \"),\n    };\n  }, [heading]);\n\n  const isPhoneOnLeft = phonePosition === \"left\";\n\n  return (\n    <div\n      ref={containerRef}\n      className={cn(\n        \"relative w-full max-w-6xl mx-auto select-none overflow-hidden min-h-[700px] sm:min-h-[720px] lg:min-h-[740px] flex items-center justify-center transition-colors border border-neutral-200/60 dark:border-white/10\",\n        className,\n      )}\n      style={{\n        borderRadius: containerRadius,\n        ...style,\n      }}\n      {...props}\n    >\n      {/* =========================================\n          SECTION 2 (SHOWCASE PHASE: AUTHENTIC WELLNESS SAGE CARD)\n          ========================================= */}\n      <div\n        className=\"absolute inset-0 pointer-events-none transition-all duration-500 shadow-xl overflow-hidden\"\n        style={{\n          opacity: sec2Ease,\n          transform: `scale(${0.96 + 0.04 * sec2Ease})`,\n          borderRadius: containerRadius,\n          background: cardBackground\n            ? cardBackground\n            : `linear-gradient(135deg, ${activeAccent} 0%, color-mix(in srgb, ${activeAccent} 78%, #0f1c12) 100%)`,\n        }}\n        aria-hidden=\"true\"\n      >\n        {/* Dark mode overlay so custom colors blend smoothly in dark mode */}\n        <div\n          className=\"absolute inset-0 hidden dark:block bg-black/60 pointer-events-none\"\n          style={{ borderRadius: containerRadius }}\n        />\n\n        {/* Soft Ambient Radial Lighting */}\n        <div\n          className=\"absolute -right-20 -bottom-20 w-[450px] h-[450px] rounded-full blur-[80px] opacity-30 pointer-events-none bg-white/20 dark:bg-white/5\"\n          aria-hidden=\"true\"\n        />\n      </div>\n\n      {/* =========================================\n          STAGE CONTENT GRID\n          ========================================= */}\n      <div\n        ref={stageRef}\n        className=\"relative z-10 w-full h-full grid grid-cols-1 lg:grid-cols-12 items-center gap-8 p-6 sm:p-8 md:p-10 overflow-hidden\"\n      >\n        {/* =========================================\n            PHONE COLUMN (MOCKUP WITH SMOOTH GLIDE MOTION)\n            Glides smoothly from Center (Section 1) to Side (Section 2)\n            ========================================= */}\n        <div\n          ref={phoneColRef}\n          className={cn(\n            \"flex justify-center items-center relative z-20 my-auto self-center\",\n            isPhoneOnLeft\n              ? \"lg:col-span-5 lg:order-1\"\n              : \"lg:col-start-8 lg:col-span-5 lg:order-2\",\n          )}\n        >\n          {/* Circular backdrop disc behind the phone (matching screenshot) */}\n          <div\n            className=\"absolute size-[360px] sm:size-[420px] rounded-full border border-white/20 dark:border-white/5 pointer-events-none transition-opacity duration-500 shadow-inner\"\n            style={{\n              opacity: sec2Ease,\n              backgroundColor: `color-mix(in srgb, ${activeAccent} 80%, white)`,\n            }}\n            aria-hidden=\"true\"\n          />\n\n          {/* iPhone Mockup Container with Motion */}\n          <div\n            style={{\n              transform: `translate(${phoneShiftX}px, ${phoneShiftY}px)`,\n              willChange: \"transform\",\n              transition: \"none\",\n            }}\n            className=\"relative flex items-center justify-center\"\n          >\n            <div className=\"scale-[0.82] xs:scale-[0.88] sm:scale-[0.94] lg:scale-100 origin-center transition-transform\">\n              <Iphone16ProMockup\n                className=\"w-[330px] h-[680px] shadow-[0_25px_60px_-15px_rgba(0,0,0,0.35)] dark:shadow-[0_25px_70px_-20px_rgba(0,0,0,0.85)]\"\n              >\n                {/* Screen Content: Authentic Yoga & Wellness App Interface */}\n                <div className=\"relative w-full h-full flex flex-col justify-between bg-white dark:bg-[#141418] text-neutral-900 dark:text-neutral-100 p-4 pt-11 pb-4 select-none font-sans overflow-hidden\">\n                  {/* Status Bar */}\n                  <div className=\"absolute top-2.5 inset-x-0 px-6 flex items-center justify-between text-[11px] font-semibold text-neutral-800 dark:text-neutral-200 z-30 pointer-events-none\">\n                    <span>9:41</span>\n                    <div className=\"flex items-center gap-1.5 text-neutral-700 dark:text-neutral-300\">\n                      <Signal size={11} />\n                      <Wifi size={11} />\n                      <Battery size={13} className=\"fill-current\" />\n                    </div>\n                  </div>\n\n                  {/* Main App Feed */}\n                  <div className=\"space-y-3\">\n                    {/* Greeting & Avatar */}\n                    <div className=\"flex items-center justify-between\">\n                      <div>\n                        <span className=\"text-[10px] font-medium text-neutral-500 dark:text-neutral-400 block\">\n                          Hello\n                        </span>\n                        <h4 className=\"text-sm sm:text-base font-bold text-neutral-950 dark:text-white tracking-tight\">\n                          {userName}\n                        </h4>\n                      </div>\n                      <div className=\"size-8 rounded-full bg-gradient-to-tr from-emerald-600 to-lime-500 p-0.5 shadow-sm\">\n                        <img\n                          src={userAvatar}\n                          alt={userName}\n                          className=\"size-full rounded-full object-cover\"\n                        />\n                      </div>\n                    </div>\n\n                    {/* Level / Filter Pills */}\n                    <div className=\"flex items-center gap-1.5\">\n                      {[\"Beginner\", \"Intermediate\", \"Advanced\"].map((level, i) => (\n                        <span\n                          key={level}\n                          style={{ borderRadius: pillRadius }}\n                          className={cn(\n                            \"text-[9px] sm:text-[10px] font-medium px-2.5 py-1 transition-colors cursor-pointer\",\n                            i === 1\n                              ? \"bg-[#172e1b] text-white font-semibold shadow-xs\"\n                              : \"bg-neutral-100 dark:bg-neutral-800 text-neutral-600 dark:text-neutral-400 hover:bg-neutral-200 dark:hover:bg-neutral-700\",\n                          )}\n                        >\n                          {level}\n                        </span>\n                      ))}\n                    </div>\n\n                    {/* Popular Workouts Card */}\n                    <div className=\"space-y-1\">\n                      <div className=\"flex items-center justify-between\">\n                        <span className=\"text-[11px] sm:text-xs font-bold text-neutral-900 dark:text-white\">\n                          Popular Workouts\n                        </span>\n                        <span className=\"text-[9px] text-neutral-400 hover:text-neutral-900 dark:hover:text-white cursor-pointer\">\n                          See all\n                        </span>\n                      </div>\n\n                      <div\n                        className=\"relative w-full h-32 overflow-hidden shadow-sm group cursor-pointer\"\n                        style={{ borderRadius: cardRadius }}\n                      >\n                        <img\n                          src={featuredWorkoutImage}\n                          alt={featuredWorkoutTitle}\n                          className=\"size-full object-cover group-hover:scale-105 transition-transform duration-500\"\n                        />\n                        <div className=\"absolute inset-0 bg-gradient-to-t from-black/85 via-black/40 to-transparent p-3 flex flex-col justify-between text-white\">\n                          <span className=\"text-[11px] sm:text-xs font-bold tracking-tight\">\n                            {featuredWorkoutTitle}\n                          </span>\n                          <div className=\"flex items-center justify-between\">\n                            <div className=\"flex items-center gap-1.5 text-[9px] font-medium\">\n                              <span\n                                style={{ borderRadius: pillRadius }}\n                                className=\"px-2 py-0.5 bg-[#ebf071] text-[#172e1b] font-semibold\"\n                              >\n                                {workoutDuration}\n                              </span>\n                              <span\n                                style={{ borderRadius: pillRadius }}\n                                className=\"px-2 py-0.5 bg-[#ebf071] text-[#172e1b] font-semibold\"\n                              >\n                                {workoutCalories}\n                              </span>\n                            </div>\n                            <div\n                              style={{ borderRadius: pillRadius }}\n                              className=\"size-6 bg-white text-black grid place-items-center shadow-md\"\n                            >\n                              <Play size={10} className=\"fill-current translate-x-0.5\" />\n                            </div>\n                          </div>\n                        </div>\n                      </div>\n                    </div>\n\n                    {/* Today's Plan Item */}\n                    <div className=\"space-y-1\">\n                      <span className=\"text-[11px] sm:text-xs font-bold text-neutral-900 dark:text-white\">\n                        Today Plan\n                      </span>\n                      <div\n                        style={{ borderRadius: cardRadius }}\n                        className=\"flex items-center gap-2.5 p-2 border border-neutral-200/80 dark:border-white/10 bg-neutral-50/80 dark:bg-white/[0.03]\"\n                      >\n                        <div\n                          style={{ borderRadius: iconRadius }}\n                          className=\"size-8 bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 grid place-items-center shrink-0\"\n                        >\n                          <Sparkles size={14} />\n                        </div>\n                        <div className=\"flex-1 min-w-0\">\n                          <span className=\"block text-[10px] sm:text-[11px] font-semibold text-neutral-900 dark:text-white truncate\">\n                            Morning Mindfulness\n                          </span>\n                          <span className=\"block text-[8px] sm:text-[9px] text-neutral-500 dark:text-neutral-400\">\n                            15 mins · Guided Audio\n                          </span>\n                        </div>\n                        <span className=\"text-[9px] sm:text-[10px] font-bold text-emerald-600 dark:text-emerald-400\">\n                          85%\n                        </span>\n                      </div>\n                    </div>\n                  </div>\n                </div>\n              </Iphone16ProMockup>\n            </div>\n          </div>\n        </div>\n\n        {/* =========================================\n            EDITORIAL CONTENT COLUMN (SECTION 2 REVEAL)\n            Reveals on the side as Section 2 emerges\n            ========================================= */}\n        <div\n          className={cn(\n            \"flex flex-col items-start text-left z-10 max-w-xl\",\n            isPhoneOnLeft\n              ? \"lg:col-span-7 lg:order-2 lg:pl-4 xl:pl-8\"\n              : \"lg:col-span-7 lg:order-1 lg:pr-4 xl:pr-8\",\n          )}\n          style={{\n            opacity: sec2Ease,\n            transform: `translateX(${contentShiftX}px)`,\n            pointerEvents: sec2Opacity > 0.6 ? \"auto\" : \"none\",\n            willChange: \"transform, opacity\",\n            transition: \"none\",\n          }}\n        >\n          {/* Top Pill Badge */}\n          {badge && (\n            <div\n              style={{ borderRadius: pillRadius }}\n              className=\"inline-flex items-center gap-2.5 px-3.5 py-1.5 border border-black/10 dark:border-white/15 bg-white/75 dark:bg-white/10 backdrop-blur-md text-xs font-semibold text-[#142918] dark:text-neutral-200 shadow-[0_2px_8px_rgba(0,0,0,0.04)] mb-5\"\n            >\n              <span className=\"relative flex size-2\">\n                <span className=\"animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-500 opacity-75\" />\n                <span className=\"relative inline-flex rounded-full size-2 bg-emerald-600 dark:bg-emerald-400\" />\n              </span>\n              <span className=\"tracking-tight\">{badge}</span>\n            </div>\n          )}\n\n          {/* Main Editorial Headline: Bold, Large 2-Line High-Impact Typography */}\n          <h2 className=\"text-3xl xs:text-4xl sm:text-5xl lg:text-[44px] xl:text-[46px] font-bold tracking-tight text-[#0d2212] dark:text-white leading-[1.12] w-full\">\n            <span className=\"block whitespace-nowrap\">{heading2Lines.line1}</span>\n            <span className=\"block mt-1 sm:mt-1.5 whitespace-nowrap\">{heading2Lines.line2}</span>\n          </h2>\n\n          {/* Subheading: Strictly in 2 Clean Balanced Lines */}\n          {descriptionText && (\n            <p className=\"mt-4 sm:mt-5 text-sm sm:text-base lg:text-[16px] text-[#243d29] dark:text-neutral-300 leading-relaxed font-normal max-w-[560px] tracking-normal\">\n              {descriptionText}\n            </p>\n          )}\n\n          {/* App Store & Google Play Store Badges */}\n          {showAppStoreBadges && (\n            <div className=\"mt-6 sm:mt-8 flex flex-wrap items-center gap-3.5 w-full sm:w-auto\">\n              {/* Apple App Store Badge */}\n              <a\n                href={appleStoreHref}\n                style={{ borderRadius: buttonRadius }}\n                className=\"inline-flex items-center gap-3 px-4 sm:px-5 py-2.5 sm:py-3 bg-black text-white hover:bg-neutral-900 border border-neutral-800 shadow-[0_4px_14px_rgba(0,0,0,0.22)] hover:shadow-[0_6px_20px_rgba(0,0,0,0.32)] hover:-translate-y-0.5 active:translate-y-0 transition-all duration-200 cursor-pointer group\"\n                aria-label=\"Download on the App Store\"\n              >\n                <svg className=\"size-5 sm:size-6 fill-current shrink-0 group-hover:scale-105 transition-transform\" viewBox=\"0 0 170 170\" aria-hidden=\"true\">\n                  <path d=\"M150.37 130.25c-2.45 5.66-5.35 10.87-8.71 15.66-4.58 6.53-8.33 11.05-11.22 13.56-4.48 4.12-9.28 6.23-14.42 6.35-3.69 0-8.14-1.05-13.32-3.18-5.19-2.12-9.97-3.17-14.34-3.17-4.58 0-9.49 1.05-14.75 3.17-5.26 2.13-9.5 3.24-12.74 3.35-4.35.13-9.16-1.9-14.42-6.08-3.7-3.03-7.61-7.79-11.73-14.28-5.88-9.25-10.4-19.46-13.56-30.64-3.17-11.18-4.75-21.84-4.75-31.98 0-14.18 3.65-26.06 10.96-35.63 7.3-9.58 16.5-14.48 27.59-14.72 5.02 0 10.44 1.25 16.27 3.75 5.82 2.5 9.77 3.78 11.83 3.83 1.85 0 5.96-1.33 12.33-3.98 6.37-2.66 11.96-3.88 16.78-3.67 12.56.63 22.84 5.38 30.84 14.25-11.04 6.72-16.43 15.93-16.18 27.63.26 9.38 3.84 17.29 10.74 23.72 6.9 6.43 15.11 10.15 24.63 11.16-2.22 6.84-4.88 13.7-7.98 20.59zM119.22 33.02c0-7.39 2.66-14.28 7.98-20.67 5.32-6.39 11.75-10.51 19.3-12.35 1.05 7.6-1.16 14.77-6.63 21.5-5.47 6.73-12.35 10.57-20.65 11.52z\" />\n                </svg>\n                <div className=\"flex flex-col text-left leading-none\">\n                  <span className=\"text-[9px] sm:text-[10px] text-neutral-300 font-sans tracking-wide\">Download on the</span>\n                  <span className=\"text-xs sm:text-sm font-semibold tracking-tight text-white mt-0.5\">App Store</span>\n                </div>\n              </a>\n\n              {/* Google Play Store Badge */}\n              <a\n                href={googlePlayHref}\n                style={{ borderRadius: buttonRadius }}\n                className=\"inline-flex items-center gap-3 px-4 sm:px-5 py-2.5 sm:py-3 bg-black text-white hover:bg-neutral-900 border border-neutral-800 shadow-[0_4px_14px_rgba(0,0,0,0.22)] hover:shadow-[0_6px_20px_rgba(0,0,0,0.32)] hover:-translate-y-0.5 active:translate-y-0 transition-all duration-200 cursor-pointer group\"\n                aria-label=\"Get it on Google Play\"\n              >\n                <svg className=\"size-5 sm:size-6 shrink-0 group-hover:scale-105 transition-transform\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n                  <path fill=\"#EA4335\" d=\"M3.6 1.48L14.07 12 3.6 22.52c-.37-.41-.6-.97-.6-1.61V3.09c0-.64.23-1.2.6-1.61z\" />\n                  <path fill=\"#FBBC04\" d=\"M17.58 8.52L14.07 12l3.51 3.48 4.02-2.3c.7-.4.7-1.96 0-2.36l-4.02-2.3z\" />\n                  <path fill=\"#4285F4\" d=\"M14.07 12L3.6 1.48C4.08 1.15 4.7 1.05 5.3 1.4l12.28 7.12L14.07 12z\" />\n                  <path fill=\"#34A853\" d=\"M14.07 12l3.51 3.48L5.3 22.6c-.6.35-1.22.25-1.7-.08L14.07 12z\" />\n                </svg>\n                <div className=\"flex flex-col text-left leading-none\">\n                  <span className=\"text-[9px] sm:text-[10px] text-neutral-300 font-sans tracking-wide uppercase\">Get it on</span>\n                  <span className=\"text-xs sm:text-sm font-semibold tracking-tight text-white mt-0.5\">Google Play</span>\n                </div>\n              </a>\n            </div>\n          )}\n\n          {/* Social Proof / Rating Trust Divider & Ratings */}\n          <div className=\"mt-6 sm:mt-8 pt-4 sm:pt-5 border-t border-black/10 dark:border-white/10 flex flex-wrap items-center gap-2.5 sm:gap-3 text-xs text-[#1c3320] dark:text-neutral-300 w-full\">\n            <div className=\"flex items-center gap-1 text-amber-500\">\n              {[...Array(5)].map((_, i) => (\n                <Star key={i} size={13} className=\"fill-current text-amber-500\" />\n              ))}\n            </div>\n            <span className=\"font-bold text-[#102414] dark:text-white tracking-tight\">4.9 / 5.0</span>\n            <span className=\"text-black/25 dark:text-white/20\">·</span>\n            <span className=\"text-[#243d29] dark:text-neutral-400\">24k+ reviews</span>\n            <span className=\"text-black/25 dark:text-white/20\">·</span>\n            <span className=\"font-semibold text-[#142e18] dark:text-emerald-400\">100k+ Active Members</span>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n\n// ==========================================\n// MAIN CTA SECTION ROUTER\n// ==========================================\nexport function CTASection({\n  variant = \"brand-pillars\",\n  ...props\n}: CtaSectionProps) {\n  switch (variant) {\n    case \"minimal\":\n      return <MinimalCta {...props} />;\n    case \"split\":\n      return <SplitCta {...props} />;\n    case \"concentric-portal\":\n    case \"orbital-lens\":\n      return <ConcentricPortalCta {...props} />;\n    case \"offer-showcase\":\n    case \"cta-offer-showcase\":\n      return <OfferShowcaseCta {...props} />;\n    case \"mobile-app\":\n    case \"app-showcase\":\n    case \"mobile-showcase\":\n    case \"mobile-mockup\":\n      return <MobileAppCta {...props} />;\n    case \"brand-pillars\":\n    default:\n      return <BrandPillarsCta {...props} />;\n  }\n}\n\nexport default CTASection;\nexport { CTASection as CtaSection };\n",
      "type": "registry:component",
      "target": "components/custom/blocks/CtaSectionBlock.tsx"
    },
    {
      "path": "src/lib/animate.ts",
      "content": "import { useEffect, useState, useSyncExternalStore, type RefObject } from \"react\";\n\n/**\n * Animation helpers for components that need no animation library: spring\n * easings for CSS transitions and the Web Animations API, plus the two browser\n * checks those components share. Installed alongside any component that uses it.\n */\n\nexport type SpringOptions = {\n  stiffness?: number;\n  damping?: number;\n  mass?: number;\n};\n\n/** A spring as a CSS timing: `duration` in milliseconds, `easing` as `linear(…)`. */\nexport type Timing = { duration: number; easing: string };\n\n/** How many points of the spring's curve the easing samples. */\nconst SAMPLES = 48;\n\n/** The time constant multiple at which a spring is within 0.1% of its target. */\nconst SETTLED = Math.log(1000);\n\n/**\n * Browsers without `linear()` get a plain ease instead: the Web Animations API\n * throws on an easing it can't parse, rather than ignoring it as CSS would.\n */\nconst supportsLinearEasing = () =>\n  typeof CSS === \"undefined\" || CSS.supports(\"transition-timing-function\", \"linear(0, 1)\");\n\n/** The response of a spring released from rest, as a CSS easing. */\nexport function springTiming({ stiffness = 100, damping = 10, mass = 1 }: SpringOptions = {}): Timing {\n  const natural = Math.sqrt(stiffness / mass);\n  const ratio = Math.min(damping / (2 * Math.sqrt(stiffness * mass)), 1);\n  const decay = ratio * natural;\n  const seconds = SETTLED / decay;\n  if (!supportsLinearEasing()) return { duration: Math.round(seconds * 1000), easing: \"ease-out\" };\n\n  const damped = natural * Math.sqrt(1 - ratio * ratio);\n  const position = (t: number) =>\n    ratio < 1\n      ? 1 - Math.exp(-decay * t) * (Math.cos(damped * t) + (decay / damped) * Math.sin(damped * t))\n      : 1 - Math.exp(-natural * t) * (1 + natural * t);\n\n  const points = Array.from({ length: SAMPLES + 1 }, (_, i) =>\n    i === SAMPLES ? \"1\" : position((i / SAMPLES) * seconds).toFixed(4),\n  );\n  return { duration: Math.round(seconds * 1000), easing: `linear(${points.join(\",\")})` };\n}\n\n/** A spring described by how long it takes and how much it bounces (0 to 1). */\nexport function bounceTiming(seconds: number, bounce: number): Timing {\n  const ratio = 1 - bounce;\n  const natural = SETTLED / (ratio * seconds);\n  return springTiming({ stiffness: natural * natural, damping: 2 * ratio * natural });\n}\n\nconst REDUCED_MOTION = \"(prefers-reduced-motion: reduce)\";\n\nconst subscribeToReducedMotion = (onChange: () => void) => {\n  const query = window.matchMedia(REDUCED_MOTION);\n  query.addEventListener(\"change\", onChange);\n  return () => query.removeEventListener(\"change\", onChange);\n};\n\n/** Whether the visitor asked for less motion. False during server rendering. */\nexport const usePrefersReducedMotion = () =>\n  useSyncExternalStore(\n    subscribeToReducedMotion,\n    () => window.matchMedia(REDUCED_MOTION).matches,\n    () => false,\n  );\n\n/** The same check, for code running outside render. */\nexport const prefersReducedMotion = () =>\n  typeof window !== \"undefined\" && window.matchMedia(REDUCED_MOTION).matches;\n\n/**\n * Whether an element is on screen. `amount` is the share of it that must show.\n * With `once` it stays true after the first time.\n */\nexport function useInView(\n  ref: RefObject<Element | null>,\n  { amount = 0, once = false }: { amount?: number; once?: boolean } = {},\n): boolean {\n  const [inView, setInView] = useState(false);\n\n  useEffect(() => {\n    const node = ref.current;\n    if (!node) return;\n    const observer = new IntersectionObserver(\n      ([entry]) => {\n        setInView(entry.isIntersecting);\n        if (entry.isIntersecting && once) observer.disconnect();\n      },\n      { threshold: amount },\n    );\n    observer.observe(node);\n    return () => observer.disconnect();\n  }, [ref, amount, once]);\n\n  return inView;\n}\n",
      "type": "registry:component",
      "target": "lib/animate.ts"
    }
  ],
  "docs": "Named exports: CTASection, CtaSection, and the PillarItem type. Accepts variant ('brand-pillars' | 'minimal' | 'split'), pillars array, custom heading, statement, summary metrics, and active accent color.",
  "categories": [
    "marketing",
    "layout"
  ],
  "type": "registry:component"
}
