@uptrademedia/site-kit 1.0.24 → 1.0.26

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.
package/dist/index.js CHANGED
@@ -1821,70 +1821,89 @@ ${moduleConfigs.join("\n")}
1821
1821
 
1822
1822
  // src/sync/api.ts
1823
1823
  var DEFAULT_API_URL = "https://api.uptrademedia.com";
1824
- async function fetchBookingTypes(orgSlug, apiUrl = DEFAULT_API_URL) {
1825
- const response = await fetch(`${apiUrl}/sync/public/${orgSlug}/types`);
1824
+ function getApiConfig3() {
1825
+ const apiUrl = typeof window !== "undefined" ? window.__SITE_KIT_API_URL__ || DEFAULT_API_URL : DEFAULT_API_URL;
1826
+ const apiKey = typeof window !== "undefined" ? window.__SITE_KIT_API_KEY__ : void 0;
1827
+ return { apiUrl, apiKey };
1828
+ }
1829
+ function buildHeaders(apiKey, isPost = false) {
1830
+ const headers = {};
1831
+ if (isPost) headers["Content-Type"] = "application/json";
1832
+ if (apiKey) headers["x-api-key"] = apiKey;
1833
+ return headers;
1834
+ }
1835
+ async function fetchBookingTypes(orgSlug, apiUrl, apiKey) {
1836
+ const cfg = getApiConfig3();
1837
+ const url = apiUrl || cfg.apiUrl;
1838
+ const key = apiKey || cfg.apiKey;
1839
+ const endpoint = key ? `${url}/sync/widget/types` : `${url}/sync/public/${orgSlug}/types`;
1840
+ const response = await fetch(endpoint, { headers: buildHeaders(key) });
1826
1841
  if (!response.ok) {
1827
1842
  throw new Error(`Failed to fetch booking types: ${response.statusText}`);
1828
1843
  }
1829
1844
  const data = await response.json();
1830
1845
  return data.types || [];
1831
1846
  }
1832
- async function fetchBookingTypeDetails(orgSlug, typeSlug, apiUrl = DEFAULT_API_URL) {
1833
- const response = await fetch(`${apiUrl}/sync/public/${orgSlug}/types/${typeSlug}`);
1847
+ async function fetchBookingTypeDetails(typeSlug, orgSlug, apiUrl, apiKey) {
1848
+ const cfg = getApiConfig3();
1849
+ const url = apiUrl || cfg.apiUrl;
1850
+ const key = apiKey || cfg.apiKey;
1851
+ const endpoint = key ? `${url}/sync/widget/types/${typeSlug}` : `${url}/sync/public/${orgSlug}/types/${typeSlug}`;
1852
+ const response = await fetch(endpoint, { headers: buildHeaders(key) });
1834
1853
  if (!response.ok) {
1835
1854
  throw new Error(`Failed to fetch booking type: ${response.statusText}`);
1836
1855
  }
1837
1856
  return response.json();
1838
1857
  }
1839
- async function fetchAvailability(orgSlug, typeSlug, date, apiUrl = DEFAULT_API_URL, timezone, hostId) {
1858
+ async function fetchAvailability(typeSlug, date, orgSlug, apiUrl, apiKey, timezone, hostId) {
1859
+ const cfg = getApiConfig3();
1860
+ const url = apiUrl || cfg.apiUrl;
1861
+ const key = apiKey || cfg.apiKey;
1840
1862
  const params = new URLSearchParams({ date });
1841
1863
  if (timezone) params.append("timezone", timezone);
1842
1864
  if (hostId) params.append("hostId", hostId);
1843
- const response = await fetch(
1844
- `${apiUrl}/sync/public/${orgSlug}/availability/${typeSlug}?${params}`
1845
- );
1865
+ const endpoint = key ? `${url}/sync/widget/availability/${typeSlug}?${params}` : `${url}/sync/public/${orgSlug}/availability/${typeSlug}?${params}`;
1866
+ const response = await fetch(endpoint, { headers: buildHeaders(key) });
1846
1867
  if (!response.ok) {
1847
1868
  throw new Error(`Failed to fetch availability: ${response.statusText}`);
1848
1869
  }
1849
1870
  const data = await response.json();
1850
1871
  return data.slots || [];
1851
1872
  }
1852
- async function createSlotHold(bookingTypeId, startTime, hostId, guestTimezone, apiUrl = DEFAULT_API_URL) {
1853
- const response = await fetch(`${apiUrl}/sync/public/hold`, {
1873
+ async function createSlotHold(holdData, apiUrl, apiKey) {
1874
+ const cfg = getApiConfig3();
1875
+ const url = apiUrl || cfg.apiUrl;
1876
+ const key = apiKey || cfg.apiKey;
1877
+ const endpoint = key ? `${url}/sync/widget/hold` : `${url}/sync/public/hold`;
1878
+ const response = await fetch(endpoint, {
1854
1879
  method: "POST",
1855
- headers: { "Content-Type": "application/json" },
1856
- body: JSON.stringify({
1857
- booking_type_id: bookingTypeId,
1858
- start_time: startTime,
1859
- host_id: hostId,
1860
- guest_timezone: guestTimezone
1861
- })
1880
+ headers: buildHeaders(key, true),
1881
+ body: JSON.stringify(holdData)
1862
1882
  });
1863
1883
  if (!response.ok) {
1864
1884
  throw new Error(`Failed to hold slot: ${response.statusText}`);
1865
1885
  }
1866
1886
  return response.json();
1867
1887
  }
1868
- async function releaseSlotHold(holdId, apiUrl = DEFAULT_API_URL) {
1869
- await fetch(`${apiUrl}/sync/public/hold/${holdId}`, {
1870
- method: "DELETE"
1888
+ async function releaseSlotHold(holdId, apiUrl, apiKey) {
1889
+ const cfg = getApiConfig3();
1890
+ const url = apiUrl || cfg.apiUrl;
1891
+ const key = apiKey || cfg.apiKey;
1892
+ const endpoint = key ? `${url}/sync/widget/hold/${holdId}` : `${url}/sync/public/hold/${holdId}`;
1893
+ await fetch(endpoint, {
1894
+ method: "DELETE",
1895
+ headers: buildHeaders(key)
1871
1896
  });
1872
1897
  }
1873
- async function createBooking(bookingTypeId, startTime, guest, timezone, hostId, holdId, apiUrl = DEFAULT_API_URL) {
1874
- const response = await fetch(`${apiUrl}/sync/public/booking`, {
1898
+ async function createBooking(bookingData, apiUrl, apiKey) {
1899
+ const cfg = getApiConfig3();
1900
+ const url = apiUrl || cfg.apiUrl;
1901
+ const key = apiKey || cfg.apiKey;
1902
+ const endpoint = key ? `${url}/sync/widget/booking` : `${url}/sync/public/booking`;
1903
+ const response = await fetch(endpoint, {
1875
1904
  method: "POST",
1876
- headers: { "Content-Type": "application/json" },
1877
- body: JSON.stringify({
1878
- booking_type_id: bookingTypeId,
1879
- start_time: startTime,
1880
- guest_name: guest.name,
1881
- guest_email: guest.email,
1882
- guest_phone: guest.phone,
1883
- guest_notes: guest.notes,
1884
- timezone,
1885
- host_id: hostId,
1886
- hold_id: holdId
1887
- })
1905
+ headers: buildHeaders(key, true),
1906
+ body: JSON.stringify(bookingData)
1888
1907
  });
1889
1908
  if (!response.ok) {
1890
1909
  const errorData = await response.json().catch(() => ({}));
@@ -1892,7 +1911,7 @@ async function createBooking(bookingTypeId, startTime, guest, timezone, hostId,
1892
1911
  }
1893
1912
  return response.json();
1894
1913
  }
1895
- async function fetchAvailableDates(orgSlug, typeSlug, startDate, endDate, apiUrl = DEFAULT_API_URL, timezone) {
1914
+ async function fetchAvailableDates(typeSlug, startDate, endDate, orgSlug, apiUrl, apiKey, timezone) {
1896
1915
  const availableDates = [];
1897
1916
  const start = new Date(startDate);
1898
1917
  const end = new Date(endDate);
@@ -1900,7 +1919,7 @@ async function fetchAvailableDates(orgSlug, typeSlug, startDate, endDate, apiUrl
1900
1919
  while (current <= end) {
1901
1920
  const dateStr = current.toISOString().split("T")[0];
1902
1921
  try {
1903
- const slots = await fetchAvailability(orgSlug, typeSlug, dateStr, apiUrl, timezone);
1922
+ const slots = await fetchAvailability(typeSlug, dateStr, orgSlug, apiUrl, apiKey, timezone);
1904
1923
  if (slots.some((s) => s.available)) {
1905
1924
  availableDates.push(dateStr);
1906
1925
  }
@@ -1948,87 +1967,183 @@ function formatDuration(minutes) {
1948
1967
  return `${hours}h ${mins}m`;
1949
1968
  }
1950
1969
  var DEFAULT_API_URL2 = "https://api.uptrademedia.com";
1970
+ var DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
1971
+ var MONTHS = [
1972
+ "January",
1973
+ "February",
1974
+ "March",
1975
+ "April",
1976
+ "May",
1977
+ "June",
1978
+ "July",
1979
+ "August",
1980
+ "September",
1981
+ "October",
1982
+ "November",
1983
+ "December"
1984
+ ];
1985
+ function isSameDay(a, b) {
1986
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
1987
+ }
1988
+ function isBeforeDay(a, b) {
1989
+ const ac = new Date(a.getFullYear(), a.getMonth(), a.getDate());
1990
+ const bc = new Date(b.getFullYear(), b.getMonth(), b.getDate());
1991
+ return ac < bc;
1992
+ }
1993
+ function calendarDays(year, month) {
1994
+ const first = new Date(year, month, 1);
1995
+ const last = new Date(year, month + 1, 0);
1996
+ const cells = [];
1997
+ for (let i = 0; i < first.getDay(); i++) cells.push(null);
1998
+ for (let d = 1; d <= last.getDate(); d++) cells.push(new Date(year, month, d));
1999
+ return cells;
2000
+ }
2001
+ function ChevronLeft() {
2002
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12.5 15L7.5 10L12.5 5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
2003
+ }
2004
+ function ChevronRight() {
2005
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7.5 5L12.5 10L7.5 15", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
2006
+ }
2007
+ function ClockIcon() {
2008
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
2009
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "8", cy: "8", r: "6.5", stroke: "currentColor", strokeWidth: "1.2" }),
2010
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 4.5V8L10 10", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
2011
+ ] });
2012
+ }
2013
+ function GlobeIcon() {
2014
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
2015
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "8", cy: "8", r: "6.5", stroke: "currentColor", strokeWidth: "1.2" }),
2016
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M1.5 8H14.5", stroke: "currentColor", strokeWidth: "1.2" }),
2017
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 1.5C9.66 3.34 10.61 5.62 10.61 8C10.61 10.38 9.66 12.66 8 14.5C6.34 12.66 5.39 10.38 5.39 8C5.39 5.62 6.34 3.34 8 1.5Z", stroke: "currentColor", strokeWidth: "1.2" })
2018
+ ] });
2019
+ }
2020
+ function CheckCircleIcon() {
2021
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "56", height: "56", viewBox: "0 0 56 56", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
2022
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "28", cy: "28", r: "28", fill: "var(--bw-primary)" }),
2023
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 28.5L24.5 35L38 21.5", stroke: "white", strokeWidth: "3", strokeLinecap: "round", strokeLinejoin: "round" })
2024
+ ] });
2025
+ }
2026
+ function CalendarPlusIcon() {
2027
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
2028
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "1.5", y: "2.5", width: "13", height: "12", rx: "1.5", stroke: "currentColor", strokeWidth: "1.2" }),
2029
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M1.5 6.5H14.5", stroke: "currentColor", strokeWidth: "1.2" }),
2030
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 1V4M11 1V4", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" }),
2031
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 9V12M6.5 10.5H9.5", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" })
2032
+ ] });
2033
+ }
2034
+ function ArrowLeftIcon() {
2035
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M10 3L5 8L10 13", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
2036
+ }
2037
+ function SpinnerIcon() {
2038
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: "bw-spinner", children: [
2039
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "10", cy: "10", r: "8", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeDasharray: "50.265", strokeDashoffset: "25", opacity: "0.3" }),
2040
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "10", cy: "10", r: "8", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeDasharray: "50.265", strokeDashoffset: "37.7" })
2041
+ ] });
2042
+ }
1951
2043
  function BookingWidget({
1952
2044
  orgSlug,
1953
- apiUrl = DEFAULT_API_URL2,
2045
+ apiKey: propApiKey,
2046
+ apiUrl: propApiUrl,
1954
2047
  bookingTypeSlug,
1955
2048
  timezone: propTimezone,
1956
2049
  className = "",
1957
- daysToShow = 14,
2050
+ daysToShow = 60,
1958
2051
  onBookingComplete,
1959
2052
  onError,
1960
2053
  hideTypeSelector = false,
1961
2054
  styles: styles2 = {}
1962
2055
  }) {
2056
+ const apiKey = propApiKey || (typeof window !== "undefined" ? window.__SITE_KIT_API_KEY__ : void 0);
2057
+ const apiUrl = propApiUrl || (typeof window !== "undefined" ? window.__SITE_KIT_API_URL__ : void 0) || DEFAULT_API_URL2;
1963
2058
  const [step, setStep] = React3.useState(bookingTypeSlug ? "datetime" : "type");
1964
2059
  const [loading, setLoading] = React3.useState(false);
2060
+ const [slotsLoading, setSlotsLoading] = React3.useState(false);
1965
2061
  const [error, setError] = React3.useState(null);
1966
2062
  const [bookingTypes, setBookingTypes] = React3.useState([]);
1967
2063
  const [selectedType, setSelectedType] = React3.useState(null);
2064
+ const [viewMonth, setViewMonth] = React3.useState(() => /* @__PURE__ */ new Date());
1968
2065
  const [selectedDate, setSelectedDate] = React3.useState(null);
1969
2066
  const [slots, setSlots] = React3.useState([]);
1970
2067
  const [selectedSlot, setSelectedSlot] = React3.useState(null);
2068
+ const [confirmedSlot, setConfirmedSlot] = React3.useState(false);
1971
2069
  const [hold, setHold] = React3.useState(null);
1972
2070
  const [guestInfo, setGuestInfo] = React3.useState({ name: "", email: "" });
1973
2071
  const [bookingResult, setBookingResult] = React3.useState(null);
2072
+ const [submitting, setSubmitting] = React3.useState(false);
2073
+ const slotsRef = React3.useRef(null);
2074
+ const today = React3.useMemo(() => {
2075
+ const d = /* @__PURE__ */ new Date();
2076
+ d.setHours(0, 0, 0, 0);
2077
+ return d;
2078
+ }, []);
1974
2079
  const timezone = React3.useMemo(() => propTimezone || detectTimezone(), [propTimezone]);
1975
- const availableDates = React3.useMemo(() => {
1976
- const dates = [];
1977
- const today = /* @__PURE__ */ new Date();
1978
- today.setHours(0, 0, 0, 0);
1979
- for (let i = 0; i < daysToShow; i++) {
1980
- const date = new Date(today);
1981
- date.setDate(date.getDate() + i);
1982
- if (date.getDay() !== 0 && date.getDay() !== 6) {
1983
- dates.push(date);
1984
- }
2080
+ const shortTz = React3.useMemo(() => {
2081
+ try {
2082
+ const parts = Intl.DateTimeFormat("en-US", { timeZone: timezone, timeZoneName: "short" }).formatToParts(/* @__PURE__ */ new Date());
2083
+ return parts.find((p) => p.type === "timeZoneName")?.value || timezone;
2084
+ } catch {
2085
+ return timezone;
1985
2086
  }
1986
- return dates;
1987
- }, [daysToShow]);
2087
+ }, [timezone]);
2088
+ const days = React3.useMemo(() => calendarDays(viewMonth.getFullYear(), viewMonth.getMonth()), [viewMonth]);
2089
+ const maxDate = React3.useMemo(() => {
2090
+ const d = new Date(today);
2091
+ d.setDate(d.getDate() + daysToShow);
2092
+ return d;
2093
+ }, [today, daysToShow]);
1988
2094
  React3.useEffect(() => {
1989
2095
  if (bookingTypeSlug) {
1990
- fetchBookingTypeDetails(orgSlug, bookingTypeSlug, apiUrl).then((type) => {
2096
+ setLoading(true);
2097
+ fetchBookingTypeDetails(bookingTypeSlug, orgSlug, apiUrl, apiKey).then((type) => {
1991
2098
  setSelectedType(type);
1992
2099
  setStep("datetime");
1993
2100
  }).catch((err) => {
1994
2101
  setError(err.message);
1995
2102
  onError?.(err);
1996
- });
2103
+ }).finally(() => setLoading(false));
1997
2104
  } else {
1998
- fetchBookingTypes(orgSlug, apiUrl).then((types) => setBookingTypes(types.filter((t) => t.is_active))).catch((err) => {
2105
+ setLoading(true);
2106
+ fetchBookingTypes(orgSlug, apiUrl, apiKey).then((types) => setBookingTypes(types.filter((t) => t.is_active))).catch((err) => {
1999
2107
  setError(err.message);
2000
2108
  onError?.(err);
2001
- });
2109
+ }).finally(() => setLoading(false));
2002
2110
  }
2003
- }, [orgSlug, bookingTypeSlug, apiUrl, onError]);
2111
+ }, [orgSlug, apiKey, bookingTypeSlug, apiUrl, onError]);
2004
2112
  React3.useEffect(() => {
2005
2113
  if (!selectedDate || !selectedType) return;
2006
2114
  const dateStr = selectedDate.toISOString().split("T")[0];
2007
- setLoading(true);
2115
+ setSlotsLoading(true);
2008
2116
  setSlots([]);
2009
2117
  setSelectedSlot(null);
2010
- fetchAvailability(orgSlug, selectedType.slug, dateStr, apiUrl, timezone).then((s) => setSlots(s.filter((slot) => slot.available))).catch((err) => {
2118
+ setConfirmedSlot(false);
2119
+ fetchAvailability(selectedType.slug, dateStr, orgSlug, apiUrl, apiKey, timezone).then((s) => setSlots(s.filter((slot) => slot.available))).catch((err) => {
2011
2120
  setError(err.message);
2012
2121
  onError?.(err);
2013
- }).finally(() => setLoading(false));
2014
- }, [selectedDate, selectedType, orgSlug, apiUrl, timezone, onError]);
2015
- const handleSlotSelect = React3.useCallback(async (slot) => {
2016
- if (!selectedType) return;
2017
- if (hold) {
2018
- await releaseSlotHold(hold.holdId, apiUrl).catch(() => {
2019
- });
2020
- }
2122
+ }).finally(() => setSlotsLoading(false));
2123
+ }, [selectedDate, selectedType, orgSlug, apiKey, apiUrl, timezone, onError]);
2124
+ const handleSlotSelect = React3.useCallback((slot) => {
2021
2125
  setSelectedSlot(slot);
2126
+ setConfirmedSlot(false);
2127
+ }, []);
2128
+ const handleSlotConfirm = React3.useCallback(async () => {
2129
+ if (!selectedType || !selectedSlot) return;
2130
+ if (hold) await releaseSlotHold(hold.holdId, apiUrl, apiKey).catch(() => {
2131
+ });
2022
2132
  setLoading(true);
2023
2133
  try {
2024
2134
  const newHold = await createSlotHold(
2025
- selectedType.id,
2026
- slot.start,
2027
- slot.hostId,
2028
- timezone,
2029
- apiUrl
2135
+ {
2136
+ bookingType: selectedType.slug,
2137
+ slotStart: selectedSlot.start,
2138
+ slotEnd: selectedSlot.end,
2139
+ hostId: selectedSlot.hostId || "",
2140
+ sessionId: `bw-${Date.now()}`
2141
+ },
2142
+ apiUrl,
2143
+ apiKey
2030
2144
  );
2031
2145
  setHold(newHold);
2146
+ setConfirmedSlot(true);
2032
2147
  setStep("form");
2033
2148
  } catch (err) {
2034
2149
  setError(err.message);
@@ -2036,21 +2151,30 @@ function BookingWidget({
2036
2151
  } finally {
2037
2152
  setLoading(false);
2038
2153
  }
2039
- }, [selectedType, hold, timezone, apiUrl, onError]);
2154
+ }, [selectedType, selectedSlot, hold, timezone, apiUrl, apiKey, onError]);
2040
2155
  const handleBookingSubmit = React3.useCallback(async (e) => {
2041
2156
  e.preventDefault();
2042
2157
  if (!selectedType || !selectedSlot) return;
2043
- setLoading(true);
2158
+ setSubmitting(true);
2044
2159
  setError(null);
2045
2160
  try {
2046
2161
  const result = await createBooking(
2047
- selectedType.id,
2048
- selectedSlot.start,
2049
- guestInfo,
2050
- timezone,
2051
- selectedSlot.hostId,
2052
- hold?.holdId,
2053
- apiUrl
2162
+ {
2163
+ bookingType: selectedType.slug,
2164
+ scheduledAt: selectedSlot.start,
2165
+ hostId: selectedSlot.hostId || "",
2166
+ name: guestInfo.name,
2167
+ email: guestInfo.email,
2168
+ phone: guestInfo.phone,
2169
+ message: guestInfo.notes,
2170
+ source: "main-site",
2171
+ timezone,
2172
+ holdId: hold?.holdId,
2173
+ sessionId: `bw-${Date.now()}`,
2174
+ sourceUrl: typeof window !== "undefined" ? window.location.href : void 0
2175
+ },
2176
+ apiUrl,
2177
+ apiKey
2054
2178
  );
2055
2179
  setBookingResult(result);
2056
2180
  setStep("success");
@@ -2059,576 +2183,538 @@ function BookingWidget({
2059
2183
  setError(err.message);
2060
2184
  onError?.(err);
2061
2185
  } finally {
2062
- setLoading(false);
2186
+ setSubmitting(false);
2063
2187
  }
2064
- }, [selectedType, selectedSlot, guestInfo, timezone, hold, apiUrl, onBookingComplete, onError]);
2188
+ }, [selectedType, selectedSlot, guestInfo, timezone, hold, apiUrl, apiKey, onBookingComplete, onError]);
2065
2189
  React3.useEffect(() => {
2066
2190
  return () => {
2067
- if (hold) {
2068
- releaseSlotHold(hold.holdId, apiUrl).catch(() => {
2069
- });
2070
- }
2191
+ if (hold) releaseSlotHold(hold.holdId, apiUrl, apiKey).catch(() => {
2192
+ });
2071
2193
  };
2072
- }, [hold, apiUrl]);
2194
+ }, [hold, apiUrl, apiKey]);
2195
+ const canGoPrev = viewMonth.getFullYear() > today.getFullYear() || viewMonth.getMonth() > today.getMonth();
2196
+ const canGoNext = viewMonth < maxDate;
2197
+ const goMonth = (dir) => {
2198
+ setViewMonth((prev) => new Date(prev.getFullYear(), prev.getMonth() + dir, 1));
2199
+ };
2200
+ const groupedSlots = React3.useMemo(() => {
2201
+ const morning = [];
2202
+ const afternoon = [];
2203
+ const evening = [];
2204
+ for (const s of slots) {
2205
+ const h = new Date(s.start).getHours();
2206
+ if (h < 12) morning.push(s);
2207
+ else if (h < 17) afternoon.push(s);
2208
+ else evening.push(s);
2209
+ }
2210
+ return { morning, afternoon, evening };
2211
+ }, [slots]);
2073
2212
  const cssVars = {
2074
- "--booking-primary": styles2.primaryColor || "#4bbf39",
2075
- "--booking-radius": styles2.borderRadius || "8px",
2076
- "--booking-font": styles2.fontFamily || "inherit"
2213
+ "--bw-primary": styles2.primaryColor || "#0069ff",
2214
+ "--bw-primary-light": styles2.primaryColor ? `color-mix(in srgb, ${styles2.primaryColor} 12%, white)` : "#e8f1ff",
2215
+ "--bw-primary-hover": styles2.primaryColor ? `color-mix(in srgb, ${styles2.primaryColor} 90%, black)` : "#0055d4",
2216
+ "--bw-radius": styles2.borderRadius || "8px",
2217
+ "--bw-font": styles2.fontFamily || "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"
2077
2218
  };
