@pollar/react 0.11.2 → 0.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1035,7 +1035,7 @@ var PollarModalFooter = () => {
1035
1035
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-footer-name", children: "Pollar" }),
1036
1036
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "pollar-footer-version", children: [
1037
1037
  "v",
1038
- "0.11.2"
1038
+ "0.11.3"
1039
1039
  ] })
1040
1040
  ] })
1041
1041
  ] });
@@ -1131,6 +1131,81 @@ function Toggle({ checked, onChange, disabled = false, busy = false, label }) {
1131
1131
  }
1132
1132
  );
1133
1133
  }
1134
+
1135
+ // src/components/modal-theme.ts
1136
+ var DEFAULT_MODAL_BORDER_RADIUS = 16;
1137
+ var DEFAULT_BUTTON_BORDER_RADIUS = 6;
1138
+ var DEFAULT_OVERLAY_Z_INDEX = 50;
1139
+ function px(value, fallback) {
1140
+ return `${typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : fallback}px`;
1141
+ }
1142
+ function color(value, fallback) {
1143
+ return typeof value === "string" && value.trim() !== "" ? value : fallback;
1144
+ }
1145
+ function parseHex(value) {
1146
+ const hex = value.trim().replace(/^#/, "");
1147
+ const full = hex.length === 3 ? [...hex].map((c) => c + c).join("") : hex;
1148
+ if (!/^[0-9a-f]{6}$/i.test(full)) return null;
1149
+ return [Number.parseInt(full.slice(0, 2), 16), Number.parseInt(full.slice(2, 4), 16), Number.parseInt(full.slice(4, 6), 16)];
1150
+ }
1151
+ function readableTextOn(background) {
1152
+ const rgb = parseHex(background);
1153
+ if (!rgb) return "#ffffff";
1154
+ const [r, g, b] = rgb;
1155
+ return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255 > 0.6 ? "#111827" : "#ffffff";
1156
+ }
1157
+ function buildModalCssVars(theme, accentColor, overrides = {}, variant = "compact") {
1158
+ const isDark = theme === "dark";
1159
+ const textColor = color(overrides.textColor, isDark ? "#ffffff" : "#111827");
1160
+ const secondaryBg = color(overrides.buttonColor, "");
1161
+ return {
1162
+ "--pollar-accent": accentColor,
1163
+ "--pollar-bg": color(overrides.backgroundColor, isDark ? "#1a1a1a" : "#ffffff"),
1164
+ "--pollar-border": isDark ? "#374151" : "#e5e7eb",
1165
+ "--pollar-text": textColor,
1166
+ "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
1167
+ // The primary button is the accent's, straight up; its label follows the
1168
+ // accent's luminance so a pale accent doesn't render white-on-white.
1169
+ "--pollar-btn-primary-fg": readableTextOn(accentColor),
1170
+ ...secondaryBg ? {
1171
+ "--pollar-btn-secondary-bg": secondaryBg,
1172
+ "--pollar-btn-secondary-fg": readableTextOn(secondaryBg)
1173
+ } : {},
1174
+ "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
1175
+ "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
1176
+ "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
1177
+ "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
1178
+ "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
1179
+ "--pollar-modal-border-radius": px(overrides.modalBorderRadius, DEFAULT_MODAL_BORDER_RADIUS),
1180
+ "--pollar-buttons-border-radius": px(overrides.buttonBorderRadius, DEFAULT_BUTTON_BORDER_RADIUS),
1181
+ "--pollar-buttons-height": "44px",
1182
+ "--pollar-input-height": "44px",
1183
+ "--pollar-input-border-radius": "0.5rem",
1184
+ "--pollar-card-border-radius": "10px",
1185
+ ...variant === "hero" ? {
1186
+ "--pollar-modal-padding": "2rem",
1187
+ "--pollar-modal-heading-size": "1.375rem",
1188
+ "--pollar-modal-subtitle-size": "0.9rem"
1189
+ } : {}
1190
+ };
1191
+ }
1192
+ function modalChrome(styles) {
1193
+ const zIndex = styles.modalZIndex;
1194
+ return {
1195
+ theme: styles.theme ?? "light",
1196
+ accentColor: styles.accentColor ?? "#005DB4",
1197
+ styleOverrides: {
1198
+ backgroundColor: styles.backgroundColor,
1199
+ textColor: styles.textColor,
1200
+ buttonColor: styles.buttonColor,
1201
+ modalBorderRadius: styles.modalBorderRadius,
1202
+ buttonBorderRadius: styles.buttonBorderRadius
1203
+ },
1204
+ overlayStyle: {
1205
+ "--pollar-overlay-z-index": typeof zIndex === "number" && Number.isFinite(zIndex) ? zIndex : DEFAULT_OVERLAY_Z_INDEX
1206
+ }
1207
+ };
1208
+ }
1134
1209
  var PERIOD_LABEL = {
1135
1210
  DAY: "every 24h",
1136
1211
  DAY_CALENDAR: "daily",
@@ -1144,7 +1219,7 @@ var REASON_LABEL = {
1144
1219
  DISTRIBUTION_RULE_NOT_STARTED: "Not started yet",
1145
1220
  DISTRIBUTION_RULE_EXPIRED: "Expired",
1146
1221
  DISTRIBUTION_RULE_EXHAUSTED: "Fully claimed",
1147
- // Per-user, per-window claim limit (resets next period) — not permanent.
1222
+ // Per-user, per-window claim limit (resets next period) - not permanent.
1148
1223
  DISTRIBUTION_RATE_LIMIT_EXCEEDED: "Claimed for this period"
1149
1224
  };
1150
1225
  function reasonLabel(reason) {
@@ -1200,6 +1275,7 @@ function RuleCard({
1200
1275
  function DistributionRulesModalTemplate({
1201
1276
  theme,
1202
1277
  accentColor,
1278
+ styleOverrides,
1203
1279
  state,
1204
1280
  claimingId,
1205
1281
  claimErrors,
@@ -1208,24 +1284,7 @@ function DistributionRulesModalTemplate({
1208
1284
  onClaim,
1209
1285
  onClose
1210
1286
  }) {
1211
- const isDark = theme === "dark";
1212
- const cssVars = {
1213
- "--pollar-accent": accentColor,
1214
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
1215
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
1216
- "--pollar-text": isDark ? "#ffffff" : "#111827",
1217
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
1218
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
1219
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
1220
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
1221
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
1222
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
1223
- "--pollar-buttons-border-radius": "6px",
1224
- "--pollar-buttons-height": "44px",
1225
- "--pollar-input-height": "44px",
1226
- "--pollar-input-border-radius": "0.5rem",
1227
- "--pollar-card-border-radius": "10px"
1228
- };
1287
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
1229
1288
  const isLoading = state.step === "loading";
1230
1289
  const rules = state.step === "loaded" ? state.rules : [];
1231
1290
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -1300,7 +1359,7 @@ function DistributionRulesModalTemplate({
1300
1359
  }
1301
1360
  function DistributionRulesModal({ onClose }) {
1302
1361
  const { getClient, styles } = usePollar();
1303
- const { theme = "light", accentColor = "#005DB4" } = styles;
1362
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
1304
1363
  const [state, setState] = react.useState({ step: "idle" });
1305
1364
  const [claimingId, setClaimingId] = react.useState(null);
1306
1365
  const [claimErrors, setClaimErrors] = react.useState({});
@@ -1343,11 +1402,12 @@ function DistributionRulesModal({ onClose }) {
1343
1402
  },
1344
1403
  [getClient]
1345
1404
  );
1346
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
1405
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
1347
1406
  DistributionRulesModalTemplate,
1348
1407
  {
1349
1408
  theme,
1350
1409
  accentColor,
1410
+ styleOverrides,
1351
1411
  state,
1352
1412
  claimingId,
1353
1413
  claimErrors,
@@ -1419,26 +1479,6 @@ function useChains() {
1419
1479
  return { chains, primaryChain, primaryAddress: addressForChain(wallets, primaryChain), ready: true };
1420
1480
  }, [wallets, configured, ready]);
1421
1481
  }
1422
- function cssVarsFor(theme, accentColor) {
1423
- const isDark = theme === "dark";
1424
- return {
1425
- "--pollar-accent": accentColor,
1426
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
1427
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
1428
- "--pollar-text": isDark ? "#ffffff" : "#111827",
1429
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
1430
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
1431
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
1432
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
1433
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
1434
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
1435
- "--pollar-buttons-border-radius": "6px",
1436
- "--pollar-buttons-height": "44px",
1437
- "--pollar-input-height": "44px",
1438
- "--pollar-input-border-radius": "0.5rem",
1439
- "--pollar-card-border-radius": "10px"
1440
- };
1441
- }
1442
1482
  function assetKey(record) {
1443
1483
  return record.code + (record.issuer ?? "");
1444
1484
  }
@@ -1485,6 +1525,7 @@ function AssetItem({
1485
1525
  function EnabledAssetsModalTemplate({
1486
1526
  theme,
1487
1527
  accentColor,
1528
+ styleOverrides,
1488
1529
  enabledAssets,
1489
1530
  walletAddress,
1490
1531
  chains,
@@ -1497,7 +1538,7 @@ function EnabledAssetsModalTemplate({
1497
1538
  onToggleTrustline,
1498
1539
  onAddCustom
1499
1540
  }) {
1500
- const cssVars = cssVarsFor(theme, accentColor);
1541
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
1501
1542
  const isLoading = enabledAssets.step === "loading";
1502
1543
  const data = useStickyData(enabledAssets.step === "loaded" ? enabledAssets.data : null);
1503
1544
  const busy = busyKey !== null;
@@ -1589,13 +1630,14 @@ function isValidIssuer(issuer) {
1589
1630
  function CustomTrustlineModalTemplate({
1590
1631
  theme,
1591
1632
  accentColor,
1633
+ styleOverrides,
1592
1634
  busy,
1593
1635
  actionError,
1594
1636
  onBack,
1595
1637
  onClose,
1596
1638
  onSubmit
1597
1639
  }) {
1598
- const cssVars = cssVarsFor(theme, accentColor);
1640
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
1599
1641
  const [code, setCode] = react.useState("");
1600
1642
  const [issuer, setIssuer] = react.useState("");
1601
1643
  const [limit, setLimit] = react.useState("");
@@ -1692,7 +1734,7 @@ function assetKey2(record) {
1692
1734
  }
1693
1735
  function EnabledAssetsModal({ onClose }) {
1694
1736
  const { enabledAssets, refreshAssets, setTrustline, wallets, styles } = usePollar();
1695
- const { theme = "light", accentColor = "#005DB4" } = styles;
1737
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
1696
1738
  const { chains } = useChains();
1697
1739
  const [selectedChain, setSelectedChain] = react.useState(null);
1698
1740
  react.useEffect(() => {
@@ -1743,11 +1785,12 @@ function EnabledAssetsModal({ onClose }) {
1743
1785
  },
1744
1786
  [runAction]
1745
1787
  );
1746
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: onClose, children: view === "list" ? /* @__PURE__ */ jsxRuntime.jsx(
1788
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: view === "list" ? /* @__PURE__ */ jsxRuntime.jsx(
1747
1789
  EnabledAssetsModalTemplate,
1748
1790
  {
1749
1791
  theme,
1750
1792
  accentColor,
1793
+ styleOverrides,
1751
1794
  enabledAssets,
1752
1795
  walletAddress,
1753
1796
  chains,
@@ -1768,6 +1811,7 @@ function EnabledAssetsModal({ onClose }) {
1768
1811
  {
1769
1812
  theme,
1770
1813
  accentColor,
1814
+ styleOverrides,
1771
1815
  busy: busyKey === "custom",
1772
1816
  actionError,
1773
1817
  onBack: () => {
@@ -1802,6 +1846,7 @@ function KycStatus({ status, className }) {
1802
1846
  function KycModalTemplate({
1803
1847
  theme,
1804
1848
  accentColor,
1849
+ styleOverrides,
1805
1850
  step,
1806
1851
  providers,
1807
1852
  selectedProvider,
@@ -1813,27 +1858,7 @@ function KycModalTemplate({
1813
1858
  onRefresh,
1814
1859
  onClose
1815
1860
  }) {
1816
- const isDark = theme === "dark";
1817
- const cssVars = {
1818
- "--pollar-accent": accentColor,
1819
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
1820
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
1821
- "--pollar-text": isDark ? "#ffffff" : "#111827",
1822
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
1823
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
1824
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
1825
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
1826
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
1827
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
1828
- "--pollar-buttons-border-radius": "6px",
1829
- "--pollar-buttons-height": "44px",
1830
- "--pollar-input-height": "44px",
1831
- "--pollar-input-border-radius": "0.5rem",
1832
- "--pollar-card-border-radius": "10px",
1833
- "--pollar-modal-padding": "2rem",
1834
- "--pollar-modal-heading-size": "1.375rem",
1835
- "--pollar-modal-subtitle-size": "0.9rem"
1836
- };
1861
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides, "hero");
1837
1862
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-card pollar-kyc-modal", style: cssVars, onClick: (e) => e.stopPropagation(), children: [
1838
1863
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-header", children: [
1839
1864
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-kyc-header-text", children: [
@@ -1932,7 +1957,7 @@ function KycModal({ onClose, country = "MX", level = "basic", onApproved }) {
1932
1957
  const [kycStatus, setKycStatus] = react.useState("none");
1933
1958
  const [isLoading, setIsLoading] = react.useState(false);
1934
1959
  const client = getClient();
1935
- const { theme = "light", accentColor = "#005DB4" } = styles;
1960
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
1936
1961
  const loadProviders = react.useCallback(() => {
1937
1962
  setIsLoading(true);
1938
1963
  return getClient().getKycProviders(country).then((result) => setProviders(result.providers)).catch(() => setProviders([])).finally(() => setIsLoading(false));
@@ -1972,11 +1997,12 @@ function KycModal({ onClose, country = "MX", level = "basic", onApproved }) {
1972
1997
  setStep("done");
1973
1998
  }
1974
1999
  }
1975
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
2000
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
1976
2001
  KycModalTemplate,
1977
2002
  {
1978
2003
  theme,
1979
2004
  accentColor,
2005
+ styleOverrides,
1980
2006
  step,
1981
2007
  providers,
1982
2008
  selectedProvider,
@@ -2168,32 +2194,10 @@ function authStateToStatus(step) {
2168
2194
  if (error.includes(step)) return "ERROR";
2169
2195
  return "NONE";
2170
2196
  }
2171
- function buildModalCssVars(theme, accentColor) {
2172
- const isDark = theme === "dark";
2173
- return {
2174
- "--pollar-accent": accentColor,
2175
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
2176
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
2177
- "--pollar-text": isDark ? "#ffffff" : "#111827",
2178
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
2179
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
2180
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
2181
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
2182
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
2183
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
2184
- "--pollar-buttons-border-radius": "6px",
2185
- "--pollar-buttons-height": "44px",
2186
- "--pollar-input-height": "44px",
2187
- "--pollar-input-border-radius": "0.5rem",
2188
- "--pollar-card-border-radius": "10px",
2189
- "--pollar-modal-padding": "2rem",
2190
- "--pollar-modal-heading-size": "1.375rem",
2191
- "--pollar-modal-subtitle-size": "0.9rem"
2192
- };
2193
- }
2194
2197
  function LoginModalTemplate({
2195
2198
  theme,
2196
2199
  accentColor,
2200
+ styleOverrides,
2197
2201
  logoUrl,
2198
2202
  emailEnabled,
2199
2203
  embeddedWallets,
@@ -2227,7 +2231,7 @@ function LoginModalTemplate({
2227
2231
  return acc;
2228
2232
  }, []);
2229
2233
  const activeGroupAdapters = walletGroups.find((g) => g.label === activeGroup)?.adapters ?? [];
2230
- const cssVars = buildModalCssVars(theme, accentColor);
2234
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides, "hero");
2231
2235
  const status = authStateToStatus(authState.step);
2232
2236
  const isLoading = status === "LOADING";
2233
2237
  const isEmailCodeError = authState.step === "error" && (authState.errorCode === core.AUTH_ERROR_CODES.EMAIL_CODE_EXPIRED || authState.errorCode === core.AUTH_ERROR_CODES.EMAIL_CODE_INVALID);
@@ -2406,12 +2410,13 @@ function LoginModalStatus({
2406
2410
  status,
2407
2411
  theme,
2408
2412
  accentColor,
2413
+ styleOverrides,
2409
2414
  logoUrl,
2410
2415
  appName,
2411
2416
  onRetry,
2412
2417
  onCancel
2413
2418
  }) {
2414
- const cssVars = buildModalCssVars(theme, accentColor);
2419
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides, "hero");
2415
2420
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-card pollar-modal", style: cssVars, onClick: (e) => e.stopPropagation(), children: [
2416
2421
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-close-btn", onClick: onCancel, "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx(
2417
2422
  "svg",
@@ -2446,6 +2451,7 @@ function PrivyLoginSubmodal({
2446
2451
  adapter,
2447
2452
  theme,
2448
2453
  accentColor,
2454
+ styleOverrides,
2449
2455
  logoUrl,
2450
2456
  appName,
2451
2457
  onBack,
@@ -2457,28 +2463,8 @@ function PrivyLoginSubmodal({
2457
2463
  const [view, setView] = react.useState("root");
2458
2464
  const [status, setStatus] = react.useState("NONE");
2459
2465
  const [message, setMessage] = react.useState("");
2460
- const isDark = theme === "dark";
2461
2466
  const isLoading = status === "LOADING";
2462
- const cssVars = {
2463
- "--pollar-accent": accentColor,
2464
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
2465
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
2466
- "--pollar-text": isDark ? "#ffffff" : "#111827",
2467
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
2468
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
2469
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
2470
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
2471
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
2472
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
2473
- "--pollar-buttons-border-radius": "6px",
2474
- "--pollar-buttons-height": "44px",
2475
- "--pollar-input-height": "44px",
2476
- "--pollar-input-border-radius": "0.5rem",
2477
- "--pollar-card-border-radius": "10px",
2478
- "--pollar-modal-padding": "2rem",
2479
- "--pollar-modal-heading-size": "1.375rem",
2480
- "--pollar-modal-subtitle-size": "0.9rem"
2481
- };
2467
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides, "hero");
2482
2468
  function fail(error) {
2483
2469
  setStatus("ERROR");
2484
2470
  setMessage(error instanceof Error ? error.message : "Something went wrong. Please try again.");
@@ -2635,8 +2621,10 @@ function LoginModal({ onClose }) {
2635
2621
  }
2636
2622
  };
2637
2623
  }, [getClient]);
2638
- const { theme = "light", accentColor = "#005DB4", logoUrl, emailEnabled, embeddedWallets, smartWallet, providers } = styles;
2624
+ const { logoUrl, emailEnabled, embeddedWallets, smartWallet, providers } = styles;
2625
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
2639
2626
  const smartWalletEnabled = smartWallet ?? false;
2627
+ const modalTitle = styles.modalTitle?.trim() || config.application?.name || "Pollar";
2640
2628
  function handleClose() {
2641
2629
  setEmail("");
2642
2630
  getClient().cancelLogin();
@@ -2684,14 +2672,15 @@ function LoginModal({ onClose }) {
2684
2672
  getClient().login({ provider });
2685
2673
  }
2686
2674
  }
2687
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: handleClose, children: configStatus !== "ready" ? /* @__PURE__ */ jsxRuntime.jsx(
2675
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: handleClose, children: configStatus !== "ready" ? /* @__PURE__ */ jsxRuntime.jsx(
2688
2676
  LoginModalStatus,
2689
2677
  {
2690
2678
  status: configStatus === "error" ? "error" : "loading",
2691
2679
  theme,
2692
2680
  accentColor,
2681
+ styleOverrides,
2693
2682
  logoUrl: logoUrl ?? null,
2694
- appName: config.application?.name ?? "Pollar",
2683
+ appName: modalTitle,
2695
2684
  onRetry: retryConfig,
2696
2685
  onCancel: handleClose
2697
2686
  }
@@ -2701,8 +2690,9 @@ function LoginModal({ onClose }) {
2701
2690
  adapter: interactiveAdapter,
2702
2691
  theme,
2703
2692
  accentColor,
2693
+ styleOverrides,
2704
2694
  logoUrl: logoUrl ?? null,
2705
- appName: config.application?.name ?? "Pollar",
2695
+ appName: modalTitle,
2706
2696
  onBack: () => setInteractiveAdapter(null),
2707
2697
  onCancel: handleClose,
2708
2698
  onAuthenticated: handleInteractiveAuthenticated
@@ -2712,6 +2702,7 @@ function LoginModal({ onClose }) {
2712
2702
  {
2713
2703
  theme,
2714
2704
  accentColor,
2705
+ styleOverrides,
2715
2706
  logoUrl: logoUrl ?? null,
2716
2707
  emailEnabled: !!emailEnabled,
2717
2708
  embeddedWallets: !!embeddedWallets,
@@ -2724,7 +2715,7 @@ function LoginModal({ onClose }) {
2724
2715
  apple: !!providers?.apple
2725
2716
  },
2726
2717
  walletAdapters,
2727
- appName: config.application?.name ?? "Pollar",
2718
+ appName: modalTitle,
2728
2719
  email,
2729
2720
  onEmailChange: setEmail,
2730
2721
  onEmailSubmit: handleEmailSubmit,
@@ -2747,34 +2738,36 @@ var RAIL_LABELS = {
2747
2738
  PSE: "PSE (Colombia)",
2748
2739
  ACH: "ACH (US)"
2749
2740
  };
2750
- function RouteDisplay({ quote, onSelect }) {
2741
+ function RouteDisplay({ quote, busy = false, disabled = false, onSelect }) {
2742
+ const inert = busy || disabled;
2743
+ const select = () => {
2744
+ if (!inert) onSelect(quote);
2745
+ };
2751
2746
  return /* @__PURE__ */ jsxRuntime.jsxs(
2752
2747
  "div",
2753
2748
  {
2754
2749
  className: "pollar-ramp-route-card",
2755
2750
  "data-recommended": quote.recommended,
2751
+ "data-busy": busy || void 0,
2752
+ "data-disabled": disabled || void 0,
2756
2753
  role: "button",
2757
- tabIndex: 0,
2758
- onClick: () => onSelect(quote),
2759
- onKeyDown: (e) => e.key === "Enter" && onSelect(quote),
2754
+ "aria-busy": busy,
2755
+ "aria-disabled": inert,
2756
+ tabIndex: inert ? -1 : 0,
2757
+ onClick: select,
2758
+ onKeyDown: (e) => e.key === "Enter" && select(),
2760
2759
  children: [
2761
2760
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-route-left", children: [
2762
2761
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-route-provider", children: quote.provider }),
2763
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "pollar-ramp-route-meta", children: [
2764
- RAIL_LABELS[quote.rail] ?? quote.rail,
2765
- " \xB7 ",
2766
- quote.protocol,
2767
- " \xB7 ",
2768
- quote.estimatedTime
2769
- ] })
2762
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-route-meta", children: busy ? "Starting\u2026" : `${RAIL_LABELS[quote.rail] ?? quote.rail} \xB7 ${quote.protocol} \xB7 ${quote.estimatedTime}` })
2770
2763
  ] }),
2771
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-route-right", children: [
2764
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-route-right", children: busy ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-spinner pollar-spinner-sm" }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2772
2765
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "pollar-ramp-route-fee", children: [
2773
2766
  quote.fee,
2774
2767
  "% fee"
2775
2768
  ] }),
2776
2769
  quote.recommended && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-route-badge", children: "Best rate" })
2777
- ] })
2770
+ ] }) })
2778
2771
  ]
2779
2772
  }
2780
2773
  );
@@ -2782,10 +2775,24 @@ function RouteDisplay({ quote, onSelect }) {
2782
2775
  var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2783
2776
  function isFieldValid(spec, raw) {
2784
2777
  const value = (raw ?? "").trim();
2785
- if (!value) return false;
2778
+ if (!value) return spec.optional === true;
2786
2779
  if (spec.type === "email") return EMAIL_RE.test(value);
2787
2780
  return true;
2788
2781
  }
2782
+ function listOf(items) {
2783
+ const last = items[items.length - 1];
2784
+ if (last === void 0) return "a few details";
2785
+ if (items.length === 1) return last;
2786
+ return `${items.slice(0, -1).join(", ")} and ${last}`;
2787
+ }
2788
+ function placeholderFor(field, fields, values) {
2789
+ if (field.placeholderFrom) {
2790
+ const source = fields.find((f) => f.key === field.placeholderFrom);
2791
+ const chosen = source?.options?.find((o) => o.value === values[field.placeholderFrom]);
2792
+ if (chosen?.placeholder) return chosen.placeholder;
2793
+ }
2794
+ return field.placeholder;
2795
+ }
2789
2796
  var LOADING_STEPS = ["Detecting your country\u2026", "Consulting providers\u2026", "Route found!"];
2790
2797
  function flagEmoji(code) {
2791
2798
  const cc = code.trim().toUpperCase();
@@ -2799,47 +2806,19 @@ var STATUS_LABEL = {
2799
2806
  completed: "Completed",
2800
2807
  failed: "Failed"
2801
2808
  };
2802
- var INSTRUCTION_LABELS = {
2803
- br_code: "Pix code",
2804
- account_holder_name: "Account holder",
2805
- bank_name: "Bank",
2806
- bank_address: "Bank address",
2807
- bank_account_number: "Account number",
2808
- bank_routing_number: "Routing number",
2809
- iban: "IBAN",
2810
- bic: "BIC",
2811
- clabe: "CLABE",
2812
- amount: "Amount",
2813
- currency: "Currency",
2814
- payment_rails: "Rails"
2815
- };
2816
- function flattenInstructions(instr) {
2817
- const out = [];
2818
- for (const [k, v] of Object.entries(instr)) {
2819
- if (k === "qrBase64") {
2820
- if (typeof v === "string" && v) out.push({ key: k, label: INSTRUCTION_LABELS[k] ?? "Payment QR", value: v, kind: "qr" });
2821
- continue;
2822
- }
2823
- if ((k === "expiresAt" || k === "expires_at") && (typeof v === "number" || typeof v === "string")) {
2824
- const ms = Number(v);
2825
- if (Number.isFinite(ms) && ms > 0) {
2826
- out.push({ key: k, label: INSTRUCTION_LABELS[k] ?? "Expires", value: new Date(ms).toLocaleString(), kind: "datetime" });
2827
- }
2828
- continue;
2829
- }
2830
- let value;
2831
- if (typeof v === "string" || typeof v === "number") value = String(v);
2832
- else if (Array.isArray(v)) value = v.filter((x) => typeof x === "string" || typeof x === "number").join(", ");
2833
- else continue;
2834
- if (!value) continue;
2835
- out.push({ key: k, label: INSTRUCTION_LABELS[k] ?? k, value, kind: "text" });
2836
- }
2837
- return out;
2809
+ function displayValue(field) {
2810
+ if (field.type !== "datetime") return field.value;
2811
+ const at = new Date(field.value);
2812
+ return Number.isNaN(at.getTime()) ? field.value : at.toLocaleString();
2838
2813
  }
2839
2814
  function RampWidgetTemplate({
2840
2815
  theme,
2841
2816
  accentColor,
2817
+ styleOverrides,
2842
2818
  step,
2819
+ flowSteps,
2820
+ flowStepIndex,
2821
+ startingQuoteId,
2843
2822
  direction,
2844
2823
  amount,
2845
2824
  currency,
@@ -2855,6 +2834,8 @@ function RampWidgetTemplate({
2855
2834
  txStatus,
2856
2835
  kycUrl,
2857
2836
  tosUrl,
2837
+ kycBlocking,
2838
+ kycJustApproved,
2858
2839
  stellarTxHash,
2859
2840
  explorerUrl,
2860
2841
  depositInstructions,
@@ -2871,31 +2852,12 @@ function RampWidgetTemplate({
2871
2852
  onOpenKyc,
2872
2853
  onOpenTos,
2873
2854
  onCompleteWithdraw,
2855
+ onBack,
2874
2856
  onRetry,
2875
2857
  onRefresh,
2876
2858
  onClose
2877
2859
  }) {
2878
- const isDark = theme === "dark";
2879
- const cssVars = {
2880
- "--pollar-accent": accentColor,
2881
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
2882
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
2883
- "--pollar-text": isDark ? "#ffffff" : "#111827",
2884
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
2885
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
2886
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
2887
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
2888
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
2889
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
2890
- "--pollar-buttons-border-radius": "6px",
2891
- "--pollar-buttons-height": "44px",
2892
- "--pollar-input-height": "44px",
2893
- "--pollar-input-border-radius": "0.5rem",
2894
- "--pollar-card-border-radius": "10px",
2895
- "--pollar-modal-padding": "2rem",
2896
- "--pollar-modal-heading-size": "1.375rem",
2897
- "--pollar-modal-subtitle-size": "0.9rem"
2898
- };
2860
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides, "hero");
2899
2861
  const stepTitle = {
2900
2862
  input: direction === "onramp" ? "Buy crypto" : "Sell crypto",
2901
2863
  loading_quote: "Finding best route",
@@ -2908,7 +2870,11 @@ function RampWidgetTemplate({
2908
2870
  input: direction === "onramp" ? "Enter the amount you want to deposit" : "Enter the amount you want to withdraw",
2909
2871
  loading_quote: "Comparing providers in real time\u2026",
2910
2872
  select_route: "All prices include fees",
2911
- contact: `${provider || "This provider"} needs your name and email to verify you`,
2873
+ // What this step actually asks for is whatever the route declared, and that
2874
+ // is rarely a name and an email: an Abroad off-ramp asks where to send the
2875
+ // money, not who you are. Say which, rather than asserting the Bridge case
2876
+ // over every provider.
2877
+ contact: `${provider || "This provider"} needs ${listOf(requiredFields.filter((f) => !f.optional).map((f) => f.label.toLowerCase()))} to continue`,
2912
2878
  status: `Finish the flow at ${provider || "the provider"} to continue`,
2913
2879
  error: "Please try again"
2914
2880
  };
@@ -2943,6 +2909,26 @@ function RampWidgetTemplate({
2943
2909
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
2944
2910
  ] })
2945
2911
  ] }),
2912
+ flowStepIndex >= 0 && flowSteps.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-steps", children: [
2913
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "pollar-ramp-steps-label", children: [
2914
+ "Step ",
2915
+ flowStepIndex + 1,
2916
+ " of ",
2917
+ flowSteps.length
2918
+ ] }),
2919
+ /* @__PURE__ */ jsxRuntime.jsx(
2920
+ "div",
2921
+ {
2922
+ className: "pollar-ramp-steps-track",
2923
+ role: "progressbar",
2924
+ "aria-valuenow": flowStepIndex + 1,
2925
+ "aria-valuemin": 1,
2926
+ "aria-valuemax": flowSteps.length,
2927
+ "aria-label": flowSteps[flowStepIndex],
2928
+ children: flowSteps.map((label, i) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-steps-segment", "data-done": i <= flowStepIndex || void 0 }, label))
2929
+ }
2930
+ )
2931
+ ] }),
2946
2932
  step === "input" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2947
2933
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-tabs", children: [
2948
2934
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2993,7 +2979,8 @@ function RampWidgetTemplate({
2993
2979
  min: "0",
2994
2980
  onChange: (e) => onAmountChange(e.target.value)
2995
2981
  }
2996
- )
2982
+ ),
2983
+ errorMsg && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-field-error", children: errorMsg })
2997
2984
  ] }),
2998
2985
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-actions", children: [
2999
2986
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: "Cancel" }),
@@ -3017,12 +3004,25 @@ function RampWidgetTemplate({
3017
3004
  ] }, i))
3018
3005
  ] }),
3019
3006
  step === "select_route" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3020
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-route-list", children: quotes.map((q, i) => /* @__PURE__ */ jsxRuntime.jsx(RouteDisplay, { quote: q, onSelect: onSelectQuote }, i)) }),
3021
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: "Cancel" })
3007
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-route-list", children: quotes.map((q, i) => /* @__PURE__ */ jsxRuntime.jsx(
3008
+ RouteDisplay,
3009
+ {
3010
+ quote: q,
3011
+ busy: startingQuoteId != null && q.quoteId === startingQuoteId,
3012
+ disabled: startingQuoteId != null && q.quoteId !== startingQuoteId,
3013
+ onSelect: onSelectQuote
3014
+ },
3015
+ i
3016
+ )) }),
3017
+ errorMsg && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "pollar-ramp-payment-note", style: { color: "var(--pollar-error-text)" }, children: errorMsg }),
3018
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: errorMsg ? "pollar-btn-primary" : "pollar-btn-secondary", onClick: onBack, children: "Back" })
3022
3019
  ] }),
