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