2078
- return /* @__PURE__ */ jsxRuntime.jsxs(
2079
- "div",
2080
- {
2081
- className: `uptrade-booking-widget ${className}`,
2082
- style: cssVars,
2083
- children: [
2084
- error && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "uptrade-booking-error", children: [
2085
- error,
2086
- /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => setError(null), children: "Dismiss" })
2087
- ] }),
2088
- step === "type" && !hideTypeSelector && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "uptrade-booking-types", children: [
2089
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "uptrade-booking-title", children: "Select a Service" }),
2090
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "uptrade-booking-type-list", children: bookingTypes.map((type) => /* @__PURE__ */ jsxRuntime.jsxs(
2091
- "button",
2092
- {
2093
- className: "uptrade-booking-type-card",
2094
- onClick: () => {
2095
- setSelectedType(type);
2096
- setStep("datetime");
2097
- },
2098
- children: [
2099
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "uptrade-booking-type-name", children: type.name }),
2100
- type.description && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "uptrade-booking-type-desc", children: type.description }),
2101
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "uptrade-booking-type-meta", children: [
2102
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatDuration(type.duration_minutes) }),
2103
- type.price_cents ? /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
2104
- "$",
2105
- (type.price_cents / 100).toFixed(2)
2106
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Free" })
2107
- ] })
2108
- ]
2109
- },
2110
- type.id
2111
- )) })
2112
- ] }),
2113
- step === "datetime" && selectedType && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "uptrade-booking-datetime", children: [
2114
- /* @__PURE__ */ jsxRuntime.jsx(
2115
- "button",
2116
- {
2117
- className: "uptrade-booking-back",
2118
- onClick: () => {
2119
- if (bookingTypeSlug) return;
2120
- setSelectedType(null);
2121
- setStep("type");
2122
- },
2123
- disabled: !!bookingTypeSlug,
2124
- children: "\u2190 Back"
2125
- }
2126
- ),
2127
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "uptrade-booking-title", children: selectedType.name }),
2128
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "uptrade-booking-subtitle", children: [
2129
- formatDuration(selectedType.duration_minutes),
2130
- " \u2022 ",
2131
- selectedType.location_type
2132
- ] }),
2133
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "uptrade-booking-dates", children: [
2134
- /* @__PURE__ */ jsxRuntime.jsx("h4", { children: "Select a Date" }),
2135
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "uptrade-booking-date-grid", children: availableDates.map((date) => {
2136
- const dateStr = date.toISOString().split("T")[0];
2137
- const isSelected = selectedDate?.toISOString().split("T")[0] === dateStr;
2138
- return /* @__PURE__ */ jsxRuntime.jsxs(
2139
- "button",
2140
- {
2141
- className: `uptrade-booking-date ${isSelected ? "selected" : ""}`,
2142
- onClick: () => setSelectedDate(date),
2143
- children: [
2144
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "uptrade-booking-date-day", children: date.toLocaleDateString("en-US", { weekday: "short" }) }),
2145
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "uptrade-booking-date-num", children: date.getDate() })
2146
- ]
2147
- },
2148
- dateStr
2149
- );
2150
- }) })
2219
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `bw-root ${className}`, style: cssVars, children: [
2220
+ error && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-error", role: "alert", children: [
2221
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: error }),
2222
+ /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => setError(null), "aria-label": "Dismiss error", children: "\xD7" })
2223
+ ] }),
2224
+ step === "type" && !hideTypeSelector && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-step bw-fade-in", children: [
2225
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "bw-heading", children: "Select a Service" }),
2226
+ loading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-loading", children: [
2227
+ /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, {}),
2228
+ " Loading services..."
2229
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bw-type-list", children: bookingTypes.map((type) => /* @__PURE__ */ jsxRuntime.jsxs(
2230
+ "button",
2231
+ {
2232
+ className: "bw-type-card",
2233
+ onClick: () => {
2234
+ setSelectedType(type);
2235
+ setStep("datetime");
2236
+ },
2237
+ children: [
2238
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-type-name", children: type.name }),
2239
+ type.description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-type-desc", children: type.description }),
2240
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "bw-type-meta", children: [
2241
+ /* @__PURE__ */ jsxRuntime.jsx(ClockIcon, {}),
2242
+ " ",
2243
+ formatDuration(type.duration_minutes),
2244
+ type.price_cents ? ` \xB7 $${(type.price_cents / 100).toFixed(2)}` : " \xB7 Free"
2245
+ ] })
2246
+ ]
2247
+ },
2248
+ type.id
2249
+ )) })
2250
+ ] }),
2251
+ step === "datetime" && selectedType && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-step bw-fade-in", children: [
2252
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-info-header", children: [
2253
+ !bookingTypeSlug && /* @__PURE__ */ jsxRuntime.jsx("button", { className: "bw-back", onClick: () => {
2254
+ setSelectedType(null);
2255
+ setStep("type");
2256
+ }, children: /* @__PURE__ */ jsxRuntime.jsx(ArrowLeftIcon, {}) }),
2257
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2258
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "bw-heading", children: selectedType.name }),
2259
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-meta-row", children: [
2260
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "bw-badge", children: [
2261
+ /* @__PURE__ */ jsxRuntime.jsx(ClockIcon, {}),
2262
+ " ",
2263
+ formatDuration(selectedType.duration_minutes)
2264
+ ] }),
2265
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "bw-badge", children: [
2266
+ /* @__PURE__ */ jsxRuntime.jsx(GlobeIcon, {}),
2267
+ " ",
2268
+ shortTz
2269
+ ] })
2270
+ ] })
2271
+ ] })
2272
+ ] }),
2273
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-datetime-layout", children: [
2274
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-calendar", children: [
2275
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-cal-header", children: [
2276
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: "bw-cal-nav", onClick: () => goMonth(-1), disabled: !canGoPrev, "aria-label": "Previous month", children: /* @__PURE__ */ jsxRuntime.jsx(ChevronLeft, {}) }),
2277
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "bw-cal-title", children: [
2278
+ MONTHS[viewMonth.getMonth()],
2279
+ " ",
2280
+ viewMonth.getFullYear()
2281
+ ] }),
2282
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: "bw-cal-nav", onClick: () => goMonth(1), disabled: !canGoNext, "aria-label": "Next month", children: /* @__PURE__ */ jsxRuntime.jsx(ChevronRight, {}) })
2151
2283
  ] }),