3023
3020
  step === "contact" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3024
3021
  requiredFields.map((f) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-field", children: [
3025
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "pollar-ramp-label", children: f.label }),
3022
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "pollar-ramp-label", children: [
3023
+ f.label,
3024
+ f.optional && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-field-optional", children: " (optional)" })
3025
+ ] }),
3026
3026
  f.type === "select" ? /* @__PURE__ */ jsxRuntime.jsxs(
3027
3027
  "select",
3028
3028
  {
@@ -3040,14 +3040,16 @@ function RampWidgetTemplate({
3040
3040
  type: f.type,
3041
3041
  className: "pollar-ramp-input",
3042
3042
  value: fieldValues[f.key] ?? "",
3043
+ placeholder: placeholderFor(f, requiredFields, fieldValues),
3043
3044
  autoComplete: f.type === "email" ? "email" : "off",
3044
3045
  onChange: (e) => onFieldChange(f.key, e.target.value)
3045
3046
  }
3046
3047
  ),
3048
+ f.hint && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-field-hint", children: f.hint }),
3047
3049
  f.type === "email" && (fieldValues[f.key] ?? "").trim() !== "" && !isFieldValid(f, fieldValues[f.key]) && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-field-error", children: "Enter a valid email address." })
3048
3050
  ] }, f.key)),
