`` — natively-branded (ODS-only) HubSpot booking flow in a Calendly-anatomy two-panel card: CONTEXT panel (host identity, title/description, duration chips, timezone) + ACTION panel (calendar/slots → details form → confirmation), fed exclusively by a host proxy. Panels stack on mobile. Full guide: [`docs/EMBEDDING_HUBSPOT_MEETINGS.md`](../../../docs/EMBEDDING_HUBSPOT_MEETINGS.md). ## Props (`HubSpotMeetingSchedulerProps`) | Prop | Type | Required | Description | |---|---|---|---| | `meetingId` | `string` | ✅ | Link id from the host's `/api/meetings` directory payload | | `apiBaseUrl` | `string` | ❌ | Proxy prefix, default `''` (same-origin `/api/meetings/*`) | | `initialAvailability` | `MeetingAvailability` | ❌ | SSR-mode seed; omitted → client mode (skeleton + self-fetch). Refetched unconditionally after mount | | `title` | `string` | ❌ | Meeting title in the context panel (host page keeps the h1) | | `description` | `string \| null` | ❌ | Short description under the title | | `hosts` | `MeetingHost[]` | ❌ | Override the hosts shown (defaults to `availability.hosts`) | | `displayTimezone` | `string` | ❌ | Pin the DISPLAY zone — rendering only, never sent upstream | | `fallbackUrl` | `string` | ❌ | "Open in HubSpot" escape hatch + fail-closed target (unsupported field types / malformed consent) | | `onBooked` | `(b: BookingConfirmation) => void` | ❌ | Fires after a confirmed booking | | `className` | `string` | ❌ | Root container classes | State machine: `slot → details → confirmed` with a details→slot back edge; the machine fully resets when the host changes `meetingId` (no in-UI "book another" action — the confirmation is terminal by design). Duration is a context-panel chip (defaults to the first offered), NOT a wizard step; the first available day is auto-selected so times are visible immediately; `submitting` lock is the double-booking guard. Errors render by `MeetingBookingErrorCode`.