2152
- selectedDate && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "uptrade-booking-times", children: [
2153
- /* @__PURE__ */ jsxRuntime.jsx("h4", { children: "Select a Time" }),
2154
- loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "uptrade-booking-loading", children: "Loading available times..." }) : slots.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "uptrade-booking-empty", children: "No times available on this date" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "uptrade-booking-time-grid", children: slots.map((slot) => /* @__PURE__ */ jsxRuntime.jsx(
2284
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bw-cal-weekdays", children: DAYS.map((d) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-cal-wd", children: d }, d)) }),
2285
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bw-cal-grid", children: days.map((date, i) => {
2286
+ if (!date) return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-cal-empty" }, `e-${i}`);
2287
+ const past = isBeforeDay(date, today);
2288
+ const future = date > maxDate;
2289
+ const disabled = past || future;
2290
+ const sel = selectedDate && isSameDay(date, selectedDate);
2291
+ const isToday = isSameDay(date, today);
2292
+ return /* @__PURE__ */ jsxRuntime.jsx(
2155
2293
  "button",
2156
2294
  {
2157
- className: `uptrade-booking-time ${selectedSlot?.start === slot.start ? "selected" : ""}`,
2158
- onClick: () => handleSlotSelect(slot),
2159
- disabled: loading,
2160
- children: formatTime(slot.start, timezone)
2295
+ className: `bw-cal-day${sel ? " selected" : ""}${isToday ? " today" : ""}${disabled ? " disabled" : ""}`,
2296
+ onClick: () => !disabled && setSelectedDate(date),
2297
+ disabled,
2298
+ "aria-label": date.toLocaleDateString("en-US", { weekday: "long", month: "long", day: "numeric" }),
2299
+ "aria-pressed": sel || void 0,
2300
+ children: date.getDate()
2161
2301
  },
2162
- slot.start
2163
- )) })
2164
- ] })
2302
+ date.toISOString()
2303
+ );
2304
+ }) })
2165
2305
  ] }),