3049
3051
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-actions", children: [
3050
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onRetry, children: "Back" }),
3052
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onBack, children: "Back" }),
3051
3053
  /* @__PURE__ */ jsxRuntime.jsx(
3052
3054
  "button",
3053
3055
  {
@@ -3119,20 +3121,44 @@ function RampWidgetTemplate({
3119
3121
  )
3120
3122
  ] })
3121
3123
  ] }),
3122
- depositInstructions && txStatus !== "completed" && flattenInstructions(depositInstructions).map(({ key, label, value, kind }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment-field", children: [
3123
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-payment-label", children: label }),
3124
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-payment-value", children: kind === "qr" ? /* @__PURE__ */ jsxRuntime.jsx(
3124
+ depositInstructions?.scannable && txStatus !== "completed" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment-field", children: [
3125
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-payment-label", children: "Payment QR" }),
3126
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-payment-value", children: depositInstructions.scannable.image.inlineSafe ? /* @__PURE__ */ jsxRuntime.jsx(
3127
+ "div",
3128
+ {
3129
+ className: "pollar-ramp-qr",
3130
+ "aria-label": "Payment QR",
3131
+ dangerouslySetInnerHTML: { __html: depositInstructions.scannable.image.data }
3132
+ }
3133
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
3125
3134
  "img",
3126
3135
  {
3127
- src: value.startsWith("data:") ? value : `data:image/png;base64,${value}`,
3128
- alt: label,
3136
+ src: `data:${depositInstructions.scannable.image.mediaType};base64,${depositInstructions.scannable.image.data}`,
3137
+ alt: "Payment QR",
3129
3138
  style: { width: "100%", maxWidth: 220, height: "auto", display: "block", margin: "0 auto" }
3130
3139
  }
3131
- ) : kind === "datetime" ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: value }) : /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
3132
- /* @__PURE__ */ jsxRuntime.jsx("code", { style: { flex: 1, wordBreak: "break-all" }, children: value }),
3133
- /* @__PURE__ */ jsxRuntime.jsx(CopyButton, { value, label: `Copy ${label}` })
3140
+ ) })
3141
+ ] }),
3142
+ depositInstructions?.scannable?.payload && depositInstructions.scannable.payloadLabel && txStatus !== "completed" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment-field", children: [
3143
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-payment-label", children: depositInstructions.scannable.payloadLabel }),
3144
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-payment-value", children: /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
3145
+ /* @__PURE__ */ jsxRuntime.jsx("code", { style: { flex: 1, wordBreak: "break-all" }, children: depositInstructions.scannable.payload }),
3146
+ /* @__PURE__ */ jsxRuntime.jsx(
3147
+ CopyButton,
3148
+ {
3149
+ value: depositInstructions.scannable.payload,
3150
+ label: `Copy ${depositInstructions.scannable.payloadLabel}`
3151
+ }
3152
+ )
3134
3153
  ] }) })
