{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "coach-scheduling",
  "title": "Coach scheduling",
  "description": "Coach scheduling is a production-ready cards component from BJORK UI, packaged for shadcn registries.",
  "dependencies": [
    "class-variance-authority",
    "clsx",
    "framer-motion",
    "lucide-react",
    "tailwind-merge",
    "web-haptics"
  ],
  "files": [
    {
      "path": "components/bjork-ui/cards/coach-scheduling-card.tsx",
      "content": "\"use client\";\n\nimport { motion, useReducedMotion, AnimatePresence } from \"framer-motion\";\nimport { useState, useRef, useEffect } from \"react\";\nimport { Check, ChevronLeft, ChevronRight, ChevronDown, Star } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport { BjorkButton } from \"@/components/bjork-ui/primitives/button\";\n\ninterface TimeSlot {\n  time: string;\n  available: boolean;\n}\n\ninterface DaySchedule {\n  date: string;\n  dayName: string;\n  dayNumber: number;\n  slots: TimeSlot[];\n  hasAvailability: boolean;\n}\n\ninterface Coach {\n  name: string;\n  title: string;\n  location: string;\n  rating: number;\n  reviewCount: number;\n  imageUrl: string;\n}\n\ninterface CoachSchedulingProps {\n  coach?: Coach;\n  locations?: string[];\n  weekSchedule?: DaySchedule[];\n  onLocationChange?: (location: string) => void;\n  onTimeSlotSelect?: (day: string, time: string) => void;\n  onWeekChange?: (direction: \"prev\" | \"next\") => void;\n  enableAnimations?: boolean;\n  className?: string;\n}\n\nconst defaultCoach: Coach = {\n  name: \"Michael Baumgardner\",\n  title: \"Tennis coach\",\n  location: \"New York\",\n  rating: 5.0,\n  reviewCount: 7,\n  imageUrl: \"https://images.unsplash.com/photo-1660463532854-f887f2a6c674\"\n};\n\nconst defaultLocations = [\n  \"Riverbank State Park Tennis Courts\",\n  \"Central Park Tennis Center\",\n  \"Brooklyn Bridge Park Courts\",\n  \"Prospect Park Tennis Center\"\n];\n\nconst defaultWeekSchedule: DaySchedule[] = [\n  {\n    date: \"Aug 17\",\n    dayName: \"Today\",\n    dayNumber: 17,\n    hasAvailability: true,\n    slots: [\n      { time: \"10:30 AM\", available: true },\n      { time: \"11:00 AM\", available: true },\n      { time: \"11:30 AM\", available: true },\n      { time: \"12:00 PM\", available: true },\n      { time: \"12:30 PM\", available: true },\n      { time: \"01:00 PM\", available: false },\n      { time: \"01:30 PM\", available: true },\n      { time: \"02:00 PM\", available: true },\n      { time: \"02:30 PM\", available: true },\n      { time: \"03:00 PM\", available: true }\n    ]\n  },\n  {\n    date: \"Aug 18\",\n    dayName: \"Tue\",\n    dayNumber: 18,\n    hasAvailability: true,\n    slots: [\n      { time: \"10:30 AM\", available: true },\n      { time: \"11:00 AM\", available: true },\n      { time: \"11:30 AM\", available: true },\n      { time: \"12:00 PM\", available: true },\n      { time: \"03:00 PM\", available: true }\n    ]\n  },\n  {\n    date: \"Aug 19\",\n    dayName: \"Wed\",\n    dayNumber: 19,\n    hasAvailability: true,\n    slots: [\n      { time: \"11:00 AM\", available: true },\n      { time: \"12:00 PM\", available: true },\n      { time: \"12:30 PM\", available: true },\n      { time: \"01:30 PM\", available: false },\n      { time: \"02:00 PM\", available: true },\n      { time: \"02:30 PM\", available: true },\n      { time: \"03:00 PM\", available: true }\n    ]\n  },\n  {\n    date: \"Aug 20\",\n    dayName: \"Thu\",\n    dayNumber: 20,\n    hasAvailability: false,\n    slots: []\n  },\n  {\n    date: \"Aug 21\",\n    dayName: \"Fri\",\n    dayNumber: 21,\n    hasAvailability: false,\n    slots: []\n  },\n  {\n    date: \"Aug 22\",\n    dayName: \"Sat\",\n    dayNumber: 22,\n    hasAvailability: false,\n    slots: []\n  }\n];\n\nexport function CoachSchedulingCard({\n  coach = defaultCoach,\n  locations = defaultLocations,\n  weekSchedule = defaultWeekSchedule,\n  onLocationChange,\n  onTimeSlotSelect,\n  onWeekChange,\n  enableAnimations = true,\n  className\n}: CoachSchedulingProps) {\n  const [selectedLocation, setSelectedLocation] = useState(locations[0]);\n  const [isLocationDropdownOpen, setIsLocationDropdownOpen] = useState(false);\n  const [weekRange] = useState(\"Aug 17 - Aug 22\");\n  const [showConfirmationView, setShowConfirmationView] = useState(false);\n  const [selectedTimeSlot, setSelectedTimeSlot] = useState<{day: string, time: string, dayName: string} | null>(null);\n  const shouldReduceMotion = useReducedMotion();\n  const shouldAnimate = enableAnimations && !shouldReduceMotion;\n  const dropdownRef = useRef<HTMLDivElement>(null);\n\n  // Close dropdown when clicking outside\n  useEffect(() => {\n    const handleClickOutside = (event: MouseEvent) => {\n      if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n        setIsLocationDropdownOpen(false);\n      }\n    };\n\n    document.addEventListener('mousedown', handleClickOutside);\n    return () => {\n      document.removeEventListener('mousedown', handleClickOutside);\n    };\n  }, []);\n\n  // Close dropdown on escape key\n  useEffect(() => {\n    const handleEscapeKey = (event: KeyboardEvent) => {\n      if (event.key === 'Escape') {\n        setIsLocationDropdownOpen(false);\n      }\n    };\n\n    document.addEventListener('keydown', handleEscapeKey);\n    return () => {\n      document.removeEventListener('keydown', handleEscapeKey);\n    };\n  }, []);\n\n  const handleLocationChange = (location: string) => {\n    setSelectedLocation(location);\n    setIsLocationDropdownOpen(false);\n    onLocationChange?.(location);\n  };\n\n  const handleTimeSlotClick = (day: string, time: string) => {\n    const dayInfo = weekSchedule.find(d => d.date === day);\n    setSelectedTimeSlot({\n      day,\n      time,\n      dayName: dayInfo?.dayName || day\n    });\n    setShowConfirmationView(true);\n    onTimeSlotSelect?.(day, time);\n  };\n\n  const handleBackToMain = () => {\n    setShowConfirmationView(false);\n    setSelectedTimeSlot(null);\n  };\n\n  const handleConfirmBooking = () => {\n    // Handle booking confirmation logic here\n    setShowConfirmationView(false);\n    setSelectedTimeSlot(null);\n  };\n\n  const handleWeekNavigation = (direction: \"prev\" | \"next\") => {\n    onWeekChange?.(direction);\n  };\n\n  // Animation variants\n  const containerVariants = {\n    hidden: { opacity: 0 },\n    visible: {\n      opacity: 1,\n      transition: {\n        staggerChildren: 0.08,\n        delayChildren: 0.1,\n      }\n    }\n  };\n\n  const itemVariants = {\n    hidden: {\n      opacity: 0,\n      x: -25,\n      scale: 0.95,\n      filter: \"blur(4px)\"\n    },\n    visible: {\n      opacity: 1,\n      x: 0,\n      scale: 1,\n      filter: \"blur(0px)\",\n      transition: {\n        type: \"spring\",\n        stiffness: 400,\n        damping: 28,\n        mass: 0.6,\n      },\n    },\n  };\n\n  const timeSlotVariants = {\n    hidden: { opacity: 0, scale: 0.8 },\n    visible: {\n      opacity: 1,\n      scale: 1,\n      transition: {\n        type: \"spring\",\n        stiffness: 400,\n        damping: 25,\n      }\n    }\n  };\n\n  return (\n    <motion.div\n      variants={shouldAnimate ? containerVariants : {}}\n      initial={shouldAnimate ? \"hidden\" : \"visible\"}\n      animate=\"visible\"\n      className={cn(\n        \"relative max-w-2xl overflow-hidden rounded-[20px] border border-[#d8d3c7] bg-[#f4f1e9] text-[#171717] shadow-[inset_0_7px_14px_rgba(255,255,255,0.62),inset_0_0.5px_0.5px_rgba(255,255,255,0.9),0_18px_34px_-16px_rgba(55,47,36,0.28)] dark:border-[#161616] dark:bg-[#121212] dark:text-[#ededed] dark:shadow-[inset_0_7px_14px_rgba(255,255,255,0.03),inset_0_0.5px_0.5px_rgba(255,255,255,0.06),0_18px_34px_-16px_rgba(0,0,0,0.9)]\",\n        className\n      )}\n    >\n      <div className=\"relative h-auto\">\n        {/* Main Content */}\n        <motion.div\n          initial={false}\n          animate={{\n            y: showConfirmationView ? \"-20px\" : \"0px\",\n            opacity: showConfirmationView ? 0.3 : 1,\n            scale: showConfirmationView ? 0.95 : 1\n          }}\n          transition={{\n            type: \"spring\",\n            stiffness: 300,\n            damping: 30,\n            mass: 0.8\n          }}\n          className=\"w-full\"\n        >\n      {/* Coach Profile Header */}\n      <motion.div\n        variants={shouldAnimate ? itemVariants : {}}\n        className=\"p-6 pb-6\"\n      >\n        <div className=\"flex items-start justify-between gap-6\">\n          {/* Left Side - Profile Image */}\n          <motion.div\n            whileHover={shouldAnimate ? {\n              scale: 1.05,\n              transition: { type: \"spring\", stiffness: 400, damping: 25 }\n            } : {}}\n            className=\"flex-shrink-0\"\n          >\n            <img\n              src={coach.imageUrl}\n              alt={coach.name}\n              className=\"w-16 h-16 rounded-lg object-cover\"\n            />\n          </motion.div>\n\n          {/* Center - Coach Info */}\n          <div className=\"flex-1 min-w-0 space-y-4\">\n            <h2 className=\"text-xl font-semibold text-foreground\">\n              {coach.name}\n            </h2>\n\n            {/* Rating and Details Row */}\n            <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n              <div className=\"flex items-center gap-1\">\n                <Star className=\"w-4 h-4 fill-[#ec5c13] text-[#ec5c13]\" />\n                <span className=\"font-medium\">{coach.rating}</span>\n                <motion.button\n                  whileHover={shouldAnimate ? {\n                    scale: 1.05,\n                    transition: { type: \"spring\", stiffness: 400, damping: 25 }\n                  } : {}}\n                  className=\"underline hover:text-foreground transition-colors\"\n                >\n                  ({coach.reviewCount} reviews)\n                </motion.button>\n              </div>\n              <span>•</span>\n              <span>{coach.title}</span>\n              <span>•</span>\n              <span>{coach.location}</span>\n            </div>\n          </div>\n\n          {/* Right Side - Pricing */}\n          <motion.div\n            initial={shouldAnimate ? {\n              opacity: 0,\n              scale: 0.8,\n              x: 20,\n              filter: \"blur(4px)\"\n            } : {}}\n            animate={shouldAnimate ? {\n              opacity: 1,\n              scale: 1,\n              x: 0,\n              filter: \"blur(0px)\"\n            } : {}}\n            transition={shouldAnimate ? {\n              type: \"spring\",\n              stiffness: 400,\n              damping: 25,\n              delay: 0.3,\n              mass: 0.6\n            } : {}}\n            className=\"text-right flex-shrink-0\"\n          >\n            <p className=\"text-xs text-muted-foreground uppercase tracking-wide mb-1\">Per Session</p>\n            <motion.p\n              className=\"text-2xl font-bold text-[#bd4514] dark:text-[#d86a2c]\"\n              initial={shouldAnimate ? { scale: 0.5 } : {}}\n              animate={shouldAnimate ? { scale: 1 } : {}}\n              transition={shouldAnimate ? {\n                type: \"spring\",\n                stiffness: 500,\n                damping: 20,\n                delay: 0.5\n              } : {}}\n            >\n              $75\n            </motion.p>\n          </motion.div>\n        </div>\n      </motion.div>\n\n      {/* Location Selector */}\n      <motion.div\n        variants={shouldAnimate ? itemVariants : {}}\n        className=\"px-6 pb-4 relative z-50\"\n        style={{ overflow: 'visible' }}\n      >\n        <label className=\"block text-sm text-muted-foreground mb-2\">\n          Choose location\n        </label>\n        <div className=\"relative z-50\" ref={dropdownRef}>\n          <motion.button\n            whileHover={shouldAnimate ? {\n              scale: 1.01,\n              transition: { type: \"spring\", stiffness: 400, damping: 25 }\n            } : {}}\n            whileTap={shouldAnimate ? { scale: 0.99 } : {}}\n            onClick={() => setIsLocationDropdownOpen(!isLocationDropdownOpen)}\n            aria-expanded={isLocationDropdownOpen}\n            aria-haspopup=\"listbox\"\n            className=\"flex w-full items-center justify-between rounded-xl border border-[#d8d3c7] bg-[#eee9df] p-3 text-[#171717] shadow-[inset_0_1px_0_rgba(255,255,255,0.72)] transition-colors hover:bg-[#e7e1d5] dark:border-[#232323] dark:bg-[#090909] dark:text-[#ededed] dark:shadow-[inset_0_1px_0_rgba(255,255,255,0.04)] dark:hover:bg-[#141414]\"\n          >\n            <span className=\"text-foreground\">{selectedLocation}</span>\n            <ChevronDown className={cn(\n              \"w-4 h-4 text-muted-foreground transition-transform\",\n              isLocationDropdownOpen && \"rotate-180\"\n            )} />\n          </motion.button>\n\n          {/* Dropdown Menu */}\n          <AnimatePresence>\n            {isLocationDropdownOpen && (\n              <motion.div\n                initial={shouldAnimate ? { opacity: 0, y: -10, scale: 0.95 } : {}}\n                animate={shouldAnimate ? { opacity: 1, y: 0, scale: 1 } : {}}\n                exit={shouldAnimate ? { opacity: 0, y: -10, scale: 0.95 } : {}}\n                transition={shouldAnimate ? { type: \"spring\", stiffness: 400, damping: 25 } : {}}\n                className=\"absolute left-0 right-0 top-full z-[9999] mt-2 overflow-hidden rounded-xl border border-[#d8d3c7] bg-[#f4f1e9] shadow-[0_18px_34px_-18px_rgba(55,47,36,0.32)] dark:border-[#232323] dark:bg-[#121212] dark:shadow-[0_18px_34px_-18px_rgba(0,0,0,0.9)]\"\n                role=\"listbox\"\n              >\n                {locations.map((location, index) => (\n                  <motion.button\n                    key={location}\n                    initial={shouldAnimate ? { opacity: 0, x: -10 } : {}}\n                    animate={shouldAnimate ? { opacity: 1, x: 0 } : {}}\n                    transition={shouldAnimate ? { delay: index * 0.05 } : {}}\n                    whileHover={shouldAnimate ? {\n                      backgroundColor: \"hsl(var(--muted))\",\n                      transition: { duration: 0.15 }\n                    } : {}}\n                    onClick={() => handleLocationChange(location)}\n                    role=\"option\"\n                    aria-selected={location === selectedLocation}\n                    className=\"w-full p-3 text-left text-[#171717] transition-colors hover:bg-[#eee9df] dark:text-[#ededed] dark:hover:bg-[#191919]\"\n                  >\n                    {location}\n                  </motion.button>\n                ))}\n              </motion.div>\n            )}\n          </AnimatePresence>\n        </div>\n      </motion.div>\n\n      {/* Separator */}\n      <motion.div\n        variants={shouldAnimate ? itemVariants : {}}\n        className=\"mx-6 border-t border-border/50\"\n      />\n\n      {/* Week Navigation */}\n      <motion.div\n        variants={shouldAnimate ? itemVariants : {}}\n        className=\"p-6 pb-4\"\n      >\n        <div className=\"flex items-center justify-between\">\n                     <motion.button\n             whileHover={shouldAnimate ? {\n               scale: 1.05,\n               transition: { type: \"spring\", stiffness: 400, damping: 25 }\n             } : {}}\n             whileTap={shouldAnimate ? { scale: 0.95 } : {}}\n             onClick={() => handleWeekNavigation(\"prev\")}\n             aria-label=\"Previous week\"\n             className=\"rounded-lg border border-transparent p-2 text-[#171717]/55 transition-colors hover:border-[#d8d3c7] hover:bg-[#eee9df] hover:text-[#171717] focus:outline-none focus:ring-2 focus:ring-[#ec5c13]/45 dark:text-[#ededed]/45 dark:hover:border-[#232323] dark:hover:bg-[#141414] dark:hover:text-[#ededed]\"\n           >\n             <ChevronLeft className=\"w-5 h-5 text-muted-foreground\" />\n           </motion.button>\n\n          <h3 className=\"font-semibold text-foreground\">\n            {weekRange}\n          </h3>\n\n                     <motion.button\n             whileHover={shouldAnimate ? {\n               scale: 1.05,\n               transition: { type: \"spring\", stiffness: 400, damping: 25 }\n             } : {}}\n             whileTap={shouldAnimate ? { scale: 0.95 } : {}}\n             onClick={() => handleWeekNavigation(\"next\")}\n             aria-label=\"Next week\"\n             className=\"rounded-lg border border-transparent p-2 text-[#171717]/55 transition-colors hover:border-[#d8d3c7] hover:bg-[#eee9df] hover:text-[#171717] focus:outline-none focus:ring-2 focus:ring-[#ec5c13]/45 dark:text-[#ededed]/45 dark:hover:border-[#232323] dark:hover:bg-[#141414] dark:hover:text-[#ededed]\"\n           >\n             <ChevronRight className=\"w-5 h-5 text-muted-foreground\" />\n           </motion.button>\n        </div>\n      </motion.div>\n\n      {/* Daily Schedule */}\n      <motion.div\n        variants={shouldAnimate ? itemVariants : {}}\n        className=\"px-6 pb-6 space-y-4\"\n      >\n        {weekSchedule.map((day) => (\n          <motion.div\n            key={day.date}\n            variants={shouldAnimate ? itemVariants : {}}\n            className=\"space-y-3\"\n          >\n            {/* Day Header */}\n            <div className=\"flex items-center justify-between\">\n              <div>\n                <h4 className=\"font-medium text-foreground\">\n                  {day.dayName}, {day.date}\n                </h4>\n              </div>\n              {!day.hasAvailability && (\n                <span className=\"text-sm text-muted-foreground\">\n                  No Availability\n                </span>\n              )}\n            </div>\n\n            {/* Time Slots */}\n            {day.hasAvailability && (\n              <motion.div\n                variants={shouldAnimate ? containerVariants : {}}\n                className=\"flex flex-wrap gap-2\"\n              >\n                {day.slots.map((slot) => (\n                                     <motion.button\n                     key={`${day.date}-${slot.time}`}\n                     variants={shouldAnimate ? timeSlotVariants : {}}\n                     whileHover={shouldAnimate && slot.available ? {\n                       scale: 1.05,\n                       y: -2,\n                       transition: { type: \"spring\", stiffness: 400, damping: 25 }\n                     } : {}}\n                     whileTap={shouldAnimate && slot.available ? { scale: 0.98 } : {}}\n                     onClick={() => slot.available && handleTimeSlotClick(day.date, slot.time)}\n                     disabled={!slot.available}\n                     aria-label={`${slot.available ? 'Book' : 'Unavailable'} time slot at ${slot.time} on ${day.dayName}, ${day.date}`}\n                     className={cn(\n                       \"px-3 py-1.5 text-sm rounded-lg border transition-colors focus:outline-none focus:ring-2 focus:ring-primary/50\",\n                       slot.available\n                         ? \"cursor-pointer border-[#d8d3c7] bg-[#f4f1e9] text-[#171717] shadow-[inset_0_1px_0_rgba(255,255,255,0.72)] hover:border-[#ec5c13]/35 hover:bg-[#eee9df] dark:border-[#232323] dark:bg-[#090909] dark:text-[#ededed] dark:shadow-[inset_0_1px_0_rgba(255,255,255,0.04)] dark:hover:border-[#ec5c13]/35 dark:hover:bg-[#141414]\"\n                         : \"cursor-not-allowed border-[#d8d3c7]/70 bg-[#eee9df]/60 text-[#171717]/36 opacity-60 dark:border-[#232323]/70 dark:bg-[#090909]/50 dark:text-[#ededed]/28\"\n                     )}\n                   >\n                     {slot.time}\n                   </motion.button>\n                ))}\n              </motion.div>\n            )}\n          </motion.div>\n        ))}\n      </motion.div>\n\n      {/* Bottom Actions */}\n      <motion.div\n        variants={shouldAnimate ? itemVariants : {}}\n        className=\"border-t border-border/50 p-6\"\n      >\n                 <div className=\"flex gap-3\">\n           <BjorkButton variant=\"outline\" className=\"flex-1\">\n             Cancel\n           </BjorkButton>\n           <BjorkButton variant=\"accent\" className=\"flex-1\">\n             Next\n           </BjorkButton>\n         </div>\n        </motion.div>\n        </motion.div>\n\n        {/* Confirmation View */}\n        <motion.div\n          initial={false}\n          animate={{\n            y: showConfirmationView ? \"0%\" : \"100%\",\n            opacity: showConfirmationView ? 1 : 0\n          }}\n          transition={{\n            type: \"spring\",\n            stiffness: 300,\n            damping: 30,\n            mass: 0.8\n          }}\n          className=\"absolute left-0 top-0 h-full w-full bg-[#f4f1e9] dark:bg-[#121212]\"\n        >\n          <div className=\"p-6 space-y-6\">\n            {/* Header with back button */}\n            <div className=\"flex items-center justify-between\">\n              <motion.button\n                whileHover={{ scale: 1.05 }}\n                whileTap={{ scale: 0.95 }}\n                onClick={handleBackToMain}\n                className=\"flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors\"\n              >\n                <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                  <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M15 19l-7-7 7-7\" />\n                </svg>\n                <span className=\"text-sm font-medium\">Back</span>\n              </motion.button>\n              <h3 className=\"text-lg font-semibold text-foreground\">Confirm Booking</h3>\n              <div></div> {/* Spacer for centering */}\n            </div>\n\n            {/* Coach info summary */}\n            <div className=\"flex items-center gap-4 rounded-xl border border-[#d8d3c7] bg-[#eee9df] p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.72)] dark:border-[#232323] dark:bg-[#090909] dark:shadow-[inset_0_1px_0_rgba(255,255,255,0.04)]\">\n              <img\n                src={coach.imageUrl}\n                alt={coach.name}\n                className=\"w-12 h-12 rounded-lg object-cover\"\n              />\n              <div>\n                <h4 className=\"font-semibold text-foreground\">{coach.name}</h4>\n                <p className=\"text-sm text-muted-foreground\">{coach.title}</p>\n              </div>\n            </div>\n\n            {/* Booking details */}\n            {selectedTimeSlot && (\n              <div className=\"space-y-4\">\n                <div className=\"text-center\">\n                  <p className=\"text-sm text-muted-foreground uppercase tracking-wide mb-2\">Your Booking</p>\n                  <div className=\"rounded-xl border border-[#d8d3c7] bg-[#eee9df] p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.72)] dark:border-[#232323] dark:bg-[#090909] dark:shadow-[inset_0_1px_0_rgba(255,255,255,0.04)]\">\n                    <p className=\"text-lg font-semibold text-foreground\">\n                      {selectedTimeSlot.dayName}, {selectedTimeSlot.day}\n                    </p>\n                    <p className=\"text-xl font-bold text-[#bd4514] dark:text-[#d86a2c]\">\n                      {selectedTimeSlot.time}\n                    </p>\n                  </div>\n                </div>\n\n                <div className=\"space-y-3\">\n                  <div className=\"flex justify-between items-center py-2\">\n                    <span className=\"text-muted-foreground\">Location:</span>\n                    <span className=\"text-foreground font-medium\">{selectedLocation}</span>\n                  </div>\n                  <div className=\"flex justify-between items-center py-2\">\n                    <span className=\"text-muted-foreground\">Duration:</span>\n                    <span className=\"text-foreground font-medium\">1 hour</span>\n                  </div>\n                  <div className=\"flex justify-between items-center py-2\">\n                    <span className=\"text-muted-foreground\">Price:</span>\n                    <span className=\"text-foreground font-medium\">$75</span>\n                  </div>\n                </div>\n              </div>\n            )}\n\n            {/* Confirm button */}\n            <BjorkButton\n              variant=\"accent\"\n              size=\"lg\"\n              onClick={handleConfirmBooking}\n              className=\"w-full\"\n            >\n              Confirm Booking\n              <Check className=\"w-4 h-4\" />\n            </BjorkButton>\n          </div>\n        </motion.div>\n      </div>\n    </motion.div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/bjork-ui/cards/coach-scheduling-card.tsx"
    },
    {
      "path": "lib/utils.ts",
      "content": "import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs))\n}\n",
      "type": "registry:lib",
      "target": "@lib/utils.ts"
    },
    {
      "path": "components/bjork-ui/primitives/button.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { motion, useReducedMotion, type HTMLMotionProps } from \"framer-motion\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { useWebHaptics } from \"web-haptics/react\";\nimport type { HapticInput, TriggerOptions } from \"web-haptics\";\nimport { cn } from \"@/lib/utils\";\n\nconst bjorkButtonVariants = cva(\n  \"inline-flex shrink-0 transform-gpu items-center justify-center gap-2 whitespace-nowrap rounded-[13px] text-sm font-medium tracking-[-0.01em] outline-none backdrop-blur-md transition-[background-color,border-color,color,box-shadow,opacity] duration-150 ease-out focus-visible:ring-2 focus-visible:ring-[#ec5c13]/45 focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--bjork-ring-offset)] disabled:pointer-events-none disabled:opacity-35 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n  {\n    variants: {\n      variant: {\n        default:\n          \"border border-transparent bg-[var(--bjork-field)] text-[color:var(--bjork-text)] shadow-[var(--bjork-shadow-surface)] hover:bg-[var(--bjork-surface-hover)]\",\n        secondary:\n          \"border border-[color:var(--bjork-border)] bg-[var(--bjork-surface-active)] text-[color:var(--bjork-text)] shadow-[var(--bjork-shadow-soft)] hover:bg-[var(--bjork-surface-hover)]\",\n        outline:\n          \"border border-[color:var(--bjork-border)] bg-[var(--bjork-field-inset)] text-[color:var(--bjork-text-strong)] shadow-[var(--bjork-shadow-soft)] hover:border-[color:var(--bjork-border-strong)] hover:bg-[var(--bjork-surface)] hover:text-[color:var(--bjork-text)]\",\n        ghost:\n          \"border border-transparent bg-transparent text-[color:var(--bjork-text-muted)] hover:bg-[var(--bjork-surface-hover)] hover:text-[color:var(--bjork-text)]\",\n        quiet:\n          \"border border-transparent bg-[var(--bjork-field-muted)] text-[color:var(--bjork-text-soft)] hover:bg-[var(--bjork-surface-hover)] hover:text-[color:var(--bjork-text-medium)]\",\n        accent:\n          \"bjork-layered-button-accent tracking-tight text-white\",\n        raised:\n          \"bjork-layered-button-raised tracking-tight\",\n      },\n      size: {\n        sm: \"h-8 px-3 text-xs\",\n        md: \"h-[45px] px-4\",\n        lg: \"h-12 px-5 text-[15px]\",\n        icon: \"size-[45px] p-0\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"md\",\n    },\n  },\n);\n\nexport interface BjorkButtonProps\n  extends Omit<HTMLMotionProps<\"button\">, \"ref\" | \"size\">,\n    VariantProps<typeof bjorkButtonVariants> {\n  haptics?: boolean;\n  hapticPattern?: HapticInput;\n  hapticOptions?: TriggerOptions;\n}\n\nexport function BjorkButton({\n  className,\n  variant,\n  size,\n  disabled,\n  haptics = true,\n  hapticPattern = \"light\",\n  hapticOptions,\n  onPointerDown,\n  ...props\n}: BjorkButtonProps) {\n  const shouldReduceMotion = useReducedMotion();\n  const { trigger } = useWebHaptics();\n\n  const handlePointerDown = React.useCallback(\n    (event: React.PointerEvent<HTMLButtonElement>) => {\n      if (!disabled && haptics) {\n        void trigger(hapticPattern, hapticOptions);\n      }\n\n      onPointerDown?.(event);\n    },\n    [disabled, hapticOptions, hapticPattern, haptics, onPointerDown, trigger],\n  );\n\n  return (\n    <motion.button\n      data-slot=\"bjork-button\"\n      className={cn(bjorkButtonVariants({ variant, size, className }))}\n      disabled={disabled}\n      onPointerDown={handlePointerDown}\n      whileHover={disabled || shouldReduceMotion ? undefined : { scale: 1.05 }}\n      whileTap={disabled || shouldReduceMotion ? undefined : { scale: 0.95 }}\n      transition={{ type: \"spring\", stiffness: 400, damping: 25 }}\n      {...props}\n    />\n  );\n}\n\nexport { bjorkButtonVariants };\n",
      "type": "registry:component",
      "target": "@components/bjork-ui/primitives/button.tsx"
    }
  ],
  "cssVars": {
    "light": {
      "bjork-bg": "#f7f3ea",
      "bjork-surface": "#fffcf6",
      "bjork-surface-muted": "rgba(250, 246, 237, 0.94)",
      "bjork-surface-hover": "#f5efe3",
      "bjork-surface-active": "#efe7d8",
      "bjork-panel": "#f8f2e7",
      "bjork-menu": "rgba(255, 252, 246, 0.98)",
      "bjork-field": "rgba(255, 252, 246, 0.96)",
      "bjork-field-muted": "rgba(248, 242, 231, 0.82)",
      "bjork-field-inset": "rgba(239, 231, 216, 0.72)",
      "bjork-error-bg": "#fff0e8",
      "bjork-text": "#171717",
      "bjork-text-strong": "rgba(23, 23, 23, 0.9)",
      "bjork-text-medium": "rgba(23, 23, 23, 0.72)",
      "bjork-text-muted": "rgba(23, 23, 23, 0.52)",
      "bjork-text-soft": "rgba(23, 23, 23, 0.36)",
      "bjork-text-faint": "rgba(23, 23, 23, 0.22)",
      "bjork-inverted-text": "#fff8f0",
      "bjork-border": "#eee6db",
      "bjork-border-muted": "#f5ede2",
      "bjork-border-strong": "#e1d7c8",
      "bjork-thumb": "#a49b8e",
      "bjork-track": "#ded6ca",
      "bjork-accent": "#ec7d43",
      "bjork-accent-hover": "#f0935f",
      "bjork-accent-soft": "rgba(236, 125, 67, 0.1)",
      "bjork-accent-muted": "rgba(236, 125, 67, 0.2)",
      "bjork-accent-badge": "rgba(236, 125, 67, 0.16)",
      "bjork-accent-badge-foreground": "#8d421d",
      "bjork-accent-foreground": "#3f2112",
      "bjork-shadow-surface": "inset 0 7px 14px rgba(88, 72, 49, 0.045), inset 0 0.5px 0.5px rgba(255, 255, 255, 0.92), inset 1px 0 0 rgba(88, 72, 49, 0.026), inset -1px 0 0 rgba(255, 255, 255, 0.68), 0 14px 22px -9px rgba(66, 52, 33, 0.11)",
      "bjork-shadow-soft": "inset 0 1px 0 rgba(88, 72, 49, 0.045), inset 0 0.5px 0.5px rgba(255, 255, 255, 0.86)",
      "bjork-shadow-panel": "inset 0 1px 0 rgba(88, 72, 49, 0.04), inset 1px 0 0 rgba(88, 72, 49, 0.024), inset -1px 0 0 rgba(255, 255, 255, 0.64), 0 18px 38px -30px rgba(66, 52, 33, 0.2)",
      "bjork-shadow-menu": "inset 0 1px 0 rgba(88, 72, 49, 0.04), inset 0 12px 24px rgba(88, 72, 49, 0.022), inset 1px 0 0 rgba(88, 72, 49, 0.024), inset -1px 0 0 rgba(255, 255, 255, 0.62), 0 22px 44px -26px rgba(66, 52, 33, 0.22)",
      "bjork-shadow-inset": "inset 0 1px 10px rgba(88, 72, 49, 0.12), inset 0 0.5px 0.5px rgba(255, 255, 255, 0.72)",
      "bjork-ring-offset": "#f7f3ea"
    },
    "dark": {
      "bjork-bg": "#111111",
      "bjork-surface": "#121212",
      "bjork-surface-muted": "rgba(22, 22, 22, 0.92)",
      "bjork-surface-hover": "#161616",
      "bjork-surface-active": "#202020",
      "bjork-panel": "#0d0d0d",
      "bjork-menu": "rgba(18, 18, 18, 0.98)",
      "bjork-field": "rgba(18, 18, 18, 0.95)",
      "bjork-field-muted": "rgba(18, 18, 18, 0.55)",
      "bjork-field-inset": "rgba(9, 9, 9, 0.75)",
      "bjork-error-bg": "#130d0a",
      "bjork-text": "#ededed",
      "bjork-text-strong": "rgba(237, 237, 237, 0.9)",
      "bjork-text-medium": "rgba(237, 237, 237, 0.72)",
      "bjork-text-muted": "rgba(237, 237, 237, 0.52)",
      "bjork-text-soft": "rgba(237, 237, 237, 0.36)",
      "bjork-text-faint": "rgba(237, 237, 237, 0.22)",
      "bjork-inverted-text": "#080808",
      "bjork-border": "#232323",
      "bjork-border-muted": "#1c1c1c",
      "bjork-border-strong": "#343434",
      "bjork-thumb": "#626262",
      "bjork-track": "#090909",
      "bjork-accent": "#ec5c13",
      "bjork-accent-hover": "#f07832",
      "bjork-accent-soft": "rgba(236, 92, 19, 0.12)",
      "bjork-accent-muted": "rgba(236, 92, 19, 0.24)",
      "bjork-accent-badge": "#ec5c13",
      "bjork-accent-badge-foreground": "#fff2ea",
      "bjork-accent-foreground": "#fff2ea",
      "bjork-shadow-surface": "inset 0 7px 14px rgba(255, 255, 255, 0.03), inset 0 0.5px 0.5px rgba(255, 255, 255, 0.06), 0 14px 20px -6px rgba(0, 0, 0, 0.45)",
      "bjork-shadow-soft": "inset 0 1px 0 rgba(255, 255, 255, 0.045)",
      "bjork-shadow-panel": "inset 0 1px 0 rgba(255, 255, 255, 0.035), 0 18px 36px -28px rgba(0, 0, 0, 0.9)",
      "bjork-shadow-menu": "inset 0 1px 0 rgba(255, 255, 255, 0.055), inset 0 12px 24px rgba(255, 255, 255, 0.018), inset 0 -18px 26px rgba(0, 0, 0, 0.24), 0 22px 42px -22px rgba(0, 0, 0, 0.9)",
      "bjork-shadow-inset": "inset 0 1px 10px rgba(0, 0, 0, 0.45)",
      "bjork-ring-offset": "#080808"
    }
  },
  "css": {
    ".bjork-range": {
      "box-shadow": "inset 0 1px 1px rgba(255, 255, 255, 0.04), 0 0 14px rgba(189, 69, 20, 0.06)"
    },
    ".bjork-range::-webkit-slider-thumb": {
      "appearance": "none",
      "width": "16px",
      "height": "8px",
      "border": "0",
      "border-radius": "999px",
      "background": "var(--bjork-thumb)",
      "box-shadow": "inset 0 1px 0 rgba(255, 255, 255, 0.18), 0 0 0 1px rgba(0, 0, 0, 0.42)"
    },
    ".bjork-range::-moz-range-thumb": {
      "width": "16px",
      "height": "8px",
      "border": "0",
      "border-radius": "999px",
      "background": "var(--bjork-thumb)",
      "box-shadow": "inset 0 1px 0 rgba(255, 255, 255, 0.18), 0 0 0 1px rgba(0, 0, 0, 0.42)"
    },
    ".bjork-layered-button-accent, .bjork-layered-button-raised": {
      "position": "relative",
      "isolation": "isolate",
      "appearance": "none",
      "overflow": "hidden",
      "border": "0",
      "border-radius": "13px",
      "box-shadow": "inset 0 1px 0 #ffffff38, 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a",
      "transition": "filter 150ms ease-out, box-shadow 150ms ease-out"
    },
    ".bjork-layered-button-accent::after, .bjork-layered-button-raised::after": {
      "position": "absolute",
      "inset": "0",
      "pointer-events": "none",
      "content": "\"\"",
      "border": "1.5px solid transparent",
      "border-radius": "inherit",
      "background": "linear-gradient(#ffffffb8, #0000003d 41% 75%, #ffffff47) border-box",
      "mix-blend-mode": "overlay",
      "-webkit-mask-image": "linear-gradient(#000, #000), linear-gradient(#000, #000)",
      "mask-image": "linear-gradient(#000, #000), linear-gradient(#000, #000)",
      "-webkit-mask-clip": "padding-box, border-box",
      "mask-clip": "padding-box, border-box",
      "-webkit-mask-origin": "padding-box, border-box",
      "mask-origin": "padding-box, border-box",
      "-webkit-mask-composite": "xor",
      "mask-composite": "exclude"
    },
    ".bjork-layered-button-accent": {
      "color": "oklch(100% 0 0)",
      "text-shadow": "0 -1px #00000040",
      "background": "radial-gradient(ellipse at -20px top, rgba(255, 255, 255, 0.14), transparent), linear-gradient(180deg, #ec5c13 0%, #ec5c13 62%, color-mix(in oklab, #ec5c13 84%, black 16%) 100%)"
    },
    ".bjork-layered-button-raised": {
      "color": "oklch(100% 0 0)",
      "text-shadow": "0 -1px #00000026",
      "background": "radial-gradient(ellipse at -20px top, #ffffff38, #fff0), linear-gradient(180deg, oklch(0.1881 0.006 265), oklch(0.5493 0.0081 265))"
    },
    ".dark .bjork-layered-button-raised": {
      "color": "oklch(0.2574 0.0056 265)",
      "text-shadow": "0 -1px #00000026",
      "background": "radial-gradient(ellipse at -20px top, #ffffff38, #fff0), linear-gradient(180deg, color-mix(in oklab, oklch(0.9674 0.0013 265) 80%, oklch(0.2574 0.0056 265) 20%), color-mix(in oklab, oklch(0.669 0.0107 265) 80%, oklch(0.2574 0.0056 265) 20%))"
    },
    "@media (hover: hover)": {
      ".bjork-layered-button-accent:hover": {
        "filter": "brightness(1.08) saturate(1.04)"
      },
      ".bjork-layered-button-raised:hover": {
        "filter": "brightness(1.08) contrast(0.9)"
      }
    }
  },
  "meta": {
    "collection": "Cards",
    "registryUrl": "https://ui.isaiahbjork.com/coach-scheduling.json",
    "installCommand": "npx shadcn@latest add https://ui.isaiahbjork.com/coach-scheduling.json"
  },
  "type": "registry:component"
}