2166
- step === "form" && selectedType && selectedSlot && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "uptrade-booking-form", children: [
2167
- /* @__PURE__ */ jsxRuntime.jsx(
2168
- "button",
2169
- {
2170
- className: "uptrade-booking-back",
2171
- onClick: () => setStep("datetime"),
2172
- children: "\u2190 Back"
2173
- }
2174
- ),
2175
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "uptrade-booking-title", children: "Your Information" }),
2176
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "uptrade-booking-subtitle", children: [
2177
- selectedType.name,
2178
- " on ",
2179
- formatDate2(selectedSlot.start, timezone),
2180
- " at ",
2181
- formatTime(selectedSlot.start, timezone)
2306
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `bw-times${selectedDate ? " visible" : ""}`, ref: slotsRef, children: !selectedDate ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-times-placeholder", children: [
2307
+ /* @__PURE__ */ jsxRuntime.jsx(CalendarPlusIcon, {}),
2308
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Select a date to view available times" })
2309
+ ] }) : slotsLoading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-loading", children: [
2310
+ /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, {}),
2311
+ " Loading times..."
2312
+ ] }) : slots.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bw-times-empty", children: "No available times on this date. Try another day." }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-times-scroll", children: [
2313
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "bw-times-date", children: selectedDate.toLocaleDateString("en-US", { weekday: "long", month: "short", day: "numeric" }) }),
2314
+ groupedSlots.morning.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-time-group", children: [
2315
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-time-label", children: "Morning" }),
2316
+ groupedSlots.morning.map((slot) => /* @__PURE__ */ jsxRuntime.jsx(TimeButton, { slot, selected: selectedSlot?.start === slot.start, confirmed: confirmedSlot && selectedSlot?.start === slot.start, onClick: () => handleSlotSelect(slot), onConfirm: handleSlotConfirm, timezone, loading }, slot.start))
2182
2317
  ] }),
2183
- hold && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "uptrade-booking-hold-notice", children: [
2184
- "This time is held for you for ",
2185
- Math.floor((new Date(hold.expiresAt).getTime() - Date.now()) / 6e4),
2186
- " minutes"
2318
+ groupedSlots.afternoon.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-time-group", children: [
2319
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-time-label", children: "Afternoon" }),
2320
+ groupedSlots.afternoon.map((slot) => /* @__PURE__ */ jsxRuntime.jsx(TimeButton, { slot, selected: selectedSlot?.start === slot.start, confirmed: confirmedSlot && selectedSlot?.start === slot.start, onClick: () => handleSlotSelect(slot), onConfirm: handleSlotConfirm, timezone, loading }, slot.start))
2187
2321
  ] }),