3135
- ] }, key)),
3154
+ ] }),
3155
+ depositInstructions && txStatus !== "completed" && depositInstructions.fields.map((f) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment-field", children: [
3156
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-payment-label", children: f.label }),
3157
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-payment-value", children: f.copyable ? /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
3158
+ /* @__PURE__ */ jsxRuntime.jsx("code", { style: { flex: 1, wordBreak: "break-all" }, children: f.value }),
3159
+ /* @__PURE__ */ jsxRuntime.jsx(CopyButton, { value: f.value, label: `Copy ${f.label}` })
3160
+ ] }) : f.type === "url" ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: f.value, target: "_blank", rel: "noopener noreferrer", children: f.value }) : /* @__PURE__ */ jsxRuntime.jsx("span", { children: displayValue(f) }) })
3161
+ ] }, f.key)),
3136
3162
  tosUrl && !depositInstructions && txStatus !== "completed" && /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "pollar-btn-primary", onClick: onOpenTos, children: [
3137
3163
  "Accept terms at ",
3138
3164
  provider
@@ -3141,6 +3167,16 @@ function RampWidgetTemplate({
3141
3167
  "Continue at ",
3142
3168
  provider
3143
3169
  ] }),
3170
+ kycBlocking && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "pollar-ramp-payment-note", children: [
3171
+ provider,
3172
+ " needs to verify your identity before this payout. Nothing has been sent yet \u2014 complete verification with ",
3173
+ provider,
3174
+ ", and this will update on its own."
3175
+ ] }),
3176
+ kycJustApproved && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "pollar-ramp-payment-note", children: [
3177
+ provider,
3178
+ " approved your verification. Request a new quote to continue \u2014 the previous one was consumed."
3179
+ ] }),
3144
3180
  canComplete && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-primary", disabled: completing, onClick: onCompleteWithdraw, children: completing ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3145
