@flamingo-stack/openframe-frontend-core 0.0.561-1852.4997.2 → 0.0.561

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/dist/{chunk-KGFCHGCW.js → chunk-44U25GD5.js} +2 -2
  2. package/dist/{chunk-DCB4EBA5.cjs → chunk-4LHEQMOV.cjs} +3 -3
  3. package/dist/chunk-4LHEQMOV.cjs.map +1 -0
  4. package/dist/{chunk-PYN65LCP.js → chunk-5AEHCGQV.js} +6 -6
  5. package/dist/{chunk-K4UCFJNL.cjs → chunk-AGQ4VTUX.cjs} +29 -29
  6. package/dist/{chunk-K4UCFJNL.cjs.map → chunk-AGQ4VTUX.cjs.map} +1 -1
  7. package/dist/{chunk-HI3A5HFI.js → chunk-B6TM3QAL.js} +4 -4
  8. package/dist/{chunk-NISIZABN.cjs → chunk-CR2CMLTQ.cjs} +4 -4
  9. package/dist/{chunk-NISIZABN.cjs.map → chunk-CR2CMLTQ.cjs.map} +1 -1
  10. package/dist/{chunk-K535WIFS.cjs → chunk-D3OY4CJU.cjs} +3 -3
  11. package/dist/{chunk-K535WIFS.cjs.map → chunk-D3OY4CJU.cjs.map} +1 -1
  12. package/dist/{chunk-RVHAX7VP.cjs → chunk-DAVVVBNL.cjs} +26 -26
  13. package/dist/{chunk-RVHAX7VP.cjs.map → chunk-DAVVVBNL.cjs.map} +1 -1
  14. package/dist/{chunk-V5VJTNOG.cjs → chunk-DHRJFUBR.cjs} +11 -11
  15. package/dist/{chunk-V5VJTNOG.cjs.map → chunk-DHRJFUBR.cjs.map} +1 -1
  16. package/dist/{chunk-LNWLK4TC.cjs → chunk-EK346HD7.cjs} +39 -39
  17. package/dist/{chunk-LNWLK4TC.cjs.map → chunk-EK346HD7.cjs.map} +1 -1
  18. package/dist/{chunk-SKQ7AMUD.cjs → chunk-FSWJCGRJ.cjs} +7 -7
  19. package/dist/{chunk-SKQ7AMUD.cjs.map → chunk-FSWJCGRJ.cjs.map} +1 -1
  20. package/dist/{chunk-3IBCKS3I.js → chunk-GKGQ4DWE.js} +121 -49
  21. package/dist/chunk-GKGQ4DWE.js.map +1 -0
  22. package/dist/{chunk-XTDUFU5O.cjs → chunk-GWRQ5EEI.cjs} +954 -882
  23. package/dist/{chunk-XTDUFU5O.cjs.map → chunk-GWRQ5EEI.cjs.map} +1 -1
  24. package/dist/{chunk-IJ6HBN2J.cjs → chunk-GYC3OTBI.cjs} +9 -9
  25. package/dist/{chunk-IJ6HBN2J.cjs.map → chunk-GYC3OTBI.cjs.map} +1 -1
  26. package/dist/{chunk-SFU6A36Q.js → chunk-JCQUWIPT.js} +3 -3
  27. package/dist/{chunk-NZNKHIFA.js → chunk-KRYLVEXU.js} +2 -2
  28. package/dist/{chunk-LSWJ5QYP.js → chunk-KWNVQZ5Y.js} +4 -4
  29. package/dist/{chunk-ZD5QQJH4.js → chunk-MLSDBNKT.js} +2 -2
  30. package/dist/{chunk-NTTSXF7Z.js → chunk-NGY4KET4.js} +2 -2
  31. package/dist/{chunk-O45VULK4.js → chunk-OCJG3X4U.js} +2 -2
  32. package/dist/{chunk-KKIAKLI6.js → chunk-ONDI64M3.js} +2 -2
  33. package/dist/{chunk-FMQO7OMJ.js → chunk-QGQ26NEE.js} +2 -2
  34. package/dist/{chunk-RSZXB6J2.js → chunk-RDKJCZHY.js} +3 -3
  35. package/dist/chunk-RDKJCZHY.js.map +1 -0
  36. package/dist/{chunk-ELFXMLSX.cjs → chunk-SUPBFRUK.cjs} +5 -5
  37. package/dist/{chunk-ELFXMLSX.cjs.map → chunk-SUPBFRUK.cjs.map} +1 -1
  38. package/dist/{chunk-BODITFZI.cjs → chunk-UJ7MSY6F.cjs} +31 -31
  39. package/dist/{chunk-BODITFZI.cjs.map → chunk-UJ7MSY6F.cjs.map} +1 -1
  40. package/dist/{chunk-INN323YY.cjs → chunk-UV53Z64O.cjs} +14 -14
  41. package/dist/{chunk-INN323YY.cjs.map → chunk-UV53Z64O.cjs.map} +1 -1
  42. package/dist/{chunk-TCQ3VL5R.cjs → chunk-WCM4BGHO.cjs} +97 -97
  43. package/dist/{chunk-TCQ3VL5R.cjs.map → chunk-WCM4BGHO.cjs.map} +1 -1
  44. package/dist/{chunk-DQI2S2SY.js → chunk-YUAKZNK5.js} +6 -6
  45. package/dist/{chunk-5XPYQ7NU.cjs → chunk-ZENRHE55.cjs} +62 -62
  46. package/dist/{chunk-5XPYQ7NU.cjs.map → chunk-ZENRHE55.cjs.map} +1 -1
  47. package/dist/{chunk-CLQ6L4JG.js → chunk-ZJ7RI62F.js} +2 -2
  48. package/dist/components/calendar.cjs +81 -4
  49. package/dist/components/calendar.cjs.map +1 -1
  50. package/dist/components/calendar.js +81 -4
  51. package/dist/components/calendar.js.map +1 -1
  52. package/dist/components/case-studies/index.cjs +9 -9
  53. package/dist/components/case-studies/index.js +3 -3
  54. package/dist/components/chat/index.cjs +3 -3
  55. package/dist/components/chat/index.js +2 -2
  56. package/dist/components/contact/index.cjs +4 -4
  57. package/dist/components/contact/index.js +3 -3
  58. package/dist/components/docs/index.cjs +6 -6
  59. package/dist/components/docs/index.js +5 -5
  60. package/dist/components/embeds/index.cjs +4 -4
  61. package/dist/components/embeds/index.js +3 -3
  62. package/dist/components/faq/index.cjs +4 -4
  63. package/dist/components/faq/index.js +3 -3
  64. package/dist/components/features/index.cjs +5 -3
  65. package/dist/components/features/index.cjs.map +1 -1
  66. package/dist/components/features/index.d.ts +1 -1
  67. package/dist/components/features/index.d.ts.map +1 -1
  68. package/dist/components/features/index.js +4 -2
  69. package/dist/components/features/use-walkthrough-video.d.ts +1 -1
  70. package/dist/components/features/use-walkthrough-video.d.ts.map +1 -1
  71. package/dist/components/features/{floating-walkthrough-video.d.ts → walkthrough-video.d.ts} +61 -7
  72. package/dist/components/features/walkthrough-video.d.ts.map +1 -0
  73. package/dist/components/help-center-pages/index.cjs +23 -23
  74. package/dist/components/help-center-pages/index.js +14 -14
  75. package/dist/components/index.cjs +144 -140
  76. package/dist/components/index.cjs.map +1 -1
  77. package/dist/components/index.js +16 -12
  78. package/dist/components/index.js.map +1 -1
  79. package/dist/components/meeting-scheduler/booking-form.d.ts.map +1 -1
  80. package/dist/components/meeting-scheduler/context-panel.d.ts +24 -6
  81. package/dist/components/meeting-scheduler/context-panel.d.ts.map +1 -1
  82. package/dist/components/meeting-scheduler/index.cjs +529 -425
  83. package/dist/components/meeting-scheduler/index.cjs.map +1 -1
  84. package/dist/components/meeting-scheduler/index.d.ts +54 -1
  85. package/dist/components/meeting-scheduler/index.d.ts.map +1 -1
  86. package/dist/components/meeting-scheduler/index.js +507 -403
  87. package/dist/components/meeting-scheduler/index.js.map +1 -1
  88. package/dist/components/meeting-scheduler/slot-picker.d.ts +35 -31
  89. package/dist/components/meeting-scheduler/slot-picker.d.ts.map +1 -1
  90. package/dist/components/navigation/index.cjs +3 -3
  91. package/dist/components/navigation/index.js +2 -2
  92. package/dist/components/onboarding-guides/index.cjs +6 -6
  93. package/dist/components/onboarding-guides/index.js +5 -5
  94. package/dist/components/related-content/index.cjs +4 -4
  95. package/dist/components/related-content/index.js +3 -3
  96. package/dist/components/tickets/index.cjs +7 -7
  97. package/dist/components/tickets/index.js +6 -6
  98. package/dist/components/ui/date-picker.d.ts +62 -1
  99. package/dist/components/ui/date-picker.d.ts.map +1 -1
  100. package/dist/components/ui/field-wrapper.d.ts +7 -0
  101. package/dist/components/ui/field-wrapper.d.ts.map +1 -1
  102. package/dist/components/ui/file-manager/index.cjs +36 -36
  103. package/dist/components/ui/file-manager/index.js +1 -1
  104. package/dist/components/ui/index.cjs +5 -3
  105. package/dist/components/ui/index.cjs.map +1 -1
  106. package/dist/components/ui/index.js +4 -2
  107. package/dist/hooks/use-meeting-booking.d.ts +31 -8
  108. package/dist/hooks/use-meeting-booking.d.ts.map +1 -1
  109. package/dist/index.cjs +7 -3
  110. package/dist/index.cjs.map +1 -1
  111. package/dist/index.js +6 -2
  112. package/package.json +1 -1
  113. package/src/components/features/{.floating-walkthrough-video.md → .walkthrough-video.md} +8 -5
  114. package/src/components/features/board/types.ts +1 -1
  115. package/src/components/features/index.ts +1 -1
  116. package/src/components/features/use-walkthrough-video.ts +1 -1
  117. package/src/components/features/{floating-walkthrough-video.tsx → walkthrough-video.tsx} +108 -22
  118. package/src/components/meeting-scheduler/booking-form.tsx +60 -51
  119. package/src/components/meeting-scheduler/confirmation.tsx +1 -1
  120. package/src/components/meeting-scheduler/context-panel.tsx +166 -68
  121. package/src/components/meeting-scheduler/index.tsx +227 -81
  122. package/src/components/meeting-scheduler/slot-picker.tsx +275 -127
  123. package/src/components/ui/date-picker.tsx +200 -47
  124. package/src/components/ui/field-wrapper.tsx +9 -2
  125. package/src/hooks/use-meeting-booking.ts +97 -73
  126. package/dist/chunk-3IBCKS3I.js.map +0 -1
  127. package/dist/chunk-7J4DGFHZ.js +0 -88
  128. package/dist/chunk-7J4DGFHZ.js.map +0 -1
  129. package/dist/chunk-DCB4EBA5.cjs.map +0 -1
  130. package/dist/chunk-MKPJYEOR.cjs +0 -88
  131. package/dist/chunk-MKPJYEOR.cjs.map +0 -1
  132. package/dist/chunk-RSZXB6J2.js.map +0 -1
  133. package/dist/components/features/floating-walkthrough-video.d.ts.map +0 -1
  134. /package/dist/{chunk-KGFCHGCW.js.map → chunk-44U25GD5.js.map} +0 -0
  135. /package/dist/{chunk-PYN65LCP.js.map → chunk-5AEHCGQV.js.map} +0 -0
  136. /package/dist/{chunk-HI3A5HFI.js.map → chunk-B6TM3QAL.js.map} +0 -0
  137. /package/dist/{chunk-SFU6A36Q.js.map → chunk-JCQUWIPT.js.map} +0 -0
  138. /package/dist/{chunk-NZNKHIFA.js.map → chunk-KRYLVEXU.js.map} +0 -0
  139. /package/dist/{chunk-LSWJ5QYP.js.map → chunk-KWNVQZ5Y.js.map} +0 -0
  140. /package/dist/{chunk-ZD5QQJH4.js.map → chunk-MLSDBNKT.js.map} +0 -0
  141. /package/dist/{chunk-NTTSXF7Z.js.map → chunk-NGY4KET4.js.map} +0 -0
  142. /package/dist/{chunk-O45VULK4.js.map → chunk-OCJG3X4U.js.map} +0 -0
  143. /package/dist/{chunk-KKIAKLI6.js.map → chunk-ONDI64M3.js.map} +0 -0
  144. /package/dist/{chunk-FMQO7OMJ.js.map → chunk-QGQ26NEE.js.map} +0 -0
  145. /package/dist/{chunk-DQI2S2SY.js.map → chunk-YUAKZNK5.js.map} +0 -0
  146. /package/dist/{chunk-CLQ6L4JG.js.map → chunk-ZJ7RI62F.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/meeting-scheduler/index.tsx","../../../src/hooks/use-meeting-booking.ts","../../../src/schemas/meeting-booking-schema.ts","../../../src/components/meeting-scheduler/context-panel.tsx","../../../src/components/meeting-scheduler/slot-picker.tsx","../../../src/components/meeting-scheduler/booking-form.tsx","../../../src/components/meeting-scheduler/confirmation.tsx","../../../src/components/meeting-scheduler/directory.tsx"],"sourcesContent":["'use client'\n\n/**\n * `<HubSpotMeetingScheduler />` — natively-branded booking flow over the\n * HubSpot Meetings scheduler, rendered entirely with ODS primitives (no\n * iframe, no third-party chrome, no scripts). Data flows exclusively through\n * a HOST PROXY (`GET {apiBaseUrl}/api/meetings/availability`,\n * `POST {apiBaseUrl}/api/meetings/book`) — the widget has zero HubSpot\n * knowledge and no secrets; see `docs/EMBEDDING_HUBSPOT_MEETINGS.md` for the\n * endpoint contract a host must serve (external embedders proxy through\n * their own backend — the book route is IP-rate-limited and bot-gated).\n *\n * LAYOUT (Calendly-anatomy, conversion-first): one bordered card, two\n * panels. Left/top — the CONTEXT panel: host identity (avatar + name +\n * title from `availability.hosts`), meeting title/description, duration\n * chips (the duration choice lives here, NOT as a wizard step — the visitor\n * always lands straight on the calendar), and the resolved timezone.\n * Right/bottom — the ACTION panel: calendar + time slots (first available\n * day auto-selected so slots are visible immediately), then the details\n * form, then the confirmation. Panels stack on mobile.\n *\n * Two modes, one component:\n * - SSR mode: pass `initialAvailability` (host-fetched) — the\n * zone-independent shell (context panel, calendar) renders server-side;\n * slot TIME labels wait for the client zone resolution (deterministic\n * first paint, no hydration mismatch), and availability is refetched\n * unconditionally after mount (the seed is scaffolding for a 60s-volatile\n * resource, not truth).\n * - Client mode: omit the seed — skeleton + self-fetch on mount.\n *\n * State machine: slot → details → confirmed, with a back edge, a \"book\n * another\" reset, and a `submitting` lock as the double-booking guard.\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { cn } from '../../utils/cn'\nimport { formatDurationCompact } from '../../utils/format'\nimport { Button } from '../ui'\nimport { BackButton } from '../layout/back-button'\nimport { useHumanitySignals } from '../../hooks/use-humanity-signals'\nimport { useMeetingBooking } from '../../hooks/use-meeting-booking'\nimport {\n isSupportedFormField,\n type BookingConfirmation,\n type MeetingAvailability,\n type MeetingBookingErrorCode,\n type MeetingHost,\n} from '../../schemas/meeting-booking-schema'\nimport { SchedulerContextPanel, ContextPanelSkeleton } from './context-panel'\nimport { SlotPicker, SlotPickerSkeleton, dayKeyInZone } from './slot-picker'\nimport { BookingForm } from './booking-form'\nimport { Confirmation } from './confirmation'\n\nexport interface HubSpotMeetingSchedulerProps {\n /** Directory id of the meeting link (from the host's `/api/meetings` payload). */\n meetingId: string\n /** Endpoints prefix, default '' (same-origin `/api/meetings/*`) — FaqSection precedent. */\n apiBaseUrl?: string\n /** SSR-mode seed (host-fetched). Omitted → client mode (self-fetch on mount). */\n initialAvailability?: MeetingAvailability\n /** Meeting title shown in the context panel (host page owns the h1). */\n title?: string\n /** Short description under the title in the context panel. */\n description?: string | null\n /** Override the hosts shown (defaults to `availability.hosts`). */\n hosts?: MeetingHost[]\n /** Pin the DISPLAY zone (rendering only — never sent upstream, never a cache key). */\n displayTimezone?: string\n /** The link's public HubSpot booking URL — the \"Open in HubSpot\" escape hatch target. */\n fallbackUrl?: string\n onBooked?: (b: BookingConfirmation) => void\n className?: string\n}\n\ntype Step = 'slot' | 'details' | 'confirmed'\n\n/**\n * Fail-closed gate: a link whose declared questions include an unsupported\n * type, or whose consent block is malformed, must NOT render a half-working\n * native form (a silently dropped required question or missing consent copy\n * is worse than no native form) — the escape hatch takes over.\n */\nfunction isNativelyBookable(availability: MeetingAvailability): boolean {\n if (!availability.formFields.every(isSupportedFormField)) return false\n const consent = availability.legalConsent\n if (consent) {\n if (typeof consent.processingConsentText !== 'string') return false\n if (!Array.isArray(consent.communicationConsentCheckboxes)) return false\n }\n return true\n}\n\nexport function HubSpotMeetingScheduler({\n meetingId,\n apiBaseUrl = '',\n initialAvailability,\n title,\n description,\n hosts,\n displayTimezone,\n fallbackUrl,\n onBooked,\n className,\n}: HubSpotMeetingSchedulerProps) {\n const {\n availability,\n isLoadingAvailability,\n availabilityError,\n monthOffset,\n setMonthOffset,\n refetchAvailability,\n book,\n isSubmitting,\n } = useMeetingBooking({ meetingId, apiBaseUrl, initialAvailability })\n\n // Zone resolution happens POST-mount (Intl) unless the embedder pins one —\n // the initial (server) render stays deterministic; time labels appear once\n // a zone exists.\n const [timezone, setTimezone] = useState<string | null>(displayTimezone ?? null)\n useEffect(() => {\n if (displayTimezone) return\n try {\n setTimezone(Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC')\n } catch {\n setTimezone('UTC')\n }\n }, [displayTimezone])\n\n const [step, setStep] = useState<Step>('slot')\n const [durationMs, setDurationMs] = useState<number | null>(null)\n const [selectedDay, setSelectedDay] = useState<string | null>(null)\n const [selectedSlot, setSelectedSlot] = useState<number | null>(null)\n const [confirmation, setConfirmation] = useState<BookingConfirmation | null>(null)\n const [bookingError, setBookingError] = useState<MeetingBookingErrorCode | null>(null)\n\n const { honeypotInputProps, getSignals, resetSignals } = useHumanitySignals()\n\n // Reset the machine when the host switches links.\n useEffect(() => {\n setStep('slot')\n setDurationMs(null)\n setSelectedDay(null)\n setSelectedSlot(null)\n setConfirmation(null)\n setBookingError(null)\n autoAdvanceCount.current = 0\n }, [meetingId])\n\n const durations = availability?.durationsMs ?? []\n const shownHosts = hosts ?? availability?.hosts ?? []\n\n // Default to the first offered duration — the visitor lands straight on\n // the calendar; duration is a context-panel chip, not a wizard step.\n useEffect(() => {\n if (durationMs == null && durations.length > 0) setDurationMs(durations[0])\n }, [durationMs, durations])\n\n const slots = useMemo(() => {\n if (!availability || durationMs == null) return []\n return availability.slotsByDurationMs[String(durationMs)] ?? []\n }, [availability, durationMs])\n\n // Auto-select the first day WITH SLOTS IN THE VISIBLE MONTH (once the zone\n // is known) so the visitor sees concrete times immediately — never a dead\n // \"pick a day\" state, and NEVER a day from another month than the calendar\n // shows (HubSpot's monthOffset payloads can carry near-term slots outside\n // the requested month — the visible grid is the authority).\n useEffect(() => {\n if (step !== 'slot' || !timezone) return\n const now = new Date()\n const visible = new Date(now.getFullYear(), now.getMonth() + monthOffset, 1)\n const monthPrefix = `${visible.getFullYear()}-${String(visible.getMonth() + 1).padStart(2, '0')}`\n const inMonth = slots.filter((ms) => dayKeyInZone(ms, timezone).startsWith(monthPrefix))\n if (inMonth.length === 0) {\n if (selectedDay && !selectedDay.startsWith(monthPrefix)) setSelectedDay(null)\n return\n }\n const dayKeys = new Set(inMonth.map((ms) => dayKeyInZone(ms, timezone)))\n if (selectedDay && dayKeys.has(selectedDay)) return\n setSelectedDay(dayKeyInZone(inMonth[0], timezone))\n }, [step, timezone, slots, selectedDay, monthOffset])\n\n // Fully-booked month with more ahead → auto-advance (bounded, ≤3 hops)\n // before showing the empty state.\n const autoAdvanceCount = useRef(0)\n useEffect(() => {\n if (\n step === 'slot' &&\n availability &&\n !isLoadingAvailability &&\n durationMs != null &&\n slots.length === 0 &&\n availability.hasMore &&\n autoAdvanceCount.current < 3\n ) {\n autoAdvanceCount.current += 1\n setMonthOffset(monthOffset + 1)\n }\n }, [step, availability, isLoadingAvailability, durationMs, slots, monthOffset, setMonthOffset])\n\n const handleSubmit = useCallback(\n async (payload: Record<string, unknown>) => {\n setBookingError(null)\n const result = await book(payload)\n if (result.ok && result.confirmation) {\n setConfirmation(result.confirmation)\n setStep('confirmed')\n onBooked?.(result.confirmation)\n return\n }\n const code = result.code ?? 'TEMPORARILY_UNAVAILABLE'\n setBookingError(code)\n if (code === 'SLOT_TAKEN') {\n // Recover: refresh the grid (the slot vanishes) and reset the timing\n // signal so the retry isn't flagged too-fast.\n setSelectedSlot(null)\n setStep('slot')\n resetSignals()\n void refetchAvailability()\n }\n },\n [book, onBooked, refetchAvailability, resetSignals],\n )\n\n const escapeHatch = fallbackUrl ? (\n <Button variant=\"outline\" size=\"small-legacy\" href={fallbackUrl} openInNewTab>\n Open in HubSpot\n </Button>\n ) : null\n\n // ---- terminal / degraded states -----------------------------------------\n\n if (isLoadingAvailability && !availability) {\n // Per-region skeletons, composed inside the SAME card wrappers as the\n // loaded state: `ContextPanelSkeleton` (profile side), `CalendarSkeleton`\n // (day buttons), `TimeChipsSkeleton` (hour options). Identical footprints\n // by construction — loading ⇄ loaded never shifts layout.\n return (\n <div className={cn('rounded-md border border-ods-border bg-ods-card overflow-hidden', className)}>\n <div className=\"flex flex-col md:flex-row\">\n <ContextPanelSkeleton className=\"p-[var(--spacing-system-lf)] md:w-80 md:shrink-0 border-b md:border-b-0 md:border-r border-ods-border\" />\n <div className=\"flex-1 min-w-0 p-[var(--spacing-system-lf)] md:min-h-[32rem] flex flex-col gap-[var(--spacing-system-md)]\">\n {/* Static heading renders REAL — it needs no data. */}\n <p className=\"text-h5 text-ods-text-secondary\">Select a date &amp; time</p>\n <SlotPickerSkeleton />\n </div>\n </div>\n </div>\n )\n }\n\n if (availabilityError || !availability) {\n return (\n <div\n className={cn(\n 'rounded-md border border-ods-border bg-ods-card p-[var(--spacing-system-lf)] flex flex-col items-start gap-[var(--spacing-system-md)]',\n className,\n )}\n >\n <p className=\"text-h6 text-ods-text-secondary\">\n We couldn&apos;t load available call times. Please try again shortly.\n </p>\n {escapeHatch}\n </div>\n )\n }\n\n if (!isNativelyBookable(availability)) {\n // Fail closed — never render a half-working native form on a link with\n // questions or consent we can't faithfully reproduce.\n return (\n <div\n className={cn(\n 'rounded-md border border-ods-border bg-ods-card p-[var(--spacing-system-lf)] flex flex-col items-start gap-[var(--spacing-system-md)]',\n className,\n )}\n >\n <p className=\"text-h6 text-ods-text-secondary\">This meeting type is booked directly on HubSpot.</p>\n {escapeHatch}\n </div>\n )\n }\n\n // ---- the card ------------------------------------------------------------\n\n return (\n <div className={cn('rounded-md border border-ods-border bg-ods-card overflow-hidden', className)}>\n <div className=\"flex flex-col md:flex-row\">\n <SchedulerContextPanel\n hosts={shownHosts}\n title={title}\n description={description}\n durationsMs={durations}\n selectedDurationMs={durationMs}\n onSelectDuration={(ms) => {\n setDurationMs(ms)\n setSelectedDay(null)\n setSelectedSlot(null)\n if (step === 'details') setStep('slot')\n }}\n timezone={timezone}\n onTimezoneChange={setTimezone}\n locked={step !== 'slot'}\n className=\"p-[var(--spacing-system-lf)] md:w-80 md:shrink-0 border-b md:border-b-0 md:border-r border-ods-border\"\n />\n\n {/* min-height pins the card so slot ⇄ details ⇄ confirmed transitions\n never jump the page (sized to the slot step, the tallest one);\n flex column so shorter steps can center vertically. */}\n <div className=\"flex-1 min-w-0 p-[var(--spacing-system-lf)] md:min-h-[32rem] flex flex-col\">\n {step === 'confirmed' && confirmation && timezone ? (\n <Confirmation confirmation={confirmation} timezone={timezone} />\n ) : step === 'details' && durationMs != null && selectedSlot != null && timezone ? (\n <div className=\"flex flex-1 flex-col gap-[var(--spacing-system-md)]\">\n {/* App-standard BackButton — pinned at the TOP of the panel,\n exactly where the slot step's \"Select a date & time\" header\n sits; the form centers in the remaining space. */}\n <BackButton\n label=\"Back\"\n onClick={() => {\n setStep('slot')\n setBookingError(null)\n }}\n />\n <div className=\"flex flex-1 flex-col justify-center gap-[var(--spacing-system-md)]\">\n <p className=\"text-h4 text-ods-text-primary\">\n {new Intl.DateTimeFormat(undefined, {\n timeZone: timezone,\n weekday: 'long',\n month: 'long',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n timeZoneName: 'short',\n }).format(new Date(selectedSlot))}{' '}\n · {formatDurationCompact(durationMs / 1000)}\n </p>\n {bookingError && bookingError !== 'SLOT_TAKEN' && (\n <div className=\"flex flex-col items-start gap-[var(--spacing-system-xs)]\">\n <p className=\"text-h6 text-ods-error\">\n {bookingError === 'TEMPORARILY_UNAVAILABLE'\n ? 'Scheduling is briefly unavailable — please try again in a minute.'\n : bookingError === 'MEETING_UNAVAILABLE'\n ? 'This meeting type has reached its booking limit for today — try another time or contact us.'\n : bookingError === 'LINK_GONE'\n ? 'This meeting type is no longer available.'\n : 'Please double-check your details and try again.'}\n </p>\n {escapeHatch}\n </div>\n )}\n <BookingForm\n availability={availability}\n meetingId={meetingId}\n startTimeMs={selectedSlot}\n durationMs={durationMs}\n timezone={timezone}\n isSubmitting={isSubmitting}\n onSubmit={handleSubmit}\n honeypotInputProps={honeypotInputProps}\n getSignals={getSignals}\n />\n </div>\n </div>\n ) : (\n <div className=\"flex flex-col gap-[var(--spacing-system-md)]\">\n <p className=\"text-h5 text-ods-text-secondary\">Select a date &amp; time</p>\n {bookingError === 'SLOT_TAKEN' && (\n <p className=\"text-h6 text-ods-error\">\n That time is no longer available — if you just submitted, check your email for a confirmation\n before rebooking.\n </p>\n )}\n {timezone && durationMs != null ? (\n slots.length > 0 || isLoadingAvailability ? (\n <SlotPicker\n slots={slots}\n timezone={timezone}\n monthOffset={monthOffset}\n onMonthOffsetChange={(o) => {\n setSelectedDay(null)\n setSelectedSlot(null)\n setMonthOffset(o)\n }}\n selectedSlot={selectedSlot}\n onSelectSlot={(ms) => {\n setSelectedSlot(ms)\n setStep('details')\n }}\n selectedDay={selectedDay}\n onSelectDay={setSelectedDay}\n isLoading={isLoadingAvailability}\n />\n ) : (\n <div className=\"flex flex-col items-start gap-[var(--spacing-system-md)]\">\n <p className=\"text-h6 text-ods-text-secondary\">\n {isLoadingAvailability ? 'Checking more dates…' : 'No call times are published right now.'}\n </p>\n {!isLoadingAvailability && escapeHatch}\n </div>\n )\n ) : (\n // Pre-mount timezone-resolution window (every SSR load hits\n // this for one frame-batch) — SAME per-region skeletons, no\n // bare rectangle, no shift when the real picker swaps in.\n <SlotPickerSkeleton />\n )}\n </div>\n )}\n </div>\n </div>\n </div>\n )\n}\n\nexport {\n MeetingSchedulerDirectory,\n MeetingSchedulerDirectoryRowSkeleton,\n type MeetingSchedulerDirectoryProps,\n} from './directory'\nexport type { MeetingAvailability, BookingConfirmation, MeetingBookingErrorCode, MeetingHost }\nexport type { SchedulingLink, SchedulingLinksPayload } from '../../schemas/meeting-booking-schema'\n","'use client'\n\nimport { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport { contentFetch } from '../utils/embed-content-fetch'\nimport { MAX_MONTH_OFFSET } from '../utils/hubspot-meetings-convention'\nimport type {\n BookingConfirmation,\n MeetingAvailability,\n MeetingBookingErrorCode,\n} from '../schemas/meeting-booking-schema'\n\n/**\n * useMeetingBooking — data layer for `<HubSpotMeetingScheduler>` (the widget\n * files never inline fetch; same split as `useContactSubmission`/ContactForm).\n *\n * Availability: plain `useState` + `useEffect` + `AbortController` self-fetch\n * (NO client cache layer). An `initialAvailability` seed (SSR mode) renders\n * immediately, but the hook REFETCHES UNCONDITIONALLY after mount — the seed\n * is first-paint scaffolding for a 60-second-volatile resource, not truth.\n *\n * Booking: single POST guarded by `isSubmitting` (the only double-booking\n * guard — the transport never retries POST). Errors surface as typed\n * `MeetingBookingErrorCode`s the widget keys its recovery UI off.\n *\n * All requests go through the host proxy (`apiBaseUrl` prefix, FaqSection\n * precedent) via `contentFetch`, so embedded hosts with an embed-auth adapter\n * inherit auth with zero extra wiring.\n */\n\nexport interface BookingResult {\n ok: boolean\n confirmation?: BookingConfirmation\n code?: MeetingBookingErrorCode\n message?: string\n}\n\nconst KNOWN_CODES: MeetingBookingErrorCode[] = [\n 'SLOT_TAKEN',\n 'VALIDATION',\n 'LINK_GONE',\n 'TEMPORARILY_UNAVAILABLE',\n 'MEETING_UNAVAILABLE',\n]\n\nexport function useMeetingBooking(options: {\n meetingId: string\n apiBaseUrl?: string\n initialAvailability?: MeetingAvailability\n}) {\n const { meetingId, apiBaseUrl = '', initialAvailability } = options\n const seededFor = initialAvailability?.meetingId === meetingId ? initialAvailability : undefined\n\n const [availability, setAvailability] = useState<MeetingAvailability | null>(seededFor ?? null)\n const [monthOffset, setMonthOffsetState] = useState(seededFor?.monthOffset ?? 0)\n const [isLoadingAvailability, setIsLoadingAvailability] = useState(!seededFor)\n const [availabilityError, setAvailabilityError] = useState<string | null>(null)\n const [isSubmitting, setIsSubmitting] = useState(false)\n const submittingRef = useRef(false)\n\n const setMonthOffset = useCallback((offset: number) => {\n setMonthOffsetState(Math.max(0, Math.min(MAX_MONTH_OFFSET, offset)))\n }, [])\n\n // Link switch resets paging — without this, the new link is fetched at the\n // PREVIOUS link's month. Layout effect so the fetch effect below sees the\n // reset offset in the same commit.\n const prevMeetingIdRef = useRef(meetingId)\n useLayoutEffect(() => {\n if (prevMeetingIdRef.current === meetingId) return\n prevMeetingIdRef.current = meetingId\n setMonthOffsetState(0)\n }, [meetingId])\n\n // Unconditional (re)fetch on mount and on (meetingId, monthOffset) change —\n // even when SSR-seeded (the seed may already be stale).\n useEffect(() => {\n const controller = new AbortController()\n let active = true\n const load = async () => {\n // Same-month refresh (the unconditional post-mount refetch of an SSR\n // seed) keeps the grid on screen; a DIFFERENT month or link has nothing\n // truthful to show — loading turns on so the widget renders its\n // per-region skeletons instead of a stale grid under a new caption.\n setAvailabilityError(null)\n if (!availability || availability.meetingId !== meetingId || availability.monthOffset !== monthOffset) {\n setIsLoadingAvailability(true)\n }\n try {\n const params = new URLSearchParams({ meeting: meetingId, monthOffset: String(monthOffset) })\n const res = await contentFetch(`${apiBaseUrl}/api/meetings/availability?${params}`, {\n signal: controller.signal,\n })\n if (!res.ok) throw new Error(`availability ${res.status}`)\n const data = (await res.json()) as MeetingAvailability\n if (active) setAvailability(data)\n } catch (err) {\n if (!active || (err instanceof DOMException && err.name === 'AbortError')) return\n setAvailabilityError(err instanceof Error ? err.message : 'Failed to load availability')\n } finally {\n if (active) setIsLoadingAvailability(false)\n }\n }\n void load()\n return () => {\n active = false\n controller.abort()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- refetch is keyed on (meetingId, monthOffset) only; `availability` is read for skeleton gating, not as a trigger\n }, [meetingId, monthOffset, apiBaseUrl])\n\n const refetchAvailability = useCallback(async () => {\n try {\n const params = new URLSearchParams({ meeting: meetingId, monthOffset: String(monthOffset) })\n const res = await contentFetch(`${apiBaseUrl}/api/meetings/availability?${params}`)\n if (res.ok) setAvailability((await res.json()) as MeetingAvailability)\n } catch {\n /* recovery refetch is best-effort — the stale grid stays visible */\n }\n }, [meetingId, monthOffset, apiBaseUrl])\n\n const book = useCallback(\n async (payload: Record<string, unknown>): Promise<BookingResult> => {\n if (submittingRef.current) return { ok: false, code: 'VALIDATION', message: 'Already submitting' }\n submittingRef.current = true\n setIsSubmitting(true)\n try {\n const res = await contentFetch(`${apiBaseUrl}/api/meetings/book`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n })\n const data = (await res.json().catch(() => ({}))) as Record<string, unknown>\n if (res.ok) return { ok: true, confirmation: data as unknown as BookingConfirmation }\n const rawCode = typeof data.code === 'string' ? data.code : ''\n const code = (KNOWN_CODES as string[]).includes(rawCode)\n ? (rawCode as MeetingBookingErrorCode)\n : 'TEMPORARILY_UNAVAILABLE'\n return { ok: false, code, message: typeof data.error === 'string' ? data.error : undefined }\n } catch {\n return { ok: false, code: 'TEMPORARILY_UNAVAILABLE' }\n } finally {\n submittingRef.current = false\n setIsSubmitting(false)\n }\n },\n [apiBaseUrl],\n )\n\n return {\n availability,\n isLoadingAvailability,\n availabilityError,\n monthOffset,\n setMonthOffset,\n refetchAvailability,\n book,\n isSubmitting,\n } as const\n}\n","import { z } from 'zod'\n\n/**\n * Meeting-booking wire contracts + validation factory.\n *\n * SERVER-SAFE tsup entry (no \"use client\" banner) with its OWN per-file\n * subpath (`./schemas/meeting-booking-schema`) — same pattern and reasons as\n * `schemas/contact-schema`: used by BOTH the lib's `<HubSpotMeetingScheduler>`\n * (client-side validation) AND the host's server-side booking route, which\n * REBUILDS the schema from the link's own fetched metadata and never trusts a\n * client-shaped instance. zod is an optional peer quarantined to per-subpath\n * verticals — do NOT re-export this module through any broad barrel.\n *\n * Every cross-boundary type for the scheduling feature lives HERE (the lib\n * owns the contract; hosts import this subpath directly, type-only where\n * possible).\n */\n\n// ---------------------------------------------------------------------------\n// Wire types\n// ---------------------------------------------------------------------------\n\n/**\n * Sanitized availability payload served by the host proxy\n * (`GET {apiBaseUrl}/api/meetings/availability?meeting=<id>&monthOffset=<n>`).\n *\n * NO timezone field on purpose: slot starts are absolute epoch-ms instants\n * (verified timezone-independent against the live API), upstream fetches are\n * UTC-pinned, and ALL zone rendering happens client-side. Slots must be\n * whitelist-copied from HubSpot's `linkAvailability` ONLY — never derived\n * from busy-time data.\n */\nexport interface MeetingAvailability {\n meetingId: string\n monthOffset: number\n hasMore: boolean\n /** Offered durations in ms — HubSpot's native unit for the booking POST. */\n durationsMs: number[]\n /** Bookable slot start times (epoch ms), keyed by duration in ms. */\n slotsByDurationMs: Record<string, number[]>\n formFields: MeetingFormField[]\n /** Verbatim whitelist-copy of HubSpot's `legalConsentOptions` when consent is enabled; null when disabled. */\n legalConsent: MeetingLegalConsent | null\n /**\n * Who the visitor is meeting — whitelisted DISPLAY projection the host DAL\n * builds from its own people data (e.g. a profiles table matched\n * server-side). NEVER carries emails or busy-time data; optional so\n * existing hosts stay wire-compatible.\n */\n hosts?: MeetingHost[]\n}\n\n/** Display-only host identity for the scheduler's context panel. */\nexport interface MeetingHost {\n name: string\n avatarUrl: string | null\n /** Job title / role line under the name (null → omitted). */\n title: string | null\n}\n\n/**\n * One scheduling link on the DIRECTORY wire (`GET /api/meetings`) — the\n * host-DAL whitelist projection consumed by `MeetingSchedulerDirectory` and\n * host pages. Never carries organizer emails/busy-time data.\n */\nexport interface SchedulingLink {\n id: string\n /** The link's public HubSpot booking URL — escape-hatch target only. */\n link: string\n /** HubSpot slug path — the row's in-app destination is `<basePath>/<slug>`. */\n slug: string\n /** Audience group key (slugified audience label; `\"other\"` in scope=all). */\n purpose: string\n title: string\n description: string | null\n kind: 'personal' | 'team'\n /** Display-only minutes projection (booking stays ms end-to-end). */\n durationsMinutes: number[]\n hosts: MeetingHost[]\n /** Earliest bookable slot (epoch ms) from the current-month payload. */\n nextAvailableMs: number | null\n}\n\nexport interface SchedulingLinksPayload {\n purposes: Array<{ purpose: string; label: string; links: SchedulingLink[] }>\n fetchedAt: string\n}\n\nexport interface MeetingFormField {\n name: string\n label: string\n type: string\n required: boolean\n options?: string[]\n}\n\n/**\n * HubSpot's consent copy, rendered VERBATIM by the widget (GDPR surface —\n * never edited, never summarized). Responses are keyed by\n * `communicationTypeId`.\n */\nexport interface MeetingLegalConsent {\n processingConsentText: string\n processingConsentCheckboxLabel: string | null\n communicationConsentText: string | null\n communicationConsentCheckboxes: Array<{\n communicationTypeId: string\n label: string\n required: boolean\n }>\n privacyPolicyText: string | null\n isLegitimateInterest: boolean\n}\n\n/**\n * Whitelisted booking result returned by the host proxy — the THIRD HubSpot\n * payload that reaches a browser, so it gets the same whitelist-copy\n * treatment as the two GETs. Nothing organizer-derived.\n */\nexport interface BookingConfirmation {\n meetingId: string\n title: string\n startTimeMs: number\n durationMs: number\n}\n\n/**\n * Typed domain errors the booking route emits; the widget keys its recovery\n * UI off these. `SLOT_TAKEN` → refetch-and-recover; `TEMPORARILY_UNAVAILABLE`\n * → retry affordance; `MEETING_UNAVAILABLE` → daily ceiling exhausted\n * (escape hatch, not a retry timer); `LINK_GONE` → link deleted upstream.\n */\nexport type MeetingBookingErrorCode =\n | 'SLOT_TAKEN'\n | 'VALIDATION'\n | 'LINK_GONE'\n | 'TEMPORARILY_UNAVAILABLE'\n | 'MEETING_UNAVAILABLE'\n\n// ---------------------------------------------------------------------------\n// Field-type vocabulary — ONE set drives the renderer AND the validator\n// ---------------------------------------------------------------------------\n\n/**\n * HubSpot custom-question types the native form supports. The widget's\n * renderer switches over THIS set and `makeBookingSchema` maps over THIS set;\n * fail-closed = a field whose `type` is not in the set (the widget then\n * renders the \"Open in HubSpot\" escape hatch for that link instead of a\n * half-working native form). Exact upstream type strings are pinned against\n * the rollout fixture link — extend here (renderer + validator move together).\n */\nexport const SUPPORTED_FORM_FIELD_TYPES = ['text', 'textarea', 'select', 'radio', 'checkbox'] as const\nexport type SupportedFormFieldType = (typeof SUPPORTED_FORM_FIELD_TYPES)[number]\n\nexport function isSupportedFormField(field: MeetingFormField): boolean {\n return (SUPPORTED_FORM_FIELD_TYPES as readonly string[]).includes(field.type)\n}\n\n// ---------------------------------------------------------------------------\n// Validators (single home — client widget and server rebuild both use these)\n// ---------------------------------------------------------------------------\n\nconst IANA_TZ_RE = /^(?:UTC|[A-Za-z_]+(?:\\/[A-Za-z0-9_+\\-]+)+)$/\n\n/**\n * IANA timezone check. Shape prefilter, then the authoritative resolution\n * test: `Intl.DateTimeFormat` throws on unknown zones. NOT\n * `Intl.supportedValuesOf('timeZone')` — that list excludes `'UTC'` itself\n * (verified in Node), which is a legitimate booking zone. Reject, never coerce.\n */\nexport function isValidIanaTimezone(tz: string): boolean {\n if (!IANA_TZ_RE.test(tz)) return false\n try {\n new Intl.DateTimeFormat('en-US', { timeZone: tz })\n return true\n } catch {\n return false\n }\n}\n\n/** BCP-47 locale shape check via `Intl.getCanonicalLocales`. Reject, never coerce. */\nexport function isValidBcp47Locale(locale: string): boolean {\n try {\n return Intl.getCanonicalLocales(locale).length > 0\n } catch {\n return false\n }\n}\n\n// ---------------------------------------------------------------------------\n// Schema factory\n// ---------------------------------------------------------------------------\n\n/**\n * Build the booking-form schema for ONE link's declared questions + consent.\n *\n * A factory (not a static schema) because per-link required questions cannot\n * be expressed statically. The widget builds it from the availability payload\n * it rendered; the server REBUILDS it from the link's own fetched metadata —\n * required-consent enforcement flows from this rebuild, not a parallel check.\n *\n * Deliberately NOT `.strict()`: the humanity-signal fields\n * (`HUMANITY_SIGNAL_KEYS` from `utils/humanity-signals`) ride alongside in\n * the same POST body (read raw by the host's bot gate BEFORE parsing) and are\n * stripped server-side before anything reaches HubSpot. zod's default\n * unknown-key stripping means the parsed output never contains them.\n *\n * `timezone`/`locale` are POST-only presentation fields (the invite renders\n * in the visitor's local time) — this schema is the ONLY place a\n * client-supplied zone is accepted; the availability path is UTC-pinned.\n */\nexport function makeBookingSchema(\n formFields: MeetingFormField[],\n legalConsent: MeetingLegalConsent | null,\n) {\n const answers: Record<string, z.ZodTypeAny> = {}\n for (const field of formFields) {\n if (!isSupportedFormField(field)) continue // unsupported types are fail-closed at render time\n let validator: z.ZodTypeAny\n switch (field.type as SupportedFormFieldType) {\n case 'checkbox':\n validator = z.boolean()\n break\n case 'select':\n case 'radio':\n validator = z\n .string()\n .refine((v) => !v || (field.options ?? []).includes(v), {\n message: `Please choose a valid option for ${field.label}`,\n })\n break\n case 'textarea':\n validator = z.string().max(5000, { message: `${field.label} is too long` })\n break\n case 'text':\n default:\n validator = z.string().max(1000, { message: `${field.label} is too long` })\n break\n }\n if (field.required) {\n validator =\n field.type === 'checkbox'\n ? z.literal(true, { message: `${field.label} is required` })\n : (validator as z.ZodString).min(1, { message: `${field.label} is required` })\n } else if (field.type !== 'checkbox') {\n validator = (validator as z.ZodString).optional().or(z.literal(''))\n } else {\n validator = z.boolean().optional()\n }\n answers[field.name] = validator\n }\n\n const requiredConsentIds = (legalConsent?.communicationConsentCheckboxes ?? [])\n .filter((c) => c.required)\n .map((c) => c.communicationTypeId)\n\n // A required answer cannot be enforced by an `.optional()` parent object —\n // omitting the `formFields` key entirely would skip every per-question\n // rule. When the link declares at least one required supported question,\n // the object itself is required.\n const hasRequiredAnswers = formFields.some((f) => isSupportedFormField(f) && f.required)\n const answersObject = z.object(answers)\n\n return z\n .object({\n meetingId: z.string().min(1),\n startTimeMs: z.number().int().positive(),\n durationMs: z.number().int().positive(),\n firstName: z.string().min(1, { message: 'First name is required' }).max(255),\n lastName: z.string().min(1, { message: 'Last name is required' }).max(255),\n email: z.string().email({ message: 'Please enter a valid email address' }).max(255),\n timezone: z.string().refine(isValidIanaTimezone, { message: 'Invalid timezone' }),\n locale: z.string().refine(isValidBcp47Locale, { message: 'Invalid locale' }).optional(),\n // Plain `.optional()` (no `.default()`) so zod's input and output types\n // match — react-hook-form's zodResolver needs them identical, and the\n // server handles `undefined` explicitly anyway. REQUIRED when the link\n // declares required questions (see `hasRequiredAnswers` above).\n formFields: hasRequiredAnswers ? answersObject : answersObject.optional(),\n legalConsentResponses: z\n .array(z.object({ communicationTypeId: z.string(), consented: z.boolean() }))\n .optional(),\n })\n // Object-level rule: `.refine` on an `.optional()` field is skipped when\n // the field is absent — required consents must reject even on a payload\n // that omits the array entirely.\n .superRefine((data, ctx) => {\n const responses = data.legalConsentResponses ?? []\n const ok = requiredConsentIds.every((id) =>\n responses.some((r) => r.communicationTypeId === id && r.consented === true),\n )\n if (!ok) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['legalConsentResponses'],\n message: 'Required consent checkboxes must be accepted',\n })\n }\n })\n}\n\nexport type MeetingBookingPayload = z.infer<ReturnType<typeof makeBookingSchema>>\n","'use client'\n\nimport { useMemo } from 'react'\nimport { SquareAvatar, AvatarStack, Button, Skeleton, Autocomplete } from '../ui'\nimport { ClockIcon } from '../icons-v2-generated'\nimport { cn } from '../../utils/cn'\nimport { formatDurationCompact } from '../../utils/format'\nimport type { MeetingHost } from '../../schemas/meeting-booking-schema'\n\n/**\n * ContextPanel — the \"who / what / how long\" side of the scheduler card\n * (Calendly-anatomy left panel; stacks on top on mobile). Everything here is\n * trust surface: host identity (avatar + name + title), the meeting's own\n * title/description, the duration (chips when the link offers several — the\n * duration choice lives HERE, not as a separate wizard step), and a\n * SEARCHABLE timezone picker (all IANA zones with live GMT offsets;\n * rendering-only — the wire is always epoch-ms).\n *\n * The picker renders only once the parent resolves the zone post-mount — SSR\n * output stays deterministic; a same-footprint skeleton holds the space.\n */\n\nexport interface SchedulerContextPanelProps {\n hosts: MeetingHost[]\n title?: string\n description?: string | null\n durationsMs: number[]\n selectedDurationMs: number | null\n onSelectDuration: (ms: number) => void\n /** Resolved IANA zone (null until the client resolves it). */\n timezone: string | null\n onTimezoneChange?: (tz: string) => void\n /** True once a slot is chosen (details/confirmed steps) — the duration and\n * timezone SELECTORS disappear entirely (the step summary carries both; a\n * disabled picker next to a form just reads as noise) and a static\n * duration line takes their place. Going Back restores them. */\n locked?: boolean\n className?: string\n}\n\nfunction zoneLabel(tz: string): string {\n try {\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone: tz,\n timeZoneName: 'shortOffset',\n }).formatToParts(new Date())\n const offset = parts.find((p) => p.type === 'timeZoneName')?.value ?? ''\n return offset ? `${tz.replace(/_/g, ' ')} (${offset})` : tz.replace(/_/g, ' ')\n } catch {\n return tz.replace(/_/g, ' ')\n }\n}\n\n/**\n * Same-footprint skeleton — swaps with the loaded panel with zero shift.\n * STATIC labels (\"Duration\", \"Timezone\") render REAL; only data-driven\n * content (host identity, duration chips, the zone value) is skeleton.\n */\nexport function ContextPanelSkeleton({ className }: { className?: string }) {\n return (\n <div className={cn('flex flex-col gap-[var(--spacing-system-mf)]', className)}>\n <div className=\"flex items-center gap-[var(--spacing-system-s)]\">\n <Skeleton className=\"h-12 w-12 rounded-full shrink-0\" />\n <div className=\"flex flex-1 flex-col gap-[var(--spacing-system-xxs)]\">\n <Skeleton className=\"h-5 w-32\" />\n <Skeleton className=\"h-4 w-24\" />\n </div>\n </div>\n <div className=\"flex flex-col gap-[var(--spacing-system-xs)]\">\n <p className=\"text-h5 text-ods-text-secondary\">Duration</p>\n <div className=\"flex gap-[var(--spacing-system-xs)]\">\n <Skeleton className=\"h-9 w-20\" />\n <Skeleton className=\"h-9 w-20\" />\n </div>\n </div>\n <div className=\"flex flex-col gap-[var(--spacing-system-xs)]\">\n <p className=\"text-h5 text-ods-text-secondary\">Timezone</p>\n <Skeleton className=\"h-12 w-full\" />\n </div>\n </div>\n )\n}\n\nexport function SchedulerContextPanel({\n hosts,\n title,\n description,\n durationsMs,\n selectedDurationMs,\n onSelectDuration,\n timezone,\n onTimezoneChange,\n locked = false,\n className,\n}: SchedulerContextPanelProps) {\n // All IANA zones with live GMT offsets — computed once, client-only (the\n // panel renders the picker only after the parent resolves a zone).\n const zoneOptions = useMemo(() => {\n if (!timezone) return []\n let zones: string[]\n try {\n // Older lib targets don't type supportedValuesOf (ES2022) — runtime-guarded.\n const intl = Intl as typeof Intl & { supportedValuesOf?: (key: string) => string[] }\n zones = intl.supportedValuesOf ? intl.supportedValuesOf('timeZone') : [timezone]\n } catch {\n zones = [timezone]\n }\n if (!zones.includes(timezone)) zones = [timezone, ...zones]\n return zones.map((tz) => ({ value: tz, label: zoneLabel(tz) }))\n }, [timezone])\n\n return (\n <div className={cn('flex flex-col gap-[var(--spacing-system-mf)]', className)}>\n {/* Host identity: full rows up to 3 hosts; larger teams (round-robin\n links can carry many members) collapse to a stacked-avatar cluster\n + count so the panel can never overflow. */}\n {hosts.length > 0 && hosts.length <= 3 && (\n <div className=\"flex flex-col gap-[var(--spacing-system-s)]\">\n {hosts.map((host) => (\n <div key={host.name} className=\"flex items-center gap-[var(--spacing-system-s)]\">\n <SquareAvatar variant=\"round\" size=\"lg\" src={host.avatarUrl ?? undefined} alt={host.name} fallback={host.name} />\n <div className=\"flex flex-col min-w-0\">\n <p className=\"text-h4 text-ods-text-primary truncate\">{host.name}</p>\n {host.title && <p className=\"text-h6 text-ods-text-secondary truncate\">{host.title}</p>}\n </div>\n </div>\n ))}\n </div>\n )}\n {hosts.length > 3 && (\n <div className=\"flex flex-col gap-[var(--spacing-system-xs)]\">\n <AvatarStack people={hosts} max={4} size=\"lg\" />\n <p className=\"text-h6 text-ods-text-secondary\">{hosts.length} hosts on this calendar</p>\n </div>\n )}\n\n {title && <p className=\"text-h3 text-ods-text-primary\">{title}</p>}\n {description && <p className=\"text-h6 text-ods-text-secondary\">{description}</p>}\n\n {locked ? (\n // Post-selection: selectors are GONE, not disabled — the step summary\n // owns the chosen time/zone; the panel keeps a single static line.\n selectedDurationMs != null && (\n <p className=\"text-h6 text-ods-text-secondary\">{formatDurationCompact(selectedDurationMs / 1000)} call</p>\n )\n ) : (\n <>\n {durationsMs.length > 1 ? (\n <div className=\"flex flex-col gap-[var(--spacing-system-xs)]\">\n <p className=\"text-h5 text-ods-text-secondary\">Duration</p>\n <div className=\"flex flex-wrap gap-[var(--spacing-system-xs)]\">\n {durationsMs.map((ms) => (\n <Button\n key={ms}\n variant={selectedDurationMs === ms ? undefined : 'outline'}\n size=\"small-legacy\"\n onClick={() => onSelectDuration(ms)}\n >\n {formatDurationCompact(ms / 1000)}\n </Button>\n ))}\n </div>\n </div>\n ) : (\n selectedDurationMs != null && (\n <p className=\"text-h6 text-ods-text-secondary\">{formatDurationCompact(selectedDurationMs / 1000)} call</p>\n )\n )}\n\n <div className=\"flex flex-col gap-[var(--spacing-system-xs)]\">\n <p className=\"text-h5 text-ods-text-secondary\">Timezone</p>\n {timezone ? (\n <Autocomplete\n value={timezone}\n onChange={(tz) => {\n if (tz) onTimezoneChange?.(tz)\n }}\n options={zoneOptions}\n placeholder=\"Search timezone…\"\n startAdornment={<ClockIcon className=\"size-4 shrink-0 text-ods-text-secondary\" />}\n noOptionsText=\"No matching timezone\"\n showClearAll={false}\n />\n ) : (\n <Skeleton className=\"h-12 w-full\" />\n )}\n </div>\n </>\n )}\n </div>\n )\n}\n","'use client'\n\nimport { useMemo } from 'react'\nimport { ChevronLeft, ChevronRight } from 'lucide-react'\nimport { Calendar } from '../calendar'\nimport { Button, Skeleton } from '../ui'\nimport { cn } from '../../utils/cn'\nimport { MAX_MONTH_OFFSET } from '../../utils/hubspot-meetings-convention'\n\n/**\n * SlotPicker — day layer + time-chip grid for the meeting scheduler.\n *\n * Day layer is the lib's ODS-styled `Calendar` (react-day-picker v9\n * wrapper) — NOT a bespoke day grid. `startMonth`/`endMonth` pin navigation\n * to [current month, current + MAX_MONTH_OFFSET] so the past is unreachable;\n * days without bookable slots are disabled (visually muted via the v9\n * `disabled` classNames). Month navigation drives the host-proxy\n * `monthOffset`. Time chips are plain `Button`s whose variant flips on\n * selection — no bespoke pill.\n *\n * LAZY STATES (no jumps, ever): each region owns a same-footprint skeleton —\n * `CalendarSkeleton` (caption + weekday row + 6×7 day grid) and\n * `TimeChipsSkeleton` (day heading + chip grid). During an availability\n * refetch (month change) BOTH regions swap to their skeletons; the region\n * containers carry fixed min-heights so loading ⇄ loaded never shifts layout.\n *\n * All slot instants are epoch-ms; every label is rendered in `timezone`\n * (resolved by the parent AFTER mount — this component never reads Intl\n * itself, keeping SSR output deterministic).\n */\n\nexport interface SlotPickerProps {\n /** Bookable slot start times (epoch ms) for the selected duration. */\n slots: number[]\n /** IANA zone every label renders in (parent resolves post-mount). */\n timezone: string\n monthOffset: number\n onMonthOffsetChange: (offset: number) => void\n selectedSlot: number | null\n onSelectSlot: (startMs: number) => void\n selectedDay: string | null\n onSelectDay: (dayKey: string) => void\n /** Availability refetch in flight (month change) → per-region skeletons. */\n isLoading?: boolean\n}\n\n/** Stable per-zone day key for an instant, e.g. \"2026-08-14\" (exported — the\n * parent uses it to auto-select the first available day). */\nexport function dayKeyInZone(ms: number, timeZone: string): string {\n return new Intl.DateTimeFormat('en-CA', { timeZone, year: 'numeric', month: '2-digit', day: '2-digit' }).format(\n new Date(ms),\n )\n}\n\n/**\n * Calendar-side day-key anchoring: react-day-picker exchanges PLAIN calendar\n * dates (local Date objects whose y/m/d ARE the cell). Convert via local\n * date parts — never through an Intl zone — or a `displayTimezone` far from\n * the browser zone shifts cells by a day.\n */\nconst pad2 = (n: number) => String(n).padStart(2, '0')\nfunction calendarDayKey(day: Date): string {\n return `${day.getFullYear()}-${pad2(day.getMonth() + 1)}-${pad2(day.getDate())}`\n}\nfunction dateFromDayKey(key: string): Date {\n const [y, m, d] = key.split('-').map(Number)\n return new Date(y, m - 1, d, 12)\n}\n\nfunction timeLabelInZone(ms: number, timeZone: string): string {\n return new Intl.DateTimeFormat(undefined, { timeZone, hour: 'numeric', minute: '2-digit' }).format(new Date(ms))\n}\n\n/** Calendar region min-height: caption (32) + gap (≤12) + table with 6 week\n * rows (8 outer border-spacing + 7×36 rows + 6×4 row gaps = 284) = 328px.\n * Sized to the TALLEST month so 5-row months, 6-row months, skeletons and\n * loaded states all occupy identical space — measured live, not estimated. */\nconst CALENDAR_MIN_H = 'min-h-[20.5rem]'\n\nconst WEEKDAY_LABELS = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']\n\n/** Deterministic month caption for a given offset (fixed en-US locale so\n * the SSR and client first-paint markup agree byte-for-byte). */\nexport function monthLabelFor(offset: number): string {\n const now = new Date()\n const month = new Date(now.getFullYear(), now.getMonth() + offset, 1)\n return new Intl.DateTimeFormat('en-US', { month: 'long', year: 'numeric' }).format(month)\n}\n\n/**\n * Same-footprint skeleton for the day-selection calendar. STATIC chrome —\n * month caption, BOXED nav chevrons, weekday labels — renders REAL (it needs\n * no data); only the day buttons are skeleton cells.\n *\n * GEOMETRY IS EXACT, not approximate — it mirrors the v9 Calendar's real\n * classes: caption row h-8 with h-8/w-8 bordered nav boxes; the grid has NO\n * horizontal gaps (the real table is `border-separate border-spacing-y-1` —\n * vertical spacing only, so cells touch horizontally) and `py-1` reproduces\n * the table's outer border-spacing edges. Any deviation here shifts the\n * times column on skeleton ⇄ loaded swap.\n */\nexport function CalendarSkeleton({ monthOffset = 0 }: { monthOffset?: number }) {\n return (\n <div className={cn('flex flex-col gap-[var(--spacing-system-s)]', CALENDAR_MIN_H)}>\n <div className=\"relative h-8\">\n <div className=\"absolute inset-x-0 top-0 flex h-8 items-center justify-between\">\n <span className=\"flex h-8 w-8 items-center justify-center rounded-md border border-ods-border opacity-60\">\n <ChevronLeft className=\"h-4 w-4 text-ods-text-muted\" />\n </span>\n <span className=\"flex h-8 w-8 items-center justify-center rounded-md border border-ods-border opacity-60\">\n <ChevronRight className=\"h-4 w-4 text-ods-text-muted\" />\n </span>\n </div>\n <div className=\"flex h-8 items-center justify-center\">\n <p className=\"text-h6 text-ods-text-primary\">{monthLabelFor(monthOffset)}</p>\n </div>\n </div>\n {/* Cells are NOT fixed-width: the real table stretches to its\n container (columns share the extra width, buttons centered) — the\n skeleton's grid tracks do exactly the same. */}\n <div className=\"flex flex-col gap-y-1 py-1\">\n <div className=\"grid grid-cols-7\">\n {WEEKDAY_LABELS.map((d) => (\n <div key={d} className=\"flex h-9 items-center justify-center text-h6 font-normal text-ods-text-secondary\">\n {d}\n </div>\n ))}\n </div>\n {Array.from({ length: 6 }, (_, row) => (\n <div key={`w-${row}`} className=\"grid grid-cols-7\">\n {Array.from({ length: 7 }, (_, col) => (\n <div key={col} className=\"flex h-9 items-center justify-center\">\n <Skeleton className=\"h-9 w-9 rounded-md\" />\n </div>\n ))}\n </div>\n ))}\n </div>\n </div>\n )\n}\n\n/**\n * The full slot-area skeleton — calendar + time-chip regions side by side in\n * the SAME flex wrappers as the loaded `SlotPicker`. This is the ONLY\n * loading visual for the slot area, used by every path that has nothing to\n * show yet (initial availability fetch, the pre-mount timezone-resolution\n * window, month-change refetch) — never a bare rectangle.\n */\nexport function SlotPickerSkeleton({ monthOffset = 0 }: { monthOffset?: number }) {\n return (\n <div className=\"flex flex-col md:flex-row gap-[var(--spacing-system-lf)]\">\n <div className={cn('shrink-0', CALENDAR_MIN_H)}>\n <CalendarSkeleton monthOffset={monthOffset} />\n </div>\n <div className=\"flex-1 min-w-0 flex flex-col gap-[var(--spacing-system-s)]\">\n <TimeChipsSkeleton />\n </div>\n </div>\n )\n}\n\n/** Same-footprint skeleton for the time-chip column. */\nexport function TimeChipsSkeleton() {\n return (\n <>\n {/* Day-heading line: text-h6 is 16px tall on mobile, 20px from md up. */}\n <Skeleton className=\"h-4 md:h-5 w-40\" />\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-[var(--spacing-system-xs)] content-start\">\n {Array.from({ length: 9 }, (_, i) => (\n <Skeleton key={i} className=\"h-10 w-full\" />\n ))}\n </div>\n </>\n )\n}\n\nexport function SlotPicker({\n slots,\n timezone,\n monthOffset,\n onMonthOffsetChange,\n selectedSlot,\n onSelectSlot,\n selectedDay,\n onSelectDay,\n isLoading = false,\n}: SlotPickerProps) {\n const slotsByDay = useMemo(() => {\n const map = new Map<string, number[]>()\n for (const ms of slots) {\n const key = dayKeyInZone(ms, timezone)\n const list = map.get(key) ?? []\n list.push(ms)\n map.set(key, list)\n }\n for (const list of map.values()) list.sort((a, b) => a - b)\n return map\n }, [slots, timezone])\n\n const now = new Date()\n const startMonth = new Date(now.getFullYear(), now.getMonth(), 1)\n const endMonth = new Date(now.getFullYear(), now.getMonth() + MAX_MONTH_OFFSET, 1)\n const visibleMonth = new Date(now.getFullYear(), now.getMonth() + monthOffset, 1)\n\n const daySlots = selectedDay ? (slotsByDay.get(selectedDay) ?? []) : []\n\n return (\n <div className=\"flex flex-col md:flex-row gap-[var(--spacing-system-lf)]\">\n <div className={cn('shrink-0', CALENDAR_MIN_H)}>\n {isLoading ? (\n <CalendarSkeleton monthOffset={monthOffset} />\n ) : (\n <Calendar\n month={visibleMonth}\n startMonth={startMonth}\n endMonth={endMonth}\n onMonthChange={(month) => {\n const offset = (month.getFullYear() - now.getFullYear()) * 12 + (month.getMonth() - now.getMonth())\n onMonthOffsetChange(Math.max(0, Math.min(MAX_MONTH_OFFSET, offset)))\n }}\n mode=\"single\"\n required\n // CONTROLLED selection: v9 needs BOTH `selected` AND `onSelect` —\n // with `selected` alone it goes uncontrolled (internal state\n // seeded at mount), silently ignoring the parent's auto-selected\n // first day. `required` disables click-to-deselect.\n selected={selectedDay ? dateFromDayKey(selectedDay) : undefined}\n onSelect={(day) => {\n if (!day) return\n const key = calendarDayKey(day)\n if (slotsByDay.has(key)) onSelectDay(key)\n }}\n disabled={(day) => !slotsByDay.has(calendarDayKey(day))}\n className=\"p-0\"\n />\n )}\n </div>\n <div className=\"flex-1 min-w-0 flex flex-col gap-[var(--spacing-system-s)]\">\n {isLoading ? (\n <TimeChipsSkeleton />\n ) : selectedDay ? (\n <>\n <p className=\"text-h6 text-ods-text-primary\">\n {/* Format the DAY-KEY itself (plain calendar date) — re-zoning a\n local instant could label an adjacent day. */}\n {new Intl.DateTimeFormat(undefined, {\n weekday: 'long',\n month: 'long',\n day: 'numeric',\n }).format(dateFromDayKey(selectedDay))}\n </p>\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-[var(--spacing-system-xs)] content-start max-h-80 overflow-y-auto\">\n {daySlots.map((ms) => {\n const isSelected = selectedSlot === ms\n return (\n <Button\n key={ms}\n variant={isSelected ? undefined : 'outline'}\n size=\"small-legacy\"\n onClick={() => onSelectSlot(ms)}\n >\n {timeLabelInZone(ms, timezone)}\n </Button>\n )\n })}\n </div>\n </>\n ) : (\n <p className=\"text-h6 text-ods-text-secondary\">\n {[...slotsByDay.keys()].some((k) =>\n k.startsWith(\n `${visibleMonth.getFullYear()}-${String(visibleMonth.getMonth() + 1).padStart(2, '0')}`,\n ),\n )\n ? 'Pick a day to see available times.'\n : 'No available times this month — try the next one.'}\n </p>\n )}\n </div>\n </div>\n )\n}\n","'use client'\n\nimport { useMemo } from 'react'\nimport { useForm, Controller } from 'react-hook-form'\nimport { zodResolver } from '@hookform/resolvers/zod'\nimport {\n makeBookingSchema,\n isSupportedFormField,\n type MeetingAvailability,\n type MeetingBookingPayload,\n} from '../../schemas/meeting-booking-schema'\nimport {\n Button,\n Input,\n Textarea,\n Label,\n Select,\n SelectTrigger,\n SelectValue,\n SelectContent,\n SelectItem,\n Checkbox,\n RadioGroup,\n RadioGroupItem,\n} from '../ui'\nimport { HoneypotField } from '../ui/honeypot-field'\n\n/**\n * BookingForm — attendee details + the link's declared custom questions +\n * verbatim legal-consent copy. ContactForm's scaffolding (react-hook-form +\n * zodResolver + lib field primitives); a sibling rather than a `<ContactForm>`\n * configuration because of the dynamic HubSpot `formFields`, the per-checkbox\n * consent model, and the first/last-name field model — none expressible via\n * `hideFields`/`extraTopField`.\n *\n * Bot protection is LOAD-BEARING: without the humanity signals in the body,\n * the host's `verifyHuman` degrades to first-party-only BotID (fails open for\n * external embedders). Honeypot + elapsed-ms are merged into the POST at\n * submit; the parent calls `resetSignals()` after a SLOT_TAKEN refetch so a\n * legitimate retry isn't flagged too-fast.\n */\n\nexport interface BookingFormProps {\n availability: MeetingAvailability\n meetingId: string\n startTimeMs: number\n durationMs: number\n /** IANA zone the confirmation/invite should render in (parent-resolved). */\n timezone: string\n isSubmitting: boolean\n onSubmit: (payload: Record<string, unknown>) => Promise<void>\n /** From useHumanitySignals — parent owns the instance so it can resetSignals(). */\n honeypotInputProps: { ref: React.Ref<HTMLInputElement>; name: string }\n getSignals: () => Record<string, string | number>\n}\n\nexport function BookingForm({\n availability,\n meetingId,\n startTimeMs,\n durationMs,\n timezone,\n isSubmitting,\n onSubmit,\n honeypotInputProps,\n getSignals,\n}: BookingFormProps) {\n const { formFields, legalConsent } = availability\n const supportedFields = useMemo(() => formFields.filter(isSupportedFormField), [formFields])\n const schema = useMemo(() => makeBookingSchema(supportedFields, legalConsent), [supportedFields, legalConsent])\n\n const {\n register,\n control,\n handleSubmit,\n formState: { errors },\n } = useForm<MeetingBookingPayload>({\n resolver: zodResolver(schema),\n defaultValues: {\n meetingId,\n startTimeMs,\n durationMs,\n timezone,\n locale: typeof navigator !== 'undefined' ? navigator.language : undefined,\n firstName: '',\n lastName: '',\n email: '',\n formFields: {},\n legalConsentResponses: (legalConsent?.communicationConsentCheckboxes ?? []).map((c) => ({\n communicationTypeId: c.communicationTypeId,\n consented: false,\n })),\n },\n })\n\n const submit = handleSubmit(async (data) => {\n await onSubmit({ ...data, meetingId, startTimeMs, durationMs, timezone, ...getSignals() })\n })\n\n const fieldError = (name: string): string | undefined => {\n const err = (errors.formFields as Record<string, { message?: string }> | undefined)?.[name]\n return err?.message\n }\n\n // Field chrome mirrors ContactForm 1:1 (`contact/contact-form.tsx`) — the\n // booking form must be indistinguishable from every other form in the app.\n const inputClass = 'bg-ods-card border-ods-border text-ods-text-primary placeholder-ods-text-secondary px-3 h-12'\n\n return (\n <form onSubmit={submit} className=\"flex flex-col space-y-4 md:space-y-6\" noValidate>\n <HoneypotField {...honeypotInputProps} />\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4 md:gap-6\">\n <div className=\"flex flex-col\">\n <Label htmlFor=\"ms-first-name\">\n First name<span className=\"text-ods-accent\">*</span>\n </Label>\n <Input\n id=\"ms-first-name\"\n autoComplete=\"given-name\"\n placeholder=\"Jane\"\n aria-invalid={!!errors.firstName}\n className={inputClass}\n {...register('firstName')}\n />\n {errors.firstName && <span className=\"text-ods-error text-h6 mt-1\">{errors.firstName.message}</span>}\n </div>\n <div className=\"flex flex-col\">\n <Label htmlFor=\"ms-last-name\">\n Last name<span className=\"text-ods-accent\">*</span>\n </Label>\n <Input\n id=\"ms-last-name\"\n autoComplete=\"family-name\"\n placeholder=\"Doe\"\n aria-invalid={!!errors.lastName}\n className={inputClass}\n {...register('lastName')}\n />\n {errors.lastName && <span className=\"text-ods-error text-h6 mt-1\">{errors.lastName.message}</span>}\n </div>\n </div>\n\n <div className=\"flex flex-col\">\n <Label htmlFor=\"ms-email\">\n Email<span className=\"text-ods-accent\">*</span>\n </Label>\n <Input\n id=\"ms-email\"\n type=\"email\"\n autoComplete=\"email\"\n placeholder=\"jane@company.com\"\n aria-invalid={!!errors.email}\n className={inputClass}\n {...register('email')}\n />\n {errors.email && <span className=\"text-ods-error text-h6 mt-1\">{errors.email.message}</span>}\n </div>\n\n {supportedFields.map((field) => (\n <div key={field.name} className=\"flex flex-col\">\n <Label htmlFor={`ms-q-${field.name}`}>\n {field.label}\n {field.required && <span className=\"text-ods-accent\">*</span>}\n </Label>\n {field.type === 'textarea' && (\n <Textarea\n id={`ms-q-${field.name}`}\n className=\"bg-ods-card border-ods-border text-ods-text-primary placeholder-ods-text-secondary px-3\"\n {...register(`formFields.${field.name}` as never)}\n />\n )}\n {field.type === 'text' && (\n <Input\n id={`ms-q-${field.name}`}\n className=\"bg-ods-card border-ods-border text-ods-text-primary placeholder-ods-text-secondary px-3 h-12\"\n {...register(`formFields.${field.name}` as never)}\n />\n )}\n {(field.type === 'select' || field.type === 'radio') && (\n <Controller\n control={control}\n name={`formFields.${field.name}` as never}\n render={({ field: rhf }) =>\n field.type === 'select' ? (\n <Select value={(rhf.value as string) ?? ''} onValueChange={rhf.onChange}>\n <SelectTrigger id={`ms-q-${field.name}`}>\n <SelectValue placeholder=\"Select…\" />\n </SelectTrigger>\n <SelectContent>\n {(field.options ?? []).map((opt) => (\n <SelectItem key={opt} value={opt}>\n {opt}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n ) : (\n <RadioGroup value={(rhf.value as string) ?? ''} onValueChange={rhf.onChange}>\n {(field.options ?? []).map((opt) => (\n <div key={opt} className=\"flex items-center gap-[var(--spacing-system-xs)]\">\n <RadioGroupItem id={`ms-q-${field.name}-${opt}`} value={opt} />\n <Label htmlFor={`ms-q-${field.name}-${opt}`}>{opt}</Label>\n </div>\n ))}\n </RadioGroup>\n )\n }\n />\n )}\n {field.type === 'checkbox' && (\n <Controller\n control={control}\n name={`formFields.${field.name}` as never}\n render={({ field: rhf }) => (\n // The question's Label above already carries the text and\n // binds to this id — a second inline label would double the\n // visible text AND the accessible name.\n <div className=\"flex items-center gap-[var(--spacing-system-xs)]\">\n <Checkbox\n id={`ms-q-${field.name}`}\n checked={Boolean(rhf.value)}\n onCheckedChange={(v) => rhf.onChange(v === true)}\n />\n </div>\n )}\n />\n )}\n {fieldError(field.name) && (\n <span className=\"text-ods-error text-h6 mt-1\">{fieldError(field.name)}</span>\n )}\n </div>\n ))}\n\n {legalConsent && (\n <div className=\"flex flex-col gap-[var(--spacing-system-xs)] border border-ods-border rounded-md p-[var(--spacing-system-md)]\">\n {/* GDPR surface — HubSpot's consent copy rendered VERBATIM, never edited. */}\n <p className=\"text-h6 text-ods-text-secondary\">{legalConsent.processingConsentText}</p>\n {legalConsent.communicationConsentText && (\n <p className=\"text-h6 text-ods-text-secondary\">{legalConsent.communicationConsentText}</p>\n )}\n <Controller\n control={control}\n name=\"legalConsentResponses\"\n render={({ field: rhf }) => (\n <>\n {legalConsent.communicationConsentCheckboxes.map((box) => {\n const responses = (rhf.value ?? []) as Array<{ communicationTypeId: string; consented: boolean }>\n const current = responses.find((r) => r.communicationTypeId === box.communicationTypeId)\n return (\n <div key={box.communicationTypeId} className=\"flex items-center gap-[var(--spacing-system-xs)]\">\n <Checkbox\n id={`ms-consent-${box.communicationTypeId}`}\n checked={current?.consented ?? false}\n onCheckedChange={(v) =>\n rhf.onChange(\n responses.map((r) =>\n r.communicationTypeId === box.communicationTypeId ? { ...r, consented: v === true } : r,\n ),\n )\n }\n />\n <Label htmlFor={`ms-consent-${box.communicationTypeId}`}>\n {box.label}\n {box.required ? ' *' : ''}\n </Label>\n </div>\n )\n })}\n </>\n )}\n />\n {legalConsent.privacyPolicyText && (\n <p className=\"text-h6 text-ods-text-secondary\">{legalConsent.privacyPolicyText}</p>\n )}\n {errors.legalConsentResponses && (\n <p className=\"text-h6 text-ods-error\">{errors.legalConsentResponses.message as string}</p>\n )}\n </div>\n )}\n\n {/* Step navigation back to the calendar lives in the step header (the\n app-standard BackButton, rendered by the parent) — the form ships\n only its submit. */}\n <div className=\"flex\">\n <Button type=\"submit\" loading={isSubmitting} disabled={isSubmitting} className=\"w-full md:w-auto\">\n Confirm booking\n </Button>\n </div>\n </form>\n )\n}\n","'use client'\n\nimport { formatDurationCompact } from '../../utils/format'\nimport type { BookingConfirmation } from '../../schemas/meeting-booking-schema'\n\n/**\n * Confirmation — terminal success state after a completed booking. Renders\n * the booked instant in the visitor's resolved zone. Deliberately NO\n * follow-up CTA: the job is done, the calendar invite is the next touchpoint\n * (a \"book another\" button here tested as noise).\n */\n\nexport interface ConfirmationProps {\n confirmation: BookingConfirmation\n timezone: string\n}\n\nexport function Confirmation({ confirmation, timezone }: ConfirmationProps) {\n const when = new Intl.DateTimeFormat(undefined, {\n timeZone: timezone,\n weekday: 'long',\n month: 'long',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n timeZoneName: 'short',\n }).format(new Date(confirmation.startTimeMs))\n\n return (\n <div className=\"flex flex-1 flex-col items-center justify-center gap-[var(--spacing-system-md)] text-center\">\n <p className=\"text-h1\" role=\"img\" aria-label=\"Celebration\">\n 🎉\n </p>\n <h3 className=\"text-h3 text-ods-text-primary\">You&apos;re booked</h3>\n <p className=\"text-h5 text-ods-text-primary\">{confirmation.title}</p>\n <p className=\"text-h6 text-ods-text-secondary\">\n {when} · {formatDurationCompact(confirmation.durationMs / 1000)}\n </p>\n <p className=\"text-h6 text-ods-text-secondary\">A calendar invite is on its way to your email.</p>\n </div>\n )\n}\n","'use client'\n\n/**\n * `<MeetingSchedulerDirectory />` — the embeddable scheduling DIRECTORY\n * block: clickable row cards (host avatars, title/description, For-<audience>\n * chip, next available time, boxed chevron) + the house\n * `PersistentPaginationWrapper`, fed by a host proxy's `GET\n * {apiBaseUrl}/api/meetings` (see `docs/EMBEDDING_HUBSPOT_MEETINGS.md`).\n * Rows navigate to `{bookingBasePath}/{slug}` — pair with\n * `<HubSpotMeetingScheduler>` on that page for the full white-label flow.\n *\n * Layout stability is engineered, not incidental: the rows area reserves one\n * FULL page (pageSize × 80px rows + gaps), the pagination slot holds its\n * measured 72px even when hidden, and the loading state renders same-shell\n * skeleton rows — so nothing below the block ever jumps across\n * loading ⇄ loaded ⇄ page changes ⇄ short last pages.\n *\n * SSR/host mode: pass `initialData` (host-fetched) to skip the client fetch.\n * Client/embed mode: omit it — skeleton page + self-fetch via `contentFetch`\n * (embed-auth adapters inherited for free).\n */\n\nimport { useEffect, useMemo, useState } from 'react'\nimport { ChevronRight } from 'lucide-react'\nimport Link from '../../embed-shims/next-link'\nimport { contentFetch } from '../../utils/embed-content-fetch'\nimport { formatDurationCompact } from '../../utils/format'\nimport { cn } from '../../utils/cn'\nimport { AvatarStack, StatusBadge, Skeleton } from '../ui'\nimport { EmptyState } from '../empty-state'\nimport { PersistentPaginationWrapper } from '../persistent-pagination'\nimport type { SchedulingLink, SchedulingLinksPayload } from '../../schemas/meeting-booking-schema'\n\nexport interface MeetingSchedulerDirectoryProps {\n /** Endpoints prefix, default '' (same-origin `/api/meetings`). */\n apiBaseUrl?: string\n /** Host-fetched seed — skips the client fetch when provided. */\n initialData?: SchedulingLinksPayload | null\n /** `scope=all` — full-portal view (non-conforming links under \"other\"). */\n includeAll?: boolean\n /** Rows per page; also the skeleton row count and the reserved rows-area height. */\n pageSize?: number\n /** Rows link to `${bookingBasePath}/${slug}`. */\n bookingBasePath?: string\n /** EmptyState CTA target for error/empty states (omit → no CTA). */\n contactHref?: string\n className?: string\n}\n\n/** Zone-aware \"next available\" label — resolved POST-mount (SSR renders the\n * static caption only, so server and client first paint agree). */\nfunction useNextAvailableLabel(ms: number | null): string | null {\n const [label, setLabel] = useState<string | null>(null)\n useEffect(() => {\n if (ms == null) return\n try {\n setLabel(\n new Intl.DateTimeFormat(undefined, {\n weekday: 'short',\n month: 'short',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n }).format(new Date(ms)),\n )\n } catch {\n setLabel(null)\n }\n }, [ms])\n return label\n}\n\nfunction DirectoryRow({\n link,\n audienceLabel,\n bookingBasePath,\n}: {\n link: SchedulingLink\n audienceLabel: string\n bookingBasePath: string\n}) {\n const nextLabel = useNextAvailableLabel(link.nextAvailableMs)\n\n const subtitle = [\n link.description,\n link.durationsMinutes.length > 0 ? link.durationsMinutes.map((m) => formatDurationCompact(m * 60)).join(' / ') : null,\n link.kind === 'team' ? 'Team' : null,\n ]\n .filter(Boolean)\n .join(' • ')\n\n return (\n <Link\n href={`${bookingBasePath}/${link.slug}`}\n prefetch={false}\n className=\"block rounded-lg no-underline text-inherit focus:outline-none focus-visible:ring-2 focus-visible:ring-ods-accent\"\n >\n <div className=\"flex w-full items-center gap-4 px-4 py-3 min-h-20 rounded-lg border border-ods-border bg-ods-card transition-colors duration-150 hover:bg-ods-bg-hover\">\n {/* Title leads at a fixed X; hosts live in the right meta cluster. */}\n <div className=\"flex min-w-0 flex-1 flex-col justify-center text-left\">\n <p className=\"text-h3 text-ods-text-primary truncate\">{link.title}</p>\n {subtitle && <p className=\"text-h6 text-ods-text-secondary truncate\">{subtitle}</p>}\n </div>\n\n {/* No audience entity (scope=all \"other\" group) → no chip. */}\n {audienceLabel && (\n <StatusBadge text={`For ${audienceLabel}`} singleLine className=\"shrink-0 hidden sm:inline-flex\" />\n )}\n\n {/* Hosts stay visible on mobile too — the facepile is the row's \"who\n you're booking with\" signal; only the chip and next-available meta\n collapse on narrow widths. */}\n <AvatarStack people={link.hosts} max={3} size=\"md\" className=\"flex shrink-0\" />\n\n <div className=\"hidden md:flex w-44 shrink-0 flex-col items-end justify-center\">\n <p className=\"text-h6 text-ods-text-secondary\">Next available</p>\n <p className=\"text-h6 text-ods-text-primary min-h-5\">\n {link.nextAvailableMs == null ? 'No times published' : (nextLabel ?? ' ')}\n </p>\n </div>\n\n {/* House boxed chevron (ChatTicketItem). */}\n <span className=\"flex size-12 shrink-0 items-center justify-center rounded-md border border-ods-border bg-ods-card\">\n <ChevronRight className=\"size-6 text-ods-text-secondary\" />\n </span>\n </div>\n </Link>\n )\n}\n\n/** Same-shell loading row (co-located-skeleton convention). */\nexport function MeetingSchedulerDirectoryRowSkeleton() {\n return (\n <div className=\"flex w-full items-center gap-4 px-4 py-3 min-h-20 rounded-lg border border-ods-border bg-ods-card\">\n <div className=\"flex min-w-0 flex-1 flex-col justify-center gap-1\">\n <Skeleton className=\"h-5 w-1/2\" />\n <Skeleton className=\"h-4 w-1/3\" />\n </div>\n <Skeleton className=\"hidden sm:block h-6 w-28 shrink-0\" />\n <Skeleton className=\"h-10 w-10 shrink-0 rounded-full\" />\n <div className=\"hidden md:flex w-44 shrink-0 flex-col items-end gap-1\">\n <Skeleton className=\"h-4 w-24\" />\n <Skeleton className=\"h-5 w-32\" />\n </div>\n <Skeleton className=\"size-12 shrink-0 rounded-md\" />\n </div>\n )\n}\n\n/** Measured pagination-slot height (PersistentPaginationWrapper = 72px). */\nconst PAGINATION_SLOT_H = 'h-[4.5rem]'\n\nexport function MeetingSchedulerDirectory({\n apiBaseUrl = '',\n initialData = null,\n includeAll = false,\n pageSize = 6,\n bookingBasePath = '/schedule-a-call',\n contactHref,\n className,\n}: MeetingSchedulerDirectoryProps) {\n const [data, setData] = useState<SchedulingLinksPayload | null>(initialData)\n const [isLoading, setIsLoading] = useState(initialData === null)\n const [error, setError] = useState<string | null>(null)\n const [page, setPage] = useState(1)\n\n useEffect(() => {\n if (initialData !== null) return // host-seeded — skip the client fetch\n const controller = new AbortController()\n let active = true\n const load = async () => {\n setIsLoading(true)\n setError(null)\n try {\n const qs = includeAll ? '?scope=all' : ''\n const res = await contentFetch(`${apiBaseUrl}/api/meetings${qs}`, { signal: controller.signal })\n if (!res.ok) throw new Error(`meetings ${res.status}`)\n const payload = (await res.json()) as SchedulingLinksPayload\n if (active) setData(payload)\n } catch (err) {\n if (!active || (err instanceof DOMException && err.name === 'AbortError')) return\n setError(err instanceof Error ? err.message : 'Failed to fetch scheduling links')\n } finally {\n if (active) setIsLoading(false)\n }\n }\n void load()\n return () => {\n active = false\n controller.abort()\n }\n }, [initialData, includeAll, apiBaseUrl])\n\n const rows = useMemo(() => {\n const purposes = data?.purposes ?? []\n return purposes.flatMap((p) => p.links.map((link) => ({ link, audienceLabel: p.label })))\n }, [data])\n\n const totalPages = Math.max(1, Math.ceil(rows.length / pageSize))\n const pageRows = rows.slice((page - 1) * pageSize, page * pageSize)\n\n // Reserved rows-area height: pageSize × 80px rows + (pageSize−1) × 16px gaps.\n const rowsMinHeight = pageSize * 80 + (pageSize - 1) * 16\n\n if (isLoading && !data) {\n return (\n <div className={cn('flex flex-col', className)}>\n <div className=\"flex w-full flex-col gap-4\" style={{ minHeight: rowsMinHeight }}>\n {Array.from({ length: pageSize }, (_, i) => (\n <MeetingSchedulerDirectoryRowSkeleton key={i} />\n ))}\n </div>\n <div className={cn('mt-8 flex w-full items-center justify-center', PAGINATION_SLOT_H)}>\n <Skeleton className=\"h-10 w-64\" />\n </div>\n </div>\n )\n }\n\n if (error) {\n return (\n <div className={cn('flex flex-col justify-center', className)} style={{ minHeight: rowsMinHeight + 104 }}>\n <EmptyState\n type=\"generic\"\n title=\"We couldn't load available call times\"\n description=\"Something went wrong on our side. Try again in a minute, or reach us directly.\"\n {...(contactHref ? { ctaText: 'Contact us', ctaHref: contactHref } : {})}\n />\n </div>\n )\n }\n\n if (rows.length === 0) {\n return (\n <div className={cn('flex flex-col justify-center', className)} style={{ minHeight: rowsMinHeight + 104 }}>\n <EmptyState\n type=\"generic\"\n title=\"No call times are published right now\"\n description=\"We're updating our calendars. Reach us directly and we'll set something up.\"\n {...(contactHref ? { ctaText: 'Contact us', ctaHref: contactHref } : {})}\n />\n </div>\n )\n }\n\n return (\n <div className={cn('flex flex-col', className)}>\n <div className=\"flex w-full flex-col gap-4\" style={{ minHeight: rowsMinHeight }}>\n {pageRows.map(({ link, audienceLabel }) => (\n <DirectoryRow key={link.id} link={link} audienceLabel={audienceLabel} bookingBasePath={bookingBasePath} />\n ))}\n </div>\n {/* House pagination block — dims in place during refetches; the slot\n holds its measured height even at ≤1 page (where the inner\n UnifiedPagination returns null). */}\n <div className={cn('mt-8 flex w-full items-center justify-center', PAGINATION_SLOT_H)}>\n {totalPages > 1 && (\n <PersistentPaginationWrapper\n isLoading={isLoading}\n currentPage={page}\n totalPages={totalPages}\n onPageChange={setPage}\n variant=\"blog\"\n />\n )}\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":["useCallback","useEffect","useMemo","useRef","useState","useMemo","Fragment","jsx","jsxs","_","useMemo","useMemo","Fragment","jsx","jsxs","useMemo","jsx","jsxs","useEffect","useMemo","useState","ChevronRight","jsx","jsxs","useState","useEffect","ChevronRight","useMemo","jsx","jsxs","useState","useEffect","useMemo","useRef","useCallback"]}
1
+ {"version":3,"sources":["../../../src/components/meeting-scheduler/index.tsx","../../../src/hooks/use-meeting-booking.ts","../../../src/schemas/meeting-booking-schema.ts","../../../src/components/meeting-scheduler/context-panel.tsx","../../../src/components/meeting-scheduler/slot-picker.tsx","../../../src/components/meeting-scheduler/booking-form.tsx","../../../src/components/meeting-scheduler/confirmation.tsx","../../../src/components/meeting-scheduler/directory.tsx"],"sourcesContent":["'use client'\n\n/**\n * `<HubSpotMeetingScheduler />` — natively-branded booking flow over the\n * HubSpot Meetings scheduler, rendered entirely with ODS primitives (no\n * iframe, no third-party chrome, no scripts). Data flows exclusively through\n * a HOST PROXY (`GET {apiBaseUrl}/api/meetings/availability`,\n * `POST {apiBaseUrl}/api/meetings/book`) — the widget has zero HubSpot\n * knowledge and no secrets; see `docs/EMBEDDING_HUBSPOT_MEETINGS.md` for the\n * endpoint contract a host must serve (external embedders proxy through\n * their own backend — the book route is IP-rate-limited and bot-gated).\n *\n * LAYOUT (Calendly-anatomy, conversion-first): one bordered card, two\n * panels. Left/top — the CONTEXT panel: an optional host-level back edge\n * (`onBack`), host identity (avatar + name + title from\n * `availability.hosts`), meeting title/description, duration chips (the\n * duration choice lives here, NOT as a wizard step — the visitor always\n * lands straight on the calendar), and the resolved timezone.\n * Right/bottom — the ACTION panel: calendar + time slots (first available\n * day auto-selected so slots are visible immediately), then the details\n * form, then the confirmation. On the slot step that panel splits again —\n * calendar column, then times column — each carrying its own heading (owned\n * by `SlotPicker`, so loading and loaded agree). Panels stack on mobile.\n *\n * Two modes, one component:\n * - SSR mode: pass `initialAvailability` (host-fetched) — the\n * zone-independent shell (context panel, calendar) renders server-side;\n * slot TIME labels wait for the client zone resolution (deterministic\n * first paint, no hydration mismatch), and availability is refetched\n * unconditionally after mount (the seed is scaffolding for a 60s-volatile\n * resource, not truth).\n * - Client mode: omit the seed — skeleton + self-fetch on mount.\n *\n * State machine: slot → details → confirmed, with a back edge, a \"book\n * another\" reset, and a `submitting` lock as the double-booking guard.\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { cn } from '../../utils/cn'\nimport { formatDurationCompact } from '../../utils/format'\nimport { Button } from '../ui'\nimport { useHumanitySignals } from '../../hooks/use-humanity-signals'\nimport { useMeetingBooking } from '../../hooks/use-meeting-booking'\nimport {\n isSupportedFormField,\n type BookingConfirmation,\n type MeetingAvailability,\n type MeetingBookingErrorCode,\n type MeetingHost,\n} from '../../schemas/meeting-booking-schema'\nimport { SchedulerContextPanel, ContextPanelSkeleton } from './context-panel'\nimport { SlotPicker, SlotPickerSkeleton, dayKeyInZone } from './slot-picker'\nimport { BookingForm } from './booking-form'\nimport { Confirmation } from './confirmation'\n\nexport interface HubSpotMeetingSchedulerProps {\n /** Directory id of the meeting link (from the host's `/api/meetings` payload). */\n meetingId: string\n /** Endpoints prefix, default '' (same-origin `/api/meetings/*`) — FaqSection precedent. */\n apiBaseUrl?: string\n /** SSR-mode seed (host-fetched). Omitted → client mode (self-fetch on mount). */\n initialAvailability?: MeetingAvailability\n /** Meeting title shown in the context panel (host page owns the h1). */\n title?: string\n /** Short description under the title in the context panel. */\n description?: string | null\n /** Override the hosts shown (defaults to `availability.hosts`). */\n hosts?: MeetingHost[]\n /** Pin the DISPLAY zone (rendering only — never sent upstream, never a cache key). */\n displayTimezone?: string\n /** The link's public HubSpot booking URL — the \"Open in HubSpot\" escape hatch target. */\n fallbackUrl?: string\n /**\n * Host-level exit, rendered as a back edge at the top of the context panel\n * (and in the loading skeleton, so it doesn't pop in). This is \"leave the\n * scheduler\", NOT the details step's back edge — it is suppressed once a\n * slot is chosen so only one Back is ever on screen. Omit it and no back\n * affordance renders at all.\n */\n onBack?: () => void\n onBooked?: (b: BookingConfirmation) => void\n className?: string\n}\n\ntype Step = 'slot' | 'details' | 'confirmed'\n\n/** Context-panel geometry — ONE definition for the loaded card and the\n * loading skeleton, which is what keeps the two footprint-identical. */\nconst CONTEXT_PANEL_CLASS =\n 'p-[var(--spacing-system-l)] shrink-0 lg:w-[280px] border-b lg:border-b-0 lg:border-r border-ods-border lg:min-h-0 lg:overflow-y-auto'\n\n/**\n * Action panel: the elastic half, scrolling inside the fixed card.\n *\n * Padded only from `lg`. Below it the calendar and times are separate sections\n * with a divider between them, and each pads itself — one padding here would\n * inset that divider from the card's edges instead of letting it run the full\n * width the way every other rule in this card does.\n */\nconst ACTION_PANEL_CLASS = 'flex-1 min-w-0 flex flex-col md:min-h-0 lg:p-[var(--spacing-system-l)]'\n\n/**\n * The action panel's inset for the steps that are ONE block — details,\n * confirmation, the slot step's error line.\n *\n * The panel itself is padded only from `lg`, because below that the slot step\n * is two sections with a rule between them and each pads itself: one padding\n * on the panel would inset that rule instead of letting it run the card's full\n * width, the way every other rule here does. Every step that is NOT split into\n * sections has to bring the same inset with it — without it the form ran edge\n * to edge on tablet, its inputs touching the card's border.\n *\n * `md:min-h-0 md:overflow-y-auto` comes with it: from `md` up the card states\n * a height, and these steps have no inner scroller of their own the way the\n * times column does, so this is where a long form gets to scroll instead of\n * being cut off at the card's edge.\n */\nconst PANEL_STEP_CLASS = 'p-[var(--spacing-system-l)] md:min-h-0 md:overflow-y-auto lg:p-0'\n\n/**\n * The widget's height on desktop: a FIXED 380px, and the ONLY size the whole\n * card states.\n *\n * A floor was not enough. Every stage has a different natural height — the\n * context panel with two hosts ~340, the calendar column ~290, a degraded\n * stage two lines — so `min-height` only pinned the SHORT ones and let the\n * tall one push the wrapper, which is exactly the screen-shake this removes.\n * With a fixed height the box is stated once and nothing inside can move it;\n * anything taller scrolls in place.\n *\n * The elastic half derives from it: the panel passes it to the slot row and\n * the times column scrolls inside what is left. The calendar does not stretch\n * — a date grid has a size, and stretching one is how a month turns into\n * page-tall bands the moment a layout stops pinning a height. It states a\n * WIDTH (`CALENDAR_W`) and its square cells make it as tall as it is wide;\n * that width is chosen to fit inside this number, and the two are checked\n * against each other in one place rather than three.\n *\n * Six week rows, always — `fixedWeeks` — is why a height can be stated at all:\n * a 5-week month and a 6-week month occupy the same box, so paging the\n * calendar cannot resize the card.\n *\n * Exported because the stability has to survive OUTSIDE the card too: a host\n * that swaps the scheduler in and out of a slot (the onboarding \"Book a call\"\n * promo does exactly that) reserves the same box for whatever it shows\n * instead, so the swap moves nothing below it.\n *\n * Goes on the BORDERED element, always. Under `box-sizing: border-box` the\n * declared height swallows the 1px edges, so a card that carries the border\n * and a host box that carries its own both come out at exactly 380 — put it on\n * an inner wrapper instead and the border lands OUTSIDE the 380, making that\n * card 2px taller than everything it is supposed to match.\n *\n * TWO numbers, because the card has two shapes: 380 as a sidebar beside the\n * calendar (`lg`), 550 as a header strip over it (`md`). Phones get neither —\n * there a card the height of the hand holding it is the right answer and the\n * page is the scroller.\n *\n * A host that reserves this box owes it one thing: content that ADAPTS to the\n * height rather than assuming it. The onboarding promo does it by letting its\n * video take the leftover space at 16:9; a stand-in that just stacks fixed\n * blocks will overflow the shorter of the two.\n */\nexport const MEETING_SCHEDULER_H = 'md:h-[34.375rem] lg:h-[23.75rem]'\n\n/**\n * The two-panel card's box, shared verbatim by the loading skeleton and the\n * loaded card so neither can drift from the other.\n *\n * Both heights come from {@link MEETING_SCHEDULER_H}; what this adds is the\n * flex column that makes them work. On TABLET the height is stated for the\n * same reason the desktop one is: a CEILING only pins the tall stages. The short ones — the empty\n * month (\"No available times in September\"), a five-week month, the cold-start\n * skeleton — each settled at their own natural height, so paging a month or\n * waiting out a fetch resized the card under the visitor. Stating the height\n * makes every stage the same box and moves the variation inside, where the\n * times column already scrolls.\n *\n * 550, not the mock's 498: the mock draws one host and no timezone field, and\n * a real link has two hosts and a zone the visitor must be able to change.\n * That is ~55px of content the mock never budgeted for, and the month below it\n * is what would otherwise pay for it.\n *\n * The strip is `shrink-0`, so what is left goes to the slot row, which spends\n * it on a calendar sized to fit (no scroll) and a times list that scrolls on\n * its own.\n *\n * `md:flex md:flex-col` is what makes that possible at all. A stated height on\n * a plain block only clips (this card is `overflow-hidden` for its corners) —\n * it is the flex column plus the inner wrapper's `min-h-0` that lets the\n * content shrink into the height instead of being cut off by it.\n */\nconst CARD_CLASS = cn(\n 'rounded-md border border-ods-border bg-ods-card overflow-hidden',\n 'md:flex md:flex-col',\n MEETING_SCHEDULER_H,\n)\n\n/** Context strip over action panel, side by side from `lg`. `md:flex-1\n * md:min-h-0` is the pair that makes it exactly as tall as the card states —\n * grow into a stated height that content does not reach, shrink into one it\n * overruns — so neither a short stage nor a long one changes the box. */\nconst CARD_INNER_CLASS = 'flex flex-col md:min-h-0 md:flex-1 lg:h-full lg:flex-row'\n\n/** The two-line stages (load failure, \"booked on HubSpot\") — the SAME box as\n * the booking card, because these are stages of one widget in one slot and a\n * stage that collapses to two lines would snap the page shut under the\n * visitor. Their content is centred in it and scrolls if a message ever\n * outgrows it. */\nconst CARD_DEGRADED_CLASS = cn(\n 'rounded-md border border-ods-border bg-ods-card p-[var(--spacing-system-lf)] flex flex-col items-start gap-[var(--spacing-system-m)]',\n 'md:justify-center md:overflow-y-auto',\n MEETING_SCHEDULER_H,\n)\n\n/**\n * Fail-closed gate: a link whose declared questions include an unsupported\n * type, or whose consent block is malformed, must NOT render a half-working\n * native form (a silently dropped required question or missing consent copy\n * is worse than no native form) — the escape hatch takes over.\n */\nfunction isNativelyBookable(availability: MeetingAvailability): boolean {\n if (!availability.formFields.every(isSupportedFormField)) return false\n const consent = availability.legalConsent\n if (consent) {\n if (typeof consent.processingConsentText !== 'string') return false\n if (!Array.isArray(consent.communicationConsentCheckboxes)) return false\n }\n return true\n}\n\nexport function HubSpotMeetingScheduler({\n meetingId,\n apiBaseUrl = '',\n initialAvailability,\n title,\n description,\n hosts,\n displayTimezone,\n fallbackUrl,\n onBack,\n onBooked,\n className,\n}: HubSpotMeetingSchedulerProps) {\n const {\n availability,\n isLoadingAvailability,\n isFetchingAvailability,\n availabilityError,\n monthOffset,\n setMonthOffset,\n refetchAvailability,\n book,\n isSubmitting,\n } = useMeetingBooking({ meetingId, apiBaseUrl, initialAvailability })\n\n // Zone resolution happens POST-mount (Intl) unless the embedder pins one —\n // the initial (server) render stays deterministic; time labels appear once\n // a zone exists.\n const [timezone, setTimezone] = useState<string | null>(displayTimezone ?? null)\n useEffect(() => {\n if (displayTimezone) return\n try {\n setTimezone(Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC')\n } catch {\n setTimezone('UTC')\n }\n }, [displayTimezone])\n\n const [step, setStep] = useState<Step>('slot')\n const [durationMs, setDurationMs] = useState<number | null>(null)\n const [selectedDay, setSelectedDay] = useState<string | null>(null)\n const [selectedSlot, setSelectedSlot] = useState<number | null>(null)\n const [confirmation, setConfirmation] = useState<BookingConfirmation | null>(null)\n const [bookingError, setBookingError] = useState<MeetingBookingErrorCode | null>(null)\n\n const { honeypotInputProps, getSignals, resetSignals } = useHumanitySignals()\n\n // Reset the machine when the host switches links.\n useEffect(() => {\n setStep('slot')\n setDurationMs(null)\n setSelectedDay(null)\n setSelectedSlot(null)\n setConfirmation(null)\n setBookingError(null)\n autoAdvanceCount.current = 0\n }, [meetingId])\n\n const durations = availability?.durationsMs ?? []\n const shownHosts = hosts ?? availability?.hosts ?? []\n\n // Default to the first offered duration — the visitor lands straight on\n // the calendar; duration is a context-panel chip, not a wizard step.\n useEffect(() => {\n if (durationMs == null && durations.length > 0) setDurationMs(durations[0])\n }, [durationMs, durations])\n\n const slots = useMemo(() => {\n if (!availability || durationMs == null) return []\n return availability.slotsByDurationMs[String(durationMs)] ?? []\n }, [availability, durationMs])\n\n // Auto-select the first day WITH SLOTS IN THE VISIBLE MONTH (once the zone\n // is known) so the visitor sees concrete times immediately — never a dead\n // \"pick a day\" state, and NEVER a day from another month than the calendar\n // shows (HubSpot's monthOffset payloads can carry near-term slots outside\n // the requested month — the visible grid is the authority).\n useEffect(() => {\n if (step !== 'slot' || !timezone) return\n const now = new Date()\n const visible = new Date(now.getFullYear(), now.getMonth() + monthOffset, 1)\n const monthPrefix = `${visible.getFullYear()}-${String(visible.getMonth() + 1).padStart(2, '0')}`\n const inMonth = slots.filter((ms) => dayKeyInZone(ms, timezone).startsWith(monthPrefix))\n if (inMonth.length === 0) {\n if (selectedDay && !selectedDay.startsWith(monthPrefix)) setSelectedDay(null)\n return\n }\n const dayKeys = new Set(inMonth.map((ms) => dayKeyInZone(ms, timezone)))\n if (selectedDay && dayKeys.has(selectedDay)) return\n setSelectedDay(dayKeyInZone(inMonth[0], timezone))\n }, [step, timezone, slots, selectedDay, monthOffset])\n\n // Fully-booked month with more ahead → auto-advance (bounded, ≤3 hops)\n // before showing the empty state.\n const autoAdvanceCount = useRef(0)\n useEffect(() => {\n if (\n step === 'slot' &&\n availability &&\n !isFetchingAvailability &&\n durationMs != null &&\n slots.length === 0 &&\n availability.hasMore &&\n autoAdvanceCount.current < 3\n ) {\n autoAdvanceCount.current += 1\n setMonthOffset(monthOffset + 1)\n }\n }, [step, availability, isFetchingAvailability, durationMs, slots, monthOffset, setMonthOffset])\n\n /** The back edge's destination from the form: the calendar, with any\n * submit error cleared so the visitor doesn't carry it back. */\n const backToSlot = useCallback(() => {\n setStep('slot')\n setBookingError(null)\n }, [])\n\n const handleSubmit = useCallback(\n async (payload: Record<string, unknown>) => {\n setBookingError(null)\n const result = await book(payload)\n if (result.ok && result.confirmation) {\n setConfirmation(result.confirmation)\n setStep('confirmed')\n onBooked?.(result.confirmation)\n return\n }\n const code = result.code ?? 'TEMPORARILY_UNAVAILABLE'\n setBookingError(code)\n if (code === 'SLOT_TAKEN') {\n // Recover: refresh the grid (the slot vanishes) and reset the timing\n // signal so the retry isn't flagged too-fast.\n setSelectedSlot(null)\n setStep('slot')\n resetSignals()\n void refetchAvailability()\n }\n },\n [book, onBooked, refetchAvailability, resetSignals],\n )\n\n const escapeHatch = fallbackUrl ? (\n <Button variant=\"outline\" size=\"small-legacy\" href={fallbackUrl} openInNewTab>\n Open in HubSpot\n </Button>\n ) : null\n\n // ---- terminal / degraded states -----------------------------------------\n\n if (isLoadingAvailability && !availability) {\n // COLD start only — a month already in the query cache renders straight\n // into the loaded card. Composed inside the SAME wrappers as that card\n // (`ContextPanelSkeleton` beside the real slot-area layout), so nothing\n // shifts when it swaps.\n return (\n <div className={cn(CARD_CLASS, className)}>\n <div className={CARD_INNER_CLASS}>\n <ContextPanelSkeleton onBack={onBack} className={CONTEXT_PANEL_CLASS} />\n <div className={ACTION_PANEL_CLASS}>\n <SlotPickerSkeleton monthOffset={monthOffset} />\n </div>\n </div>\n </div>\n )\n }\n\n if (availabilityError || !availability) {\n return (\n <div\n className={cn(CARD_DEGRADED_CLASS, className)}\n >\n <p className=\"text-h6 text-ods-text-secondary\">\n We couldn&apos;t load available call times. Please try again shortly.\n </p>\n {escapeHatch}\n </div>\n )\n }\n\n if (!isNativelyBookable(availability)) {\n // Fail closed — never render a half-working native form on a link with\n // questions or consent we can't faithfully reproduce.\n return (\n <div\n className={cn(CARD_DEGRADED_CLASS, className)}\n >\n <p className=\"text-h6 text-ods-text-secondary\">This meeting type is booked directly on HubSpot.</p>\n {escapeHatch}\n </div>\n )\n }\n\n // ---- the card ------------------------------------------------------------\n\n return (\n <div className={cn(CARD_CLASS, className)}>\n <div className={CARD_INNER_CLASS}>\n <SchedulerContextPanel\n hosts={shownHosts}\n title={title}\n description={description}\n durationsMs={durations}\n selectedDurationMs={durationMs}\n onSelectDuration={(ms) => {\n setDurationMs(ms)\n setSelectedDay(null)\n setSelectedSlot(null)\n if (step === 'details') setStep('slot')\n }}\n timezone={timezone}\n onTimezoneChange={setTimezone}\n // ONE back edge for the whole card, in the panel that never swaps.\n // Its destination is simply \"the previous step\": from the form back\n // to the calendar, from the calendar out of the scheduler (if the\n // host gave us an exit at all). Two Backs on screen reading the same\n // word with different destinations was the ambiguity the details\n // step used to carry.\n onBack={step === 'details' ? backToSlot : onBack}\n locked={step !== 'slot'}\n // The zone still governs every time on screen — including the\n // summary line on the form — so the picker stays until there is\n // nothing left to re-read in it.\n showTimezone={step !== 'confirmed'}\n className={CONTEXT_PANEL_CLASS}\n />\n\n {/* Scrolls INSIDE the fixed card rather than growing it — see\n MEETING_SCHEDULER_H. `min-h-0` is what lets it: a flex item's\n default `min-height:auto` refuses to shrink below its content. */}\n <div className={ACTION_PANEL_CLASS}>\n {step === 'confirmed' && confirmation && timezone ? (\n <div className={cn('flex flex-1 flex-col', PANEL_STEP_CLASS)}>\n <Confirmation confirmation={confirmation} timezone={timezone} />\n </div>\n ) : step === 'details' && durationMs != null && selectedSlot != null && timezone ? (\n <div className={cn('flex flex-1 flex-col gap-[var(--spacing-system-m)]', PANEL_STEP_CLASS)}>\n {/* Top-aligned, and no back edge of its own: the ONE back edge\n lives in the context panel at every step (see `contextBack`),\n which is where the design puts it and the only spot that\n stays put while this side swaps between calendar and form. */}\n <p className=\"text-h4 text-ods-text-primary\">\n {new Intl.DateTimeFormat(undefined, {\n timeZone: timezone,\n weekday: 'long',\n month: 'long',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n timeZoneName: 'short',\n }).format(new Date(selectedSlot))}{' '}\n · {formatDurationCompact(durationMs / 1000)}\n </p>\n {bookingError && bookingError !== 'SLOT_TAKEN' && (\n <div className=\"flex flex-col items-start gap-[var(--spacing-system-xs)]\">\n <p className=\"text-h6 text-ods-error\">\n {bookingError === 'TEMPORARILY_UNAVAILABLE'\n ? 'Scheduling is briefly unavailable — please try again in a minute.'\n : bookingError === 'MEETING_UNAVAILABLE'\n ? 'This meeting type has reached its booking limit for today — try another time or contact us.'\n : bookingError === 'LINK_GONE'\n ? 'This meeting type is no longer available.'\n : 'Please double-check your details and try again.'}\n </p>\n {escapeHatch}\n </div>\n )}\n <BookingForm\n availability={availability}\n meetingId={meetingId}\n startTimeMs={selectedSlot}\n durationMs={durationMs}\n timezone={timezone}\n isSubmitting={isSubmitting}\n onSubmit={handleSubmit}\n honeypotInputProps={honeypotInputProps}\n getSignals={getSignals}\n />\n </div>\n ) : (\n <div className=\"flex flex-col gap-[var(--spacing-system-m)] md:min-h-0 md:flex-1\">\n {bookingError === 'SLOT_TAKEN' && (\n <p className=\"px-[var(--spacing-system-l)] pt-[var(--spacing-system-l)] text-h6 text-ods-error lg:p-0\">\n That time is no longer available — if you just submitted, check your email for a confirmation\n before rebooking.\n </p>\n )}\n {timezone && durationMs != null ? (\n // The picker owns BOTH the loading and the empty month now —\n // it keeps the calendar up either way, so there is no branch\n // here that can take it off screen mid-flow.\n <SlotPicker\n slots={slots}\n timezone={timezone}\n monthOffset={monthOffset}\n onMonthOffsetChange={(o) => {\n setSelectedDay(null)\n setSelectedSlot(null)\n setMonthOffset(o)\n }}\n selectedSlot={selectedSlot}\n onSelectSlot={(ms) => {\n setSelectedSlot(ms)\n setStep('details')\n }}\n selectedDay={selectedDay}\n onSelectDay={setSelectedDay}\n isLoading={isLoadingAvailability}\n />\n ) : durations.length === 0 && !isFetchingAvailability ? (\n // The LINK publishes nothing at all — a different thing from a\n // fully-booked month, and the only case the escape hatch is\n // the right answer to.\n <div className=\"flex flex-col items-start gap-[var(--spacing-system-m)]\">\n <p className=\"text-h6 text-ods-text-secondary\">No call times are published right now.</p>\n {escapeHatch}\n </div>\n ) : (\n // Pre-mount timezone-resolution window (every SSR load hits\n // this for one frame-batch) — same layout, real calendar, so\n // nothing shifts when the picker takes over.\n <SlotPickerSkeleton monthOffset={monthOffset} />\n )}\n </div>\n )}\n </div>\n </div>\n </div>\n )\n}\n\nexport {\n MeetingSchedulerDirectory,\n MeetingSchedulerDirectoryRowSkeleton,\n type MeetingSchedulerDirectoryProps,\n} from './directory'\nexport type { MeetingAvailability, BookingConfirmation, MeetingBookingErrorCode, MeetingHost }\nexport type { SchedulingLink, SchedulingLinksPayload } from '../../schemas/meeting-booking-schema'\n","'use client'\n\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'\nimport { useCallback, useLayoutEffect, useRef, useState } from 'react'\nimport { contentFetch } from '../utils/embed-content-fetch'\nimport { MAX_MONTH_OFFSET } from '../utils/hubspot-meetings-convention'\nimport type {\n BookingConfirmation,\n MeetingAvailability,\n MeetingBookingErrorCode,\n} from '../schemas/meeting-booking-schema'\n\n/**\n * useMeetingBooking — data layer for `<HubSpotMeetingScheduler>` (the widget\n * files never inline fetch; same split as `useContactSubmission`/ContactForm).\n *\n * REQUIRES a `QueryClientProvider` in the host, like every other query-backed\n * surface in this lib (tickets, chat, onboarding-guides). `@tanstack/react-query`\n * is a declared peer dependency; the lib never mounts a provider of its own.\n *\n * Availability is a per-month GET, cached per (host, link, month). The cache is\n * the point: paging back to a month already seen is INSTANT and silent, which\n * is what a hand-rolled `useEffect` + `useState` could not do — it re-fetched\n * and re-skeletoned the same month every time the visitor stepped back.\n *\n * Deliberately NOT `placeholderData: keepPreviousData`. The payload is\n * month-scoped, so last month's slots under this month's caption describe days\n * that aren't on screen — a grid that lights up the wrong dates is worse than\n * one that is briefly inert. What kills the flicker instead is WHERE the\n * loading state lands: the calendar is fully derivable from the date alone, so\n * it keeps rendering (with nothing selectable yet) while only the times column\n * — the part that genuinely has no answer — shows a placeholder.\n *\n * `staleTime` is deliberately short: HubSpot slots go stale in about a minute,\n * and a slot that is gone by the time it is clicked costs a booking. Fresh\n * enough to trust, cached enough not to blink.\n *\n * Booking: a mutation, guarded so a second submit can't start while one is in\n * flight (the transport never retries POST — this is the only double-booking\n * guard). Errors surface as typed `MeetingBookingErrorCode`s the widget keys\n * its recovery UI off, never as thrown rejections.\n *\n * All requests go through the host proxy (`apiBaseUrl` prefix, FaqSection\n * precedent) via `contentFetch`, so embedded hosts with an embed-auth adapter\n * inherit auth with zero extra wiring.\n */\n\nexport interface BookingResult {\n ok: boolean\n confirmation?: BookingConfirmation\n code?: MeetingBookingErrorCode\n message?: string\n}\n\nconst KNOWN_CODES: MeetingBookingErrorCode[] = [\n 'SLOT_TAKEN',\n 'VALIDATION',\n 'LINK_GONE',\n 'TEMPORARILY_UNAVAILABLE',\n 'MEETING_UNAVAILABLE',\n]\n\n/** ~half HubSpot's own slot volatility window — see the docblock. */\nconst AVAILABILITY_STALE_MS = 30_000\n\n/** Query key for one month of one link. Exported so a host can prefetch or\n * invalidate a month it knows changed (e.g. after booking elsewhere). */\nexport function meetingAvailabilityKey(apiBaseUrl: string, meetingId: string, monthOffset: number) {\n return ['meeting-availability', apiBaseUrl, meetingId, monthOffset] as const\n}\n\nexport function useMeetingBooking(options: {\n meetingId: string\n apiBaseUrl?: string\n initialAvailability?: MeetingAvailability\n}) {\n const { meetingId, apiBaseUrl = '', initialAvailability } = options\n const queryClient = useQueryClient()\n\n const seededOffset = initialAvailability?.meetingId === meetingId ? initialAvailability.monthOffset : undefined\n const [monthOffset, setMonthOffsetState] = useState(seededOffset ?? 0)\n\n const setMonthOffset = useCallback((offset: number) => {\n setMonthOffsetState(Math.max(0, Math.min(MAX_MONTH_OFFSET, offset)))\n }, [])\n\n // Link switch resets paging — without this, the new link is queried at the\n // PREVIOUS link's month. Layout effect so the query below sees the reset\n // offset in the same commit.\n const prevMeetingIdRef = useRef(meetingId)\n useLayoutEffect(() => {\n if (prevMeetingIdRef.current === meetingId) return\n prevMeetingIdRef.current = meetingId\n setMonthOffsetState(0)\n }, [meetingId])\n\n // The SSR seed covers exactly one (link, month) cell of the cache.\n const seed =\n initialAvailability?.meetingId === meetingId && initialAvailability.monthOffset === monthOffset\n ? initialAvailability\n : undefined\n\n const availabilityQuery = useQuery({\n queryKey: meetingAvailabilityKey(apiBaseUrl, meetingId, monthOffset),\n queryFn: async ({ signal }) => {\n const params = new URLSearchParams({ meeting: meetingId, monthOffset: String(monthOffset) })\n const res = await contentFetch(`${apiBaseUrl}/api/meetings/availability?${params}`, { signal })\n if (!res.ok) throw new Error(`availability ${res.status}`)\n return (await res.json()) as MeetingAvailability\n },\n staleTime: AVAILABILITY_STALE_MS,\n initialData: seed,\n // Stamped as infinitely old on purpose: the seed is first-paint\n // scaffolding for a 60-second-volatile resource, not truth, so it paints\n // immediately AND is refetched immediately.\n initialDataUpdatedAt: seed ? 0 : undefined,\n })\n\n const refetchAvailability = useCallback(async () => {\n // Recovery path (a slot taken from under the visitor): drop the cached\n // month so the grid reloads from the network, not from the copy that\n // still lists the taken slot. Best-effort — a failure leaves the stale\n // grid up rather than blanking the card.\n await queryClient\n .invalidateQueries({ queryKey: meetingAvailabilityKey(apiBaseUrl, meetingId, monthOffset) })\n .catch(() => undefined)\n }, [queryClient, apiBaseUrl, meetingId, monthOffset])\n\n const bookMutation = useMutation<BookingResult, never, Record<string, unknown>>({\n mutationFn: async (payload) => {\n try {\n const res = await contentFetch(`${apiBaseUrl}/api/meetings/book`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n })\n const data = (await res.json().catch(() => ({}))) as Record<string, unknown>\n if (res.ok) return { ok: true, confirmation: data as unknown as BookingConfirmation }\n const rawCode = typeof data.code === 'string' ? data.code : ''\n const code = (KNOWN_CODES as string[]).includes(rawCode)\n ? (rawCode as MeetingBookingErrorCode)\n : 'TEMPORARILY_UNAVAILABLE'\n return { ok: false, code, message: typeof data.error === 'string' ? data.error : undefined }\n } catch {\n return { ok: false, code: 'TEMPORARILY_UNAVAILABLE' }\n }\n },\n // A booking is not a read — never retried, at any layer.\n retry: false,\n })\n\n // `isPending` alone would not stop a second submit dispatched in the same\n // tick as the first; the ref closes that window the way the pre-query\n // implementation did.\n const submittingRef = useRef(false)\n const { mutateAsync } = bookMutation\n const book = useCallback(\n async (payload: Record<string, unknown>): Promise<BookingResult> => {\n if (submittingRef.current) return { ok: false, code: 'VALIDATION', message: 'Already submitting' }\n submittingRef.current = true\n try {\n return await mutateAsync(payload)\n } finally {\n submittingRef.current = false\n }\n },\n [mutateAsync],\n )\n\n return {\n availability: availabilityQuery.data ?? null,\n /** No availability for this month yet — the times column has nothing to show. */\n isLoadingAvailability: availabilityQuery.isPending,\n /** ANY request in flight, including a silent revalidation of cached data. */\n isFetchingAvailability: availabilityQuery.isFetching,\n availabilityError: availabilityQuery.error ? availabilityQuery.error.message : null,\n monthOffset,\n setMonthOffset,\n refetchAvailability,\n book,\n isSubmitting: bookMutation.isPending,\n } as const\n}\n","import { z } from 'zod'\n\n/**\n * Meeting-booking wire contracts + validation factory.\n *\n * SERVER-SAFE tsup entry (no \"use client\" banner) with its OWN per-file\n * subpath (`./schemas/meeting-booking-schema`) — same pattern and reasons as\n * `schemas/contact-schema`: used by BOTH the lib's `<HubSpotMeetingScheduler>`\n * (client-side validation) AND the host's server-side booking route, which\n * REBUILDS the schema from the link's own fetched metadata and never trusts a\n * client-shaped instance. zod is an optional peer quarantined to per-subpath\n * verticals — do NOT re-export this module through any broad barrel.\n *\n * Every cross-boundary type for the scheduling feature lives HERE (the lib\n * owns the contract; hosts import this subpath directly, type-only where\n * possible).\n */\n\n// ---------------------------------------------------------------------------\n// Wire types\n// ---------------------------------------------------------------------------\n\n/**\n * Sanitized availability payload served by the host proxy\n * (`GET {apiBaseUrl}/api/meetings/availability?meeting=<id>&monthOffset=<n>`).\n *\n * NO timezone field on purpose: slot starts are absolute epoch-ms instants\n * (verified timezone-independent against the live API), upstream fetches are\n * UTC-pinned, and ALL zone rendering happens client-side. Slots must be\n * whitelist-copied from HubSpot's `linkAvailability` ONLY — never derived\n * from busy-time data.\n */\nexport interface MeetingAvailability {\n meetingId: string\n monthOffset: number\n hasMore: boolean\n /** Offered durations in ms — HubSpot's native unit for the booking POST. */\n durationsMs: number[]\n /** Bookable slot start times (epoch ms), keyed by duration in ms. */\n slotsByDurationMs: Record<string, number[]>\n formFields: MeetingFormField[]\n /** Verbatim whitelist-copy of HubSpot's `legalConsentOptions` when consent is enabled; null when disabled. */\n legalConsent: MeetingLegalConsent | null\n /**\n * Who the visitor is meeting — whitelisted DISPLAY projection the host DAL\n * builds from its own people data (e.g. a profiles table matched\n * server-side). NEVER carries emails or busy-time data; optional so\n * existing hosts stay wire-compatible.\n */\n hosts?: MeetingHost[]\n}\n\n/** Display-only host identity for the scheduler's context panel. */\nexport interface MeetingHost {\n name: string\n avatarUrl: string | null\n /** Job title / role line under the name (null → omitted). */\n title: string | null\n}\n\n/**\n * One scheduling link on the DIRECTORY wire (`GET /api/meetings`) — the\n * host-DAL whitelist projection consumed by `MeetingSchedulerDirectory` and\n * host pages. Never carries organizer emails/busy-time data.\n */\nexport interface SchedulingLink {\n id: string\n /** The link's public HubSpot booking URL — escape-hatch target only. */\n link: string\n /** HubSpot slug path — the row's in-app destination is `<basePath>/<slug>`. */\n slug: string\n /** Audience group key (slugified audience label; `\"other\"` in scope=all). */\n purpose: string\n title: string\n description: string | null\n kind: 'personal' | 'team'\n /** Display-only minutes projection (booking stays ms end-to-end). */\n durationsMinutes: number[]\n hosts: MeetingHost[]\n /** Earliest bookable slot (epoch ms) from the current-month payload. */\n nextAvailableMs: number | null\n}\n\nexport interface SchedulingLinksPayload {\n purposes: Array<{ purpose: string; label: string; links: SchedulingLink[] }>\n fetchedAt: string\n}\n\nexport interface MeetingFormField {\n name: string\n label: string\n type: string\n required: boolean\n options?: string[]\n}\n\n/**\n * HubSpot's consent copy, rendered VERBATIM by the widget (GDPR surface —\n * never edited, never summarized). Responses are keyed by\n * `communicationTypeId`.\n */\nexport interface MeetingLegalConsent {\n processingConsentText: string\n processingConsentCheckboxLabel: string | null\n communicationConsentText: string | null\n communicationConsentCheckboxes: Array<{\n communicationTypeId: string\n label: string\n required: boolean\n }>\n privacyPolicyText: string | null\n isLegitimateInterest: boolean\n}\n\n/**\n * Whitelisted booking result returned by the host proxy — the THIRD HubSpot\n * payload that reaches a browser, so it gets the same whitelist-copy\n * treatment as the two GETs. Nothing organizer-derived.\n */\nexport interface BookingConfirmation {\n meetingId: string\n title: string\n startTimeMs: number\n durationMs: number\n}\n\n/**\n * Typed domain errors the booking route emits; the widget keys its recovery\n * UI off these. `SLOT_TAKEN` → refetch-and-recover; `TEMPORARILY_UNAVAILABLE`\n * → retry affordance; `MEETING_UNAVAILABLE` → daily ceiling exhausted\n * (escape hatch, not a retry timer); `LINK_GONE` → link deleted upstream.\n */\nexport type MeetingBookingErrorCode =\n | 'SLOT_TAKEN'\n | 'VALIDATION'\n | 'LINK_GONE'\n | 'TEMPORARILY_UNAVAILABLE'\n | 'MEETING_UNAVAILABLE'\n\n// ---------------------------------------------------------------------------\n// Field-type vocabulary — ONE set drives the renderer AND the validator\n// ---------------------------------------------------------------------------\n\n/**\n * HubSpot custom-question types the native form supports. The widget's\n * renderer switches over THIS set and `makeBookingSchema` maps over THIS set;\n * fail-closed = a field whose `type` is not in the set (the widget then\n * renders the \"Open in HubSpot\" escape hatch for that link instead of a\n * half-working native form). Exact upstream type strings are pinned against\n * the rollout fixture link — extend here (renderer + validator move together).\n */\nexport const SUPPORTED_FORM_FIELD_TYPES = ['text', 'textarea', 'select', 'radio', 'checkbox'] as const\nexport type SupportedFormFieldType = (typeof SUPPORTED_FORM_FIELD_TYPES)[number]\n\nexport function isSupportedFormField(field: MeetingFormField): boolean {\n return (SUPPORTED_FORM_FIELD_TYPES as readonly string[]).includes(field.type)\n}\n\n// ---------------------------------------------------------------------------\n// Validators (single home — client widget and server rebuild both use these)\n// ---------------------------------------------------------------------------\n\nconst IANA_TZ_RE = /^(?:UTC|[A-Za-z_]+(?:\\/[A-Za-z0-9_+\\-]+)+)$/\n\n/**\n * IANA timezone check. Shape prefilter, then the authoritative resolution\n * test: `Intl.DateTimeFormat` throws on unknown zones. NOT\n * `Intl.supportedValuesOf('timeZone')` — that list excludes `'UTC'` itself\n * (verified in Node), which is a legitimate booking zone. Reject, never coerce.\n */\nexport function isValidIanaTimezone(tz: string): boolean {\n if (!IANA_TZ_RE.test(tz)) return false\n try {\n new Intl.DateTimeFormat('en-US', { timeZone: tz })\n return true\n } catch {\n return false\n }\n}\n\n/** BCP-47 locale shape check via `Intl.getCanonicalLocales`. Reject, never coerce. */\nexport function isValidBcp47Locale(locale: string): boolean {\n try {\n return Intl.getCanonicalLocales(locale).length > 0\n } catch {\n return false\n }\n}\n\n// ---------------------------------------------------------------------------\n// Schema factory\n// ---------------------------------------------------------------------------\n\n/**\n * Build the booking-form schema for ONE link's declared questions + consent.\n *\n * A factory (not a static schema) because per-link required questions cannot\n * be expressed statically. The widget builds it from the availability payload\n * it rendered; the server REBUILDS it from the link's own fetched metadata —\n * required-consent enforcement flows from this rebuild, not a parallel check.\n *\n * Deliberately NOT `.strict()`: the humanity-signal fields\n * (`HUMANITY_SIGNAL_KEYS` from `utils/humanity-signals`) ride alongside in\n * the same POST body (read raw by the host's bot gate BEFORE parsing) and are\n * stripped server-side before anything reaches HubSpot. zod's default\n * unknown-key stripping means the parsed output never contains them.\n *\n * `timezone`/`locale` are POST-only presentation fields (the invite renders\n * in the visitor's local time) — this schema is the ONLY place a\n * client-supplied zone is accepted; the availability path is UTC-pinned.\n */\nexport function makeBookingSchema(\n formFields: MeetingFormField[],\n legalConsent: MeetingLegalConsent | null,\n) {\n const answers: Record<string, z.ZodTypeAny> = {}\n for (const field of formFields) {\n if (!isSupportedFormField(field)) continue // unsupported types are fail-closed at render time\n let validator: z.ZodTypeAny\n switch (field.type as SupportedFormFieldType) {\n case 'checkbox':\n validator = z.boolean()\n break\n case 'select':\n case 'radio':\n validator = z\n .string()\n .refine((v) => !v || (field.options ?? []).includes(v), {\n message: `Please choose a valid option for ${field.label}`,\n })\n break\n case 'textarea':\n validator = z.string().max(5000, { message: `${field.label} is too long` })\n break\n case 'text':\n default:\n validator = z.string().max(1000, { message: `${field.label} is too long` })\n break\n }\n if (field.required) {\n validator =\n field.type === 'checkbox'\n ? z.literal(true, { message: `${field.label} is required` })\n : (validator as z.ZodString).min(1, { message: `${field.label} is required` })\n } else if (field.type !== 'checkbox') {\n validator = (validator as z.ZodString).optional().or(z.literal(''))\n } else {\n validator = z.boolean().optional()\n }\n answers[field.name] = validator\n }\n\n const requiredConsentIds = (legalConsent?.communicationConsentCheckboxes ?? [])\n .filter((c) => c.required)\n .map((c) => c.communicationTypeId)\n\n // A required answer cannot be enforced by an `.optional()` parent object —\n // omitting the `formFields` key entirely would skip every per-question\n // rule. When the link declares at least one required supported question,\n // the object itself is required.\n const hasRequiredAnswers = formFields.some((f) => isSupportedFormField(f) && f.required)\n const answersObject = z.object(answers)\n\n return z\n .object({\n meetingId: z.string().min(1),\n startTimeMs: z.number().int().positive(),\n durationMs: z.number().int().positive(),\n firstName: z.string().min(1, { message: 'First name is required' }).max(255),\n lastName: z.string().min(1, { message: 'Last name is required' }).max(255),\n email: z.string().email({ message: 'Please enter a valid email address' }).max(255),\n timezone: z.string().refine(isValidIanaTimezone, { message: 'Invalid timezone' }),\n locale: z.string().refine(isValidBcp47Locale, { message: 'Invalid locale' }).optional(),\n // Plain `.optional()` (no `.default()`) so zod's input and output types\n // match — react-hook-form's zodResolver needs them identical, and the\n // server handles `undefined` explicitly anyway. REQUIRED when the link\n // declares required questions (see `hasRequiredAnswers` above).\n formFields: hasRequiredAnswers ? answersObject : answersObject.optional(),\n legalConsentResponses: z\n .array(z.object({ communicationTypeId: z.string(), consented: z.boolean() }))\n .optional(),\n })\n // Object-level rule: `.refine` on an `.optional()` field is skipped when\n // the field is absent — required consents must reject even on a payload\n // that omits the array entirely.\n .superRefine((data, ctx) => {\n const responses = data.legalConsentResponses ?? []\n const ok = requiredConsentIds.every((id) =>\n responses.some((r) => r.communicationTypeId === id && r.consented === true),\n )\n if (!ok) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['legalConsentResponses'],\n message: 'Required consent checkboxes must be accepted',\n })\n }\n })\n}\n\nexport type MeetingBookingPayload = z.infer<ReturnType<typeof makeBookingSchema>>\n","'use client'\n\nimport { useMemo } from 'react'\nimport { SquareAvatar, AvatarStack, Button, Skeleton, Autocomplete } from '../ui'\nimport { BackButton } from '../layout/back-button'\nimport { cn } from '../../utils/cn'\nimport { formatDurationCompact } from '../../utils/format'\nimport type { MeetingHost } from '../../schemas/meeting-booking-schema'\n\n/**\n * ContextPanel — the \"who / what / how long\" side of the scheduler card\n * (Calendly-anatomy left panel; stacks on top on mobile). Everything here is\n * trust surface: host identity (avatar + name + title), the meeting's own\n * title/description, the duration (chips when the link offers several — the\n * duration choice lives HERE, not as a separate wizard step), and a\n * SEARCHABLE timezone picker (all IANA zones with live GMT offsets;\n * rendering-only — the wire is always epoch-ms).\n *\n * `onBack` (optional) puts the card's ONE back edge at the TOP of this panel —\n * the one spot that stays put while the action side swaps between calendar,\n * form and confirmation. What it means is the caller's call: the scheduler\n * points it at the previous STEP from the form, and at the host's own exit\n * from the calendar. One affordance, one place, so the visitor never has to\n * work out which of two \"Back\"s goes where.\n *\n * The picker renders only once the parent resolves the zone post-mount — SSR\n * output stays deterministic; a same-footprint skeleton holds the space.\n */\n\nexport interface SchedulerContextPanelProps {\n hosts: MeetingHost[]\n title?: string\n description?: string | null\n durationsMs: number[]\n selectedDurationMs: number | null\n onSelectDuration: (ms: number) => void\n /** Resolved IANA zone (null until the client resolves it). */\n timezone: string | null\n onTimezoneChange?: (tz: string) => void\n /** Host-level exit (renders a `BackButton` at the top). Omitted → no back\n * affordance, which is what an embedder with its own page chrome wants. */\n onBack?: () => void\n /** Label for {@link onBack}. */\n backLabel?: string\n /** True once a slot is chosen (details/confirmed steps) — the duration\n * CHIPS give way to a static duration line, since the step summary already\n * states the length and a live selector beside a filled-in form invites a\n * change that would throw the form away. Going Back restores them. */\n locked?: boolean\n /** Whether the timezone picker renders. Default true: the zone governs every\n * time on screen, the form's summary line included, so it stays reachable\n * right up to the confirmation — where there is nothing left to re-read. */\n showTimezone?: boolean\n className?: string\n}\n\n/** Panel stack — one rhythm shared by the loaded panel and its skeleton\n * (16px on phones, 24px from tablet up, via the responsive spacing token). */\nconst PANEL_STACK = 'flex flex-col gap-[var(--spacing-system-l)]'\n\n/**\n * Identity + duration: ONE line below `lg`, stacked from `lg` up.\n *\n * The panel is a 280px sidebar from `lg` and a full-width header strip below\n * it — and a header strip has horizontal room the sidebar never had, so the\n * duration moves up beside the host instead of taking a line of its own. Same\n * two children, different axis; nothing is hidden at any width.\n */\nconst IDENTITY_ROW =\n 'flex items-center justify-between gap-[var(--spacing-system-m)] lg:flex-col lg:items-stretch lg:gap-[var(--spacing-system-l)]'\n\n/**\n * Everything under the back edge, in TWO groups: identity/title/duration, and\n * the timezone field beside them.\n *\n * Side by side only on TABLET. There the panel is a full-width strip over the\n * calendar, and it is competing with the calendar for the card's height — a\n * timezone field on a line of its own costs 100px there, which is a third of\n * what the month needs to render without scrolling. Sideways it costs nothing:\n * the strip is ~725px wide and the identity block uses less than half of it.\n *\n * Below `md` (phone) and from `lg` up (280px sidebar) there is no horizontal\n * room to spend, so both fall back to the plain stack.\n */\nconst STRIP_BODY =\n 'flex flex-col gap-[var(--spacing-system-l)] md:flex-row md:items-start lg:flex-col'\nconst STRIP_MAIN = 'flex min-w-0 flex-col gap-[var(--spacing-system-l)] md:flex-1'\n/** 280px — the same width this panel has as a sidebar, so the field is the\n * size the visitor meets at every other breakpoint. */\nconst STRIP_ASIDE = 'flex flex-col gap-[var(--spacing-system-xxs)] md:w-[280px] md:shrink-0 lg:w-full'\n\n/** The back affordance sits flush with the panel's top padding: `BackButton`'s\n * own 12px block padding would otherwise double the 24px stack gap under it. */\nconst BACK_BUTTON_CLASS = 'py-0'\n\n/** Field labels (\"Duration\", \"Timezone\") — body-weight primary, 4px above\n * their control, so the panel reads as a form rather than as a stack of\n * section headers. */\nconst FIELD_LABEL_CLASS = 'text-h4 text-ods-text-primary'\n\nfunction zoneLabel(tz: string): string {\n try {\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone: tz,\n timeZoneName: 'shortOffset',\n }).formatToParts(new Date())\n const offset = parts.find((p) => p.type === 'timeZoneName')?.value ?? ''\n return offset ? `${tz.replace(/_/g, ' ')} (${offset})` : tz.replace(/_/g, ' ')\n } catch {\n return tz.replace(/_/g, ' ')\n }\n}\n\n/**\n * Same-footprint skeleton — swaps with the loaded panel with zero shift.\n * STATIC labels (\"Duration\", \"Timezone\") render REAL; only data-driven\n * content (host identity, duration chips, the zone value) is skeleton.\n */\nexport function ContextPanelSkeleton({\n className,\n onBack,\n backLabel = 'Back',\n}: {\n className?: string\n onBack?: () => void\n backLabel?: string\n}) {\n return (\n <div className={cn(PANEL_STACK, className)}>\n {/* The back edge renders REAL here — it needs no data, and holding the\n space with a bar would just move on load. */}\n {onBack && <BackButton label={backLabel} onClick={onBack} className={BACK_BUTTON_CLASS} />}\n {/* Same two groups, same rows, as the loaded panel — so the header strip\n keeps its height and nothing re-flows when it swaps in. */}\n <div className={STRIP_BODY}>\n <div className={STRIP_MAIN}>\n <div className={IDENTITY_ROW}>\n <div className=\"flex min-w-0 flex-1 items-center gap-[var(--spacing-system-xs)] lg:flex-none\">\n <Skeleton className=\"h-8 w-8 shrink-0 rounded-full\" />\n <div className=\"flex flex-1 flex-col gap-[var(--spacing-system-xxs)]\">\n <Skeleton className=\"h-5 w-32\" />\n <Skeleton className=\"h-4 w-24\" />\n </div>\n </div>\n <Skeleton className=\"h-4 w-20 shrink-0\" />\n </div>\n </div>\n <div className={STRIP_ASIDE}>\n <p className={FIELD_LABEL_CLASS}>Timezone</p>\n <Skeleton className=\"h-12 w-full\" />\n </div>\n </div>\n </div>\n )\n}\n\nexport function SchedulerContextPanel({\n hosts,\n title,\n description,\n durationsMs,\n selectedDurationMs,\n onSelectDuration,\n timezone,\n onTimezoneChange,\n onBack,\n backLabel = 'Back',\n locked = false,\n showTimezone = true,\n className,\n}: SchedulerContextPanelProps) {\n // All IANA zones with live GMT offsets — computed once, client-only (the\n // panel renders the picker only after the parent resolves a zone).\n const zoneOptions = useMemo(() => {\n if (!timezone) return []\n let zones: string[]\n try {\n // Older lib targets don't type supportedValuesOf (ES2022) — runtime-guarded.\n const intl = Intl as typeof Intl & { supportedValuesOf?: (key: string) => string[] }\n zones = intl.supportedValuesOf ? intl.supportedValuesOf('timeZone') : [timezone]\n } catch {\n zones = [timezone]\n }\n if (!zones.includes(timezone)) zones = [timezone, ...zones]\n return zones.map((tz) => ({ value: tz, label: zoneLabel(tz) }))\n }, [timezone])\n\n return (\n <div className={cn(PANEL_STACK, className)}>\n {/* The card's ONE back edge, at every step — the caller decides where it\n goes (previous step, or out of the scheduler). It sits in this panel\n because this is the half that never swaps. */}\n {onBack && <BackButton label={backLabel} onClick={onBack} className={BACK_BUTTON_CLASS} />}\n\n <div className={STRIP_BODY}>\n <div className={STRIP_MAIN}>\n <div className={IDENTITY_ROW}>\n {/* Host identity: full rows up to 3 hosts; larger teams (round-robin\n links can carry many members) collapse to a stacked-avatar cluster\n + count so the panel can never overflow. */}\n {hosts.length > 0 && hosts.length <= 3 && (\n <div className=\"flex flex-col gap-[var(--spacing-system-s)]\">\n {hosts.map((host) => (\n <div key={host.name} className=\"flex items-center gap-[var(--spacing-system-xs)]\">\n <SquareAvatar\n variant=\"round\"\n size=\"sm\"\n src={host.avatarUrl ?? undefined}\n alt={host.name}\n fallback={host.name}\n />\n <div className=\"flex flex-col min-w-0\">\n <p className=\"text-h4 text-ods-text-primary truncate\">{host.name}</p>\n {host.title && <p className=\"text-h6 text-ods-text-secondary truncate\">{host.title}</p>}\n </div>\n </div>\n ))}\n </div>\n )}\n {hosts.length > 3 && (\n <div className=\"flex flex-col gap-[var(--spacing-system-xs)]\">\n <AvatarStack people={hosts} max={4} size=\"lg\" />\n <p className=\"text-h6 text-ods-text-secondary\">{hosts.length} hosts on this calendar</p>\n </div>\n )}\n\n {locked || durationsMs.length <= 1\n ? // Post-selection, or a link that offers one length: a static line,\n // which is what sits beside the host on the header-strip layouts.\n selectedDurationMs != null && (\n <p className=\"shrink-0 text-h6 text-ods-text-secondary\">\n {formatDurationCompact(selectedDurationMs / 1000)} call\n </p>\n )\n : null}\n </div>\n\n {title && <p className=\"text-h3 text-ods-text-primary\">{title}</p>}\n {description && <p className=\"text-h6 text-ods-text-secondary\">{description}</p>}\n\n {/* Several lengths on offer: chips, which need a full row of their\n own at every width — so they stay OUT of the identity row. */}\n {!locked && durationsMs.length > 1 && (\n <div className=\"flex flex-col gap-[var(--spacing-system-xxs)]\">\n <p className={FIELD_LABEL_CLASS}>Duration</p>\n <div className=\"flex flex-wrap gap-[var(--spacing-system-xs)]\">\n {durationsMs.map((ms) => (\n <Button\n key={ms}\n variant={selectedDurationMs === ms ? undefined : 'outline'}\n size=\"small-legacy\"\n onClick={() => onSelectDuration(ms)}\n >\n {formatDurationCompact(ms / 1000)}\n </Button>\n ))}\n </div>\n </div>\n )}\n </div>\n\n {showTimezone && (\n // Once a slot is chosen the picker is desktop-only, which is what all\n // three mocks draw. On the narrow layouts this panel is a strip\n // COMPETING with the form for the screen's height, and the step's own\n // summary line states the zone one row below — so the fact survives,\n // only the control steps aside. The 280px sidebar has the room, so\n // there it stays.\n <div className={cn(STRIP_ASIDE, locked && 'max-lg:hidden')}>\n <p className={FIELD_LABEL_CLASS}>Timezone</p>\n {timezone ? (\n <Autocomplete\n value={timezone}\n onChange={(tz) => {\n if (tz) onTimezoneChange?.(tz)\n }}\n options={zoneOptions}\n placeholder=\"Search timezone…\"\n noOptionsText=\"No matching timezone\"\n showClearAll={false}\n />\n ) : (\n <Skeleton className=\"h-12 w-full\" />\n )}\n </div>\n )}\n </div>\n </div>\n )\n}\n","'use client'\n\nimport { useCallback, useMemo } from 'react'\nimport { ChevronRight } from 'lucide-react'\nimport { Button, DatePickerCalendar, Skeleton } from '../ui'\nimport { cn } from '../../utils/cn'\nimport { MAX_MONTH_OFFSET } from '../../utils/hubspot-meetings-convention'\n\n/**\n * SlotPicker — day layer + time-chip grid for the meeting scheduler.\n *\n * Day layer is the design system's own `DatePickerCalendar`, in its `bare`\n * variant — the SAME component behind `DatePicker`'s popover and\n * `DateFilterMenu`, not a second day grid styled to look like it; `bare` only\n * drops the card surface and inset it would otherwise draw inside this panel. `fromDate`/`toDate` pin\n * navigation to [current month, current + MAX_MONTH_OFFSET] so the past is\n * unreachable, and `disabledDays` greys out days with no bookable slot —\n * bounds and availability are different questions, so they arrive separately.\n * Month navigation drives the host-proxy `monthOffset`, which is why the month\n * is CONTROLLED here rather than owned by the calendar. Time chips are plain\n * `Button`s whose variant flips on selection — no bespoke pill.\n *\n * LOADING (no blink, ever): the two columns know different amounts, so they\n * load differently. A CALENDAR is fully derivable from the date — which month,\n * which weekday each cell falls on — and only WHICH DAYS ARE BOOKABLE needs\n * the network. So it never unmounts: while a month loads it renders for real\n * with nothing yet selectable, which is exactly true, and lights up in place.\n * Only the times column, which genuinely has no answer until the response\n * lands, shows a placeholder.\n *\n * Swapping the calendar for a skeleton was the old behaviour and it is what\n * made paging months flash: the whole grid vanished and rebuilt to say\n * something it already knew.\n *\n * Each column carries its OWN heading — \"Select Date & Time\" over the\n * calendar, the chosen day over the chips. The headings live HERE rather than\n * above the whole picker so each sits with the thing it labels, and so the\n * skeletons reproduce them without the parent rendering a heading it cannot\n * keep in step.\n *\n * All slot instants are epoch-ms; every label is rendered in `timezone`\n * (resolved by the parent AFTER mount — this component never reads Intl\n * itself, keeping SSR output deterministic).\n */\n\nexport interface SlotPickerProps {\n /** Bookable slot start times (epoch ms) for the selected duration. */\n slots: number[]\n /** IANA zone every label renders in (parent resolves post-mount). */\n timezone: string\n monthOffset: number\n onMonthOffsetChange: (offset: number) => void\n selectedSlot: number | null\n onSelectSlot: (startMs: number) => void\n selectedDay: string | null\n onSelectDay: (dayKey: string) => void\n /** Availability refetch in flight (month change) → per-region skeletons. */\n isLoading?: boolean\n}\n\n/** Stable per-zone day key for an instant, e.g. \"2026-08-14\" (exported — the\n * parent uses it to auto-select the first available day). */\nexport function dayKeyInZone(ms: number, timeZone: string): string {\n return new Intl.DateTimeFormat('en-CA', { timeZone, year: 'numeric', month: '2-digit', day: '2-digit' }).format(\n new Date(ms),\n )\n}\n\n/**\n * Calendar-side day-key anchoring: react-day-picker exchanges PLAIN calendar\n * dates (local Date objects whose y/m/d ARE the cell). Convert via local\n * date parts — never through an Intl zone — or a `displayTimezone` far from\n * the browser zone shifts cells by a day.\n */\nconst pad2 = (n: number) => String(n).padStart(2, '0')\nfunction calendarDayKey(day: Date): string {\n return `${day.getFullYear()}-${pad2(day.getMonth() + 1)}-${pad2(day.getDate())}`\n}\nfunction dateFromDayKey(key: string): Date {\n const [y, m, d] = key.split('-').map(Number)\n return new Date(y, m - 1, d, 12)\n}\n\nfunction timeLabelInZone(ms: number, timeZone: string): string {\n return new Intl.DateTimeFormat(undefined, { timeZone, hour: 'numeric', minute: '2-digit' }).format(new Date(ms))\n}\n\n/**\n * The calendar column's width, once the layout stops handing it one: on a\n * phone the section spans the card, from `md` up it takes a stated width and\n * the times column gets the rest.\n *\n * Width no longer decides height — `DatePickerCalendar bare` is a fixed 272px\n * block at any width — so these two numbers answer one question only: how much\n * room the month CAPTION needs. The two DS icon buttons and their gaps take\n * 104px of the header, and \"September 2026\" is ~131 at text-h4, so the grid\n * needs ~245px of inner width before the label starts colliding with the\n * next-month button. 304 on tablet (256 inner, this column pads itself) and\n * 256 on the desktop card, where the panel supplies the padding.\n *\n * That is the trade the standard button size buys: the chrome keeps its\n * 48px targets and the COLUMN widens to house it, rather than the buttons\n * shrinking to fit a narrower column.\n */\nconst CALENDAR_W = 'md:w-[19rem] md:shrink-0 lg:w-[16rem]'\n\n/** Column heading (\"Select Date & Time\" / the chosen day) — body-weight\n * primary, 4px above its content, mirrored by both skeletons. */\nconst COLUMN_HEADING_CLASS = 'shrink-0 text-h4 text-ods-text-primary'\nconst COLUMN_STACK = 'flex min-h-0 flex-col gap-[var(--spacing-system-xxs)]'\n\n/** Static label over the calendar. Exported so a host rendering its own\n * chrome around the picker can stay in step with it. */\nexport const SLOT_PICKER_HEADING = 'Select Date & Time'\n\n/**\n * Chip grid: `auto-fill` over `minmax(5.5rem, 1fr)` — as many chips per row as\n * fit at the minimum, then the leftover width shared out between them.\n *\n * Both halves matter. The 5.5rem MINIMUM is the widest 12-hour label\n * (\"12:45 PM\") plus the chip's padding: the mocks draw 65px chips five across\n * a phone, but they are lettered in a 24-hour locale (\"13:45\") and this\n * renders in the visitor's own — a chip that clips its own time is worse than\n * one column fewer. The `1fr` MAXIMUM is what makes the grid reach the card's\n * edge like the design instead of stopping short with a ragged strip of empty\n * space beside the last column, which is what a fixed track width leaves\n * behind at every width that is not an exact multiple of it.\n *\n * `auto-fill` is what keeps `1fr` safe here: the track COUNT still comes from\n * the minimum, so a wide column answers with more chips rather than with\n * fifteen-minute slots stretched into 200px slabs.\n *\n * Every track is the same width, so rows line up and nothing staggers.\n */\nconst CHIP_GRID_CLASS = cn(\n 'grid grid-cols-[repeat(auto-fill,minmax(5.5rem,1fr))] content-start gap-[var(--spacing-system-xs)]',\n 'overflow-y-auto',\n // PHONE: a stated window (~3 rows, the mock's 156px) that scrolls, rather\n // than a list that runs the length of the day. Nothing else on the phone\n // card has a height — the design lets each step be as tall as it needs —\n // but a busy day is forty slots, and letting them all through turns a\n // 700px card into a 1500px one and buries whatever the page puts below\n // it. This is the one nested scroller worth having: a small, obviously\n // bounded list with a visible edge, exactly as drawn.\n 'max-h-[9.75rem] md:max-h-none',\n // No height of its own, and none borrowed from the calendar either: the card\n // states one height, the panel and this row pass it down, and what is left\n // after the column heading IS the scroll box. A long day scrolls inside it.\n //\n // `min-h-0` is the load-bearing half — a flex item's default\n // `min-height:auto` refuses to shrink below its content, so without it the\n // chips grow the column, then the row, then the card, and `overflow-y-auto`\n // never gets a box smaller than its contents to scroll inside. That is the\n // exact failure that kept this card at 404px when it was told to be 318.\n 'flex-1 min-h-0',\n)\n/** The track sets the width; the chip fills it, so labels can't stagger it.\n * The tighter inline padding is what lets the track hold a 12-hour label —\n * the Button's own is sized for prose, not for a time. */\nconst CHIP_CLASS = 'w-full px-[var(--spacing-system-xxs)]'\n\n/**\n * The two columns, at three widths.\n *\n * < md phone — stacked sections, a full-bleed rule between them\n * md tablet — side by side, the calendar at its stated width and the\n * times taking the rest, a vertical rule between them\n * lg desktop — side by side inside the panel's own padding, separated by\n * a gap (the card already has a rule there: the context\n * panel's edge)\n *\n * From `md` up the row takes what the card has left (`flex-1 min-h-0`) and\n * hands each column that same definite height. The calendar is a fixed 272px\n * block and does not scroll; the times column holds whatever the day offers\n * and scrolls ON ITS OWN. Two boxes, one height, one scrollbar — the calendar\n * stays put while the visitor runs down the times.\n *\n * Below `md` the card pins no height, so the row simply grows and the PAGE\n * scrolls — a nested scroller inside a hand-width card traps the gesture and\n * is worse than the thing it saves.\n */\nconst SLOT_ROW_CLASS = cn(\n 'flex min-w-0 flex-col md:flex-row',\n // Default `stretch` at every width, deliberately. Stacked, that is what\n // makes each section span the card — an `items-start` here (it was here, to\n // stop a column growing taller than its month) collapses both sections to\n // their intrinsic WIDTH instead, which on a phone left the calendar and the\n // chips huddled against the left edge with half the card empty. The\n // calendar no longer needs protecting: its height is fixed, not derived.\n 'md:min-h-0 md:flex-1 lg:gap-[var(--spacing-system-lf)]',\n)\n\n/** Shared by both columns: self-padded until the panel takes over at `lg`.\n * Widths are per-column — the calendar states one (`CALENDAR_W`) and the\n * times take what is left, rather than a 50/50 split that would size the\n * month by the card's width. */\nconst COLUMN_BOX = 'p-[var(--spacing-system-l)] lg:p-0'\n\n/** The rule between the two, wherever the layout puts it. */\nconst COLUMN_DIVIDER = 'border-b border-ods-border md:border-b-0 md:border-r lg:border-0'\n\n\n/** Deterministic month caption for a given offset (fixed en-US locale so\n * the SSR and client first-paint markup agree byte-for-byte). */\nexport function monthLabelFor(offset: number): string {\n const now = new Date()\n const month = new Date(now.getFullYear(), now.getMonth() + offset, 1)\n return new Intl.DateTimeFormat('en-US', { month: 'long', year: 'numeric' }).format(month)\n}\n\n/** Nothing is bookable yet — used while a month is still loading. */\nconst DISABLE_EVERY_DAY = () => true\nconst NOOP = () => undefined\n\ninterface DayCalendarProps {\n monthOffset: number\n onMonthOffsetChange: (offset: number) => void\n selectedDay: string | null\n onSelectDay: (dayKey: string) => void\n /** Days that ARE bookable; `null` while the month is still loading. */\n slotsByDay: Map<string, number[]> | null\n}\n\n/**\n * The day layer — one component for every state, which is the whole point:\n * loading a month changes WHICH DAYS ARE ENABLED, nothing else, so the grid\n * stays mounted and the visitor never watches it disappear and come back.\n *\n * `aria-busy` carries what the missing days mean to assistive tech, since\n * visually it is just an inert month.\n */\nfunction DayCalendar({ monthOffset, onMonthOffsetChange, selectedDay, onSelectDay, slotsByDay }: DayCalendarProps) {\n const now = new Date()\n const startMonth = new Date(now.getFullYear(), now.getMonth(), 1)\n // Day-0 of the FOLLOWING month = the last day of the last bookable one.\n // `DatePickerCalendar` takes day bounds, not month bounds, so an end date\n // pointing at the 1st would disable the rest of that month.\n const endMonth = new Date(now.getFullYear(), now.getMonth() + MAX_MONTH_OFFSET + 1, 0)\n const visibleMonth = new Date(now.getFullYear(), now.getMonth() + monthOffset, 1)\n\n // Stable identity: the calendar memoizes its matcher list on this value, so\n // a fresh closure per render would rebuild it every render.\n const dayHasNoSlots = useCallback(\n (day: Date) => !slotsByDay?.has(calendarDayKey(day)),\n [slotsByDay],\n )\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col\" aria-busy={slotsByDay === null}>\n <DatePickerCalendar\n mode=\"single\"\n bare\n // CONTROLLED month: paging it is a DATA event here (the parent\n // refetches availability for the new offset), not just a view change\n // the calendar could own.\n month={visibleMonth}\n onMonthChange={(month) => {\n const offset = (month.getFullYear() - now.getFullYear()) * 12 + (month.getMonth() - now.getMonth())\n onMonthOffsetChange(Math.max(0, Math.min(MAX_MONTH_OFFSET, offset)))\n }}\n fromDate={startMonth}\n toDate={endMonth}\n selected={selectedDay ? dateFromDayKey(selectedDay) : undefined}\n onSelect={(value) => {\n const day = value as Date | undefined\n if (!day) return\n const key = calendarDayKey(day)\n if (slotsByDay?.has(key)) onSelectDay(key)\n }}\n disabledDays={slotsByDay === null ? DISABLE_EVERY_DAY : dayHasNoSlots}\n />\n </div>\n )\n}\n\n/**\n * The slot area before the first response — the pre-mount window while the\n * display timezone resolves, and the first availability request.\n *\n * The calendar half is REAL, not a placeholder: see the module docblock. Only\n * the times column is a skeleton, and it is the same one the loaded picker\n * shows while a month loads, so there is no swap between the two.\n */\nexport function SlotPickerSkeleton({ monthOffset = 0 }: { monthOffset?: number }) {\n return (\n <div className={SLOT_ROW_CLASS}>\n <div className={cn(COLUMN_STACK, COLUMN_BOX, COLUMN_DIVIDER, CALENDAR_W, 'md:overflow-y-auto')}>\n {/* Needs no data — the heading renders REAL in every loading state. */}\n <p className={COLUMN_HEADING_CLASS}>{SLOT_PICKER_HEADING}</p>\n <DayCalendar\n monthOffset={monthOffset}\n onMonthOffsetChange={NOOP}\n selectedDay={null}\n onSelectDay={NOOP}\n slotsByDay={null}\n />\n </div>\n <div className={cn(COLUMN_STACK, COLUMN_BOX, 'min-w-0 md:flex-1')}>\n <TimeChipsSkeleton />\n </div>\n </div>\n )\n}\n\n/** Same-footprint skeleton for the time-chip column. */\nexport function TimeChipsSkeleton() {\n return (\n <>\n {/* The day label is data — but it is held inside the REAL heading line\n box, so the skeleton's first row sits exactly where the label lands. */}\n <div className={COLUMN_HEADING_CLASS}>\n <Skeleton className=\"inline-block h-4 w-40 align-middle\" />\n </div>\n <div className={CHIP_GRID_CLASS}>\n {/* Enough to fill the visible scroll box at any column count — the\n grid's track count follows the card width, so no fixed number\n lands flush and a partial last row is the honest shape anyway. */}\n {Array.from({ length: 16 }, (_, i) => (\n // h-11 md:h-12 — the loaded chip's own default-size height.\n <Skeleton key={i} className={cn(CHIP_CLASS, 'h-11 md:h-12')} />\n ))}\n </div>\n </>\n )\n}\n\nexport function SlotPicker({\n slots,\n timezone,\n monthOffset,\n onMonthOffsetChange,\n selectedSlot,\n onSelectSlot,\n selectedDay,\n onSelectDay,\n isLoading = false,\n}: SlotPickerProps) {\n const slotsByDay = useMemo(() => {\n const map = new Map<string, number[]>()\n for (const ms of slots) {\n const key = dayKeyInZone(ms, timezone)\n const list = map.get(key) ?? []\n list.push(ms)\n map.set(key, list)\n }\n for (const list of map.values()) list.sort((a, b) => a - b)\n return map\n }, [slots, timezone])\n\n const visibleMonth = useMemo(() => {\n const now = new Date()\n return new Date(now.getFullYear(), now.getMonth() + monthOffset, 1)\n }, [monthOffset])\n\n const daySlots = selectedDay ? (slotsByDay.get(selectedDay) ?? []) : []\n\n // Whether the VISIBLE month has anything at all. HubSpot's monthOffset\n // payloads can carry near-term slots from outside the requested month, so\n // this asks about the grid on screen, not about the payload's size.\n const monthPrefix = `${visibleMonth.getFullYear()}-${String(visibleMonth.getMonth() + 1).padStart(2, '0')}`\n const monthHasSlots = [...slotsByDay.keys()].some((key) => key.startsWith(monthPrefix))\n\n return (\n <div className={SLOT_ROW_CLASS}>\n <div className={cn(COLUMN_STACK, COLUMN_BOX, COLUMN_DIVIDER, CALENDAR_W, 'md:overflow-y-auto')}>\n <p className={COLUMN_HEADING_CLASS}>{SLOT_PICKER_HEADING}</p>\n {/* Stays mounted through a month load — `slotsByDay: null` is what\n \"nothing selectable yet\" looks like, not an unmount. */}\n <DayCalendar\n monthOffset={monthOffset}\n onMonthOffsetChange={onMonthOffsetChange}\n selectedDay={selectedDay}\n onSelectDay={onSelectDay}\n slotsByDay={isLoading ? null : slotsByDay}\n />\n </div>\n <div className={cn(COLUMN_STACK, COLUMN_BOX, 'min-w-0 md:flex-1')}>\n {isLoading ? (\n <TimeChipsSkeleton />\n ) : selectedDay ? (\n <>\n <p className={COLUMN_HEADING_CLASS}>\n {/* Format the DAY-KEY itself (plain calendar date) — re-zoning a\n local instant could label an adjacent day. */}\n {new Intl.DateTimeFormat(undefined, {\n weekday: 'long',\n month: 'long',\n day: 'numeric',\n }).format(dateFromDayKey(selectedDay))}\n </p>\n <div className={CHIP_GRID_CLASS}>\n {daySlots.map((ms) => {\n const isSelected = selectedSlot === ms\n return (\n <Button\n key={ms}\n variant={isSelected ? undefined : 'outline'}\n onClick={() => onSelectSlot(ms)}\n className={CHIP_CLASS}\n >\n {timeLabelInZone(ms, timezone)}\n </Button>\n )\n })}\n </div>\n </>\n ) : monthHasSlots ? (\n <p className=\"text-h6 text-ods-text-secondary\">Pick a day to see available times.</p>\n ) : (\n // A fully-booked month used to just SAY \"try the next one\" and leave\n // the visitor to find the chevron in the other column. The way out\n // of a dead end belongs in the dead end.\n <div className=\"flex flex-col items-start gap-[var(--spacing-system-s)]\">\n <p className=\"text-h6 text-ods-text-secondary\">\n No available times in {monthLabelFor(monthOffset)}.\n </p>\n {monthOffset < MAX_MONTH_OFFSET && (\n <Button\n variant=\"outline\"\n onClick={() => onMonthOffsetChange(monthOffset + 1)}\n rightIcon={<ChevronRight className=\"size-5\" />}\n >\n Check {monthLabelFor(monthOffset + 1)}\n </Button>\n )}\n </div>\n )}\n </div>\n </div>\n )\n}\n","'use client'\n\nimport { useMemo } from 'react'\nimport { useForm, Controller } from 'react-hook-form'\nimport { zodResolver } from '@hookform/resolvers/zod'\nimport {\n makeBookingSchema,\n isSupportedFormField,\n type MeetingAvailability,\n type MeetingBookingPayload,\n} from '../../schemas/meeting-booking-schema'\nimport {\n Button,\n FieldWrapper,\n Input,\n Textarea,\n Label,\n Select,\n SelectTrigger,\n SelectValue,\n SelectContent,\n SelectItem,\n Checkbox,\n RadioGroup,\n RadioGroupItem,\n} from '../ui'\nimport { HoneypotField } from '../ui/honeypot-field'\n\n/**\n * BookingForm — attendee details + the link's declared custom questions +\n * verbatim legal-consent copy. ContactForm's scaffolding (react-hook-form +\n * zodResolver + lib field primitives); a sibling rather than a `<ContactForm>`\n * configuration because of the dynamic HubSpot `formFields`, the per-checkbox\n * consent model, and the first/last-name field model — none expressible via\n * `hideFields`/`extraTopField`.\n *\n * Bot protection is LOAD-BEARING: without the humanity signals in the body,\n * the host's `verifyHuman` degrades to first-party-only BotID (fails open for\n * external embedders). Honeypot + elapsed-ms are merged into the POST at\n * submit; the parent calls `resetSignals()` after a SLOT_TAKEN refetch so a\n * legitimate retry isn't flagged too-fast.\n */\n\nexport interface BookingFormProps {\n availability: MeetingAvailability\n meetingId: string\n startTimeMs: number\n durationMs: number\n /** IANA zone the confirmation/invite should render in (parent-resolved). */\n timezone: string\n isSubmitting: boolean\n onSubmit: (payload: Record<string, unknown>) => Promise<void>\n /** From useHumanitySignals — parent owns the instance so it can resetSignals(). */\n honeypotInputProps: { ref: React.Ref<HTMLInputElement>; name: string }\n getSignals: () => Record<string, string | number>\n}\n\nexport function BookingForm({\n availability,\n meetingId,\n startTimeMs,\n durationMs,\n timezone,\n isSubmitting,\n onSubmit,\n honeypotInputProps,\n getSignals,\n}: BookingFormProps) {\n const { formFields, legalConsent } = availability\n const supportedFields = useMemo(() => formFields.filter(isSupportedFormField), [formFields])\n const schema = useMemo(() => makeBookingSchema(supportedFields, legalConsent), [supportedFields, legalConsent])\n\n const {\n register,\n control,\n handleSubmit,\n formState: { errors },\n } = useForm<MeetingBookingPayload>({\n resolver: zodResolver(schema),\n defaultValues: {\n meetingId,\n startTimeMs,\n durationMs,\n timezone,\n locale: typeof navigator !== 'undefined' ? navigator.language : undefined,\n firstName: '',\n lastName: '',\n email: '',\n formFields: {},\n legalConsentResponses: (legalConsent?.communicationConsentCheckboxes ?? []).map((c) => ({\n communicationTypeId: c.communicationTypeId,\n consented: false,\n })),\n },\n })\n\n const submit = handleSubmit(async (data) => {\n await onSubmit({ ...data, meetingId, startTimeMs, durationMs, timezone, ...getSignals() })\n })\n\n const fieldError = (name: string): string | undefined => {\n const err = (errors.formFields as Record<string, { message?: string }> | undefined)?.[name]\n return err?.message\n }\n\n // Field chrome mirrors ContactForm 1:1 (`contact/contact-form.tsx`) — the\n // booking form must be indistinguishable from every other form in the app.\n const inputClass = 'bg-ods-card border-ods-border text-ods-text-primary placeholder-ods-text-secondary px-3 h-11 md:h-12'\n // One step ABOVE the `spacing system/m` the design names (16/24 instead of\n // 12/16), because the field messages hang out of flow: they need ~16px on a\n // phone and ~20 on desktop of clear space under the control, and `m` leaves\n // 12/16 — four pixels short at both ends, so an error would print over the\n // next field's label. The design has no error state drawn; this is the\n // smallest ODS step that houses it.\n const FORM_STACK = 'flex flex-col gap-[var(--spacing-system-l)]'\n\n return (\n <form onSubmit={submit} className={FORM_STACK} noValidate>\n <HoneypotField {...honeypotInputProps} />\n\n {/* Email first, name pair below — the order the desktop and mobile mocks\n both draw. The pair stays TWO columns even on a phone (164px each at\n 375): two short fields side by side cost one line instead of two on\n the layout that can least afford them.\n\n Every field goes through `FieldWrapper`, which hangs its message OUT\n OF FLOW below the control. That is the whole reason it is here: a\n message rendered in flow grows its field, which pushes everything\n under it down and — inside a card that states its height — walks the\n submit button off the bottom the moment validation fails. Required-ness\n is carried by `required` on the control (read out by assistive tech),\n not by an asterisk the design does not draw. */}\n <FieldWrapper label=\"Email\" htmlFor=\"ms-email\" error={errors.email?.message}>\n <Input\n id=\"ms-email\"\n type=\"email\"\n required\n autoComplete=\"email\"\n placeholder=\"jane@company.com\"\n aria-invalid={!!errors.email}\n className={inputClass}\n {...register('email')}\n />\n </FieldWrapper>\n\n <div className=\"grid grid-cols-2 gap-[var(--spacing-system-m)]\">\n <FieldWrapper label=\"First Name\" htmlFor=\"ms-first-name\" error={errors.firstName?.message}>\n <Input\n id=\"ms-first-name\"\n required\n autoComplete=\"given-name\"\n placeholder=\"Jane\"\n aria-invalid={!!errors.firstName}\n className={inputClass}\n {...register('firstName')}\n />\n </FieldWrapper>\n <FieldWrapper label=\"Last Name\" htmlFor=\"ms-last-name\" error={errors.lastName?.message}>\n <Input\n id=\"ms-last-name\"\n required\n autoComplete=\"family-name\"\n placeholder=\"Doe\"\n aria-invalid={!!errors.lastName}\n className={inputClass}\n {...register('lastName')}\n />\n </FieldWrapper>\n </div>\n\n {supportedFields.map((field) => (\n <FieldWrapper\n key={field.name}\n label={field.label}\n htmlFor={`ms-q-${field.name}`}\n error={fieldError(field.name)}\n >\n {field.type === 'textarea' && (\n <Textarea\n id={`ms-q-${field.name}`}\n className=\"bg-ods-card border-ods-border text-ods-text-primary placeholder-ods-text-secondary px-3\"\n {...register(`formFields.${field.name}` as never)}\n />\n )}\n {field.type === 'text' && (\n <Input\n id={`ms-q-${field.name}`}\n className=\"bg-ods-card border-ods-border text-ods-text-primary placeholder-ods-text-secondary px-3 h-11 md:h-12\"\n {...register(`formFields.${field.name}` as never)}\n />\n )}\n {(field.type === 'select' || field.type === 'radio') && (\n <Controller\n control={control}\n name={`formFields.${field.name}` as never}\n render={({ field: rhf }) =>\n field.type === 'select' ? (\n <Select value={(rhf.value as string) ?? ''} onValueChange={rhf.onChange}>\n <SelectTrigger id={`ms-q-${field.name}`}>\n <SelectValue placeholder=\"Select…\" />\n </SelectTrigger>\n <SelectContent>\n {(field.options ?? []).map((opt) => (\n <SelectItem key={opt} value={opt}>\n {opt}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n ) : (\n <RadioGroup value={(rhf.value as string) ?? ''} onValueChange={rhf.onChange}>\n {(field.options ?? []).map((opt) => (\n <div key={opt} className=\"flex items-center gap-[var(--spacing-system-xs)]\">\n <RadioGroupItem id={`ms-q-${field.name}-${opt}`} value={opt} />\n <Label htmlFor={`ms-q-${field.name}-${opt}`}>{opt}</Label>\n </div>\n ))}\n </RadioGroup>\n )\n }\n />\n )}\n {field.type === 'checkbox' && (\n <Controller\n control={control}\n name={`formFields.${field.name}` as never}\n render={({ field: rhf }) => (\n // The question's Label above already carries the text and\n // binds to this id — a second inline label would double the\n // visible text AND the accessible name.\n <div className=\"flex items-center gap-[var(--spacing-system-xs)]\">\n <Checkbox\n id={`ms-q-${field.name}`}\n checked={Boolean(rhf.value)}\n onCheckedChange={(v) => rhf.onChange(v === true)}\n />\n </div>\n )}\n />\n )}\n </FieldWrapper>\n ))}\n\n {legalConsent && (\n // Same out-of-flow message as the fields above, so a missed consent\n // box doesn't shove the submit button down the card. With no error the\n // wrapper is `display:contents` and the panel below is the flex item,\n // exactly as it was.\n <FieldWrapper error={errors.legalConsentResponses?.message as string | undefined}>\n <div className=\"flex flex-col gap-[var(--spacing-system-xs)] border border-ods-border rounded-md p-[var(--spacing-system-m)]\">\n {/* GDPR surface — HubSpot's consent copy rendered VERBATIM, never edited. */}\n <p className=\"text-h6 text-ods-text-secondary\">{legalConsent.processingConsentText}</p>\n {legalConsent.communicationConsentText && (\n <p className=\"text-h6 text-ods-text-secondary\">{legalConsent.communicationConsentText}</p>\n )}\n <Controller\n control={control}\n name=\"legalConsentResponses\"\n render={({ field: rhf }) => (\n <>\n {legalConsent.communicationConsentCheckboxes.map((box) => {\n const responses = (rhf.value ?? []) as Array<{ communicationTypeId: string; consented: boolean }>\n const current = responses.find((r) => r.communicationTypeId === box.communicationTypeId)\n return (\n <div key={box.communicationTypeId} className=\"flex items-center gap-[var(--spacing-system-xs)]\">\n <Checkbox\n id={`ms-consent-${box.communicationTypeId}`}\n checked={current?.consented ?? false}\n onCheckedChange={(v) =>\n rhf.onChange(\n responses.map((r) =>\n r.communicationTypeId === box.communicationTypeId ? { ...r, consented: v === true } : r,\n ),\n )\n }\n />\n <Label htmlFor={`ms-consent-${box.communicationTypeId}`}>{box.label}</Label>\n </div>\n )\n })}\n </>\n )}\n />\n {legalConsent.privacyPolicyText && (\n <p className=\"text-h6 text-ods-text-secondary\">{legalConsent.privacyPolicyText}</p>\n )}\n </div>\n </FieldWrapper>\n )}\n\n {/* Step navigation back to the calendar lives in the step header (the\n app-standard BackButton, rendered by the parent) — the form ships\n only its submit. */}\n <div className=\"flex\">\n <Button type=\"submit\" loading={isSubmitting} disabled={isSubmitting}>\n Confirm Booking\n </Button>\n </div>\n </form>\n )\n}\n","'use client'\n\nimport { formatDurationCompact } from '../../utils/format'\nimport type { BookingConfirmation } from '../../schemas/meeting-booking-schema'\n\n/**\n * Confirmation — terminal success state after a completed booking. Renders\n * the booked instant in the visitor's resolved zone. Deliberately NO\n * follow-up CTA: the job is done, the calendar invite is the next touchpoint\n * (a \"book another\" button here tested as noise).\n */\n\nexport interface ConfirmationProps {\n confirmation: BookingConfirmation\n timezone: string\n}\n\nexport function Confirmation({ confirmation, timezone }: ConfirmationProps) {\n const when = new Intl.DateTimeFormat(undefined, {\n timeZone: timezone,\n weekday: 'long',\n month: 'long',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n timeZoneName: 'short',\n }).format(new Date(confirmation.startTimeMs))\n\n return (\n <div className=\"flex flex-1 flex-col items-center justify-center gap-[var(--spacing-system-m)] text-center\">\n <p className=\"text-h1\" role=\"img\" aria-label=\"Celebration\">\n 🎉\n </p>\n <h3 className=\"text-h3 text-ods-text-primary\">You&apos;re booked</h3>\n <p className=\"text-h5 text-ods-text-primary\">{confirmation.title}</p>\n <p className=\"text-h6 text-ods-text-secondary\">\n {when} · {formatDurationCompact(confirmation.durationMs / 1000)}\n </p>\n <p className=\"text-h6 text-ods-text-secondary\">A calendar invite is on its way to your email.</p>\n </div>\n )\n}\n","'use client'\n\n/**\n * `<MeetingSchedulerDirectory />` — the embeddable scheduling DIRECTORY\n * block: clickable row cards (host avatars, title/description, For-<audience>\n * chip, next available time, boxed chevron) + the house\n * `PersistentPaginationWrapper`, fed by a host proxy's `GET\n * {apiBaseUrl}/api/meetings` (see `docs/EMBEDDING_HUBSPOT_MEETINGS.md`).\n * Rows navigate to `{bookingBasePath}/{slug}` — pair with\n * `<HubSpotMeetingScheduler>` on that page for the full white-label flow.\n *\n * Layout stability is engineered, not incidental: the rows area reserves one\n * FULL page (pageSize × 80px rows + gaps), the pagination slot holds its\n * measured 72px even when hidden, and the loading state renders same-shell\n * skeleton rows — so nothing below the block ever jumps across\n * loading ⇄ loaded ⇄ page changes ⇄ short last pages.\n *\n * SSR/host mode: pass `initialData` (host-fetched) to skip the client fetch.\n * Client/embed mode: omit it — skeleton page + self-fetch via `contentFetch`\n * (embed-auth adapters inherited for free).\n */\n\nimport { useEffect, useMemo, useState } from 'react'\nimport { ChevronRight } from 'lucide-react'\nimport Link from '../../embed-shims/next-link'\nimport { contentFetch } from '../../utils/embed-content-fetch'\nimport { formatDurationCompact } from '../../utils/format'\nimport { cn } from '../../utils/cn'\nimport { AvatarStack, StatusBadge, Skeleton } from '../ui'\nimport { EmptyState } from '../empty-state'\nimport { PersistentPaginationWrapper } from '../persistent-pagination'\nimport type { SchedulingLink, SchedulingLinksPayload } from '../../schemas/meeting-booking-schema'\n\nexport interface MeetingSchedulerDirectoryProps {\n /** Endpoints prefix, default '' (same-origin `/api/meetings`). */\n apiBaseUrl?: string\n /** Host-fetched seed — skips the client fetch when provided. */\n initialData?: SchedulingLinksPayload | null\n /** `scope=all` — full-portal view (non-conforming links under \"other\"). */\n includeAll?: boolean\n /** Rows per page; also the skeleton row count and the reserved rows-area height. */\n pageSize?: number\n /** Rows link to `${bookingBasePath}/${slug}`. */\n bookingBasePath?: string\n /** EmptyState CTA target for error/empty states (omit → no CTA). */\n contactHref?: string\n className?: string\n}\n\n/** Zone-aware \"next available\" label — resolved POST-mount (SSR renders the\n * static caption only, so server and client first paint agree). */\nfunction useNextAvailableLabel(ms: number | null): string | null {\n const [label, setLabel] = useState<string | null>(null)\n useEffect(() => {\n if (ms == null) return\n try {\n setLabel(\n new Intl.DateTimeFormat(undefined, {\n weekday: 'short',\n month: 'short',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n }).format(new Date(ms)),\n )\n } catch {\n setLabel(null)\n }\n }, [ms])\n return label\n}\n\nfunction DirectoryRow({\n link,\n audienceLabel,\n bookingBasePath,\n}: {\n link: SchedulingLink\n audienceLabel: string\n bookingBasePath: string\n}) {\n const nextLabel = useNextAvailableLabel(link.nextAvailableMs)\n\n const subtitle = [\n link.description,\n link.durationsMinutes.length > 0 ? link.durationsMinutes.map((m) => formatDurationCompact(m * 60)).join(' / ') : null,\n link.kind === 'team' ? 'Team' : null,\n ]\n .filter(Boolean)\n .join(' • ')\n\n return (\n <Link\n href={`${bookingBasePath}/${link.slug}`}\n prefetch={false}\n className=\"block rounded-lg no-underline text-inherit focus:outline-none focus-visible:ring-2 focus-visible:ring-ods-accent\"\n >\n <div className=\"flex w-full items-center gap-4 px-4 py-3 min-h-20 rounded-lg border border-ods-border bg-ods-card transition-colors duration-150 hover:bg-ods-bg-hover\">\n {/* Title leads at a fixed X; hosts live in the right meta cluster. */}\n <div className=\"flex min-w-0 flex-1 flex-col justify-center text-left\">\n <p className=\"text-h3 text-ods-text-primary truncate\">{link.title}</p>\n {subtitle && <p className=\"text-h6 text-ods-text-secondary truncate\">{subtitle}</p>}\n </div>\n\n {/* No audience entity (scope=all \"other\" group) → no chip. */}\n {audienceLabel && (\n <StatusBadge text={`For ${audienceLabel}`} singleLine className=\"shrink-0 hidden sm:inline-flex\" />\n )}\n\n {/* Hosts stay visible on mobile too — the facepile is the row's \"who\n you're booking with\" signal; only the chip and next-available meta\n collapse on narrow widths. */}\n <AvatarStack people={link.hosts} max={3} size=\"md\" className=\"flex shrink-0\" />\n\n <div className=\"hidden md:flex w-44 shrink-0 flex-col items-end justify-center\">\n <p className=\"text-h6 text-ods-text-secondary\">Next available</p>\n <p className=\"text-h6 text-ods-text-primary min-h-5\">\n {link.nextAvailableMs == null ? 'No times published' : (nextLabel ?? ' ')}\n </p>\n </div>\n\n {/* House boxed chevron (ChatTicketItem). */}\n <span className=\"flex size-12 shrink-0 items-center justify-center rounded-md border border-ods-border bg-ods-card\">\n <ChevronRight className=\"size-6 text-ods-text-secondary\" />\n </span>\n </div>\n </Link>\n )\n}\n\n/** Same-shell loading row (co-located-skeleton convention). */\nexport function MeetingSchedulerDirectoryRowSkeleton() {\n return (\n <div className=\"flex w-full items-center gap-4 px-4 py-3 min-h-20 rounded-lg border border-ods-border bg-ods-card\">\n <div className=\"flex min-w-0 flex-1 flex-col justify-center gap-1\">\n <Skeleton className=\"h-5 w-1/2\" />\n <Skeleton className=\"h-4 w-1/3\" />\n </div>\n <Skeleton className=\"hidden sm:block h-6 w-28 shrink-0\" />\n <Skeleton className=\"h-10 w-10 shrink-0 rounded-full\" />\n <div className=\"hidden md:flex w-44 shrink-0 flex-col items-end gap-1\">\n <Skeleton className=\"h-4 w-24\" />\n <Skeleton className=\"h-5 w-32\" />\n </div>\n <Skeleton className=\"size-12 shrink-0 rounded-md\" />\n </div>\n )\n}\n\n/** Measured pagination-slot height (PersistentPaginationWrapper = 72px). */\nconst PAGINATION_SLOT_H = 'h-[4.5rem]'\n\nexport function MeetingSchedulerDirectory({\n apiBaseUrl = '',\n initialData = null,\n includeAll = false,\n pageSize = 6,\n bookingBasePath = '/schedule-a-call',\n contactHref,\n className,\n}: MeetingSchedulerDirectoryProps) {\n const [data, setData] = useState<SchedulingLinksPayload | null>(initialData)\n const [isLoading, setIsLoading] = useState(initialData === null)\n const [error, setError] = useState<string | null>(null)\n const [page, setPage] = useState(1)\n\n useEffect(() => {\n if (initialData !== null) return // host-seeded — skip the client fetch\n const controller = new AbortController()\n let active = true\n const load = async () => {\n setIsLoading(true)\n setError(null)\n try {\n const qs = includeAll ? '?scope=all' : ''\n const res = await contentFetch(`${apiBaseUrl}/api/meetings${qs}`, { signal: controller.signal })\n if (!res.ok) throw new Error(`meetings ${res.status}`)\n const payload = (await res.json()) as SchedulingLinksPayload\n if (active) setData(payload)\n } catch (err) {\n if (!active || (err instanceof DOMException && err.name === 'AbortError')) return\n setError(err instanceof Error ? err.message : 'Failed to fetch scheduling links')\n } finally {\n if (active) setIsLoading(false)\n }\n }\n void load()\n return () => {\n active = false\n controller.abort()\n }\n }, [initialData, includeAll, apiBaseUrl])\n\n const rows = useMemo(() => {\n const purposes = data?.purposes ?? []\n return purposes.flatMap((p) => p.links.map((link) => ({ link, audienceLabel: p.label })))\n }, [data])\n\n const totalPages = Math.max(1, Math.ceil(rows.length / pageSize))\n const pageRows = rows.slice((page - 1) * pageSize, page * pageSize)\n\n // Reserved rows-area height: pageSize × 80px rows + (pageSize−1) × 16px gaps.\n const rowsMinHeight = pageSize * 80 + (pageSize - 1) * 16\n\n if (isLoading && !data) {\n return (\n <div className={cn('flex flex-col', className)}>\n <div className=\"flex w-full flex-col gap-4\" style={{ minHeight: rowsMinHeight }}>\n {Array.from({ length: pageSize }, (_, i) => (\n <MeetingSchedulerDirectoryRowSkeleton key={i} />\n ))}\n </div>\n <div className={cn('mt-8 flex w-full items-center justify-center', PAGINATION_SLOT_H)}>\n <Skeleton className=\"h-10 w-64\" />\n </div>\n </div>\n )\n }\n\n if (error) {\n return (\n <div className={cn('flex flex-col justify-center', className)} style={{ minHeight: rowsMinHeight + 104 }}>\n <EmptyState\n type=\"generic\"\n title=\"We couldn't load available call times\"\n description=\"Something went wrong on our side. Try again in a minute, or reach us directly.\"\n {...(contactHref ? { ctaText: 'Contact us', ctaHref: contactHref } : {})}\n />\n </div>\n )\n }\n\n if (rows.length === 0) {\n return (\n <div className={cn('flex flex-col justify-center', className)} style={{ minHeight: rowsMinHeight + 104 }}>\n <EmptyState\n type=\"generic\"\n title=\"No call times are published right now\"\n description=\"We're updating our calendars. Reach us directly and we'll set something up.\"\n {...(contactHref ? { ctaText: 'Contact us', ctaHref: contactHref } : {})}\n />\n </div>\n )\n }\n\n return (\n <div className={cn('flex flex-col', className)}>\n <div className=\"flex w-full flex-col gap-4\" style={{ minHeight: rowsMinHeight }}>\n {pageRows.map(({ link, audienceLabel }) => (\n <DirectoryRow key={link.id} link={link} audienceLabel={audienceLabel} bookingBasePath={bookingBasePath} />\n ))}\n </div>\n {/* House pagination block — dims in place during refetches; the slot\n holds its measured height even at ≤1 page (where the inner\n UnifiedPagination returns null). */}\n <div className={cn('mt-8 flex w-full items-center justify-center', PAGINATION_SLOT_H)}>\n {totalPages > 1 && (\n <PersistentPaginationWrapper\n isLoading={isLoading}\n currentPage={page}\n totalPages={totalPages}\n onPageChange={setPage}\n variant=\"blog\"\n />\n )}\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":["useCallback","useEffect","useMemo","useRef","useState","useCallback","useMemo","jsx","jsxs","useCallback","useMemo","useMemo","Fragment","jsx","jsxs","useMemo","jsx","jsxs","useMemo","useState","ChevronRight","jsx","jsxs","useState","ChevronRight","useMemo","jsx","jsxs","useState","useEffect","useMemo","useRef","useCallback"]}