2188
- /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleBookingSubmit, children: [
2189
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "uptrade-booking-field", children: [
2190
- /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "guest-name", children: "Name *" }),
2191
- /* @__PURE__ */ jsxRuntime.jsx(
2192
- "input",
2193
- {
2194
- id: "guest-name",
2195
- type: "text",
2196
- required: true,
2197
- value: guestInfo.name,
2198
- onChange: (e) => setGuestInfo((prev) => ({ ...prev, name: e.target.value })),
2199
- placeholder: "Your full name"
2200
- }
2201
- )
2202
- ] }),
2203
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "uptrade-booking-field", children: [
2204
- /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "guest-email", children: "Email *" }),
2205
- /* @__PURE__ */ jsxRuntime.jsx(
2206
- "input",
2207
- {
2208
- id: "guest-email",
2209
- type: "email",
2210
- required: true,
2211
- value: guestInfo.email,
2212
- onChange: (e) => setGuestInfo((prev) => ({ ...prev, email: e.target.value })),
2213
- placeholder: "your@email.com"
2214
- }
2215
- )
2216
- ] }),
2217
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "uptrade-booking-field", children: [
2218
- /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "guest-phone", children: "Phone" }),
2219
- /* @__PURE__ */ jsxRuntime.jsx(
2220
- "input",
2221
- {
2222
- id: "guest-phone",
2223
- type: "tel",
2224
- value: guestInfo.phone || "",
2225
- onChange: (e) => setGuestInfo((prev) => ({ ...prev, phone: e.target.value })),
2226
- placeholder: "(555) 123-4567"
2227
- }
2228
- )
2229
- ] }),
2230
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "uptrade-booking-field", children: [
2231
- /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "guest-notes", children: "Additional Notes" }),
2232
- /* @__PURE__ */ jsxRuntime.jsx(
2233
- "textarea",
2234
- {
2235
- id: "guest-notes",
2236
- value: guestInfo.notes || "",
2237
- onChange: (e) => setGuestInfo((prev) => ({ ...prev, notes: e.target.value })),
2238
- placeholder: "Anything you'd like us to know...",
2239
- rows: 3
2240
- }
2241
- )
2242
- ] }),
2243
- /* @__PURE__ */ jsxRuntime.jsx(
2244
- "button",
2245
- {
2246
- type: "submit",
2247
- className: "uptrade-booking-submit",
2248
- disabled: loading,
2249
- children: loading ? "Booking..." : "Confirm Booking"
2250
- }
2251
- )
2322
+ groupedSlots.evening.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-time-group", children: [
2323
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-time-label", children: "Evening" }),
2324
+ groupedSlots.evening.map((slot) => /* @__PURE__ */ jsxRuntime.jsx(TimeButton, { slot, selected: selectedSlot?.start === slot.start, confirmed: confirmedSlot && selectedSlot?.start === slot.start, onClick: () => handleSlotSelect(slot), onConfirm: handleSlotConfirm, timezone, loading }, slot.start))
2252
2325
  ] })
2253
- ] }),
2254
- step === "success" && bookingResult && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "uptrade-booking-success", children: [
2255
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "uptrade-booking-success-icon", children: "\u2713" }),
2256
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "uptrade-booking-title", children: "Booking Confirmed!" }),
2257
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "uptrade-booking-confirmation-code", children: [
2258
- "Confirmation: ",
2259
- bookingResult.booking.confirmationCode
2260
- ] }),
2261
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "uptrade-booking-details", children: [
2262
- /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
2263
- /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "When:" }),
2264
- " ",
2265
- formatDate2(bookingResult.booking.scheduledAt, timezone),
2266
- " at ",
2267
- formatTime(bookingResult.booking.scheduledAt, timezone)
2268
- ] }),
2269
- /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
2270
- /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Duration:" }),
2326
+ ] }) })
2327
+ ] })
2328
+ ] }),
2329
+ step === "form" && selectedType && selectedSlot && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-step bw-fade-in", children: [
2330
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-info-header", children: [
2331
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: "bw-back", onClick: () => setStep("datetime"), children: /* @__PURE__ */ jsxRuntime.jsx(ArrowLeftIcon, {}) }),
2332
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2333
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "bw-heading", children: "Your Details" }),
2334
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-meta-row", children: [
2335
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "bw-badge", children: [
2336
+ /* @__PURE__ */ jsxRuntime.jsx(ClockIcon, {}),
2271
2337
  " ",
2272
- formatDuration(bookingResult.booking.durationMinutes)
2338
+ formatDuration(selectedType.duration_minutes)
2273
2339
  ] }),
2274
- bookingResult.booking.hostName && /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
2275
- /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "With:" }),
2276
- " ",
2277
- bookingResult.booking.hostName
2340
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "bw-badge-accent", children: [
2341
+ formatDate2(selectedSlot.start, timezone),
2342
+ " \xB7 ",
2343
+ formatTime(selectedSlot.start, timezone)
2278
2344
  ] })
2345
+ ] })
2346
+ ] })
2347
+ ] }),
2348
+ hold && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-hold-notice", children: [
2349
+ /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, {}),
2350
+ " Slot held for you \u2014 complete your details to confirm."
2351
+ ] }),
2352
+ /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleBookingSubmit, className: "bw-form", children: [
2353
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-field", children: [
2354
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { htmlFor: "bw-name", children: [
2355
+ "Name ",
2356
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-req", children: "*" })
2279
2357
  ] }),
2280
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "uptrade-booking-calendar-links", children: [
2281
- /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Add to calendar:" }),
2282
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "uptrade-booking-calendar-buttons", children: [
2283
- /* @__PURE__ */ jsxRuntime.jsx(
2284
- "a",
2285
- {
2286
- href: bookingResult.calendarLinks.google,
2287
- target: "_blank",
2288
- rel: "noopener noreferrer",
2289
- className: "uptrade-booking-calendar-btn",
2290
- children: "Google"
2291
- }
2292
- ),
2293
- /* @__PURE__ */ jsxRuntime.jsx(
2294
- "a",
2295
- {
2296
- href: bookingResult.calendarLinks.outlook,
2297
- target: "_blank",
2298
- rel: "noopener noreferrer",
2299
- className: "uptrade-booking-calendar-btn",
2300
- children: "Outlook"
2301
- }
2302
- ),
2303
- /* @__PURE__ */ jsxRuntime.jsx(
2304
- "a",
2305
- {
2306
- href: bookingResult.calendarLinks.ics,
2307
- download: true,
2308
- className: "uptrade-booking-calendar-btn",
2309
- children: "Download .ics"
2310
- }
2311
- )
2312
- ] })
2358
+ /* @__PURE__ */ jsxRuntime.jsx(
2359
+ "input",
2360
+ {
2361
+ id: "bw-name",
2362
+ type: "text",
2363
+ required: true,
2364
+ value: guestInfo.name,
2365
+ onChange: (e) => setGuestInfo((p) => ({ ...p, name: e.target.value })),
2366
+ placeholder: "Jane Smith",
2367
+ autoComplete: "name"
2368
+ }
2369
+ )
2370
+ ] }),
2371
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-field", children: [
2372
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { htmlFor: "bw-email", children: [
2373
+ "Email ",
2374
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-req", children: "*" })
2313
2375
  ] }),
2314
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "uptrade-booking-email-notice", children: "A confirmation email has been sent to your email address." })
2376
+ /* @__PURE__ */ jsxRuntime.jsx(
2377
+ "input",
2378
+ {
2379
+ id: "bw-email",
2380
+ type: "email",
2381
+ required: true,
2382
+ value: guestInfo.email,
2383
+ onChange: (e) => setGuestInfo((p) => ({ ...p, email: e.target.value })),
2384
+ placeholder: "jane@example.com",
2385
+ autoComplete: "email"
2386
+ }
2387
+ )
2315
2388
  ] }),
