@pollar/react 0.11.2 → 0.11.3-rc.1

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
@@ -1033,7 +1033,7 @@ var PollarModalFooter = () => {
1033
1033
  /* @__PURE__ */ jsx("span", { className: "pollar-footer-name", children: "Pollar" }),
1034
1034
  /* @__PURE__ */ jsxs("span", { className: "pollar-footer-version", children: [
1035
1035
  "v",
1036
- "0.11.2"
1036
+ "0.11.3-rc.1"
1037
1037
  ] })
1038
1038
  ] })
1039
1039
  ] });
@@ -1129,6 +1129,81 @@ function Toggle({ checked, onChange, disabled = false, busy = false, label }) {
1129
1129
  }
1130
1130
  );
1131
1131
  }
1132
+
1133
+ // src/components/modal-theme.ts
1134
+ var DEFAULT_MODAL_BORDER_RADIUS = 16;
1135
+ var DEFAULT_BUTTON_BORDER_RADIUS = 6;
1136
+ var DEFAULT_OVERLAY_Z_INDEX = 50;
1137
+ function px(value, fallback) {
1138
+ return `${typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : fallback}px`;
1139
+ }
1140
+ function color(value, fallback) {
1141
+ return typeof value === "string" && value.trim() !== "" ? value : fallback;
1142
+ }
1143
+ function parseHex(value) {
1144
+ const hex = value.trim().replace(/^#/, "");
1145
+ const full = hex.length === 3 ? [...hex].map((c) => c + c).join("") : hex;
1146
+ if (!/^[0-9a-f]{6}$/i.test(full)) return null;
1147
+ return [Number.parseInt(full.slice(0, 2), 16), Number.parseInt(full.slice(2, 4), 16), Number.parseInt(full.slice(4, 6), 16)];
1148
+ }
1149
+ function readableTextOn(background) {
1150
+ const rgb = parseHex(background);
1151
+ if (!rgb) return "#ffffff";
1152
+ const [r, g, b] = rgb;
1153
+ return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255 > 0.6 ? "#111827" : "#ffffff";
1154
+ }
1155
+ function buildModalCssVars(theme, accentColor, overrides = {}, variant = "compact") {
1156
+ const isDark = theme === "dark";
1157
+ const textColor = color(overrides.textColor, isDark ? "#ffffff" : "#111827");
1158
+ const secondaryBg = color(overrides.buttonColor, "");
1159
+ return {
1160
+ "--pollar-accent": accentColor,
1161
+ "--pollar-bg": color(overrides.backgroundColor, isDark ? "#1a1a1a" : "#ffffff"),
1162
+ "--pollar-border": isDark ? "#374151" : "#e5e7eb",
1163
+ "--pollar-text": textColor,
1164
+ "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
1165
+ // The primary button is the accent's, straight up; its label follows the
1166
+ // accent's luminance so a pale accent doesn't render white-on-white.
1167
+ "--pollar-btn-primary-fg": readableTextOn(accentColor),
1168
+ ...secondaryBg ? {
1169
+ "--pollar-btn-secondary-bg": secondaryBg,
1170
+ "--pollar-btn-secondary-fg": readableTextOn(secondaryBg)
1171
+ } : {},
1172
+ "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
1173
+ "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
1174
+ "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
1175
+ "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
1176
+ "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
1177
+ "--pollar-modal-border-radius": px(overrides.modalBorderRadius, DEFAULT_MODAL_BORDER_RADIUS),
1178
+ "--pollar-buttons-border-radius": px(overrides.buttonBorderRadius, DEFAULT_BUTTON_BORDER_RADIUS),
1179
+ "--pollar-buttons-height": "44px",
1180
+ "--pollar-input-height": "44px",
1181
+ "--pollar-input-border-radius": "0.5rem",
1182
+ "--pollar-card-border-radius": "10px",
1183
+ ...variant === "hero" ? {
1184
+ "--pollar-modal-padding": "2rem",
1185
+ "--pollar-modal-heading-size": "1.375rem",
1186
+ "--pollar-modal-subtitle-size": "0.9rem"
1187
+ } : {}
1188
+ };
1189
+ }
1190
+ function modalChrome(styles) {
1191
+ const zIndex = styles.modalZIndex;
1192
+ return {
1193
+ theme: styles.theme ?? "light",
1194
+ accentColor: styles.accentColor ?? "#005DB4",
1195
+ styleOverrides: {
1196
+ backgroundColor: styles.backgroundColor,
1197
+ textColor: styles.textColor,
1198
+ buttonColor: styles.buttonColor,
1199
+ modalBorderRadius: styles.modalBorderRadius,
1200
+ buttonBorderRadius: styles.buttonBorderRadius
1201
+ },
1202
+ overlayStyle: {
1203
+ "--pollar-overlay-z-index": typeof zIndex === "number" && Number.isFinite(zIndex) ? zIndex : DEFAULT_OVERLAY_Z_INDEX
1204
+ }
1205
+ };
1206
+ }
1132
1207
  var PERIOD_LABEL = {
1133
1208
  DAY: "every 24h",
1134
1209
  DAY_CALENDAR: "daily",
@@ -1198,6 +1273,7 @@ function RuleCard({
1198
1273
  function DistributionRulesModalTemplate({
1199
1274
  theme,
1200
1275
  accentColor,
1276
+ styleOverrides,
1201
1277
  state,
1202
1278
  claimingId,
1203
1279
  claimErrors,
@@ -1206,24 +1282,7 @@ function DistributionRulesModalTemplate({
1206
1282
  onClaim,
1207
1283
  onClose
1208
1284
  }) {
1209
- const isDark = theme === "dark";
1210
- const cssVars = {
1211
- "--pollar-accent": accentColor,
1212
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
1213
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
1214
- "--pollar-text": isDark ? "#ffffff" : "#111827",
1215
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
1216
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
1217
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
1218
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
1219
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
1220
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
1221
- "--pollar-buttons-border-radius": "6px",
1222
- "--pollar-buttons-height": "44px",
1223
- "--pollar-input-height": "44px",
1224
- "--pollar-input-border-radius": "0.5rem",
1225
- "--pollar-card-border-radius": "10px"
1226
- };
1285
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
1227
1286
  const isLoading = state.step === "loading";
1228
1287
  const rules = state.step === "loaded" ? state.rules : [];
1229
1288
  return /* @__PURE__ */ jsxs(
@@ -1298,7 +1357,7 @@ function DistributionRulesModalTemplate({
1298
1357
  }
1299
1358
  function DistributionRulesModal({ onClose }) {
1300
1359
  const { getClient, styles } = usePollar();
1301
- const { theme = "light", accentColor = "#005DB4" } = styles;
1360
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
1302
1361
  const [state, setState] = useState({ step: "idle" });
1303
1362
  const [claimingId, setClaimingId] = useState(null);
1304
1363
  const [claimErrors, setClaimErrors] = useState({});
@@ -1341,11 +1400,12 @@ function DistributionRulesModal({ onClose }) {
1341
1400
  },
1342
1401
  [getClient]
1343
1402
  );
1344
- return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsx(
1403
+ return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsx(
1345
1404
  DistributionRulesModalTemplate,
1346
1405
  {
1347
1406
  theme,
1348
1407
  accentColor,
1408
+ styleOverrides,
1349
1409
  state,
1350
1410
  claimingId,
1351
1411
  claimErrors,
@@ -1417,26 +1477,6 @@ function useChains() {
1417
1477
  return { chains, primaryChain, primaryAddress: addressForChain(wallets, primaryChain), ready: true };
1418
1478
  }, [wallets, configured, ready]);
1419
1479
  }
1420
- function cssVarsFor(theme, accentColor) {
1421
- const isDark = theme === "dark";
1422
- return {
1423
- "--pollar-accent": accentColor,
1424
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
1425
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
1426
- "--pollar-text": isDark ? "#ffffff" : "#111827",
1427
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
1428
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
1429
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
1430
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
1431
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
1432
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
1433
- "--pollar-buttons-border-radius": "6px",
1434
- "--pollar-buttons-height": "44px",
1435
- "--pollar-input-height": "44px",
1436
- "--pollar-input-border-radius": "0.5rem",
1437
- "--pollar-card-border-radius": "10px"
1438
- };
1439
- }
1440
1480
  function assetKey(record) {
1441
1481
  return record.code + (record.issuer ?? "");
1442
1482
  }
@@ -1483,6 +1523,7 @@ function AssetItem({
1483
1523
  function EnabledAssetsModalTemplate({
1484
1524
  theme,
1485
1525
  accentColor,
1526
+ styleOverrides,
1486
1527
  enabledAssets,
1487
1528
  walletAddress,
1488
1529
  chains,
@@ -1495,7 +1536,7 @@ function EnabledAssetsModalTemplate({
1495
1536
  onToggleTrustline,
1496
1537
  onAddCustom
1497
1538
  }) {
1498
- const cssVars = cssVarsFor(theme, accentColor);
1539
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
1499
1540
  const isLoading = enabledAssets.step === "loading";
1500
1541
  const data = useStickyData(enabledAssets.step === "loaded" ? enabledAssets.data : null);
1501
1542
  const busy = busyKey !== null;
@@ -1587,13 +1628,14 @@ function isValidIssuer(issuer) {
1587
1628
  function CustomTrustlineModalTemplate({
1588
1629
  theme,
1589
1630
  accentColor,
1631
+ styleOverrides,
1590
1632
  busy,
1591
1633
  actionError,
1592
1634
  onBack,
1593
1635
  onClose,
1594
1636
  onSubmit
1595
1637
  }) {
1596
- const cssVars = cssVarsFor(theme, accentColor);
1638
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
1597
1639
  const [code, setCode] = useState("");
1598
1640
  const [issuer, setIssuer] = useState("");
1599
1641
  const [limit, setLimit] = useState("");
@@ -1690,7 +1732,7 @@ function assetKey2(record) {
1690
1732
  }
1691
1733
  function EnabledAssetsModal({ onClose }) {
1692
1734
  const { enabledAssets, refreshAssets, setTrustline, wallets, styles } = usePollar();
1693
- const { theme = "light", accentColor = "#005DB4" } = styles;
1735
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
1694
1736
  const { chains } = useChains();
1695
1737
  const [selectedChain, setSelectedChain] = useState(null);
1696
1738
  useEffect(() => {
@@ -1741,11 +1783,12 @@ function EnabledAssetsModal({ onClose }) {
1741
1783
  },
1742
1784
  [runAction]
1743
1785
  );
1744
- return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", onClick: onClose, children: view === "list" ? /* @__PURE__ */ jsx(
1786
+ return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: view === "list" ? /* @__PURE__ */ jsx(
1745
1787
  EnabledAssetsModalTemplate,
1746
1788
  {
1747
1789
  theme,
1748
1790
  accentColor,
1791
+ styleOverrides,
1749
1792
  enabledAssets,
1750
1793
  walletAddress,
1751
1794
  chains,
@@ -1766,6 +1809,7 @@ function EnabledAssetsModal({ onClose }) {
1766
1809
  {
1767
1810
  theme,
1768
1811
  accentColor,
1812
+ styleOverrides,
1769
1813
  busy: busyKey === "custom",
1770
1814
  actionError,
1771
1815
  onBack: () => {
@@ -1800,6 +1844,7 @@ function KycStatus({ status, className }) {
1800
1844
  function KycModalTemplate({
1801
1845
  theme,
1802
1846
  accentColor,
1847
+ styleOverrides,
1803
1848
  step,
1804
1849
  providers,
1805
1850
  selectedProvider,
@@ -1811,27 +1856,7 @@ function KycModalTemplate({
1811
1856
  onRefresh,
1812
1857
  onClose
1813
1858
  }) {
1814
- const isDark = theme === "dark";
1815
- const cssVars = {
1816
- "--pollar-accent": accentColor,
1817
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
1818
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
1819
- "--pollar-text": isDark ? "#ffffff" : "#111827",
1820
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
1821
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
1822
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
1823
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
1824
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
1825
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
1826
- "--pollar-buttons-border-radius": "6px",
1827
- "--pollar-buttons-height": "44px",
1828
- "--pollar-input-height": "44px",
1829
- "--pollar-input-border-radius": "0.5rem",
1830
- "--pollar-card-border-radius": "10px",
1831
- "--pollar-modal-padding": "2rem",
1832
- "--pollar-modal-heading-size": "1.375rem",
1833
- "--pollar-modal-subtitle-size": "0.9rem"
1834
- };
1859
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides, "hero");
1835
1860
  return /* @__PURE__ */ jsxs("div", { className: "pollar-modal-card pollar-kyc-modal", style: cssVars, onClick: (e) => e.stopPropagation(), children: [
1836
1861
  /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header", children: [
1837
1862
  /* @__PURE__ */ jsxs("div", { className: "pollar-kyc-header-text", children: [
@@ -1930,7 +1955,7 @@ function KycModal({ onClose, country = "MX", level = "basic", onApproved }) {
1930
1955
  const [kycStatus, setKycStatus] = useState("none");
1931
1956
  const [isLoading, setIsLoading] = useState(false);
1932
1957
  const client = getClient();
1933
- const { theme = "light", accentColor = "#005DB4" } = styles;
1958
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
1934
1959
  const loadProviders = useCallback(() => {
1935
1960
  setIsLoading(true);
1936
1961
  return getClient().getKycProviders(country).then((result) => setProviders(result.providers)).catch(() => setProviders([])).finally(() => setIsLoading(false));
@@ -1970,11 +1995,12 @@ function KycModal({ onClose, country = "MX", level = "basic", onApproved }) {
1970
1995
  setStep("done");
1971
1996
  }
1972
1997
  }
1973
- return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsx(
1998
+ return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsx(
1974
1999
  KycModalTemplate,
1975
2000
  {
1976
2001
  theme,
1977
2002
  accentColor,
2003
+ styleOverrides,
1978
2004
  step,
1979
2005
  providers,
1980
2006
  selectedProvider,
@@ -2166,32 +2192,10 @@ function authStateToStatus(step) {
2166
2192
  if (error.includes(step)) return "ERROR";
2167
2193
  return "NONE";
2168
2194
  }
2169
- function buildModalCssVars(theme, accentColor) {
2170
- const isDark = theme === "dark";
2171
- return {
2172
- "--pollar-accent": accentColor,
2173
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
2174
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
2175
- "--pollar-text": isDark ? "#ffffff" : "#111827",
2176
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
2177
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
2178
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
2179
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
2180
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
2181
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
2182
- "--pollar-buttons-border-radius": "6px",
2183
- "--pollar-buttons-height": "44px",
2184
- "--pollar-input-height": "44px",
2185
- "--pollar-input-border-radius": "0.5rem",
2186
- "--pollar-card-border-radius": "10px",
2187
- "--pollar-modal-padding": "2rem",
2188
- "--pollar-modal-heading-size": "1.375rem",
2189
- "--pollar-modal-subtitle-size": "0.9rem"
2190
- };
2191
- }
2192
2195
  function LoginModalTemplate({
2193
2196
  theme,
2194
2197
  accentColor,
2198
+ styleOverrides,
2195
2199
  logoUrl,
2196
2200
  emailEnabled,
2197
2201
  embeddedWallets,
@@ -2225,7 +2229,7 @@ function LoginModalTemplate({
2225
2229
  return acc;
2226
2230
  }, []);
2227
2231
  const activeGroupAdapters = walletGroups.find((g) => g.label === activeGroup)?.adapters ?? [];
2228
- const cssVars = buildModalCssVars(theme, accentColor);
2232
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides, "hero");
2229
2233
  const status = authStateToStatus(authState.step);
2230
2234
  const isLoading = status === "LOADING";
2231
2235
  const isEmailCodeError = authState.step === "error" && (authState.errorCode === AUTH_ERROR_CODES.EMAIL_CODE_EXPIRED || authState.errorCode === AUTH_ERROR_CODES.EMAIL_CODE_INVALID);
@@ -2404,12 +2408,13 @@ function LoginModalStatus({
2404
2408
  status,
2405
2409
  theme,
2406
2410
  accentColor,
2411
+ styleOverrides,
2407
2412
  logoUrl,
2408
2413
  appName,
2409
2414
  onRetry,
2410
2415
  onCancel
2411
2416
  }) {
2412
- const cssVars = buildModalCssVars(theme, accentColor);
2417
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides, "hero");
2413
2418
  return /* @__PURE__ */ jsxs("div", { className: "pollar-modal-card pollar-modal", style: cssVars, onClick: (e) => e.stopPropagation(), children: [
2414
2419
  /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-close-btn", onClick: onCancel, "aria-label": "Close", children: /* @__PURE__ */ jsx(
2415
2420
  "svg",
@@ -2444,6 +2449,7 @@ function PrivyLoginSubmodal({
2444
2449
  adapter,
2445
2450
  theme,
2446
2451
  accentColor,
2452
+ styleOverrides,
2447
2453
  logoUrl,
2448
2454
  appName,
2449
2455
  onBack,
@@ -2455,28 +2461,8 @@ function PrivyLoginSubmodal({
2455
2461
  const [view, setView] = useState("root");
2456
2462
  const [status, setStatus] = useState("NONE");
2457
2463
  const [message, setMessage] = useState("");
2458
- const isDark = theme === "dark";
2459
2464
  const isLoading = status === "LOADING";
2460
- const cssVars = {
2461
- "--pollar-accent": accentColor,
2462
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
2463
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
2464
- "--pollar-text": isDark ? "#ffffff" : "#111827",
2465
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
2466
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
2467
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
2468
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
2469
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
2470
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
2471
- "--pollar-buttons-border-radius": "6px",
2472
- "--pollar-buttons-height": "44px",
2473
- "--pollar-input-height": "44px",
2474
- "--pollar-input-border-radius": "0.5rem",
2475
- "--pollar-card-border-radius": "10px",
2476
- "--pollar-modal-padding": "2rem",
2477
- "--pollar-modal-heading-size": "1.375rem",
2478
- "--pollar-modal-subtitle-size": "0.9rem"
2479
- };
2465
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides, "hero");
2480
2466
  function fail(error) {
2481
2467
  setStatus("ERROR");
2482
2468
  setMessage(error instanceof Error ? error.message : "Something went wrong. Please try again.");
@@ -2633,8 +2619,10 @@ function LoginModal({ onClose }) {
2633
2619
  }
2634
2620
  };
2635
2621
  }, [getClient]);
2636
- const { theme = "light", accentColor = "#005DB4", logoUrl, emailEnabled, embeddedWallets, smartWallet, providers } = styles;
2622
+ const { logoUrl, emailEnabled, embeddedWallets, smartWallet, providers } = styles;
2623
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
2637
2624
  const smartWalletEnabled = smartWallet ?? false;
2625
+ const modalTitle = styles.modalTitle?.trim() || config.application?.name || "Pollar";
2638
2626
  function handleClose() {
2639
2627
  setEmail("");
2640
2628
  getClient().cancelLogin();
@@ -2682,14 +2670,15 @@ function LoginModal({ onClose }) {
2682
2670
  getClient().login({ provider });
2683
2671
  }
2684
2672
  }
2685
- return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", onClick: handleClose, children: configStatus !== "ready" ? /* @__PURE__ */ jsx(
2673
+ return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: handleClose, children: configStatus !== "ready" ? /* @__PURE__ */ jsx(
2686
2674
  LoginModalStatus,
2687
2675
  {
2688
2676
  status: configStatus === "error" ? "error" : "loading",
2689
2677
  theme,
2690
2678
  accentColor,
2679
+ styleOverrides,
2691
2680
  logoUrl: logoUrl ?? null,
2692
- appName: config.application?.name ?? "Pollar",
2681
+ appName: modalTitle,
2693
2682
  onRetry: retryConfig,
2694
2683
  onCancel: handleClose
2695
2684
  }
@@ -2699,8 +2688,9 @@ function LoginModal({ onClose }) {
2699
2688
  adapter: interactiveAdapter,
2700
2689
  theme,
2701
2690
  accentColor,
2691
+ styleOverrides,
2702
2692
  logoUrl: logoUrl ?? null,
2703
- appName: config.application?.name ?? "Pollar",
2693
+ appName: modalTitle,
2704
2694
  onBack: () => setInteractiveAdapter(null),
2705
2695
  onCancel: handleClose,
2706
2696
  onAuthenticated: handleInteractiveAuthenticated
@@ -2710,6 +2700,7 @@ function LoginModal({ onClose }) {
2710
2700
  {
2711
2701
  theme,
2712
2702
  accentColor,
2703
+ styleOverrides,
2713
2704
  logoUrl: logoUrl ?? null,
2714
2705
  emailEnabled: !!emailEnabled,
2715
2706
  embeddedWallets: !!embeddedWallets,
@@ -2722,7 +2713,7 @@ function LoginModal({ onClose }) {
2722
2713
  apple: !!providers?.apple
2723
2714
  },
2724
2715
  walletAdapters,
2725
- appName: config.application?.name ?? "Pollar",
2716
+ appName: modalTitle,
2726
2717
  email,
2727
2718
  onEmailChange: setEmail,
2728
2719
  onEmailSubmit: handleEmailSubmit,
@@ -2745,34 +2736,36 @@ var RAIL_LABELS = {
2745
2736
  PSE: "PSE (Colombia)",
2746
2737
  ACH: "ACH (US)"
2747
2738
  };
2748
- function RouteDisplay({ quote, onSelect }) {
2739
+ function RouteDisplay({ quote, busy = false, disabled = false, onSelect }) {
2740
+ const inert = busy || disabled;
2741
+ const select = () => {
2742
+ if (!inert) onSelect(quote);
2743
+ };
2749
2744
  return /* @__PURE__ */ jsxs(
2750
2745
  "div",
2751
2746
  {
2752
2747
  className: "pollar-ramp-route-card",
2753
2748
  "data-recommended": quote.recommended,
2749
+ "data-busy": busy || void 0,
2750
+ "data-disabled": disabled || void 0,
2754
2751
  role: "button",
2755
- tabIndex: 0,
2756
- onClick: () => onSelect(quote),
2757
- onKeyDown: (e) => e.key === "Enter" && onSelect(quote),
2752
+ "aria-busy": busy,
2753
+ "aria-disabled": inert,
2754
+ tabIndex: inert ? -1 : 0,
2755
+ onClick: select,
2756
+ onKeyDown: (e) => e.key === "Enter" && select(),
2758
2757
  children: [
2759
2758
  /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-route-left", children: [
2760
2759
  /* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-provider", children: quote.provider }),
2761
- /* @__PURE__ */ jsxs("span", { className: "pollar-ramp-route-meta", children: [
2762
- RAIL_LABELS[quote.rail] ?? quote.rail,
2763
- " \xB7 ",
2764
- quote.protocol,
2765
- " \xB7 ",
2766
- quote.estimatedTime
2767
- ] })
2760
+ /* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-meta", children: busy ? "Starting\u2026" : `${RAIL_LABELS[quote.rail] ?? quote.rail} \xB7 ${quote.protocol} \xB7 ${quote.estimatedTime}` })
2768
2761
  ] }),
2769
- /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-route-right", children: [
2762
+ /* @__PURE__ */ jsx("div", { className: "pollar-ramp-route-right", children: busy ? /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2770
2763
  /* @__PURE__ */ jsxs("span", { className: "pollar-ramp-route-fee", children: [
2771
2764
  quote.fee,
2772
2765
  "% fee"
2773
2766
  ] }),
2774
2767
  quote.recommended && /* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-badge", children: "Best rate" })
2775
- ] })
2768
+ ] }) })
2776
2769
  ]
2777
2770
  }
2778
2771
  );
@@ -2780,10 +2773,24 @@ function RouteDisplay({ quote, onSelect }) {
2780
2773
  var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2781
2774
  function isFieldValid(spec, raw) {
2782
2775
  const value = (raw ?? "").trim();
2783
- if (!value) return false;
2776
+ if (!value) return spec.optional === true;
2784
2777
  if (spec.type === "email") return EMAIL_RE.test(value);
2785
2778
  return true;
2786
2779
  }
2780
+ function listOf(items) {
2781
+ const last = items[items.length - 1];
2782
+ if (last === void 0) return "a few details";
2783
+ if (items.length === 1) return last;
2784
+ return `${items.slice(0, -1).join(", ")} and ${last}`;
2785
+ }
2786
+ function placeholderFor(field, fields, values) {
2787
+ if (field.placeholderFrom) {
2788
+ const source = fields.find((f) => f.key === field.placeholderFrom);
2789
+ const chosen = source?.options?.find((o) => o.value === values[field.placeholderFrom]);
2790
+ if (chosen?.placeholder) return chosen.placeholder;
2791
+ }
2792
+ return field.placeholder;
2793
+ }
2787
2794
  var LOADING_STEPS = ["Detecting your country\u2026", "Consulting providers\u2026", "Route found!"];
2788
2795
  function flagEmoji(code) {
2789
2796
  const cc = code.trim().toUpperCase();
@@ -2797,47 +2804,19 @@ var STATUS_LABEL = {
2797
2804
  completed: "Completed",
2798
2805
  failed: "Failed"
2799
2806
  };
2800
- var INSTRUCTION_LABELS = {
2801
- br_code: "Pix code",
2802
- account_holder_name: "Account holder",
2803
- bank_name: "Bank",
2804
- bank_address: "Bank address",
2805
- bank_account_number: "Account number",
2806
- bank_routing_number: "Routing number",
2807
- iban: "IBAN",
2808
- bic: "BIC",
2809
- clabe: "CLABE",
2810
- amount: "Amount",
2811
- currency: "Currency",
2812
- payment_rails: "Rails"
2813
- };
2814
- function flattenInstructions(instr) {
2815
- const out = [];
2816
- for (const [k, v] of Object.entries(instr)) {
2817
- if (k === "qrBase64") {
2818
- if (typeof v === "string" && v) out.push({ key: k, label: INSTRUCTION_LABELS[k] ?? "Payment QR", value: v, kind: "qr" });
2819
- continue;
2820
- }
2821
- if ((k === "expiresAt" || k === "expires_at") && (typeof v === "number" || typeof v === "string")) {
2822
- const ms = Number(v);
2823
- if (Number.isFinite(ms) && ms > 0) {
2824
- out.push({ key: k, label: INSTRUCTION_LABELS[k] ?? "Expires", value: new Date(ms).toLocaleString(), kind: "datetime" });
2825
- }
2826
- continue;
2827
- }
2828
- let value;
2829
- if (typeof v === "string" || typeof v === "number") value = String(v);
2830
- else if (Array.isArray(v)) value = v.filter((x) => typeof x === "string" || typeof x === "number").join(", ");
2831
- else continue;
2832
- if (!value) continue;
2833
- out.push({ key: k, label: INSTRUCTION_LABELS[k] ?? k, value, kind: "text" });
2834
- }
2835
- return out;
2807
+ function displayValue(field) {
2808
+ if (field.type !== "datetime") return field.value;
2809
+ const at = new Date(field.value);
2810
+ return Number.isNaN(at.getTime()) ? field.value : at.toLocaleString();
2836
2811
  }
2837
2812
  function RampWidgetTemplate({
2838
2813
  theme,
2839
2814
  accentColor,
2815
+ styleOverrides,
2840
2816
  step,
2817
+ flowSteps,
2818
+ flowStepIndex,
2819
+ startingQuoteId,
2841
2820
  direction,
2842
2821
  amount,
2843
2822
  currency,
@@ -2853,6 +2832,8 @@ function RampWidgetTemplate({
2853
2832
  txStatus,
2854
2833
  kycUrl,
2855
2834
  tosUrl,
2835
+ kycBlocking,
2836
+ kycJustApproved,
2856
2837
  stellarTxHash,
2857
2838
  explorerUrl,
2858
2839
  depositInstructions,
@@ -2869,31 +2850,12 @@ function RampWidgetTemplate({
2869
2850
  onOpenKyc,
2870
2851
  onOpenTos,
2871
2852
  onCompleteWithdraw,
2853
+ onBack,
2872
2854
  onRetry,
2873
2855
  onRefresh,
2874
2856
  onClose
2875
2857
  }) {
2876
- const isDark = theme === "dark";
2877
- const cssVars = {
2878
- "--pollar-accent": accentColor,
2879
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
2880
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
2881
- "--pollar-text": isDark ? "#ffffff" : "#111827",
2882
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
2883
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
2884
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
2885
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
2886
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
2887
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
2888
- "--pollar-buttons-border-radius": "6px",
2889
- "--pollar-buttons-height": "44px",
2890
- "--pollar-input-height": "44px",
2891
- "--pollar-input-border-radius": "0.5rem",
2892
- "--pollar-card-border-radius": "10px",
2893
- "--pollar-modal-padding": "2rem",
2894
- "--pollar-modal-heading-size": "1.375rem",
2895
- "--pollar-modal-subtitle-size": "0.9rem"
2896
- };
2858
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides, "hero");
2897
2859
  const stepTitle = {
2898
2860
  input: direction === "onramp" ? "Buy crypto" : "Sell crypto",
2899
2861
  loading_quote: "Finding best route",
@@ -2906,7 +2868,11 @@ function RampWidgetTemplate({
2906
2868
  input: direction === "onramp" ? "Enter the amount you want to deposit" : "Enter the amount you want to withdraw",
2907
2869
  loading_quote: "Comparing providers in real time\u2026",
2908
2870
  select_route: "All prices include fees",
2909
- contact: `${provider || "This provider"} needs your name and email to verify you`,
2871
+ // What this step actually asks for is whatever the route declared, and that
2872
+ // is rarely a name and an email: an Abroad off-ramp asks where to send the
2873
+ // money, not who you are. Say which, rather than asserting the Bridge case
2874
+ // over every provider.
2875
+ contact: `${provider || "This provider"} needs ${listOf(requiredFields.filter((f) => !f.optional).map((f) => f.label.toLowerCase()))} to continue`,
2910
2876
  status: `Finish the flow at ${provider || "the provider"} to continue`,
2911
2877
  error: "Please try again"
2912
2878
  };
@@ -2941,6 +2907,26 @@ function RampWidgetTemplate({
2941
2907
  /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
2942
2908
  ] })
2943
2909
  ] }),
2910
+ flowStepIndex >= 0 && flowSteps.length > 0 && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-steps", children: [
2911
+ /* @__PURE__ */ jsxs("span", { className: "pollar-ramp-steps-label", children: [
2912
+ "Step ",
2913
+ flowStepIndex + 1,
2914
+ " of ",
2915
+ flowSteps.length
2916
+ ] }),
2917
+ /* @__PURE__ */ jsx(
2918
+ "div",
2919
+ {
2920
+ className: "pollar-ramp-steps-track",
2921
+ role: "progressbar",
2922
+ "aria-valuenow": flowStepIndex + 1,
2923
+ "aria-valuemin": 1,
2924
+ "aria-valuemax": flowSteps.length,
2925
+ "aria-label": flowSteps[flowStepIndex],
2926
+ children: flowSteps.map((label, i) => /* @__PURE__ */ jsx("span", { className: "pollar-ramp-steps-segment", "data-done": i <= flowStepIndex || void 0 }, label))
2927
+ }
2928
+ )
2929
+ ] }),
2944
2930
  step === "input" && /* @__PURE__ */ jsxs(Fragment, { children: [
2945
2931
  /* @__PURE__ */ jsxs("div", { className: "pollar-tabs", children: [
2946
2932
  /* @__PURE__ */ jsx(
@@ -2991,7 +2977,8 @@ function RampWidgetTemplate({
2991
2977
  min: "0",
2992
2978
  onChange: (e) => onAmountChange(e.target.value)
2993
2979
  }
2994
- )
2980
+ ),
2981
+ errorMsg && /* @__PURE__ */ jsx("span", { className: "pollar-ramp-field-error", children: errorMsg })
2995
2982
  ] }),
2996
2983
  /* @__PURE__ */ jsxs("div", { className: "pollar-modal-actions", children: [
2997
2984
  /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: "Cancel" }),
@@ -3015,12 +3002,25 @@ function RampWidgetTemplate({
3015
3002
  ] }, i))
3016
3003
  ] }),
3017
3004
  step === "select_route" && /* @__PURE__ */ jsxs(Fragment, { children: [
3018
- /* @__PURE__ */ jsx("div", { className: "pollar-ramp-route-list", children: quotes.map((q, i) => /* @__PURE__ */ jsx(RouteDisplay, { quote: q, onSelect: onSelectQuote }, i)) }),
3019
- /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: "Cancel" })
3005
+ /* @__PURE__ */ jsx("div", { className: "pollar-ramp-route-list", children: quotes.map((q, i) => /* @__PURE__ */ jsx(
3006
+ RouteDisplay,
3007
+ {
3008
+ quote: q,
3009
+ busy: startingQuoteId != null && q.quoteId === startingQuoteId,
3010
+ disabled: startingQuoteId != null && q.quoteId !== startingQuoteId,
3011
+ onSelect: onSelectQuote
3012
+ },
3013
+ i
3014
+ )) }),
3015
+ errorMsg && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", style: { color: "var(--pollar-error-text)" }, children: errorMsg }),
3016
+ /* @__PURE__ */ jsx("button", { type: "button", className: errorMsg ? "pollar-btn-primary" : "pollar-btn-secondary", onClick: onBack, children: "Back" })
3020
3017
  ] }),
3021
3018
  step === "contact" && /* @__PURE__ */ jsxs(Fragment, { children: [
3022
3019
  requiredFields.map((f) => /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-field", children: [
3023
- /* @__PURE__ */ jsx("label", { className: "pollar-ramp-label", children: f.label }),
3020
+ /* @__PURE__ */ jsxs("label", { className: "pollar-ramp-label", children: [
3021
+ f.label,
3022
+ f.optional && /* @__PURE__ */ jsx("span", { className: "pollar-ramp-field-optional", children: " (optional)" })
3023
+ ] }),
3024
3024
  f.type === "select" ? /* @__PURE__ */ jsxs(
3025
3025
  "select",
3026
3026
  {
@@ -3038,14 +3038,16 @@ function RampWidgetTemplate({
3038
3038
  type: f.type,
3039
3039
  className: "pollar-ramp-input",
3040
3040
  value: fieldValues[f.key] ?? "",
3041
+ placeholder: placeholderFor(f, requiredFields, fieldValues),
3041
3042
  autoComplete: f.type === "email" ? "email" : "off",
3042
3043
  onChange: (e) => onFieldChange(f.key, e.target.value)
3043
3044
  }
3044
3045
  ),
3046
+ f.hint && /* @__PURE__ */ jsx("span", { className: "pollar-ramp-field-hint", children: f.hint }),
3045
3047
  f.type === "email" && (fieldValues[f.key] ?? "").trim() !== "" && !isFieldValid(f, fieldValues[f.key]) && /* @__PURE__ */ jsx("span", { className: "pollar-ramp-field-error", children: "Enter a valid email address." })
3046
3048
  ] }, f.key)),
3047
3049
  /* @__PURE__ */ jsxs("div", { className: "pollar-modal-actions", children: [
3048
- /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onRetry, children: "Back" }),
3050
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onBack, children: "Back" }),
3049
3051
  /* @__PURE__ */ jsx(
3050
3052
  "button",
3051
3053
  {
@@ -3117,20 +3119,44 @@ function RampWidgetTemplate({
3117
3119
  )
3118
3120
  ] })
3119
3121
  ] }),
3120
- depositInstructions && txStatus !== "completed" && flattenInstructions(depositInstructions).map(({ key, label, value, kind }) => /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
3121
- /* @__PURE__ */ jsx("span", { className: "pollar-ramp-payment-label", children: label }),
3122
- /* @__PURE__ */ jsx("div", { className: "pollar-ramp-payment-value", children: kind === "qr" ? /* @__PURE__ */ jsx(
3122
+ depositInstructions?.scannable && txStatus !== "completed" && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
3123
+ /* @__PURE__ */ jsx("span", { className: "pollar-ramp-payment-label", children: "Payment QR" }),
3124
+ /* @__PURE__ */ jsx("div", { className: "pollar-ramp-payment-value", children: depositInstructions.scannable.image.inlineSafe ? /* @__PURE__ */ jsx(
3125
+ "div",
3126
+ {
3127
+ className: "pollar-ramp-qr",
3128
+ "aria-label": "Payment QR",
3129
+ dangerouslySetInnerHTML: { __html: depositInstructions.scannable.image.data }
3130
+ }
3131
+ ) : /* @__PURE__ */ jsx(
3123
3132
  "img",
3124
3133
  {
3125
- src: value.startsWith("data:") ? value : `data:image/png;base64,${value}`,
3126
- alt: label,
3134
+ src: `data:${depositInstructions.scannable.image.mediaType};base64,${depositInstructions.scannable.image.data}`,
3135
+ alt: "Payment QR",
3127
3136
  style: { width: "100%", maxWidth: 220, height: "auto", display: "block", margin: "0 auto" }
3128
3137
  }
3129
- ) : kind === "datetime" ? /* @__PURE__ */ jsx("span", { children: value }) : /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
3130
- /* @__PURE__ */ jsx("code", { style: { flex: 1, wordBreak: "break-all" }, children: value }),
3131
- /* @__PURE__ */ jsx(CopyButton, { value, label: `Copy ${label}` })
3138
+ ) })
3139
+ ] }),
3140
+ depositInstructions?.scannable?.payload && depositInstructions.scannable.payloadLabel && txStatus !== "completed" && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
3141
+ /* @__PURE__ */ jsx("span", { className: "pollar-ramp-payment-label", children: depositInstructions.scannable.payloadLabel }),
3142
+ /* @__PURE__ */ jsx("div", { className: "pollar-ramp-payment-value", children: /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
3143
+ /* @__PURE__ */ jsx("code", { style: { flex: 1, wordBreak: "break-all" }, children: depositInstructions.scannable.payload }),
3144
+ /* @__PURE__ */ jsx(
3145
+ CopyButton,
3146
+ {
3147
+ value: depositInstructions.scannable.payload,
3148
+ label: `Copy ${depositInstructions.scannable.payloadLabel}`
3149
+ }
3150
+ )
3132
3151
  ] }) })
3133
- ] }, key)),
3152
+ ] }),
3153
+ depositInstructions && txStatus !== "completed" && depositInstructions.fields.map((f) => /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
3154
+ /* @__PURE__ */ jsx("span", { className: "pollar-ramp-payment-label", children: f.label }),
3155
+ /* @__PURE__ */ jsx("div", { className: "pollar-ramp-payment-value", children: f.copyable ? /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
3156
+ /* @__PURE__ */ jsx("code", { style: { flex: 1, wordBreak: "break-all" }, children: f.value }),
3157
+ /* @__PURE__ */ jsx(CopyButton, { value: f.value, label: `Copy ${f.label}` })
3158
+ ] }) : f.type === "url" ? /* @__PURE__ */ jsx("a", { href: f.value, target: "_blank", rel: "noopener noreferrer", children: f.value }) : /* @__PURE__ */ jsx("span", { children: displayValue(f) }) })
3159
+ ] }, f.key)),
3134
3160
  tosUrl && !depositInstructions && txStatus !== "completed" && /* @__PURE__ */ jsxs("button", { type: "button", className: "pollar-btn-primary", onClick: onOpenTos, children: [
3135
3161
  "Accept terms at ",
3136
3162
  provider
@@ -3139,6 +3165,16 @@ function RampWidgetTemplate({
3139
3165
  "Continue at ",
3140
3166
  provider
3141
3167
  ] }),
3168
+ kycBlocking && /* @__PURE__ */ jsxs("p", { className: "pollar-ramp-payment-note", children: [
3169
+ provider,
3170
+ " needs to verify your identity before this payout. Nothing has been sent yet \u2014 complete verification with ",
3171
+ provider,
3172
+ ", and this will update on its own."
3173
+ ] }),
3174
+ kycJustApproved && /* @__PURE__ */ jsxs("p", { className: "pollar-ramp-payment-note", children: [
3175
+ provider,
3176
+ " approved your verification. Request a new quote to continue \u2014 the previous one was consumed."
3177
+ ] }),
3142
3178
  canComplete && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-primary", disabled: completing, onClick: onCompleteWithdraw, children: completing ? /* @__PURE__ */ jsxs(Fragment, { children: [
3143
3179
  /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }),
3144
3180
  "Submitting\u2026"
@@ -3159,11 +3195,53 @@ var TERMINAL = ["completed", "failed"];
3159
3195
  function requiredFieldsOf(quote) {
3160
3196
  return quote.requiredFields ?? [];
3161
3197
  }
3198
+ function flowStepsOf(quotes, selected) {
3199
+ const needsDetails = selected != null ? requiredFieldsOf(selected).length > 0 : quotes.some((q) => requiredFieldsOf(q).length > 0);
3200
+ return needsDetails ? ["Amount", "Provider", "Details", "Complete"] : ["Amount", "Provider", "Complete"];
3201
+ }
3202
+ var STEP_LABEL = {
3203
+ input: "Amount",
3204
+ loading_quote: "Amount",
3205
+ select_route: "Provider",
3206
+ contact: "Details",
3207
+ status: "Complete"
3208
+ };
3209
+ function brokenLimitOf(amount, quote) {
3210
+ const { minAmount, maxAmount } = quote;
3211
+ if (!Number.isFinite(amount)) return null;
3212
+ if (minAmount != null && amount < minAmount) return { limit: "min", value: minAmount };
3213
+ if (maxAmount != null && amount > maxAmount) return { limit: "max", value: maxAmount };
3214
+ return null;
3215
+ }
3216
+ function limitMessage(limit, value, currency) {
3217
+ return `The ${limit === "min" ? "minimum" : "maximum"} amount for this route is ${value} ${currency}.`;
3218
+ }
3219
+ var RAMP_ERROR_MESSAGES = {
3220
+ SDK_RAMPS_QUOTE_EXPIRED: "This quote expired. Request a new one and try again.",
3221
+ SDK_RAMPS_ASSET_NOT_ENABLED: "This currency is not available for that route right now.",
3222
+ SDK_RAMPS_KYC_REQUIRED: "The provider needs to verify your identity before continuing.",
3223
+ SDK_RAMPS_WALLET_UNSUPPORTED: "This wallet type cannot be used for this ramp.",
3224
+ SDK_RAMPS_PROVIDER_NOT_CONFIGURED: "This ramp provider is not configured for this app yet.",
3225
+ SDK_RAMPS_ANCHOR_ERROR: "The provider rejected the request. Please try again in a moment.",
3226
+ SDK_RAMPS_BRIDGE_ERROR: "The provider rejected the request. Please try again in a moment."
3227
+ };
3228
+ function rampErrorMessage(e, fallback) {
3229
+ const body = e?.body;
3230
+ const code = e?.code;
3231
+ if (typeof code !== "string") return e instanceof Error ? e.message : fallback;
3232
+ const limit = body?.limit;
3233
+ const value = body?.limitAmount;
3234
+ const currency = body?.limitCurrency;
3235
+ if ((limit === "min" || limit === "max") && typeof value === "number" && typeof currency === "string") {
3236
+ return limitMessage(limit, value, currency);
3237
+ }
3238
+ return RAMP_ERROR_MESSAGES[code] ?? (e instanceof Error ? e.message : fallback);
3239
+ }
3162
3240
  function RampWidget({ onClose }) {
3163
3241
  const { getClient, signTx, wallet, styles, network } = usePollar();
3164
3242
  const walletAddress = wallet?.address ?? "";
3165
3243
  const client = getClient();
3166
- const { theme = "light", accentColor = "#005DB4" } = styles;
3244
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
3167
3245
  const [step, setStep] = useState("input");
3168
3246
  const [direction, setDirection] = useState("onramp");
3169
3247
  const [amount, setAmount] = useState("");
@@ -3181,6 +3259,8 @@ function RampWidget({ onClose }) {
3181
3259
  const [provider, setProvider] = useState("");
3182
3260
  const [kycUrl, setKycUrl] = useState(null);
3183
3261
  const [tosUrl, setTosUrl] = useState(null);
3262
+ const [kycPending, setKycPending] = useState(false);
3263
+ const [kycApproved, setKycApproved] = useState(false);
3184
3264
  const [txStatus, setTxStatus] = useState(null);
3185
3265
  const [stellarTxHash, setStellarTxHash] = useState(null);
3186
3266
  const [depositInstructions, setDepositInstructions] = useState(null);
@@ -3209,6 +3289,23 @@ function RampWidget({ onClose }) {
3209
3289
  clearInterval(id);
3210
3290
  };
3211
3291
  }, [step, txId, txStatus, client]);
3292
+ useEffect(() => {
3293
+ if (step !== "status" || !kycPending || kycApproved) return;
3294
+ let active = true;
3295
+ const check = async () => {
3296
+ try {
3297
+ const { hasApproved } = await client.getRampKycStatus();
3298
+ if (active && hasApproved) setKycApproved(true);
3299
+ } catch {
3300
+ }
3301
+ };
3302
+ void check();
3303
+ const id = setInterval(check, 1e4);
3304
+ return () => {
3305
+ active = false;
3306
+ clearInterval(id);
3307
+ };
3308
+ }, [step, kycPending, kycApproved, client]);
3212
3309
  async function loadCountries(resetSelection) {
3213
3310
  setCountriesLoading(true);
3214
3311
  try {
@@ -3254,7 +3351,7 @@ function RampWidget({ onClose }) {
3254
3351
  setRefreshing(false);
3255
3352
  }
3256
3353
  }
3257
- function resetToInput() {
3354
+ function resetToInput({ keepMessage = false } = {}) {
3258
3355
  setStep("input");
3259
3356
  setQuotes([]);
3260
3357
  setSelectedQuote(null);
@@ -3266,7 +3363,7 @@ function RampWidget({ onClose }) {
3266
3363
  setTxStatus(null);
3267
3364
  setStellarTxHash(null);
3268
3365
  setDepositInstructions(null);
3269
- setErrorMsg(null);
3366
+ if (!keepMessage) setErrorMsg(null);
3270
3367
  }
3271
3368
  async function resumeWithSignature(id, ps) {
3272
3369
  const outcome = await signTx(ps.unsignedXdr);
@@ -3290,6 +3387,8 @@ function RampWidget({ onClose }) {
3290
3387
  }
3291
3388
  setKycUrl(result.kycUrl ?? null);
3292
3389
  setTosUrl(result.tosUrl ?? null);
3390
+ setKycPending(result.kycRequired === true);
3391
+ if (result.kycRequired) setKycApproved(false);
3293
3392
  setTxStatus(result.status);
3294
3393
  setStellarTxHash(result.stellarTxHash ?? null);
3295
3394
  setDepositInstructions(result.depositInstructions ?? null);
@@ -3310,7 +3409,7 @@ function RampWidget({ onClose }) {
3310
3409
  setQuotes(list);
3311
3410
  setStep("select_route");
3312
3411
  } catch (e) {
3313
- setErrorMsg(e instanceof Error ? e.message : "Failed to fetch quotes.");
3412
+ setErrorMsg(rampErrorMessage(e, "Failed to fetch quotes."));
3314
3413
  setStep("error");
3315
3414
  } finally {
3316
3415
  setIsLoading(false);
@@ -3319,8 +3418,13 @@ function RampWidget({ onClose }) {
3319
3418
  function handleSelectQuote(quote) {
3320
3419
  setSelectedQuote(quote);
3321
3420
  setErrorMsg(null);
3421
+ const broken = brokenLimitOf(Number(amount), quote);
3422
+ if (broken) {
3423
+ setErrorMsg(limitMessage(broken.limit, broken.value, currency));
3424
+ return;
3425
+ }
3322
3426
  const fields = requiredFieldsOf(quote);
3323
- const missing = fields.some((f) => !(fieldValues[f.key] ?? "").trim());
3427
+ const missing = fields.some((f) => !f.optional && !(fieldValues[f.key] ?? "").trim());
3324
3428
  if (fields.length > 0 && missing) {
3325
3429
  setStep("contact");
3326
3430
  return;
@@ -3349,7 +3453,7 @@ function RampWidget({ onClose }) {
3349
3453
  const result = direction === "onramp" ? await client.createOnRamp(base) : await client.createOffRamp(base);
3350
3454
  await applyResult(result);
3351
3455
  } catch (e) {
3352
- setErrorMsg(e instanceof Error ? e.message : "Failed to start the ramp.");
3456
+ setErrorMsg(rampErrorMessage(e, "Failed to start the ramp."));
3353
3457
  setStep("error");
3354
3458
  } finally {
3355
3459
  setIsLoading(false);
@@ -3382,13 +3486,20 @@ function RampWidget({ onClose }) {
3382
3486
  setCompleting(false);
3383
3487
  }
3384
3488
  }
3385
- const canComplete = direction === "offramp" && step === "status" && txStatus !== "completed" && !stellarTxHash;
3386
- return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsx(
3489
+ const kycBlocking = kycPending && !kycApproved;
3490
+ const canComplete = direction === "offramp" && step === "status" && txStatus !== "completed" && !stellarTxHash && !kycBlocking;
3491
+ const flowSteps = flowStepsOf(quotes, selectedQuote);
3492
+ const flowStepIndex = flowSteps.indexOf(STEP_LABEL[step] ?? "");
3493
+ return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsx(
3387
3494
  RampWidgetTemplate,
3388
3495
  {
3389
3496
  theme,
3390
3497
  accentColor,
3498
+ styleOverrides,
3391
3499
  step,
3500
+ flowSteps,
3501
+ flowStepIndex,
3502
+ startingQuoteId: isLoading && selectedQuote ? selectedQuote.quoteId : null,
3392
3503
  direction,
3393
3504
  amount,
3394
3505
  currency,
@@ -3404,6 +3515,8 @@ function RampWidget({ onClose }) {
3404
3515
  txStatus,
3405
3516
  kycUrl,
3406
3517
  tosUrl,
3518
+ kycBlocking,
3519
+ kycJustApproved: kycPending && kycApproved,
3407
3520
  stellarTxHash,
3408
3521
  explorerUrl: stellarTxHash ? `https://stellar.expert/explorer/${network === "mainnet" ? "public" : "testnet"}/tx/${stellarTxHash}` : null,
3409
3522
  depositInstructions,
@@ -3411,7 +3524,10 @@ function RampWidget({ onClose }) {
3411
3524
  completing,
3412
3525
  errorMsg,
3413
3526
  onDirectionChange: setDirection,
3414
- onAmountChange: setAmount,
3527
+ onAmountChange: (next) => {
3528
+ setAmount(next);
3529
+ setErrorMsg(null);
3530
+ },
3415
3531
  onFieldChange: setFieldValue,
3416
3532
  onCountryChange: handleCountryChange,
3417
3533
  onFindRoute: handleFindRoute,
@@ -3420,7 +3536,8 @@ function RampWidget({ onClose }) {
3420
3536
  onOpenKyc: handleOpenKyc,
3421
3537
  onOpenTos: handleOpenTos,
3422
3538
  onCompleteWithdraw: handleCompleteWithdraw,
3423
- onRetry: resetToInput,
3539
+ onBack: () => resetToInput({ keepMessage: true }),
3540
+ onRetry: () => resetToInput(),
3424
3541
  onRefresh: handleRefresh,
3425
3542
  onClose
3426
3543
  }
@@ -3477,6 +3594,7 @@ var CHAIN_NAME = {
3477
3594
  function ReceiveModalTemplate({
3478
3595
  theme,
3479
3596
  accentColor,
3597
+ styleOverrides,
3480
3598
  walletAddress,
3481
3599
  chains,
3482
3600
  selectedChain,
@@ -3486,23 +3604,7 @@ function ReceiveModalTemplate({
3486
3604
  onClose
3487
3605
  }) {
3488
3606
  const isDark = theme === "dark";
3489
- const cssVars = {
3490
- "--pollar-accent": accentColor,
3491
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
3492
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
3493
- "--pollar-text": isDark ? "#ffffff" : "#111827",
3494
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
3495
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
3496
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
3497
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
3498
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
3499
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
3500
- "--pollar-buttons-border-radius": "6px",
3501
- "--pollar-buttons-height": "44px",
3502
- "--pollar-input-height": "44px",
3503
- "--pollar-input-border-radius": "0.5rem",
3504
- "--pollar-card-border-radius": "10px"
3505
- };
3607
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
3506
3608
  const chainName = selectedChain ? CHAIN_NAME[selectedChain] ?? selectedChain : "wallet";
3507
3609
  return /* @__PURE__ */ jsxs(
3508
3610
  "div",
@@ -3567,7 +3669,7 @@ function ReceiveModalTemplate({
3567
3669
  }
3568
3670
  function ReceiveModal({ onClose }) {
3569
3671
  const { wallets, styles } = usePollar();
3570
- const { theme = "light", accentColor = "#005DB4" } = styles;
3672
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
3571
3673
  const [copied, setCopied] = useState(false);
3572
3674
  const copyTimerRef = useRef(null);
3573
3675
  const { chains } = useChains();
@@ -3593,11 +3695,12 @@ function ReceiveModal({ onClose }) {
3593
3695
  }, 2e3);
3594
3696
  });
3595
3697
  }
3596
- return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsx(
3698
+ return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsx(
3597
3699
  ReceiveModalTemplate,
3598
3700
  {
3599
3701
  theme,
3600
3702
  accentColor,
3703
+ styleOverrides,
3601
3704
  walletAddress,
3602
3705
  chains,
3603
3706
  selectedChain,
@@ -3860,6 +3963,7 @@ function assetKey3(record) {
3860
3963
  function SendModalTemplate({
3861
3964
  theme,
3862
3965
  accentColor,
3966
+ styleOverrides,
3863
3967
  step,
3864
3968
  txTitle,
3865
3969
  assets,
@@ -3893,24 +3997,7 @@ function SendModalTemplate({
3893
3997
  onRetry,
3894
3998
  onDone
3895
3999
  }) {
3896
- const isDark = theme === "dark";
3897
- const cssVars = {
3898
- "--pollar-accent": accentColor,
3899
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
3900
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
3901
- "--pollar-text": isDark ? "#ffffff" : "#111827",
3902
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
3903
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
3904
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
3905
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
3906
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
3907
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
3908
- "--pollar-buttons-border-radius": "6px",
3909
- "--pollar-buttons-height": "44px",
3910
- "--pollar-input-height": "44px",
3911
- "--pollar-input-border-radius": "0.5rem",
3912
- "--pollar-card-border-radius": "10px"
3913
- };
4000
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
3914
4001
  const selectedKey = selectedAsset ? assetKey3(selectedAsset) : "";
3915
4002
  const canSubmit = canSendOnChain && !!selectedAsset && !!amount && !!destination.trim() && !isLoadingBalance;
3916
4003
  const title = step === "form" ? "Send" : txTitle;
@@ -4070,7 +4157,7 @@ function SendModal({ onClose }) {
4070
4157
  styles
4071
4158
  } = usePollar();
4072
4159
  const walletType = wallet?.custody === "external" ? wallet.provider : null;
4073
- const { theme = "light", accentColor = "#005DB4" } = styles;
4160
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
4074
4161
  const [step, setStep] = useState("form");
4075
4162
  const [amount, setAmount] = useState("");
4076
4163
  const [destination, setDestination] = useState("");
@@ -4203,11 +4290,12 @@ function SendModal({ onClose }) {
4203
4290
  setShowXdr(false);
4204
4291
  setCopied(false);
4205
4292
  }
4206
- return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", onClick: !isInProgress ? onClose : void 0, children: /* @__PURE__ */ jsx(
4293
+ return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: !isInProgress ? onClose : void 0, children: /* @__PURE__ */ jsx(
4207
4294
  SendModalTemplate,
4208
4295
  {
4209
4296
  theme,
4210
4297
  accentColor,
4298
+ styleOverrides,
4211
4299
  step,
4212
4300
  txTitle,
4213
4301
  assets: sortedAssets,
@@ -4259,6 +4347,7 @@ function formatAmount3(value) {
4259
4347
  function SwapModalTemplate({
4260
4348
  theme,
4261
4349
  accentColor,
4350
+ styleOverrides,
4262
4351
  step,
4263
4352
  txTitle,
4264
4353
  sellOptions,
@@ -4298,7 +4387,6 @@ function SwapModalTemplate({
4298
4387
  onRetry,
4299
4388
  onDone
4300
4389
  }) {
4301
- const isDark = theme === "dark";
4302
4390
  const [customOpen, setCustomOpen] = useState(false);
4303
4391
  const [customCode, setCustomCode] = useState("");
4304
4392
  const [customIssuer, setCustomIssuer] = useState("");
@@ -4314,23 +4402,7 @@ function SwapModalTemplate({
4314
4402
  setCustomError("");
4315
4403
  setCustomOpen(false);
4316
4404
  };
4317
- const cssVars = {
4318
- "--pollar-accent": accentColor,
4319
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
4320
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
4321
- "--pollar-text": isDark ? "#ffffff" : "#111827",
4322
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
4323
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
4324
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
4325
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
4326
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
4327
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
4328
- "--pollar-buttons-border-radius": "6px",
4329
- "--pollar-buttons-height": "44px",
4330
- "--pollar-input-height": "44px",
4331
- "--pollar-input-border-radius": "0.5rem",
4332
- "--pollar-card-border-radius": "10px"
4333
- };
4405
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
4334
4406
  const sellKey = selectedSell ? assetOptionKey(selectedSell) : "";
4335
4407
  const buyKey = selectedBuy ? assetOptionKey(selectedBuy) : "";
4336
4408
  const canSwap = !smartUnsupported && !!selectedSell && !!selectedBuy && !!amount && !!quote && !quoteLoading && !isLoadingData;
@@ -4592,7 +4664,7 @@ function SwapModal({ onClose }) {
4592
4664
  } = usePollar();
4593
4665
  const walletType = wallet?.custody === "external" ? wallet.provider : null;
4594
4666
  const smartUnsupported = wallet?.custody === "smart";
4595
- const { theme = "light", accentColor = "#005DB4" } = styles;
4667
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
4596
4668
  const [step, setStep] = useState("form");
4597
4669
  const [selectedSell, setSelectedSell] = useState(null);
4598
4670
  const [selectedBuy, setSelectedBuy] = useState(null);
@@ -4797,11 +4869,12 @@ function SwapModal({ onClose }) {
4797
4869
  setShowXdr(false);
4798
4870
  setCopied(false);
4799
4871
  }
4800
- return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", onClick: !isInProgress ? onClose : void 0, children: /* @__PURE__ */ jsx(
4872
+ return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: !isInProgress ? onClose : void 0, children: /* @__PURE__ */ jsx(
4801
4873
  SwapModalTemplate,
4802
4874
  {
4803
4875
  theme,
4804
4876
  accentColor,
4877
+ styleOverrides,
4805
4878
  step,
4806
4879
  txTitle,
4807
4880
  sellOptions,
@@ -4879,8 +4952,7 @@ function EarnModal({ onClose }) {
4879
4952
  } = usePollar();
4880
4953
  const walletType = wallet?.custody === "external" ? wallet.provider : null;
4881
4954
  const smartUnsupported = wallet?.custody === "smart";
4882
- const { theme = "light", accentColor = "#005DB4" } = styles;
4883
- const isDark = theme === "dark";
4955
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
4884
4956
  const [step, setStep] = useState("form");
4885
4957
  const [tab, setTab] = useState("deposit");
4886
4958
  const [providers, setProviders] = useState(null);
@@ -4976,23 +5048,7 @@ function EarnModal({ onClose }) {
4976
5048
  const parsedAmount = parseFloat(amount);
4977
5049
  const exceedsAvailable = tab === "deposit" ? walletAvailable != null && parsedAmount > parseFloat(walletAvailable) : position != null && parsedAmount > parseFloat(position.withdrawable);
4978
5050
  const canSubmit = !smartUnsupported && !!provider && !!opportunityId && !!amount && parsedAmount > 0 && !exceedsAvailable && !providersLoading && !loadingOpps;
4979
- const cssVars = {
4980
- "--pollar-accent": accentColor,
4981
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
4982
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
4983
- "--pollar-text": isDark ? "#ffffff" : "#111827",
4984
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
4985
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
4986
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
4987
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
4988
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
4989
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
4990
- "--pollar-buttons-border-radius": "6px",
4991
- "--pollar-buttons-height": "44px",
4992
- "--pollar-input-height": "44px",
4993
- "--pollar-input-border-radius": "0.5rem",
4994
- "--pollar-card-border-radius": "10px"
4995
- };
5051
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
4996
5052
  async function handleSubmit() {
4997
5053
  setFormError("");
4998
5054
  if (smartUnsupported) {
@@ -5071,7 +5127,7 @@ function EarnModal({ onClose }) {
5071
5127
  }
5072
5128
  }
5073
5129
  const title = step === "form" ? "Earn" : txTitle;
5074
- return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", onClick: !isInProgress ? onClose : void 0, children: /* @__PURE__ */ jsxs(
5130
+ return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: !isInProgress ? onClose : void 0, children: /* @__PURE__ */ jsxs(
5075
5131
  "div",
5076
5132
  {
5077
5133
  className: "pollar-modal-card pollar-send-modal",
@@ -5319,6 +5375,7 @@ function shortIp(hash) {
5319
5375
  function SessionsModalTemplate({
5320
5376
  theme,
5321
5377
  accentColor,
5378
+ styleOverrides,
5322
5379
  state,
5323
5380
  revokingFamilyId,
5324
5381
  signingOutEverywhere,
@@ -5327,24 +5384,7 @@ function SessionsModalTemplate({
5327
5384
  onLogoutEverywhere,
5328
5385
  onClose
5329
5386
  }) {
5330
- const isDark = theme === "dark";
5331
- const cssVars = {
5332
- "--pollar-accent": accentColor,
5333
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
5334
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
5335
- "--pollar-text": isDark ? "#ffffff" : "#111827",
5336
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
5337
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
5338
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
5339
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
5340
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
5341
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
5342
- "--pollar-buttons-border-radius": "6px",
5343
- "--pollar-buttons-height": "44px",
5344
- "--pollar-input-height": "44px",
5345
- "--pollar-input-border-radius": "0.5rem",
5346
- "--pollar-card-border-radius": "10px"
5347
- };
5387
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
5348
5388
  const isLoading = state.step === "loading";
5349
5389
  const sessions = state.step === "loaded" ? state.sessions : [];
5350
5390
  const otherCount = sessions.filter((s) => !s.current).length;
@@ -5456,7 +5496,7 @@ function SessionsModalTemplate({
5456
5496
  }
5457
5497
  function SessionsModal({ onClose }) {
5458
5498
  const { getClient, styles, sessions } = usePollar();
5459
- const { theme = "light", accentColor = "#005DB4" } = styles;
5499
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
5460
5500
  const [revokingFamilyId, setRevokingFamilyId] = useState(null);
5461
5501
  const [signingOutEverywhere, setSigningOutEverywhere] = useState(false);
5462
5502
  const load = useCallback(() => {
@@ -5494,11 +5534,12 @@ function SessionsModal({ onClose }) {
5494
5534
  setSigningOutEverywhere(false);
5495
5535
  }
5496
5536
  }, [getClient, onClose]);
5497
- return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsx(
5537
+ return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsx(
5498
5538
  SessionsModalTemplate,
5499
5539
  {
5500
5540
  theme,
5501
5541
  accentColor,
5542
+ styleOverrides,
5502
5543
  state: sessions,
5503
5544
  revokingFamilyId,
5504
5545
  signingOutEverywhere,
@@ -5512,6 +5553,7 @@ function SessionsModal({ onClose }) {
5512
5553
  function TransactionModalTemplate({
5513
5554
  theme,
5514
5555
  accentColor,
5556
+ styleOverrides,
5515
5557
  transaction,
5516
5558
  showXdr,
5517
5559
  copied,
@@ -5523,24 +5565,7 @@ function TransactionModalTemplate({
5523
5565
  onCopyHash,
5524
5566
  onRetry
5525
5567
  }) {
5526
- const isDark = theme === "dark";
5527
- const cssVars = {
5528
- "--pollar-accent": accentColor,
5529
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
5530
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
5531
- "--pollar-text": isDark ? "#ffffff" : "#111827",
5532
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
5533
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
5534
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
5535
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
5536
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
5537
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
5538
- "--pollar-buttons-border-radius": "6px",
5539
- "--pollar-buttons-height": "44px",
5540
- "--pollar-input-height": "44px",
5541
- "--pollar-input-border-radius": "0.5rem",
5542
- "--pollar-card-border-radius": "10px"
5543
- };
5568
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
5544
5569
  return /* @__PURE__ */ jsxs("div", { className: "pollar-modal-card pollar-tx-modal", "data-theme": theme, style: cssVars, onClick: (e) => e.stopPropagation(), children: [
5545
5570
  /* @__PURE__ */ jsx("div", { className: "pollar-modal-header", children: /* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: "Transaction" }) }),
5546
5571
  /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-close-btn", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx(
@@ -5579,7 +5604,7 @@ function TransactionModalTemplate({
5579
5604
  function TransactionModal({ onClose }) {
5580
5605
  const { getClient, styles, tx: transaction, network, wallet } = usePollar();
5581
5606
  const walletType = wallet?.custody === "external" ? wallet.provider : null;
5582
- const { theme = "light", accentColor = "#005DB4" } = styles;
5607
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
5583
5608
  const [showXdr, setShowXdr] = useState(false);
5584
5609
  const [copied, setCopied] = useState(false);
5585
5610
  const copyTimerRef = useRef(null);
@@ -5614,11 +5639,12 @@ function TransactionModal({ onClose }) {
5614
5639
  await getClient().signAndSubmitTx(transaction.buildData.unsignedXdr);
5615
5640
  }
5616
5641
  }
5617
- return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsx(
5642
+ return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsx(
5618
5643
  TransactionModalTemplate,
5619
5644
  {
5620
5645
  theme,
5621
5646
  accentColor,
5647
+ styleOverrides,
5622
5648
  transaction,
5623
5649
  showXdr,
5624
5650
  copied,
@@ -5675,6 +5701,7 @@ function renderSummary(summary) {
5675
5701
  function TxHistoryModalTemplate({
5676
5702
  theme,
5677
5703
  accentColor,
5704
+ styleOverrides,
5678
5705
  txHistory,
5679
5706
  offset,
5680
5707
  chains,
@@ -5686,24 +5713,7 @@ function TxHistoryModalTemplate({
5686
5713
  onNext,
5687
5714
  onClose
5688
5715
  }) {
5689
- const isDark = theme === "dark";
5690
- const cssVars = {
5691
- "--pollar-accent": accentColor,
5692
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
5693
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
5694
- "--pollar-text": isDark ? "#ffffff" : "#111827",
5695
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
5696
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
5697
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
5698
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
5699
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
5700
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
5701
- "--pollar-buttons-border-radius": "6px",
5702
- "--pollar-buttons-height": "44px",
5703
- "--pollar-input-height": "44px",
5704
- "--pollar-input-border-radius": "0.5rem",
5705
- "--pollar-card-border-radius": "10px"
5706
- };
5716
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
5707
5717
  const isLoading = txHistory.step === "loading";
5708
5718
  const records = txHistory.step === "loaded" ? txHistory.data.records : [];
5709
5719
  const total = txHistory.step === "loaded" ? txHistory.data.total : 0;
@@ -5875,7 +5885,7 @@ function TxHistoryModalTemplate({
5875
5885
  var PAGE_SIZE2 = 10;
5876
5886
  function TxHistoryModal({ onClose }) {
5877
5887
  const { getClient, styles, txHistory, wallets } = usePollar();
5878
- const { theme = "light", accentColor = "#005DB4" } = styles;
5888
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
5879
5889
  const [offset, setOffset] = useState(0);
5880
5890
  const { chains } = useChains();
5881
5891
  const [selectedChain, setSelectedChain] = useState(null);
@@ -5897,11 +5907,12 @@ function TxHistoryModal({ onClose }) {
5897
5907
  const paged = (nextOffset) => {
5898
5908
  if (selectedChain) load(nextOffset, selectedChain);
5899
5909
  };
5900
- return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsx(
5910
+ return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsx(
5901
5911
  TxHistoryModalTemplate,
5902
5912
  {
5903
5913
  theme,
5904
5914
  accentColor,
5915
+ styleOverrides,
5905
5916
  txHistory,
5906
5917
  offset,
5907
5918
  chains,
@@ -5954,6 +5965,7 @@ function BalanceItem({ record, faucet }) {
5954
5965
  function WalletBalanceModalTemplate({
5955
5966
  theme,
5956
5967
  accentColor,
5968
+ styleOverrides,
5957
5969
  walletBalance,
5958
5970
  walletAddress,
5959
5971
  chains,
@@ -5963,24 +5975,7 @@ function WalletBalanceModalTemplate({
5963
5975
  onRefresh,
5964
5976
  onClose
5965
5977
  }) {
5966
- const isDark = theme === "dark";
5967
- const cssVars = {
5968
- "--pollar-accent": accentColor,
5969
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
5970
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
5971
- "--pollar-text": isDark ? "#ffffff" : "#111827",
5972
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
5973
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
5974
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
5975
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
5976
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
5977
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
5978
- "--pollar-buttons-border-radius": "6px",
5979
- "--pollar-buttons-height": "44px",
5980
- "--pollar-input-height": "44px",
5981
- "--pollar-input-border-radius": "0.5rem",
5982
- "--pollar-card-border-radius": "10px"
5983
- };
5978
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
5984
5979
  const isLoading = walletBalance.step === "loading";
5985
5980
  const data = useStickyData(walletBalance.step === "loaded" ? walletBalance.data : null);
5986
5981
  const balances = (data?.balances ?? []).filter((b) => resolveChain(b.chain) === selectedChain);
@@ -6054,7 +6049,7 @@ function WalletBalanceModalTemplate({
6054
6049
  }
6055
6050
  function WalletBalanceModal({ onClose }) {
6056
6051
  const { walletBalance, refreshWalletBalance, wallets, network, styles } = usePollar();
6057
- const { theme = "light", accentColor = "#005DB4" } = styles;
6052
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
6058
6053
  const { chains } = useChains();
6059
6054
  const [selectedChain, setSelectedChain] = useState(null);
6060
6055
  useEffect(() => {
@@ -6064,11 +6059,12 @@ function WalletBalanceModal({ onClose }) {
6064
6059
  useEffect(() => {
6065
6060
  void refreshWalletBalance();
6066
6061
  }, [refreshWalletBalance]);
6067
- return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsx(
6062
+ return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsx(
6068
6063
  WalletBalanceModalTemplate,
6069
6064
  {
6070
6065
  theme,
6071
6066
  accentColor,
6067
+ styleOverrides,
6072
6068
  walletBalance,
6073
6069
  walletAddress,
6074
6070
  chains,
@@ -6150,9 +6146,17 @@ function PollarProvider({
6150
6146
  onStorageDegrade,
6151
6147
  children
6152
6148
  }) {
6153
- const [pollarClient] = useState(
6154
- () => client instanceof PollarClient ? client : new PollarClient({ passkey: browserPasskeyCeremony, passkeySign: browserPasskeySigner, ...client })
6155
- );
6149
+ const [pollarClient] = useState(() => {
6150
+ if (client instanceof PollarClient) {
6151
+ client.setPasskeyDefaults({ passkey: browserPasskeyCeremony, passkeySign: browserPasskeySigner });
6152
+ return client;
6153
+ }
6154
+ return new PollarClient({
6155
+ ...client,
6156
+ passkey: client.passkey ?? browserPasskeyCeremony,
6157
+ passkeySign: client.passkeySign ?? browserPasskeySigner
6158
+ });
6159
+ });
6156
6160
  const ownsClientRef = useRef(!(client instanceof PollarClient));
6157
6161
  const destroyTimerRef = useRef(null);
6158
6162
  useEffect(() => {
@@ -6921,6 +6925,6 @@ function WalletButton() {
6921
6925
  );
6922
6926
  }
6923
6927
 
6924
- export { ChainSelect, DistributionRulesModal, DistributionRulesModalTemplate, EarnModal, EnabledAssetsModal, EnabledAssetsModalTemplate, KycModal, KycModalTemplate, KycStatus, LoginModalTemplate, PollarProvider, RampWidget, RampWidgetTemplate, ReceiveModal, ReceiveModalTemplate, RouteDisplay, SendModal, SendModalTemplate, SessionsModal, SessionsModalTemplate, SwapModal, SwapModalTemplate, TransactionModalTemplate, TxHistoryModalTemplate, TxStatusView, WalletBalanceModal, WalletBalanceModalTemplate, WalletButton, WalletButtonTemplate, addressForChain, chainsOf, createPollarAdapterHook, resolveChain, useChains, usePollar };
6928
+ export { ChainSelect, DistributionRulesModal, DistributionRulesModalTemplate, EarnModal, EnabledAssetsModal, EnabledAssetsModalTemplate, KycModal, KycModalTemplate, KycStatus, LoginModalTemplate, PollarProvider, RampWidget, RampWidgetTemplate, ReceiveModal, ReceiveModalTemplate, RouteDisplay, SendModal, SendModalTemplate, SessionsModal, SessionsModalTemplate, SwapModal, SwapModalTemplate, TransactionModalTemplate, TxHistoryModalTemplate, TxStatusView, WalletBalanceModal, WalletBalanceModalTemplate, WalletButton, WalletButtonTemplate, addressForChain, browserPasskeyCeremony, browserPasskeySigner, buildModalCssVars, chainsOf, createPollarAdapterHook, modalChrome, readableTextOn, resolveChain, useChains, usePollar };
6925
6929
  //# sourceMappingURL=index.mjs.map
6926
6930
  //# sourceMappingURL=index.mjs.map