3181
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-spinner pollar-spinner-sm" }),
3146
3182
  "Submitting\u2026"
@@ -3161,11 +3197,53 @@ var TERMINAL = ["completed", "failed"];
3161
3197
  function requiredFieldsOf(quote) {
3162
3198
  return quote.requiredFields ?? [];
3163
3199
  }
3200
+ function flowStepsOf(quotes, selected) {
3201
+ const needsDetails = selected != null ? requiredFieldsOf(selected).length > 0 : quotes.some((q) => requiredFieldsOf(q).length > 0);
3202
+ return needsDetails ? ["Amount", "Provider", "Details", "Complete"] : ["Amount", "Provider", "Complete"];
3203
+ }
3204
+ var STEP_LABEL = {
3205
+ input: "Amount",
3206
+ loading_quote: "Amount",
3207
+ select_route: "Provider",
3208
+ contact: "Details",
3209
+ status: "Complete"
3210
+ };
3211
+ function brokenLimitOf(amount, quote) {
3212
+ const { minAmount, maxAmount } = quote;
3213
+ if (!Number.isFinite(amount)) return null;
3214
+ if (minAmount != null && amount < minAmount) return { limit: "min", value: minAmount };
3215
+ if (maxAmount != null && amount > maxAmount) return { limit: "max", value: maxAmount };
3216
+ return null;
3217
+ }
3218
+ function limitMessage(limit, value, currency) {
3219
+ return `The ${limit === "min" ? "minimum" : "maximum"} amount for this route is ${value} ${currency}.`;
3220
+ }
3221
+ var RAMP_ERROR_MESSAGES = {
3222
+ SDK_RAMPS_QUOTE_EXPIRED: "This quote expired. Request a new one and try again.",
3223
+ SDK_RAMPS_ASSET_NOT_ENABLED: "This currency is not available for that route right now.",
3224
+ SDK_RAMPS_KYC_REQUIRED: "The provider needs to verify your identity before continuing.",
3225
+ SDK_RAMPS_WALLET_UNSUPPORTED: "This wallet type cannot be used for this ramp.",
3226
+ SDK_RAMPS_PROVIDER_NOT_CONFIGURED: "This ramp provider is not configured for this app yet.",
3227
+ SDK_RAMPS_ANCHOR_ERROR: "The provider rejected the request. Please try again in a moment.",
3228
+ SDK_RAMPS_BRIDGE_ERROR: "The provider rejected the request. Please try again in a moment."
3229
+ };
3230
+ function rampErrorMessage(e, fallback) {
3231
+ const body = e?.body;
3232
+ const code = e?.code;
3233
+ if (typeof code !== "string") return e instanceof Error ? e.message : fallback;
3234
+ const limit = body?.limit;
3235
+ const value = body?.limitAmount;
3236
+ const currency = body?.limitCurrency;
3237
+ if ((limit === "min" || limit === "max") && typeof value === "number" && typeof currency === "string") {
3238
+ return limitMessage(limit, value, currency);
3239
+ }
3240
+ return RAMP_ERROR_MESSAGES[code] ?? (e instanceof Error ? e.message : fallback);
3241
+ }
3164
3242
  function RampWidget({ onClose }) {
3165
3243
  const { getClient, signTx, wallet, styles, network } = usePollar();
3166
3244
  const walletAddress = wallet?.address ?? "";
3167
3245
  const client = getClient();
3168
- const { theme = "light", accentColor = "#005DB4" } = styles;
3246
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
3169
3247
  const [step, setStep] = react.useState("input");
3170
3248
  const [direction, setDirection] = react.useState("onramp");
3171
3249
  const [amount, setAmount] = react.useState("");
@@ -3183,6 +3261,8 @@ function RampWidget({ onClose }) {
3183
3261
  const [provider, setProvider] = react.useState("");
3184
3262
  const [kycUrl, setKycUrl] = react.useState(null);
3185
3263
  const [tosUrl, setTosUrl] = react.useState(null);
3264
+ const [kycPending, setKycPending] = react.useState(false);
3265
+ const [kycApproved, setKycApproved] = react.useState(false);
3186
3266
  const [txStatus, setTxStatus] = react.useState(null);
3187
3267
  const [stellarTxHash, setStellarTxHash] = react.useState(null);
3188
3268
  const [depositInstructions, setDepositInstructions] = react.useState(null);
@@ -3211,6 +3291,23 @@ function RampWidget({ onClose }) {
3211
3291
  clearInterval(id);
3212
3292
  };
3213
3293
  }, [step, txId, txStatus, client]);