2316
- /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
2317
- .uptrade-booking-widget {
2318
- font-family: var(--booking-font);
2319
- max-width: 480px;
2320
- margin: 0 auto;
2321
- padding: 24px;
2322
- border: 1px solid #e5e5e5;
2323
- border-radius: var(--booking-radius);
2324
- background: #fff;
2325
- }
2326
-
2327
- .uptrade-booking-error {
2328
- background: #fef2f2;
2329
- border: 1px solid #fecaca;
2330
- color: #b91c1c;
2331
- padding: 12px;
2332
- border-radius: var(--booking-radius);
2333
- margin-bottom: 16px;
2334
- display: flex;
2335
- justify-content: space-between;
2336
- align-items: center;
2337
- }
2338
-
2339
- .uptrade-booking-error button {
2340
- background: none;
2341
- border: none;
2342
- color: inherit;
2343
- cursor: pointer;
2344
- text-decoration: underline;
2345
- }
2346
-
2347
- .uptrade-booking-title {
2348
- font-size: 1.25rem;
2349
- font-weight: 600;
2350
- margin: 0 0 8px 0;
2351
- }
2352
-
2353
- .uptrade-booking-subtitle {
2354
- color: #666;
2355
- margin: 0 0 16px 0;
2356
- }
2357
-
2358
- .uptrade-booking-back {
2359
- background: none;
2360
- border: none;
2361
- color: var(--booking-primary);
2362
- cursor: pointer;
2363
- padding: 0;
2364
- margin-bottom: 16px;
2365
- font-size: 0.875rem;
2366
- }
2367
-
2368
- .uptrade-booking-back:disabled {
2369
- display: none;
2370
- }
2371
-
2372
- .uptrade-booking-type-list {
2373
- display: flex;
2374
- flex-direction: column;
2375
- gap: 12px;
2376
- }
2377
-
2378
- .uptrade-booking-type-card {
2379
- text-align: left;
2380
- padding: 16px;
2381
- border: 1px solid #e5e5e5;
2382
- border-radius: var(--booking-radius);
2383
- background: #fff;
2384
- cursor: pointer;
2385
- transition: border-color 0.2s, box-shadow 0.2s;
2386
- }
2387
-
2388
- .uptrade-booking-type-card:hover {
2389
- border-color: var(--booking-primary);
2390
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--booking-primary) 15%, transparent);
2391
- }
2392
-
2393
- .uptrade-booking-type-name {
2394
- font-weight: 600;
2395
- margin-bottom: 4px;
2396
- }
2397
-
2398
- .uptrade-booking-type-desc {
2399
- color: #666;
2400
- font-size: 0.875rem;
2401
- margin-bottom: 8px;
2402
- }
2403
-
2404
- .uptrade-booking-type-meta {
2405
- display: flex;
2406
- gap: 12px;
2407
- font-size: 0.75rem;
2408
- color: #888;
2409
- }
2410
-
2411
- .uptrade-booking-date-grid {
2412
- display: flex;
2413
- gap: 8px;
2414
- flex-wrap: wrap;
2415
- margin-bottom: 24px;
2416
- }
2417
-
2418
- .uptrade-booking-date {
2419
- display: flex;
2420
- flex-direction: column;
2421
- align-items: center;
2422
- padding: 8px 12px;
2423
- border: 1px solid #e5e5e5;
2424
- border-radius: var(--booking-radius);
2425
- background: #fff;
2426
- cursor: pointer;
2427
- transition: all 0.2s;
2428
- }
2429
-
2430
- .uptrade-booking-date:hover {
2431
- border-color: var(--booking-primary);
2432
- }
2433
-
2434
- .uptrade-booking-date.selected {
2435
- background: var(--booking-primary);
2436
- border-color: var(--booking-primary);
2437
- color: #fff;
2438
- }
2439
-
2440
- .uptrade-booking-date-day {
2441
- font-size: 0.625rem;
2442
- text-transform: uppercase;
2443
- }
2444
-
2445
- .uptrade-booking-date-num {
2446
- font-size: 1.125rem;
2447
- font-weight: 600;
2448
- }
2449
-
2450
- .uptrade-booking-time-grid {
2451
- display: grid;
2452
- grid-template-columns: repeat(3, 1fr);
2453
- gap: 8px;
2454
- }
2455
-
2456
- .uptrade-booking-time {
2457
- padding: 10px;
2458
- border: 1px solid #e5e5e5;
2459
- border-radius: var(--booking-radius);
2460
- background: #fff;
2461
- cursor: pointer;
2462
- font-size: 0.875rem;
2463
- transition: all 0.2s;
2464
- }
2465
-
2466
- .uptrade-booking-time:hover:not(:disabled) {
2467
- border-color: var(--booking-primary);
2468
- }
2469
-
2470
- .uptrade-booking-time.selected {
2471
- background: var(--booking-primary);
2472
- border-color: var(--booking-primary);
2473
- color: #fff;
2474
- }
2475
-
2476
- .uptrade-booking-time:disabled {
2477
- opacity: 0.5;
2478
- cursor: not-allowed;
2479
- }
2480
-
2481
- .uptrade-booking-loading,
2482
- .uptrade-booking-empty {
2483
- text-align: center;
2484
- padding: 24px;
2485
- color: #666;
2486
- }
2487
-
2488
- .uptrade-booking-hold-notice {
2489
- background: #fef3c7;
2490
- border: 1px solid #fcd34d;
2491
- color: #92400e;
2492
- padding: 8px 12px;
2493
- border-radius: var(--booking-radius);
2494
- font-size: 0.875rem;
2495
- margin-bottom: 16px;
2496
- }
2497
-
2498
- .uptrade-booking-field {
2499
- margin-bottom: 16px;
2500
- }
2501
-
2502
- .uptrade-booking-field label {
2503
- display: block;
2504
- margin-bottom: 4px;
2505
- font-size: 0.875rem;
2506
- font-weight: 500;
2507
- }
2508
-
2509
- .uptrade-booking-field input,
2510
- .uptrade-booking-field textarea {
2511
- width: 100%;
2512
- padding: 10px 12px;
2513
- border: 1px solid #e5e5e5;
2514
- border-radius: var(--booking-radius);
2515
- font-size: 1rem;
2516
- font-family: inherit;
2517
- }
2518
-
2519
- .uptrade-booking-field input:focus,
2520
- .uptrade-booking-field textarea:focus {
2521
- outline: none;
2522
- border-color: var(--booking-primary);
2523
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--booking-primary) 15%, transparent);
2524
- }
2525
-
2526
- .uptrade-booking-submit {
2527
- width: 100%;
2528
- padding: 14px;
2529
- background: var(--booking-primary);
2530
- color: #fff;
2531
- border: none;
2532
- border-radius: var(--booking-radius);
2533
- font-size: 1rem;
2534
- font-weight: 600;
2535
- cursor: pointer;
2536
- transition: opacity 0.2s;
2537
- }
2538
-
2539
- .uptrade-booking-submit:hover:not(:disabled) {
2540
- opacity: 0.9;
2541
- }
2542
-
2543
- .uptrade-booking-submit:disabled {
2544
- opacity: 0.5;
2545
- cursor: not-allowed;
2546
- }
2547
-
2548
- .uptrade-booking-success {
2549
- text-align: center;
2550
- }
2551
-
2552
- .uptrade-booking-success-icon {
2553
- width: 64px;
2554
- height: 64px;
2555
- margin: 0 auto 16px;
2556
- background: var(--booking-primary);
2557
- color: #fff;
2558
- border-radius: 50%;
2559
- display: flex;
2560
- align-items: center;
2561
- justify-content: center;
2562
- font-size: 2rem;
2563
- }
2564
-
2565
- .uptrade-booking-confirmation-code {
2566
- font-family: monospace;
2567
- background: #f5f5f5;
2568
- padding: 8px 16px;
2569
- border-radius: var(--booking-radius);
2570
- display: inline-block;
2571
- margin-bottom: 24px;
2572
- }
2573
-
2574
- .uptrade-booking-details {
2575
- text-align: left;
2576
- background: #f9f9f9;
2577
- padding: 16px;
2578
- border-radius: var(--booking-radius);
2579
- margin-bottom: 24px;
2580
- }
2581
-
2582
- .uptrade-booking-details p {
2583
- margin: 0 0 8px 0;
2584
- }
2585
-
2586
- .uptrade-booking-details p:last-child {
2587
- margin-bottom: 0;
2588
- }
2589
-
2590
- .uptrade-booking-calendar-links {
2591
- margin-bottom: 24px;
2592
- }
2593
-
2594
- .uptrade-booking-calendar-buttons {
2595
- display: flex;
2596
- gap: 8px;
2597
- justify-content: center;
2598
- margin-top: 8px;
2599
- }
2600
-
2601
- .uptrade-booking-calendar-btn {
2602
- padding: 8px 16px;
2603
- border: 1px solid #e5e5e5;
2604
- border-radius: var(--booking-radius);
2605
- text-decoration: none;
2606
- color: #333;
2607
- font-size: 0.875rem;
2608
- transition: all 0.2s;
2609
- }
2610
-
2611
- .uptrade-booking-calendar-btn:hover {
2612
- border-color: var(--booking-primary);
2613
- color: var(--booking-primary);
2614
- }
2615
-
2616
- .uptrade-booking-email-notice {
2617
- color: #666;
2618
- font-size: 0.875rem;
2619
- }
2620
-
2621
- h4 {
2622
- font-size: 0.875rem;
2623
- font-weight: 600;
2624
- margin: 0 0 12px 0;
2625
- color: #666;
2626
- }
2627
- ` })
2628
- ]
2629
- }
2630
- );
2389
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-field", children: [
2390
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "bw-phone", children: "Phone" }),
2391
+ /* @__PURE__ */ jsxRuntime.jsx(
2392
+ "input",
2393
+ {
2394
+ id: "bw-phone",
2395
+ type: "tel",
2396
+ value: guestInfo.phone || "",
2397
+ onChange: (e) => setGuestInfo((p) => ({ ...p, phone: e.target.value })),
2398
+ placeholder: "(555) 123-4567",
2399
+ autoComplete: "tel"
2400
+ }
2401
+ )
2402
+ ] }),
2403
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-field", children: [
2404
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "bw-notes", children: "Notes" }),
2405
+ /* @__PURE__ */ jsxRuntime.jsx(
2406
+ "textarea",
2407
+ {
2408
+ id: "bw-notes",
2409
+ value: guestInfo.notes || "",
2410
+ onChange: (e) => setGuestInfo((p) => ({ ...p, notes: e.target.value })),
2411
+ placeholder: "Anything you'd like us to know...",
2412
+ rows: 3
2413
+ }
2414
+ )
2415
+ ] }),
2416
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", className: "bw-submit", disabled: submitting || !guestInfo.name || !guestInfo.email, children: submitting ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2417
+ /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, {}),
2418
+ " Confirming..."
2419
+ ] }) : "Confirm Booking" })
2420
+ ] })
2421
+ ] }),
2422
+ step === "success" && bookingResult && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-step bw-fade-in bw-success", children: [
2423
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bw-success-icon", children: /* @__PURE__ */ jsxRuntime.jsx(CheckCircleIcon, {}) }),
2424
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "bw-heading", children: "You\u2019re Booked!" }),
2425
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "bw-conf-code", children: bookingResult.booking.confirmationCode }),
2426
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-details-card", children: [
2427
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-detail-row", children: [
2428
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-detail-label", children: "When" }),
2429
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "bw-detail-value", children: [
2430
+ formatDate2(bookingResult.booking.scheduledAt, timezone),
2431
+ /* @__PURE__ */ jsxRuntime.jsx("br", {}),
2432
+ formatTime(bookingResult.booking.scheduledAt, timezone),
2433
+ " (",
2434
+ shortTz,
2435
+ ")"
2436
+ ] })
2437
+ ] }),
2438
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-detail-row", children: [
2439
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-detail-label", children: "Duration" }),
2440
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-detail-value", children: formatDuration(bookingResult.booking.durationMinutes) })
2441
+ ] }),
2442
+ bookingResult.booking.hostName && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-detail-row", children: [
2443
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-detail-label", children: "With" }),
2444
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-detail-value", children: bookingResult.booking.hostName })
2445
+ ] })
2446
+ ] }),
2447
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "bw-cal-links-label", children: "Add to your calendar" }),
2448
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-cal-links", children: [
2449
+ /* @__PURE__ */ jsxRuntime.jsxs("a", { href: bookingResult.calendarLinks.google, target: "_blank", rel: "noopener noreferrer", className: "bw-cal-link", children: [
2450
+ /* @__PURE__ */ jsxRuntime.jsx(CalendarPlusIcon, {}),
2451
+ " Google"
2452
+ ] }),
2453
+ /* @__PURE__ */ jsxRuntime.jsxs("a", { href: bookingResult.calendarLinks.outlook, target: "_blank", rel: "noopener noreferrer", className: "bw-cal-link", children: [
2454
+ /* @__PURE__ */ jsxRuntime.jsx(CalendarPlusIcon, {}),
2455
+ " Outlook"
2456
+ ] }),
2457
+ /* @__PURE__ */ jsxRuntime.jsxs("a", { href: bookingResult.calendarLinks.ics, download: true, className: "bw-cal-link", children: [
2458
+ /* @__PURE__ */ jsxRuntime.jsx(CalendarPlusIcon, {}),
2459
+ " iCal"
2460
+ ] })
2461
+ ] }),
2462
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "bw-email-notice", children: [
2463
+ "A confirmation email has been sent to ",
2464
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: guestInfo.email }),
2465
+ "."
2466
+ ] })
2467
+ ] }),
2468
+ loading && step === "datetime" && !selectedType && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-loading", children: [
2469
+ /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, {}),
2470
+ " Loading..."
2471
+ ] }),
2472
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: WIDGET_CSS })
2473
+ ] });
2474
+ }
2475
+ function TimeButton({
2476
+ slot,
2477
+ selected,
2478
+ confirmed,
2479
+ onClick,
2480
+ onConfirm,
2481
+ timezone,
2482
+ loading
2483
+ }) {
2484
+ if (selected && !confirmed) {
2485
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bw-time-btn-wrap selected", children: [
2486
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bw-time-text", children: formatTime(slot.start, timezone) }),
2487
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: "bw-time-confirm", onClick: onConfirm, disabled: loading, children: loading ? /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, {}) : "Confirm" })
2488
+ ] });
2489
+ }
2490
+ return /* @__PURE__ */ jsxRuntime.jsx("button", { className: `bw-time-btn${selected ? " selected" : ""}`, onClick, disabled: loading, children: formatTime(slot.start, timezone) });
2491
+ }
2492
+ var WIDGET_CSS = `
2493
+ /* \u2500\u2500 Base \u2500\u2500 */
2494
+ .bw-root {
2495
+ font-family: var(--bw-font);
2496
+ color: #1a1a1a;
2497
+ line-height: 1.5;
2498
+ -webkit-font-smoothing: antialiased;
2499
+ box-sizing: border-box;
2500
+ }
2501
+ .bw-root *, .bw-root *::before, .bw-root *::after { box-sizing: border-box; }
2502
+
2503
+ /* \u2500\u2500 Animation \u2500\u2500 */
2504
+ @keyframes bw-fade-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
2505
+ @keyframes bw-spin { to { transform: rotate(360deg); } }
2506
+ .bw-fade-in { animation: bw-fade-in 0.25s ease-out; }
2507
+ .bw-spinner { animation: bw-spin 0.8s linear infinite; }
2508
+
2509
+ /* \u2500\u2500 Error \u2500\u2500 */
2510
+ .bw-error {
2511
+ display: flex; align-items: center; justify-content: space-between; gap: 12px;
2512
+ background: #fef2f2; border: 1px solid #fecaca; color: #b91c1c;
2513
+ padding: 10px 14px; border-radius: var(--bw-radius); margin-bottom: 16px; font-size: 0.875rem;
2514
+ }
2515
+ .bw-error button { background: none; border: none; color: inherit; cursor: pointer; font-size: 1.25rem; line-height: 1; padding: 0; }
2516
+
2517
+ /* \u2500\u2500 Headings \u2500\u2500 */
2518
+ .bw-heading { font-size: 1.125rem; font-weight: 600; margin: 0; letter-spacing: -0.01em; }
2519
+ .bw-meta-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
2520
+ .bw-badge {
2521
+ display: inline-flex; align-items: center; gap: 4px;
2522
+ font-size: 0.8125rem; color: #6b7280; font-weight: 400;
2523
+ }
2524
+ .bw-badge-accent {
2525
+ display: inline-flex; align-items: center; gap: 4px;
2526
+ font-size: 0.8125rem; color: var(--bw-primary); font-weight: 500;
2527
+ }
2528
+
2529
+ /* \u2500\u2500 Info Header \u2500\u2500 */
2530
+ .bw-info-header { display: flex; align-items: flex-start; gap: 8px; margin-bottom: 20px; }
2531
+ .bw-back {
2532
+ display: inline-flex; align-items: center; justify-content: center;
2533
+ width: 32px; height: 32px; border-radius: 50%; border: 1px solid #e5e7eb;
2534
+ background: #fff; cursor: pointer; color: #4b5563; flex-shrink: 0; margin-top: 1px;
2535
+ transition: all 0.15s;
2536
+ }
2537
+ .bw-back:hover { background: #f3f4f6; border-color: #d1d5db; }
2538
+
2539
+ /* \u2500\u2500 Loading \u2500\u2500 */
2540
+ .bw-loading {
2541
+ display: flex; align-items: center; justify-content: center; gap: 8px;
2542
+ padding: 32px; color: #6b7280; font-size: 0.875rem;
2543
+ }
2544
+
2545
+ /* \u2500\u2500 Type Selection \u2500\u2500 */
2546
+ .bw-type-list { display: flex; flex-direction: column; gap: 10px; }
2547
+ .bw-type-card {
2548
+ display: flex; flex-direction: column; text-align: left; gap: 4px;
2549
+ padding: 16px; border: 1.5px solid #e5e7eb; border-radius: var(--bw-radius);
2550
+ background: #fff; cursor: pointer; transition: all 0.15s;
2551
+ }
2552
+ .bw-type-card:hover { border-color: var(--bw-primary); box-shadow: 0 0 0 3px var(--bw-primary-light); }
2553
+ .bw-type-name { font-weight: 600; font-size: 0.9375rem; }
2554
+ .bw-type-desc { color: #6b7280; font-size: 0.8125rem; }
2555
+ .bw-type-meta { display: flex; align-items: center; gap: 4px; font-size: 0.8125rem; color: #9ca3af; margin-top: 4px; }
2556
+
2557
+ /* \u2500\u2500 Date-Time Layout \u2500\u2500 */
2558
+ .bw-datetime-layout {
2559
+ display: flex; gap: 0; border-top: 1px solid #f0f0f0; padding-top: 16px;
2631
2560
  }
2561
+ @media (max-width: 559px) {
2562
+ .bw-datetime-layout { flex-direction: column; }
2563
+ }
2564
+ @media (min-width: 560px) {
2565
+ .bw-datetime-layout { min-height: 340px; }
2566
+ .bw-calendar { flex: 1 1 auto; padding-right: 16px; border-right: 1px solid #f0f0f0; }
2567
+ .bw-times { width: 180px; flex-shrink: 0; padding-left: 16px; }
2568
+ }
2569
+
2570
+ /* \u2500\u2500 Calendar \u2500\u2500 */
2571
+ .bw-cal-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
2572
+ .bw-cal-title { font-weight: 600; font-size: 0.9375rem; }
2573
+ .bw-cal-nav {
2574
+ display: inline-flex; align-items: center; justify-content: center;
2575
+ width: 32px; height: 32px; border-radius: 50%; border: none;
2576
+ background: transparent; cursor: pointer; color: #374151; transition: background 0.15s;
2577
+ }
2578
+ .bw-cal-nav:hover:not(:disabled) { background: #f3f4f6; }
2579
+ .bw-cal-nav:disabled { opacity: 0.25; cursor: default; }
2580
+
2581
+ .bw-cal-weekdays { display: grid; grid-template-columns: repeat(7, 1fr); gap: 0; text-align: center; margin-bottom: 4px; }
2582
+ .bw-cal-wd { font-size: 0.6875rem; font-weight: 600; color: #9ca3af; text-transform: uppercase; letter-spacing: 0.04em; padding: 4px 0; }
2583
+
2584
+ .bw-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
2585
+ .bw-cal-empty { aspect-ratio: 1; }
2586
+ .bw-cal-day {
2587
+ aspect-ratio: 1; display: flex; align-items: center; justify-content: center;
2588
+ border: none; background: none; border-radius: 50%;
2589
+ font-size: 0.8125rem; font-weight: 500; cursor: pointer;
2590
+ color: #1a1a1a; transition: all 0.15s; position: relative;
2591
+ }
2592
+ .bw-cal-day:hover:not(.disabled):not(.selected) { background: #f3f4f6; }
2593
+ .bw-cal-day.today:not(.selected)::after {
2594
+ content: ''; position: absolute; bottom: 3px; left: 50%; transform: translateX(-50%);
2595
+ width: 4px; height: 4px; border-radius: 50%; background: var(--bw-primary);
2596
+ }
2597
+ .bw-cal-day.selected {
2598
+ background: var(--bw-primary); color: #fff; font-weight: 600;
2599
+ }
2600
+ .bw-cal-day.disabled { color: #d1d5db; cursor: default; }
2601
+
2602
+ /* \u2500\u2500 Time Slots \u2500\u2500 */
2603
+ .bw-times { transition: opacity 0.2s; }
2604
+ .bw-times:not(.visible) { opacity: 0.5; }
2605
+ .bw-times.visible { opacity: 1; }
2606
+ .bw-times-placeholder {
2607
+ display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
2608
+ height: 100%; min-height: 160px; color: #9ca3af; font-size: 0.8125rem; text-align: center; padding: 16px;
2609
+ }
2610
+ .bw-times-empty {
2611
+ display: flex; align-items: center; justify-content: center;
2612
+ height: 100%; min-height: 160px; color: #9ca3af; font-size: 0.8125rem; text-align: center; padding: 16px;
2613
+ }
2614
+ .bw-times-scroll { overflow-y: auto; max-height: 340px; }
2615
+ .bw-times-date { font-weight: 600; font-size: 0.8125rem; color: #374151; margin: 0 0 12px 0; }
2616
+ .bw-time-group { margin-bottom: 16px; }
2617
+ .bw-time-label { display: block; font-size: 0.6875rem; font-weight: 600; color: #9ca3af; text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 6px; }
2618
+
2619
+ .bw-time-btn {
2620
+ display: block; width: 100%;
2621
+ padding: 10px 12px; margin-bottom: 6px;
2622
+ border: 1.5px solid #e5e7eb; border-radius: var(--bw-radius);
2623
+ background: #fff; cursor: pointer;
2624
+ font-size: 0.875rem; font-weight: 500; color: var(--bw-primary); text-align: center;
2625
+ transition: all 0.15s;
2626
+ }
2627
+ .bw-time-btn:hover:not(:disabled) {
2628
+ border-color: var(--bw-primary); background: var(--bw-primary-light);
2629
+ }
2630
+ .bw-time-btn.selected {
2631
+ border-color: var(--bw-primary); background: var(--bw-primary); color: #fff;
2632
+ }
2633
+ .bw-time-btn:disabled { opacity: 0.5; cursor: not-allowed; }
2634
+
2635
+ .bw-time-btn-wrap {
2636
+ display: flex; align-items: center; gap: 6px; margin-bottom: 6px;
2637
+ border: 1.5px solid var(--bw-primary); border-radius: var(--bw-radius); overflow: hidden;
2638
+ animation: bw-fade-in 0.15s ease-out;
2639
+ }
2640
+ .bw-time-btn-wrap .bw-time-text {
2641
+ flex: 1; padding: 10px 12px; font-size: 0.875rem; font-weight: 500; color: #374151; text-align: center;
2642
+ background: var(--bw-primary-light);
2643
+ }
2644
+ .bw-time-confirm {
2645
+ display: inline-flex; align-items: center; justify-content: center; gap: 4px;
2646
+ padding: 10px 16px; border: none;
2647
+ background: var(--bw-primary); color: #fff;
2648
+ font-size: 0.8125rem; font-weight: 600; cursor: pointer;
2649
+ transition: background 0.15s;
2650
+ }
2651
+ .bw-time-confirm:hover:not(:disabled) { background: var(--bw-primary-hover); }
2652
+ .bw-time-confirm:disabled { opacity: 0.7; cursor: not-allowed; }
2653
+
2654
+ /* \u2500\u2500 Form \u2500\u2500 */
2655
+ .bw-hold-notice {
2656
+ display: flex; align-items: center; gap: 8px;
2657
+ background: var(--bw-primary-light); color: var(--bw-primary);
2658
+ padding: 10px 14px; border-radius: var(--bw-radius);
2659
+ font-size: 0.8125rem; font-weight: 500; margin-bottom: 20px;
2660
+ }
2661
+ .bw-form { display: flex; flex-direction: column; gap: 16px; }
2662
+ .bw-field label {
2663
+ display: block; font-size: 0.8125rem; font-weight: 500; color: #374151; margin-bottom: 4px;
2664
+ }
2665
+ .bw-req { color: #ef4444; }
2666
+ .bw-field input, .bw-field textarea {
2667
+ display: block; width: 100%;
2668
+ padding: 10px 12px; border: 1.5px solid #e5e7eb; border-radius: var(--bw-radius);
2669
+ font-size: 0.9375rem; font-family: inherit; color: #1a1a1a; background: #fff;
2670
+ transition: border-color 0.15s, box-shadow 0.15s;
2671
+ }
2672
+ .bw-field input::placeholder, .bw-field textarea::placeholder { color: #c0c5cc; }
2673
+ .bw-field input:focus, .bw-field textarea:focus {
2674
+ outline: none; border-color: var(--bw-primary);
2675
+ box-shadow: 0 0 0 3px var(--bw-primary-light);
2676
+ }
2677
+ .bw-submit {
2678
+ display: inline-flex; align-items: center; justify-content: center; gap: 8px;
2679
+ width: 100%; padding: 14px; margin-top: 4px;
2680
+ background: var(--bw-primary); color: #fff; border: none;
2681
+ border-radius: var(--bw-radius); font-size: 0.9375rem; font-weight: 600;
2682
+ cursor: pointer; transition: background 0.15s;
2683
+ }
2684
+ .bw-submit:hover:not(:disabled) { background: var(--bw-primary-hover); }
2685
+ .bw-submit:disabled { opacity: 0.55; cursor: not-allowed; }
2686
+
2687
+ /* \u2500\u2500 Success \u2500\u2500 */
2688
+ .bw-success { text-align: center; }
2689
+ .bw-success-icon { margin: 0 auto 16px; width: 56px; height: 56px; animation: bw-fade-in 0.4s ease-out; }
2690
+ .bw-conf-code {
2691
+ display: inline-block; font-family: 'SF Mono', 'Fira Code', monospace;
2692
+ background: #f3f4f6; padding: 6px 14px; border-radius: 6px;
2693
+ font-size: 0.8125rem; color: #6b7280; margin: 4px 0 20px; letter-spacing: 0.04em;
2694
+ }
2695
+
2696
+ .bw-details-card {
2697
+ text-align: left; background: #f9fafb; border: 1px solid #f0f0f0;
2698
+ border-radius: var(--bw-radius); padding: 16px; margin-bottom: 20px;
2699
+ }
2700
+ .bw-detail-row { display: flex; gap: 12px; padding: 8px 0; border-bottom: 1px solid #f0f0f0; }
2701
+ .bw-detail-row:last-child { border-bottom: none; }
2702
+ .bw-detail-label { width: 70px; flex-shrink: 0; font-size: 0.8125rem; color: #9ca3af; font-weight: 500; }
2703
+ .bw-detail-value { font-size: 0.875rem; font-weight: 500; color: #374151; }
2704
+
2705
+ .bw-cal-links-label { font-size: 0.8125rem; color: #6b7280; margin: 0 0 8px 0; }
2706
+ .bw-cal-links { display: flex; gap: 8px; justify-content: center; margin-bottom: 20px; flex-wrap: wrap; }
2707
+ .bw-cal-link {
2708
+ display: inline-flex; align-items: center; gap: 6px;
2709
+ padding: 8px 14px; border: 1.5px solid #e5e7eb; border-radius: var(--bw-radius);
2710
+ text-decoration: none; color: #374151; font-size: 0.8125rem; font-weight: 500;
2711
+ transition: all 0.15s;
2712
+ }
2713
+ .bw-cal-link:hover { border-color: var(--bw-primary); color: var(--bw-primary); }
2714
+
2715
+ .bw-email-notice { font-size: 0.8125rem; color: #9ca3af; margin: 0; }
2716
+ .bw-email-notice strong { color: #374151; font-weight: 500; }
2717
+ `;
2632
2718
 
2633
2719
  Object.defineProperty(exports, "TestimonialSection", {
2634
2720
  enumerable: true,