3294
+ react.useEffect(() => {
3295
+ if (step !== "status" || !kycPending || kycApproved) return;
3296
+ let active = true;
3297
+ const check = async () => {
3298
+ try {
3299
+ const { hasApproved } = await client.getRampKycStatus();
3300
+ if (active && hasApproved) setKycApproved(true);
3301
+ } catch {
3302
+ }
3303
+ };
3304
+ void check();
3305
+ const id = setInterval(check, 1e4);
3306
+ return () => {
3307
+ active = false;
3308
+ clearInterval(id);
3309
+ };
3310
+ }, [step, kycPending, kycApproved, client]);
3214
3311
  async function loadCountries(resetSelection) {
3215
3312
  setCountriesLoading(true);
3216
3313
  try {
@@ -3256,7 +3353,7 @@ function RampWidget({ onClose }) {
3256
3353
  setRefreshing(false);
3257
3354
  }
3258
3355
  }
3259
- function resetToInput() {
3356
+ function resetToInput({ keepMessage = false } = {}) {
3260
3357
  setStep("input");
3261
3358
  setQuotes([]);
3262
3359
  setSelectedQuote(null);
@@ -3268,7 +3365,7 @@ function RampWidget({ onClose }) {
3268
3365
  setTxStatus(null);
3269
3366
  setStellarTxHash(null);
3270
3367
  setDepositInstructions(null);
3271
- setErrorMsg(null);
3368
+ if (!keepMessage) setErrorMsg(null);
3272
3369
  }
3273
3370
  async function resumeWithSignature(id, ps) {
3274
3371
  const outcome = await signTx(ps.unsignedXdr);
@@ -3292,6 +3389,8 @@ function RampWidget({ onClose }) {
3292
3389
  }
3293
3390
  setKycUrl(result.kycUrl ?? null);
3294
3391
  setTosUrl(result.tosUrl ?? null);
3392
+ setKycPending(result.kycRequired === true);
3393
+ if (result.kycRequired) setKycApproved(false);
3295
3394
  setTxStatus(result.status);
3296
3395
  setStellarTxHash(result.stellarTxHash ?? null);
3297
3396
  setDepositInstructions(result.depositInstructions ?? null);
@@ -3312,7 +3411,7 @@ function RampWidget({ onClose }) {
3312
3411
  setQuotes(list);
3313
3412
  setStep("select_route");
3314
3413
  } catch (e) {
3315
- setErrorMsg(e instanceof Error ? e.message : "Failed to fetch quotes.");
3414
+ setErrorMsg(rampErrorMessage(e, "Failed to fetch quotes."));
3316
3415
  setStep("error");
3317
3416
  } finally {
3318
3417
  setIsLoading(false);
@@ -3321,8 +3420,13 @@ function RampWidget({ onClose }) {
3321
3420
  function handleSelectQuote(quote) {
3322
3421
  setSelectedQuote(quote);
3323
3422
  setErrorMsg(null);
3423
+ const broken = brokenLimitOf(Number(amount), quote);
3424
+ if (broken) {
3425
+ setErrorMsg(limitMessage(broken.limit, broken.value, currency));
3426
+ return;
3427
+ }
3324
3428
  const fields = requiredFieldsOf(quote);
3325
- const missing = fields.some((f) => !(fieldValues[f.key] ?? "").trim());
3429
+ const missing = fields.some((f) => !f.optional && !(fieldValues[f.key] ?? "").trim());
3326
3430
  if (fields.length > 0 && missing) {
3327
3431
  setStep("contact");
3328
3432
  return;
@@ -3351,7 +3455,7 @@ function RampWidget({ onClose }) {
3351
3455
  const result = direction === "onramp" ? await client.createOnRamp(base) : await client.createOffRamp(base);
3352
3456
  await applyResult(result);
3353
3457
  } catch (e) {
3354
- setErrorMsg(e instanceof Error ? e.message : "Failed to start the ramp.");
3458
+ setErrorMsg(rampErrorMessage(e, "Failed to start the ramp."));
3355
3459
  setStep("error");
3356
3460
  } finally {
3357
3461
  setIsLoading(false);
@@ -3384,13 +3488,20 @@ function RampWidget({ onClose }) {
3384
3488
  setCompleting(false);
3385
3489
  }
3386
3490
  }
3387
- const canComplete = direction === "offramp" && step === "status" && txStatus !== "completed" && !stellarTxHash;
3388
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
3491
+ const kycBlocking = kycPending && !kycApproved;
3492
+ const canComplete = direction === "offramp" && step === "status" && txStatus !== "completed" && !stellarTxHash && !kycBlocking;
3493
+ const flowSteps = flowStepsOf(quotes, selectedQuote);
3494
+ const flowStepIndex = flowSteps.indexOf(STEP_LABEL[step] ?? "");
3495
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
3389
3496
  RampWidgetTemplate,
3390
3497
  {
3391
3498
  theme,
3392
3499
  accentColor,
3500
+ styleOverrides,
3393
3501
  step,
3502
+ flowSteps,
3503
+ flowStepIndex,
3504
+ startingQuoteId: isLoading && selectedQuote ? selectedQuote.quoteId : null,
3394
3505
  direction,
3395
3506
  amount,
3396
3507
  currency,
@@ -3406,6 +3517,8 @@ function RampWidget({ onClose }) {
3406
3517
  txStatus,
3407
3518
  kycUrl,
3408
3519
  tosUrl,
3520
+ kycBlocking,
3521
+ kycJustApproved: kycPending && kycApproved,
3409
3522
  stellarTxHash,
3410
3523
  explorerUrl: stellarTxHash ? `https://stellar.expert/explorer/${network === "mainnet" ? "public" : "testnet"}/tx/${stellarTxHash}` : null,
3411
3524
  depositInstructions,
@@ -3413,7 +3526,10 @@ function RampWidget({ onClose }) {
3413
3526
  completing,
3414
3527
  errorMsg,
3415
3528
  onDirectionChange: setDirection,
3416
- onAmountChange: setAmount,
3529
+ onAmountChange: (next) => {
3530
+ setAmount(next);
3531
+ setErrorMsg(null);
3532
+ },
3417
3533
  onFieldChange: setFieldValue,
3418
3534
  onCountryChange: handleCountryChange,
3419
3535
  onFindRoute: handleFindRoute,
@@ -3422,7 +3538,8 @@ function RampWidget({ onClose }) {
3422
3538
  onOpenKyc: handleOpenKyc,
3423
3539
  onOpenTos: handleOpenTos,
3424
3540
  onCompleteWithdraw: handleCompleteWithdraw,
3425
- onRetry: resetToInput,
3541
+ onBack: () => resetToInput({ keepMessage: true }),
3542
+ onRetry: () => resetToInput(),
3426
3543
  onRefresh: handleRefresh,
3427
3544
  onClose
3428
3545
  }
@@ -3479,6 +3596,7 @@ var CHAIN_NAME = {
3479
3596
  function ReceiveModalTemplate({
3480
3597
  theme,
3481
3598
  accentColor,
3599
+ styleOverrides,
3482
3600
  walletAddress,
3483
3601
  chains,
3484
3602
  selectedChain,
@@ -3488,23 +3606,7 @@ function ReceiveModalTemplate({
3488
3606
  onClose
3489
3607
  }) {
3490
3608
  const isDark = theme === "dark";
3491
- const cssVars = {
3492
- "--pollar-accent": accentColor,
3493
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
3494
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
3495
- "--pollar-text": isDark ? "#ffffff" : "#111827",
3496
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
3497
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
3498
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
3499
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
3500
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
3501
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
3502
- "--pollar-buttons-border-radius": "6px",
3503
- "--pollar-buttons-height": "44px",
3504
- "--pollar-input-height": "44px",
3505
- "--pollar-input-border-radius": "0.5rem",
3506
- "--pollar-card-border-radius": "10px"
3507
- };
3609
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
3508
3610
  const chainName = selectedChain ? CHAIN_NAME[selectedChain] ?? selectedChain : "wallet";
3509
3611
  return /* @__PURE__ */ jsxRuntime.jsxs(
3510
3612
  "div",
@@ -3569,7 +3671,7 @@ function ReceiveModalTemplate({
3569
3671
  }
3570
3672
  function ReceiveModal({ onClose }) {
3571
3673
  const { wallets, styles } = usePollar();
3572
- const { theme = "light", accentColor = "#005DB4" } = styles;
3674
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
3573
3675
  const [copied, setCopied] = react.useState(false);
3574
3676
  const copyTimerRef = react.useRef(null);
3575
3677
  const { chains } = useChains();
@@ -3595,11 +3697,12 @@ function ReceiveModal({ onClose }) {
3595
3697
  }, 2e3);
3596
3698
  });
3597
3699
  }
3598
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
3700
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
3599
3701
  ReceiveModalTemplate,
3600
3702
  {
3601
3703
  theme,
3602
3704
  accentColor,
3705
+ styleOverrides,
3603
3706
  walletAddress,
3604
3707
  chains,
3605
3708
  selectedChain,
@@ -3862,6 +3965,7 @@ function assetKey3(record) {
3862
3965
  function SendModalTemplate({
3863
3966
  theme,
3864
3967
  accentColor,
3968
+ styleOverrides,
3865
3969
  step,
3866
3970
  txTitle,
3867
3971
  assets,
@@ -3895,24 +3999,7 @@ function SendModalTemplate({
3895
3999
  onRetry,
3896
4000
  onDone
3897
4001
  }) {
3898
- const isDark = theme === "dark";
3899
- const cssVars = {
3900
- "--pollar-accent": accentColor,
3901
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
3902
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
3903
- "--pollar-text": isDark ? "#ffffff" : "#111827",
3904
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
3905
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
3906
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
3907
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
3908
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
3909
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
3910
- "--pollar-buttons-border-radius": "6px",
3911
- "--pollar-buttons-height": "44px",
3912
- "--pollar-input-height": "44px",
3913
- "--pollar-input-border-radius": "0.5rem",
3914
- "--pollar-card-border-radius": "10px"
3915
- };
4002
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
3916
4003
  const selectedKey = selectedAsset ? assetKey3(selectedAsset) : "";
3917
4004
  const canSubmit = canSendOnChain && !!selectedAsset && !!amount && !!destination.trim() && !isLoadingBalance;
3918
4005
  const title = step === "form" ? "Send" : txTitle;
@@ -3982,7 +4069,7 @@ function SendModalTemplate({
3982
4069
  options: assets.map((a) => ({
3983
4070
  key: assetKey3(a),
3984
4071
  code: a.code,
3985
- // Unreadable (null) drops the "— X available" suffix instead of
4072
+ // Unreadable (null) drops the "- X available" suffix instead of
3986
4073
  // claiming a zero balance.
3987
4074
  available: a.available ?? void 0,
3988
4075
  enabledInApp: a.enabledInApp
@@ -4072,7 +4159,7 @@ function SendModal({ onClose }) {
4072
4159
  styles
4073
4160
  } = usePollar();
4074
4161
  const walletType = wallet?.custody === "external" ? wallet.provider : null;
4075
- const { theme = "light", accentColor = "#005DB4" } = styles;
4162
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
4076
4163
  const [step, setStep] = react.useState("form");
4077
4164
  const [amount, setAmount] = react.useState("");
4078
4165
  const [destination, setDestination] = react.useState("");
@@ -4205,11 +4292,12 @@ function SendModal({ onClose }) {
4205
4292
  setShowXdr(false);
4206
4293
  setCopied(false);
4207
4294
  }
4208
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: !isInProgress ? onClose : void 0, children: /* @__PURE__ */ jsxRuntime.jsx(
4295
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: !isInProgress ? onClose : void 0, children: /* @__PURE__ */ jsxRuntime.jsx(
4209
4296
  SendModalTemplate,
4210
4297
  {
4211
4298
  theme,
4212
4299
  accentColor,
4300
+ styleOverrides,
4213
4301
  step,
4214
4302
  txTitle,
4215
4303
  assets: sortedAssets,
@@ -4261,6 +4349,7 @@ function formatAmount3(value) {
4261
4349
  function SwapModalTemplate({
4262
4350
  theme,
4263
4351
  accentColor,
4352
+ styleOverrides,
4264
4353
  step,
4265
4354
  txTitle,
4266
4355
  sellOptions,
@@ -4300,7 +4389,6 @@ function SwapModalTemplate({
4300
4389
  onRetry,
4301
4390
  onDone
4302
4391
  }) {
4303
- const isDark = theme === "dark";
4304
4392
  const [customOpen, setCustomOpen] = react.useState(false);
4305
4393
  const [customCode, setCustomCode] = react.useState("");
4306
4394
  const [customIssuer, setCustomIssuer] = react.useState("");
@@ -4316,23 +4404,7 @@ function SwapModalTemplate({
4316
4404
  setCustomError("");
4317
4405
  setCustomOpen(false);
4318
4406
  };
4319
- const cssVars = {
4320
- "--pollar-accent": accentColor,
4321
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
4322
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
4323
- "--pollar-text": isDark ? "#ffffff" : "#111827",
4324
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
4325
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
4326
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
4327
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
4328
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
4329
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
4330
- "--pollar-buttons-border-radius": "6px",
4331
- "--pollar-buttons-height": "44px",
4332
- "--pollar-input-height": "44px",
4333
- "--pollar-input-border-radius": "0.5rem",
4334
- "--pollar-card-border-radius": "10px"
4335
- };
4407
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
4336
4408
  const sellKey = selectedSell ? assetOptionKey(selectedSell) : "";
4337
4409
  const buyKey = selectedBuy ? assetOptionKey(selectedBuy) : "";
4338
4410
  const canSwap = !smartUnsupported && !!selectedSell && !!selectedBuy && !!amount && !!quote && !quoteLoading && !isLoadingData;
@@ -4594,7 +4666,7 @@ function SwapModal({ onClose }) {
4594
4666
  } = usePollar();
4595
4667
  const walletType = wallet?.custody === "external" ? wallet.provider : null;
4596
4668
  const smartUnsupported = wallet?.custody === "smart";
4597
- const { theme = "light", accentColor = "#005DB4" } = styles;
4669
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
4598
4670
  const [step, setStep] = react.useState("form");
4599
4671
  const [selectedSell, setSelectedSell] = react.useState(null);
4600
4672
  const [selectedBuy, setSelectedBuy] = react.useState(null);
@@ -4799,11 +4871,12 @@ function SwapModal({ onClose }) {
4799
4871
  setShowXdr(false);
4800
4872
  setCopied(false);
4801
4873
  }
4802
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: !isInProgress ? onClose : void 0, children: /* @__PURE__ */ jsxRuntime.jsx(
4874
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: !isInProgress ? onClose : void 0, children: /* @__PURE__ */ jsxRuntime.jsx(
4803
4875
  SwapModalTemplate,
4804
4876
  {
4805
4877
  theme,
4806
4878
  accentColor,
4879
+ styleOverrides,
4807
4880
  step,
4808
4881
  txTitle,
4809
4882
  sellOptions,
@@ -4881,8 +4954,7 @@ function EarnModal({ onClose }) {
4881
4954
  } = usePollar();
4882
4955
  const walletType = wallet?.custody === "external" ? wallet.provider : null;
4883
4956
  const smartUnsupported = wallet?.custody === "smart";
4884
- const { theme = "light", accentColor = "#005DB4" } = styles;
4885
- const isDark = theme === "dark";
4957
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
4886
4958
  const [step, setStep] = react.useState("form");
4887
4959
  const [tab, setTab] = react.useState("deposit");
4888
4960
  const [providers, setProviders] = react.useState(null);
@@ -4978,23 +5050,7 @@ function EarnModal({ onClose }) {
4978
5050
  const parsedAmount = parseFloat(amount);
4979
5051
  const exceedsAvailable = tab === "deposit" ? walletAvailable != null && parsedAmount > parseFloat(walletAvailable) : position != null && parsedAmount > parseFloat(position.withdrawable);
4980
5052
  const canSubmit = !smartUnsupported && !!provider && !!opportunityId && !!amount && parsedAmount > 0 && !exceedsAvailable && !providersLoading && !loadingOpps;
4981
- const cssVars = {
4982
- "--pollar-accent": accentColor,
4983
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
4984
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
4985
- "--pollar-text": isDark ? "#ffffff" : "#111827",
4986
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
4987
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
4988
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
4989
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
4990
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
4991
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
4992
- "--pollar-buttons-border-radius": "6px",
4993
- "--pollar-buttons-height": "44px",
4994
- "--pollar-input-height": "44px",
4995
- "--pollar-input-border-radius": "0.5rem",
4996
- "--pollar-card-border-radius": "10px"
4997
- };
5053
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
4998
5054
  async function handleSubmit() {
4999
5055
  setFormError("");
5000
5056
  if (smartUnsupported) {
@@ -5073,7 +5129,7 @@ function EarnModal({ onClose }) {
5073
5129
  }
5074
5130
  }
5075
5131
  const title = step === "form" ? "Earn" : txTitle;
5076
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: !isInProgress ? onClose : void 0, children: /* @__PURE__ */ jsxRuntime.jsxs(
5132
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: !isInProgress ? onClose : void 0, children: /* @__PURE__ */ jsxRuntime.jsxs(
5077
5133
  "div",
5078
5134
  {
5079
5135
  className: "pollar-modal-card pollar-send-modal",
@@ -5321,6 +5377,7 @@ function shortIp(hash) {
5321
5377
  function SessionsModalTemplate({
5322
5378
  theme,
5323
5379
  accentColor,
5380
+ styleOverrides,
5324
5381
  state,
5325
5382
  revokingFamilyId,
5326
5383
  signingOutEverywhere,
@@ -5329,24 +5386,7 @@ function SessionsModalTemplate({
5329
5386
  onLogoutEverywhere,
5330
5387
  onClose
5331
5388
  }) {
5332
- const isDark = theme === "dark";
5333
- const cssVars = {
5334
- "--pollar-accent": accentColor,
5335
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
5336
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
5337
- "--pollar-text": isDark ? "#ffffff" : "#111827",
5338
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
5339
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
5340
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
5341
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
5342
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
5343
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
5344
- "--pollar-buttons-border-radius": "6px",
5345
- "--pollar-buttons-height": "44px",
5346
- "--pollar-input-height": "44px",
5347
- "--pollar-input-border-radius": "0.5rem",
5348
- "--pollar-card-border-radius": "10px"
5349
- };
5389
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
5350
5390
  const isLoading = state.step === "loading";
5351
5391
  const sessions = state.step === "loaded" ? state.sessions : [];
5352
5392
  const otherCount = sessions.filter((s) => !s.current).length;
@@ -5458,7 +5498,7 @@ function SessionsModalTemplate({
5458
5498
  }
5459
5499
  function SessionsModal({ onClose }) {
5460
5500
  const { getClient, styles, sessions } = usePollar();
5461
- const { theme = "light", accentColor = "#005DB4" } = styles;
5501
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
5462
5502
  const [revokingFamilyId, setRevokingFamilyId] = react.useState(null);
5463
5503
  const [signingOutEverywhere, setSigningOutEverywhere] = react.useState(false);
5464
5504
  const load = react.useCallback(() => {
@@ -5496,11 +5536,12 @@ function SessionsModal({ onClose }) {
5496
5536
  setSigningOutEverywhere(false);
5497
5537
  }
5498
5538
  }, [getClient, onClose]);
5499
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
5539
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
5500
5540
  SessionsModalTemplate,
5501
5541
  {
5502
5542
  theme,
5503
5543
  accentColor,
5544
+ styleOverrides,
5504
5545
  state: sessions,
5505
5546
  revokingFamilyId,
5506
5547
  signingOutEverywhere,
@@ -5514,6 +5555,7 @@ function SessionsModal({ onClose }) {
5514
5555
  function TransactionModalTemplate({
5515
5556
  theme,
5516
5557
  accentColor,
5558
+ styleOverrides,
5517
5559
  transaction,
5518
5560
  showXdr,
5519
5561
  copied,
@@ -5525,24 +5567,7 @@ function TransactionModalTemplate({
5525
5567
  onCopyHash,
5526
5568
  onRetry
5527
5569
  }) {
5528
- const isDark = theme === "dark";
5529
- const cssVars = {
5530
- "--pollar-accent": accentColor,
5531
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
5532
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
5533
- "--pollar-text": isDark ? "#ffffff" : "#111827",
5534
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
5535
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
5536
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
5537
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
5538
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
5539
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
5540
- "--pollar-buttons-border-radius": "6px",
5541
- "--pollar-buttons-height": "44px",
5542
- "--pollar-input-height": "44px",
5543
- "--pollar-input-border-radius": "0.5rem",
5544
- "--pollar-card-border-radius": "10px"
5545
- };
5570
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
5546
5571
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-card pollar-tx-modal", "data-theme": theme, style: cssVars, onClick: (e) => e.stopPropagation(), children: [
5547
5572
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-header", children: /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "pollar-modal-title", children: "Transaction" }) }),
5548
5573
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-close-btn", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -5581,7 +5606,7 @@ function TransactionModalTemplate({
5581
5606
  function TransactionModal({ onClose }) {
5582
5607
  const { getClient, styles, tx: transaction, network, wallet } = usePollar();
5583
5608
  const walletType = wallet?.custody === "external" ? wallet.provider : null;
5584
- const { theme = "light", accentColor = "#005DB4" } = styles;
5609
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
5585
5610
  const [showXdr, setShowXdr] = react.useState(false);
5586
5611
  const [copied, setCopied] = react.useState(false);
5587
5612
  const copyTimerRef = react.useRef(null);
@@ -5616,11 +5641,12 @@ function TransactionModal({ onClose }) {
5616
5641
  await getClient().signAndSubmitTx(transaction.buildData.unsignedXdr);
5617
5642
  }
5618
5643
  }
5619
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
5644
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
5620
5645
  TransactionModalTemplate,
5621
5646
  {
5622
5647
  theme,
5623
5648
  accentColor,
5649
+ styleOverrides,
5624
5650
  transaction,
5625
5651
  showXdr,
5626
5652
  copied,
@@ -5677,6 +5703,7 @@ function renderSummary(summary) {
5677
5703
  function TxHistoryModalTemplate({
5678
5704
  theme,
5679
5705
  accentColor,
5706
+ styleOverrides,
5680
5707
  txHistory,
5681
5708
  offset,
5682
5709
  chains,
@@ -5688,24 +5715,7 @@ function TxHistoryModalTemplate({
5688
5715
  onNext,
5689
5716
  onClose
5690
5717
  }) {
5691
- const isDark = theme === "dark";
5692
- const cssVars = {
5693
- "--pollar-accent": accentColor,
5694
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
5695
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
5696
- "--pollar-text": isDark ? "#ffffff" : "#111827",
5697
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
5698
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
5699
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
5700
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
5701
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
5702
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
5703
- "--pollar-buttons-border-radius": "6px",
5704
- "--pollar-buttons-height": "44px",
5705
- "--pollar-input-height": "44px",
5706
- "--pollar-input-border-radius": "0.5rem",
5707
- "--pollar-card-border-radius": "10px"
5708
- };
5718
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
5709
5719
  const isLoading = txHistory.step === "loading";
5710
5720
  const records = txHistory.step === "loaded" ? txHistory.data.records : [];
5711
5721
  const total = txHistory.step === "loaded" ? txHistory.data.total : 0;
@@ -5877,7 +5887,7 @@ function TxHistoryModalTemplate({
5877
5887
  var PAGE_SIZE2 = 10;
5878
5888
  function TxHistoryModal({ onClose }) {
5879
5889
  const { getClient, styles, txHistory, wallets } = usePollar();
5880
- const { theme = "light", accentColor = "#005DB4" } = styles;
5890
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
5881
5891
  const [offset, setOffset] = react.useState(0);
5882
5892
  const { chains } = useChains();
5883
5893
  const [selectedChain, setSelectedChain] = react.useState(null);
@@ -5899,11 +5909,12 @@ function TxHistoryModal({ onClose }) {
5899
5909
  const paged = (nextOffset) => {
5900
5910
  if (selectedChain) load(nextOffset, selectedChain);
5901
5911
  };
5902
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
5912
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
5903
5913
  TxHistoryModalTemplate,
5904
5914
  {
5905
5915
  theme,
5906
5916
  accentColor,
5917
+ styleOverrides,
5907
5918
  txHistory,
5908
5919
  offset,
5909
5920
  chains,
@@ -5956,6 +5967,7 @@ function BalanceItem({ record, faucet }) {
5956
5967
  function WalletBalanceModalTemplate({
5957
5968
  theme,
5958
5969
  accentColor,
5970
+ styleOverrides,
5959
5971
  walletBalance,
5960
5972
  walletAddress,
5961
5973
  chains,
@@ -5965,24 +5977,7 @@ function WalletBalanceModalTemplate({
5965
5977
  onRefresh,
5966
5978
  onClose
5967
5979
  }) {
5968
- const isDark = theme === "dark";
5969
- const cssVars = {
5970
- "--pollar-accent": accentColor,
5971
- "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
5972
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
5973
- "--pollar-text": isDark ? "#ffffff" : "#111827",
5974
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
5975
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
5976
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
5977
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
5978
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
5979
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
5980
- "--pollar-buttons-border-radius": "6px",
5981
- "--pollar-buttons-height": "44px",
5982
- "--pollar-input-height": "44px",
5983
- "--pollar-input-border-radius": "0.5rem",
5984
- "--pollar-card-border-radius": "10px"
5985
- };
5980
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
5986
5981
  const isLoading = walletBalance.step === "loading";
5987
5982
  const data = useStickyData(walletBalance.step === "loaded" ? walletBalance.data : null);
5988
5983
  const balances = (data?.balances ?? []).filter((b) => resolveChain(b.chain) === selectedChain);
@@ -6056,7 +6051,7 @@ function WalletBalanceModalTemplate({
6056
6051
  }
6057
6052
  function WalletBalanceModal({ onClose }) {
6058
6053
  const { walletBalance, refreshWalletBalance, wallets, network, styles } = usePollar();
6059
- const { theme = "light", accentColor = "#005DB4" } = styles;
6054
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
6060
6055
  const { chains } = useChains();
6061
6056
  const [selectedChain, setSelectedChain] = react.useState(null);
6062
6057
  react.useEffect(() => {
@@ -6066,11 +6061,12 @@ function WalletBalanceModal({ onClose }) {
6066
6061
  react.useEffect(() => {
6067
6062
  void refreshWalletBalance();
6068
6063
  }, [refreshWalletBalance]);
6069
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
6064
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
6070
6065
  WalletBalanceModalTemplate,
6071
6066
  {
6072
6067
  theme,
6073
6068
  accentColor,
6069
+ styleOverrides,
6074
6070
  walletBalance,
6075
6071
  walletAddress,
6076
6072
  chains,
@@ -6110,7 +6106,7 @@ var browserPasskeyCeremony = async ({ challenge, mode }) => {
6110
6106
  challenge,
6111
6107
  rp: { id: rpId, name: rpId },
6112
6108
  user: { id: userId, name: "Smart Wallet", displayName: "Smart Wallet" },
6113
- // ES256 (secp256r1) — the curve the on-chain WebAuthn verifier expects.
6109
+ // ES256 (secp256r1) - the curve the on-chain WebAuthn verifier expects.
6114
6110
  pubKeyCredParams: [{ type: "public-key", alg: -7 }],
6115
6111
  authenticatorSelection: { residentKey: "required", userVerification: "required" },
6116
6112
  attestation: "none",
@@ -6145,6 +6141,7 @@ function sessionsEqual(a, b) {
6145
6141
  return a.clientSessionId === b.clientSessionId && a.userId === b.userId && a.status === b.status && a.token?.accessToken === b.token?.accessToken && a.token?.refreshToken === b.token?.refreshToken && a.token?.expiresAt === b.token?.expiresAt && a.wallet?.address === b.wallet?.address;
6146
6142
  }
6147
6143
  var PollarContext = react.createContext(null);
6144
+ var clientByConfig = /* @__PURE__ */ new WeakMap();
6148
6145
  function PollarProvider({
6149
6146
  client,
6150
6147
  appConfig: appConfigProp,
@@ -6152,13 +6149,30 @@ function PollarProvider({
6152
6149
  onStorageDegrade,
6153
6150
  children
6154
6151
  }) {
6155
- const [pollarClient] = react.useState(
6156
- () => client instanceof core.PollarClient ? client : new core.PollarClient({ passkey: browserPasskeyCeremony, passkeySign: browserPasskeySigner, ...client })
6157
- );
6158
- const ownsClientRef = react.useRef(!(client instanceof core.PollarClient));
6152
+ const [pollarClient] = react.useState(() => {
6153
+ if (core.isPollarClient(client)) {
6154
+ client.setPasskeyDefaults({ passkey: browserPasskeyCeremony, passkeySign: browserPasskeySigner });
6155
+ return client;
6156
+ }
6157
+ const alreadyBuilt = clientByConfig.get(client);
6158
+ if (alreadyBuilt) return alreadyBuilt;
6159
+ const built = new core.PollarClient({
6160
+ ...client,
6161
+ passkey: client.passkey ?? browserPasskeyCeremony,
6162
+ passkeySign: client.passkeySign ?? browserPasskeySigner
6163
+ });
6164
+ clientByConfig.set(client, built);
6165
+ return built;
6166
+ });
6167
+ const ownsClientRef = react.useRef(!core.isPollarClient(client));
6159
6168
  const destroyTimerRef = react.useRef(null);
6169
+ const builtFromConfigRef = react.useRef(core.isPollarClient(client) ? null : client);
6160
6170
  react.useEffect(() => {
6161
6171
  if (!ownsClientRef.current) return;
6172
+ if (builtFromConfigRef.current) {
6173
+ clientByConfig.delete(builtFromConfigRef.current);
6174
+ builtFromConfigRef.current = null;
6175
+ }
6162
6176
  if (destroyTimerRef.current) {
6163
6177
  clearTimeout(destroyTimerRef.current);
6164
6178
  destroyTimerRef.current = null;
@@ -6802,7 +6816,6 @@ function WalletButton() {
6802
6816
  } = usePollar();
6803
6817
  const { primaryAddress } = useChains();
6804
6818
  const walletAddress = primaryAddress || (wallet?.address ?? "");
6805
- const walletType = wallet?.custody === "external" ? wallet.provider : null;
6806
6819
  const [open, setOpen] = react.useState(false);
6807
6820
  const [copied, setCopied] = react.useState(false);
6808
6821
  const [creating, setCreating] = react.useState(false);
@@ -6903,7 +6916,6 @@ function WalletButton() {
6903
6916
  itemColor,
6904
6917
  wrapperRef,
6905
6918
  isInProgress,
6906
- walletType,
6907
6919
  showCreateAccount: canCreateAccount,
6908
6920
  creatingAccount: creating,
6909
6921
  onToggleOpen: () => setOpen((v) => !v),
@@ -6953,8 +6965,13 @@ exports.WalletBalanceModalTemplate = WalletBalanceModalTemplate;
6953
6965
  exports.WalletButton = WalletButton;
6954
6966
  exports.WalletButtonTemplate = WalletButtonTemplate;
6955
6967
  exports.addressForChain = addressForChain;
6968
+ exports.browserPasskeyCeremony = browserPasskeyCeremony;
6969
+ exports.browserPasskeySigner = browserPasskeySigner;
6970
+ exports.buildModalCssVars = buildModalCssVars;
6956
6971
  exports.chainsOf = chainsOf;
6957
6972
  exports.createPollarAdapterHook = createPollarAdapterHook;
6973
+ exports.modalChrome = modalChrome;
6974
+ exports.readableTextOn = readableTextOn;
6958
6975
  exports.resolveChain = resolveChain;
6959
6976
  exports.useChains = useChains;
6960
6977
  exports.usePollar = usePollar;