@loafmarkets/ui 0.1.384 → 0.1.386

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
@@ -7,6 +7,7 @@ import { twMerge } from 'tailwind-merge';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
8
  import styled10, { keyframes, css } from 'styled-components';
9
9
  import * as ReactDOM from 'react-dom';
10
+ import { createPortal } from 'react-dom';
10
11
  import { Newspaper } from 'lucide-react';
11
12
  import * as LightweightCharts from 'lightweight-charts';
12
13
  import { MdKingBed, MdBathtub, MdDirectionsCar, MdPool, MdYard, MdFitnessCenter } from 'react-icons/md';
@@ -1874,6 +1875,7 @@ var Orderbook = React5.forwardRef(
1874
1875
  amountPrecision = 2,
1875
1876
  defaultTab = "orderbook",
1876
1877
  onTabChange,
1878
+ showTradesTab = true,
1877
1879
  rightHeader,
1878
1880
  variant = "auto",
1879
1881
  userOrderPrices,
@@ -1887,7 +1889,9 @@ var Orderbook = React5.forwardRef(
1887
1889
  ...props
1888
1890
  }, ref) => {
1889
1891
  const resolvedRightHeader = rightHeader ?? /* @__PURE__ */ jsx(LoafLiquidityBadge, { className: "text-[0.6rem] orderbook-ll-badge", onClick: onLoafLiquidityClick });
1890
- const [tab, setTab] = React5.useState(defaultTab);
1892
+ const [tab, setTabState] = React5.useState(defaultTab);
1893
+ const setTab = setTabState;
1894
+ const effectiveTab = showTradesTab ? tab : "orderbook";
1891
1895
  const [tradeFilter, setTradeFilter] = React5.useState("all");
1892
1896
  const viewportCompact = useViewportCompact(COMPACT_BREAKPOINT_PX);
1893
1897
  const { userAskPrices, userBidPrices } = React5.useMemo(() => {
@@ -1981,7 +1985,8 @@ var Orderbook = React5.forwardRef(
1981
1985
  const midClass = midChangePercent == null ? "text-white" : midChangePercent >= 0 ? "text-[#0ecb81]" : "text-[#f6465d]";
1982
1986
  const tradeFiltered = trades.filter((t) => tradeFilter === "all" || t.type === tradeFilter);
1983
1987
  const layoutProps = {
1984
- tab,
1988
+ tab: effectiveTab,
1989
+ showTradesTab,
1985
1990
  handleTab,
1986
1991
  tradeFilter,
1987
1992
  setTradeFilter,
@@ -2058,6 +2063,7 @@ function SkeletonRow({ compact }) {
2058
2063
  }
2059
2064
  function DesktopOrderbookLayout({
2060
2065
  tab,
2066
+ showTradesTab = true,
2061
2067
  handleTab,
2062
2068
  tradeFilter,
2063
2069
  setTradeFilter,
@@ -2107,7 +2113,7 @@ function DesktopOrderbookLayout({
2107
2113
  ]
2108
2114
  }
2109
2115
  ),
2110
- /* @__PURE__ */ jsxs(
2116
+ showTradesTab ? /* @__PURE__ */ jsxs(
2111
2117
  "button",
2112
2118
  {
2113
2119
  type: "button",
@@ -2121,7 +2127,7 @@ function DesktopOrderbookLayout({
2121
2127
  tab === "trades" ? /* @__PURE__ */ jsx("span", { className: "absolute -bottom-px left-0 h-[2px] w-full bg-[#E6C87E]" }) : null
2122
2128
  ]
2123
2129
  }
2124
- )
2130
+ ) : null
2125
2131
  ] }),
2126
2132
  tab === "trades" ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-xs", children: [
2127
2133
  /* @__PURE__ */ jsx(
@@ -2260,6 +2266,7 @@ function DesktopOrderbookLayout({
2260
2266
  }
2261
2267
  function MobileOrderbookLayout({
2262
2268
  tab,
2269
+ showTradesTab = true,
2263
2270
  handleTab,
2264
2271
  tradeFilter,
2265
2272
  setTradeFilter,
@@ -2308,7 +2315,7 @@ function MobileOrderbookLayout({
2308
2315
  children: "Orderbook"
2309
2316
  }
2310
2317
  ),
2311
- /* @__PURE__ */ jsx(
2318
+ showTradesTab ? /* @__PURE__ */ jsx(
2312
2319
  "span",
2313
2320
  {
2314
2321
  onClick: () => handleTab("trades"),
@@ -2322,7 +2329,7 @@ function MobileOrderbookLayout({
2322
2329
  },
2323
2330
  children: "Trades"
2324
2331
  }
2325
- )
2332
+ ) : null
2326
2333
  ] }),
2327
2334
  tab === "trades" ? /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 text-[0.6rem] text-white/70", children: ["all", "buy", "sell"].map((filter) => /* @__PURE__ */ jsx(
2328
2335
  "button",
@@ -5010,9 +5017,9 @@ var PulsingDot = styled10.span`
5010
5017
  `;
5011
5018
  function fmtCompactUsd(n) {
5012
5019
  const abs = Math.abs(n);
5013
- if (abs >= 1e6) return `$${(abs / 1e6).toFixed(2)}M`;
5014
- if (abs >= 1e3) return `$${(abs / 1e3).toFixed(1)}K`;
5015
- return `$${abs.toFixed(2)}`;
5020
+ if (abs >= 1e6) return `${(abs / 1e6).toFixed(2)}M`;
5021
+ if (abs >= 1e3) return `${(abs / 1e3).toFixed(1)}K`;
5022
+ return `${abs.toFixed(2)}`;
5016
5023
  }
5017
5024
  function useIsNarrow(maxWidthPx = 768) {
5018
5025
  const query = `(max-width:${maxWidthPx}px)`;
@@ -5035,8 +5042,6 @@ var DEFAULT_ABOUT_PATH = "/about";
5035
5042
  var DEFAULT_LEARN_PATH = "/learn";
5036
5043
  var DEFAULT_API_PATH = "/api";
5037
5044
  var DEFAULT_PROPERTY_MAP_PATH = "/map";
5038
- var DEFAULT_LEADERBOARD_PATH = "/league";
5039
- var DEFAULT_LEADERBOARD_PAGE_PATH = "/leaderboard";
5040
5045
  var DEFAULT_PORTFOLIO_PATH = "/portfolio";
5041
5046
  var DEFAULT_LOAF_LIQUIDITY_PATH = "/loaf-liquidity";
5042
5047
  var DEFAULT_AUTH_RETURN_PARAM = "returnTo";
@@ -5057,8 +5062,6 @@ var Header = ({
5057
5062
  apiPath = DEFAULT_API_PATH,
5058
5063
  offeringsPath,
5059
5064
  propertyMapPath = DEFAULT_PROPERTY_MAP_PATH,
5060
- leaderboardPath = DEFAULT_LEADERBOARD_PATH,
5061
- leaderboardPagePath = DEFAULT_LEADERBOARD_PAGE_PATH,
5062
5065
  portfolioPath = DEFAULT_PORTFOLIO_PATH,
5063
5066
  loafLiquidityPath = DEFAULT_LOAF_LIQUIDITY_PATH,
5064
5067
  authLoginUrl,
@@ -5332,8 +5335,6 @@ var Header = ({
5332
5335
  const resolvedOfferingsPath = offeringsPath ?? void 0;
5333
5336
  const offeringsHref = resolvedOfferingsPath ?? resolvedHomePath;
5334
5337
  const resolvedPropertyMapPath = propertyMapPath ?? DEFAULT_PROPERTY_MAP_PATH;
5335
- const resolvedLeaderboardPath = leaderboardPath ?? DEFAULT_LEADERBOARD_PATH;
5336
- const resolvedLeaderboardPagePath = leaderboardPagePath ?? DEFAULT_LEADERBOARD_PAGE_PATH;
5337
5338
  const resolvedPortfolioPath = portfolioPath ?? DEFAULT_PORTFOLIO_PATH;
5338
5339
  const resolvedLoafLiquidityPath = loafLiquidityPath ?? DEFAULT_LOAF_LIQUIDITY_PATH;
5339
5340
  const inferredActiveTab = (() => {
@@ -5344,8 +5345,6 @@ var Header = ({
5344
5345
  if (locationPath === resolvedApiPath) return "api";
5345
5346
  if (resolvedOfferingsPath && locationPath.startsWith(resolvedOfferingsPath)) return "offerings";
5346
5347
  if (locationPath === resolvedPropertyMapPath) return "propertyMap";
5347
- if (locationPath === resolvedLeaderboardPagePath) return "leaderboardPage";
5348
- if (locationPath === resolvedLeaderboardPath) return "leaderboard";
5349
5348
  if (locationPath === resolvedPortfolioPath) return "portfolio";
5350
5349
  return null;
5351
5350
  })();
@@ -5354,8 +5353,6 @@ var Header = ({
5354
5353
  const homeActive = resolvedActiveTab === "home";
5355
5354
  const offeringsActive = resolvedActiveTab === "offerings";
5356
5355
  const propertyMapActive = resolvedActiveTab === "propertyMap";
5357
- const leaderboardActive = resolvedActiveTab === "leaderboard";
5358
- const leaderboardPageActive = resolvedActiveTab === "leaderboardPage";
5359
5356
  const aboutActive = resolvedActiveTab === "about";
5360
5357
  const portfolioActive = resolvedActiveTab === "portfolio";
5361
5358
  const learnActive = resolvedActiveTab === "learn";
@@ -5363,7 +5360,7 @@ var Header = ({
5363
5360
  return /* @__PURE__ */ jsxs(Fragment, { children: [
5364
5361
  /* @__PURE__ */ jsx(SafeAreaCover, {}),
5365
5362
  /* @__PURE__ */ jsx(Overlay, { $isOpen: isMobileMenuOpen, onClick: () => setIsMobileMenuOpen(false) }),
5366
- /* @__PURE__ */ jsxs(HeaderContainer, { id: "loaf-header", $bgOpacity: headerBgOpacity, children: [
5363
+ /* @__PURE__ */ jsxs(HeaderContainer, { id: "loaf-header", $bgOpacity: headerBgOpacity, $menuOpen: isMobileMenuOpen, children: [
5367
5364
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center" }, children: [
5368
5365
  /* @__PURE__ */ jsxs(Logo, { children: [
5369
5366
  /* @__PURE__ */ jsx(LogoLink, { href: logoHref ?? resolvedHomePath, onClick: handleLogoNavigation, children: /* @__PURE__ */ jsx("img", { src: Loaf_logo_Banner_default, alt: "LOAF Logo" }) }),
@@ -5439,91 +5436,48 @@ var Header = ({
5439
5436
  /* @__PURE__ */ jsx(
5440
5437
  NavLink,
5441
5438
  {
5442
- href: resolvedLeaderboardPath,
5443
- className: leaderboardActive ? "active" : "",
5439
+ href: resolvedLearnPath,
5440
+ className: learnActive ? "active" : "",
5444
5441
  onClick: (event) => {
5445
5442
  event.preventDefault();
5446
- handleNavigation(resolvedLeaderboardPath);
5443
+ handleNavigation(resolvedLearnPath);
5447
5444
  },
5448
- children: "Competition"
5445
+ children: "Learn"
5449
5446
  }
5450
5447
  ),
5451
5448
  /* @__PURE__ */ jsx(
5452
5449
  NavLink,
5453
5450
  {
5454
- href: resolvedLeaderboardPagePath,
5455
- className: leaderboardPageActive ? "active" : "",
5451
+ href: resolvedAboutPath,
5452
+ className: aboutActive ? "active" : "",
5456
5453
  onClick: (event) => {
5457
5454
  event.preventDefault();
5458
- handleNavigation(resolvedLeaderboardPagePath);
5455
+ handleNavigation(resolvedAboutPath);
5459
5456
  },
5460
- children: "Leaderboard"
5457
+ children: "About"
5461
5458
  }
5462
5459
  ),
5463
5460
  /* @__PURE__ */ jsx(
5464
5461
  NavLink,
5465
5462
  {
5466
- href: resolvedLearnPath,
5467
- className: learnActive ? "active" : "",
5463
+ href: resolvedApiPath,
5464
+ className: apiActive ? "active" : "",
5468
5465
  onClick: (event) => {
5469
5466
  event.preventDefault();
5470
- handleNavigation(resolvedLearnPath);
5467
+ handleNavigation(resolvedApiPath);
5471
5468
  },
5472
- children: "Learn"
5469
+ children: "API"
5470
+ }
5471
+ ),
5472
+ /* @__PURE__ */ jsx(
5473
+ NavLink,
5474
+ {
5475
+ href: "https://docs.loafmarkets.com/en/",
5476
+ target: "_blank",
5477
+ rel: "noopener noreferrer",
5478
+ children: "Docs"
5473
5479
  }
5474
5480
  ),
5475
- /* @__PURE__ */ jsxs(MoreMenu, { className: "more-menu", children: [
5476
- /* @__PURE__ */ jsxs(
5477
- MoreTrigger,
5478
- {
5479
- type: "button",
5480
- className: `${isMoreMenuOpen ? "open" : ""} ${aboutActive || apiActive ? "active" : ""}`,
5481
- "aria-haspopup": "true",
5482
- "aria-expanded": isMoreMenuOpen,
5483
- onClick: () => setIsMoreMenuOpen((open) => !open),
5484
- children: [
5485
- "More",
5486
- /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M6 9l6 6 6-6", strokeLinecap: "round", strokeLinejoin: "round" }) })
5487
- ]
5488
- }
5489
- ),
5490
- isMoreMenuOpen && /* @__PURE__ */ jsxs(MoreDropdown, { children: [
5491
- /* @__PURE__ */ jsx(
5492
- MoreItem,
5493
- {
5494
- href: resolvedAboutPath,
5495
- className: aboutActive ? "active" : "",
5496
- onClick: (event) => {
5497
- event.preventDefault();
5498
- handleNavigation(resolvedAboutPath);
5499
- },
5500
- children: "About"
5501
- }
5502
- ),
5503
- /* @__PURE__ */ jsx(
5504
- MoreItem,
5505
- {
5506
- href: resolvedApiPath,
5507
- className: apiActive ? "active" : "",
5508
- onClick: (event) => {
5509
- event.preventDefault();
5510
- handleNavigation(resolvedApiPath);
5511
- },
5512
- children: "API"
5513
- }
5514
- ),
5515
- /* @__PURE__ */ jsx(
5516
- MoreItem,
5517
- {
5518
- href: "https://docs.loafmarkets.com/en/",
5519
- target: "_blank",
5520
- rel: "noopener noreferrer",
5521
- onClick: () => setIsMoreMenuOpen(false),
5522
- children: "Docs"
5523
- }
5524
- )
5525
- ] })
5526
- ] }),
5527
5481
  /* @__PURE__ */ jsx("div", { style: { marginLeft: "auto", display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx(
5528
5482
  "div",
5529
5483
  {
@@ -5564,8 +5518,6 @@ var Header = ({
5564
5518
  ),
5565
5519
  /* @__PURE__ */ jsx(MobileNavItem, { onClick: () => handleNavigation(offeringsHref), children: "Initial Offerings" }),
5566
5520
  /* @__PURE__ */ jsx(MobileNavItem, { onClick: () => handleNavigation(resolvedPropertyMapPath), children: "Property Map" }),
5567
- /* @__PURE__ */ jsx(MobileNavItem, { onClick: () => handleNavigation(resolvedLeaderboardPath), children: "Competition" }),
5568
- /* @__PURE__ */ jsx(MobileNavItem, { onClick: () => handleNavigation(resolvedLeaderboardPagePath), children: "Leaderboard" }),
5569
5521
  /* @__PURE__ */ jsx(MobileNavItem, { onClick: () => handleNavigation(resolvedLearnPath), children: "Learn" }),
5570
5522
  /* @__PURE__ */ jsx(MobileNavItem, { onClick: () => handleNavigation(resolvedAboutPath), children: "About" }),
5571
5523
  /* @__PURE__ */ jsx(MobileNavItem, { onClick: () => handleNavigation(resolvedApiPath), children: "API" }),
@@ -5839,7 +5791,8 @@ var Overlay = styled10.div`
5839
5791
  height: 100%;
5840
5792
  background-color: rgba(0, 0, 0, 0.7);
5841
5793
  backdrop-filter: blur(3px);
5842
- z-index: 970;
5794
+ /* Above the trade page's sticky/console layers (z 2000) + close modal (10001). */
5795
+ z-index: 10070;
5843
5796
  `;
5844
5797
  var MobileOnlyButton = styled10.button.attrs({ type: "button" })`
5845
5798
  display: none;
@@ -5887,7 +5840,11 @@ var HeaderContainer = styled10.header`
5887
5840
  top: 0;
5888
5841
  left: 0;
5889
5842
  right: 0;
5890
- z-index: 1000;
5843
+ /* Normally 1000. When the mobile menu is open, lift the whole header
5844
+ (and the Nav drawer trapped inside its stacking context) above the
5845
+ backdrop Overlay (10070) AND the trade page's modal layers (10001) so
5846
+ the drawer renders on top instead of being dimmed by the backdrop. */
5847
+ z-index: ${({ $menuOpen }) => $menuOpen ? 10075 : 1e3};
5891
5848
  width: 100%;
5892
5849
  /* Fallback min-height for browsers that can't evaluate calc+env */
5893
5850
  min-height: 56px;
@@ -5981,7 +5938,8 @@ var Nav = styled10.nav`
5981
5938
  max-width: 100%;
5982
5939
  height: calc(100dvh - var(--loaf-menu-top, calc(56px + max(env(safe-area-inset-top, 0px), var(--telegram-safe-top, 0px)))));
5983
5940
  background: rgb(17, 17, 19);
5984
- z-index: 980;
5941
+ /* Above the trade page's sticky/console layers (z 2000) + close modal (10001). */
5942
+ z-index: 10080;
5985
5943
  /* Gentle fade + slide-down instead of an abrupt slide-in. */
5986
5944
  opacity: ${(props) => props.$isOpen ? "1" : "0"};
5987
5945
  transform: ${(props) => props.$isOpen ? "translateY(0)" : "translateY(-14px)"};
@@ -5999,9 +5957,12 @@ var Nav = styled10.nav`
5999
5957
  display: flex;
6000
5958
  flex-direction: column;
6001
5959
  align-items: center;
6002
- justify-content: center;
5960
+ /* safe-center keeps centering when the menu fits, but top-aligns (honouring the
5961
+ 2.5rem top padding) when the logged-in menu overflows — otherwise flex
5962
+ centering pushes the first item + padding above the scroll area and clips it. */
5963
+ justify-content: safe center;
6003
5964
  min-height: 100%;
6004
- padding: 2rem 1.5rem 2.5rem;
5965
+ padding: 2.5rem 1.5rem 2.5rem;
6005
5966
  gap: 0.4rem;
6006
5967
  }
6007
5968
  }
@@ -6074,77 +6035,6 @@ var NavLink = styled10.a`
6074
6035
  }
6075
6036
  }
6076
6037
  `;
6077
- var MoreMenu = styled10.div`
6078
- position: relative;
6079
- display: flex;
6080
- align-items: center;
6081
- height: 56px;
6082
- `;
6083
- var MoreTrigger = styled10.button`
6084
- margin: 0 12px;
6085
- padding: 0 4px;
6086
- height: 56px;
6087
- display: inline-flex;
6088
- align-items: center;
6089
- gap: 4px;
6090
- background: none;
6091
- border: none;
6092
- cursor: pointer;
6093
- color: #eaecef;
6094
- font-weight: 500;
6095
- font-size: 14px;
6096
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", Arial, sans-serif;
6097
- letter-spacing: 0.2px;
6098
- transition: color 0.2s ease;
6099
-
6100
- &:hover,
6101
- &.active {
6102
- color: #E6C87E;
6103
- }
6104
-
6105
- svg {
6106
- width: 14px;
6107
- height: 14px;
6108
- transition: transform 0.2s ease;
6109
- }
6110
- &.open svg {
6111
- transform: rotate(180deg);
6112
- }
6113
- `;
6114
- var MoreDropdown = styled10.div`
6115
- position: absolute;
6116
- top: 100%;
6117
- right: 0;
6118
- min-width: 160px;
6119
- padding: 6px;
6120
- background: #1e2026;
6121
- border: 1px solid #2b3139;
6122
- border-radius: 10px;
6123
- box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4);
6124
- display: flex;
6125
- flex-direction: column;
6126
- z-index: 1100;
6127
- `;
6128
- var MoreItem = styled10.a`
6129
- padding: 10px 12px;
6130
- border-radius: 6px;
6131
- color: #eaecef;
6132
- font-weight: 500;
6133
- font-size: 14px;
6134
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", Arial, sans-serif;
6135
- letter-spacing: 0.2px;
6136
- text-decoration: none;
6137
- cursor: pointer;
6138
- transition: background 0.15s ease, color 0.15s ease;
6139
-
6140
- &:hover {
6141
- background: #2b3139;
6142
- color: #E6C87E;
6143
- }
6144
- &.active {
6145
- color: #E6C87E;
6146
- }
6147
- `;
6148
6038
  var Button2 = styled10.button.attrs({ type: "button" })`
6149
6039
  padding: 0.5rem 1rem;
6150
6040
  border-radius: 6px;
@@ -6711,6 +6601,17 @@ PropertySubheader.displayName = "PropertySubheader";
6711
6601
  var DEFAULT_LOGO_SRC = Loaf_logo_Banner_default;
6712
6602
  var DEFAULT_LOGO_ALT = "Loaf";
6713
6603
  var OTP_INPUT_LENGTH = 6;
6604
+ var CODE_PREFIX = "LOAF-";
6605
+ var CODE_SUFFIX_LEN = 5;
6606
+ var normalizeAccessCode = (raw) => CODE_PREFIX + raw.toUpperCase().replace(/^LOAF-?/, "").replace(/[^A-Z0-9]/g, "").slice(0, CODE_SUFFIX_LEN);
6607
+ var friendlyError = (err, fallback) => {
6608
+ const data = err?.response?.data;
6609
+ if (typeof data?.error === "string" && data.error.trim()) return data.error;
6610
+ if (typeof data?.message === "string" && data.message.trim()) return data.message;
6611
+ const msg = err instanceof Error ? err.message : "";
6612
+ if (msg && !/request failed with status code/i.test(msg) && !/network error/i.test(msg)) return msg;
6613
+ return fallback;
6614
+ };
6714
6615
  var LoginPopup = ({
6715
6616
  onClose,
6716
6617
  onOpenEarlyAccess,
@@ -6729,10 +6630,13 @@ var LoginPopup = ({
6729
6630
  renderKycWidget,
6730
6631
  onFundWallet,
6731
6632
  initialView,
6633
+ initialCode,
6732
6634
  kycStatus: kycStatusProp,
6733
6635
  walletAddress,
6734
6636
  onSubmitReferralCode,
6735
- onJoinWaitlist
6637
+ onValidateCode,
6638
+ onCheckAccess,
6639
+ gate
6736
6640
  }) => {
6737
6641
  const [view, setView] = useState(() => initialView ?? "main");
6738
6642
  const [email, setEmail] = useState("");
@@ -6740,19 +6644,17 @@ var LoginPopup = ({
6740
6644
  const [otp, setOtp] = useState(Array(OTP_INPUT_LENGTH).fill(""));
6741
6645
  const [error, setError] = useState("");
6742
6646
  const [copied, setCopied] = useState(false);
6743
- const [referralCode, setReferralCode] = useState("");
6647
+ const [referralCode, setReferralCode] = useState(
6648
+ () => gate && initialCode ? normalizeAccessCode(initialCode) : ""
6649
+ );
6744
6650
  const [referralLoading, setReferralLoading] = useState(false);
6745
6651
  const [referralError, setReferralError] = useState("");
6746
- const [waitlistLoading, setWaitlistLoading] = useState(false);
6747
- const [waitlistMessage, setWaitlistMessage] = useState("");
6652
+ const [codeStatus, setCodeStatus] = useState("idle");
6653
+ const [signInRevealed, setSignInRevealed] = useState(false);
6654
+ const [otpStatus, setOtpStatus] = useState("idle");
6655
+ const [signInMode, setSignInMode] = useState(false);
6748
6656
  const [loading, setLoading] = useState(false);
6749
6657
  const [isSignUp, setIsSignUp] = useState(false);
6750
- const [loginTitle] = useState(() => {
6751
- if (typeof window === "undefined") return null;
6752
- const t = window.__LOAF_LOGIN_TITLE__ ?? null;
6753
- if (t) delete window.__LOAF_LOGIN_TITLE__;
6754
- return t;
6755
- });
6756
6658
  const [fundingAmount] = useState("");
6757
6659
  const [kycLoading, setKycLoading] = useState(false);
6758
6660
  const [showKycWidget, setShowKycWidget] = useState(false);
@@ -6762,6 +6664,64 @@ var LoginPopup = ({
6762
6664
  const [fundingError, setFundingError] = useState("");
6763
6665
  const [transakWidgetUrl, setTransakWidgetUrl] = useState(null);
6764
6666
  const suppressAutoCloseRef = React5__default.useRef(false);
6667
+ const validatedCodeRef = React5__default.useRef(null);
6668
+ const finishGate = React5__default.useCallback(async () => {
6669
+ if (!gate) return true;
6670
+ const code = validatedCodeRef.current;
6671
+ if (code) {
6672
+ try {
6673
+ await onSubmitReferralCode?.(code);
6674
+ window.dispatchEvent(new CustomEvent("loaf:onboarding-start"));
6675
+ return true;
6676
+ } catch {
6677
+ }
6678
+ }
6679
+ const hasAccess = onCheckAccess ? await onCheckAccess() : true;
6680
+ if (hasAccess) return true;
6681
+ suppressAutoCloseRef.current = true;
6682
+ validatedCodeRef.current = null;
6683
+ setOtp(Array(OTP_INPUT_LENGTH).fill(""));
6684
+ setOtpStatus("idle");
6685
+ setSignInMode(false);
6686
+ setSignInRevealed(false);
6687
+ setCodeStatus(code ? "invalid" : "idle");
6688
+ setView("referral");
6689
+ setError(
6690
+ code ? "That code can't be redeemed anymore. Enter a different access code." : "Enter your access code to continue."
6691
+ );
6692
+ return false;
6693
+ }, [gate, onSubmitReferralCode, onCheckAccess]);
6694
+ const runCodeValidation = React5__default.useCallback(async (fullCode) => {
6695
+ setCodeStatus("checking");
6696
+ setError("");
6697
+ try {
6698
+ const ok = onValidateCode ? await onValidateCode(fullCode) : true;
6699
+ if (!ok) {
6700
+ validatedCodeRef.current = null;
6701
+ setCodeStatus("invalid");
6702
+ return;
6703
+ }
6704
+ validatedCodeRef.current = fullCode;
6705
+ setCodeStatus("valid");
6706
+ if (isAuthenticated) {
6707
+ if (await finishGate()) setTimeout(() => onClose(), 500);
6708
+ return;
6709
+ }
6710
+ setIsSignUp(true);
6711
+ setSignInRevealed(true);
6712
+ } catch {
6713
+ validatedCodeRef.current = null;
6714
+ setCodeStatus("invalid");
6715
+ }
6716
+ }, [onValidateCode, isAuthenticated, finishGate, onClose]);
6717
+ const autoCodeTriedRef = React5__default.useRef(false);
6718
+ useEffect(() => {
6719
+ if (autoCodeTriedRef.current || !gate || !initialCode) return;
6720
+ const full = normalizeAccessCode(initialCode);
6721
+ if (full.length !== CODE_PREFIX.length + CODE_SUFFIX_LEN) return;
6722
+ autoCodeTriedRef.current = true;
6723
+ void runCodeValidation(full);
6724
+ }, [gate, initialCode, runCodeValidation]);
6765
6725
  useEffect(() => {
6766
6726
  if (typeof initialView === "string") {
6767
6727
  setView(initialView);
@@ -6816,11 +6776,11 @@ var LoginPopup = ({
6816
6776
  }, [autoCloseOnAuth, currentUser, isAuthenticated, onClose, view]);
6817
6777
  useEffect(() => {
6818
6778
  const handleEsc = (e) => {
6819
- if (e.key === "Escape") onClose();
6779
+ if (e.key === "Escape" && !gate) onClose();
6820
6780
  };
6821
6781
  window.addEventListener("keydown", handleEsc);
6822
6782
  return () => window.removeEventListener("keydown", handleEsc);
6823
- }, [onClose]);
6783
+ }, [onClose, gate]);
6824
6784
  const handleWalletLogin = async () => {
6825
6785
  if (onWalletLogin) {
6826
6786
  suppressAutoCloseRef.current = true;
@@ -6832,12 +6792,16 @@ var LoginPopup = ({
6832
6792
  setView("wallet-handle");
6833
6793
  return;
6834
6794
  }
6835
- suppressAutoCloseRef.current = false;
6836
- onClose();
6795
+ if (await finishGate()) {
6796
+ suppressAutoCloseRef.current = false;
6797
+ onClose();
6798
+ }
6837
6799
  return;
6838
6800
  } catch (err) {
6839
6801
  console.error("[LoginTrace][Popup] Wallet login failed", err);
6840
6802
  suppressAutoCloseRef.current = false;
6803
+ setError(friendlyError(err, "Wallet login failed. Please try again."));
6804
+ return;
6841
6805
  }
6842
6806
  }
6843
6807
  onClose();
@@ -6859,6 +6823,13 @@ var LoginPopup = ({
6859
6823
  setError("");
6860
6824
  try {
6861
6825
  await onWalletSignup(handle.trim());
6826
+ if (gate) {
6827
+ if (await finishGate()) {
6828
+ suppressAutoCloseRef.current = false;
6829
+ onClose();
6830
+ }
6831
+ return;
6832
+ }
6862
6833
  if (onSubmitReferralCode) {
6863
6834
  setView("referral");
6864
6835
  } else {
@@ -6870,6 +6841,11 @@ var LoginPopup = ({
6870
6841
  setLoading(false);
6871
6842
  }
6872
6843
  };
6844
+ const handleEmailClick = (signUp = false) => {
6845
+ setIsSignUp(signUp);
6846
+ setView("email");
6847
+ setError("");
6848
+ };
6873
6849
  const handleSendCode = async (event) => {
6874
6850
  event?.preventDefault();
6875
6851
  if (!email || !email.includes("@")) {
@@ -6882,6 +6858,7 @@ var LoginPopup = ({
6882
6858
  }
6883
6859
  setLoading(true);
6884
6860
  setError("");
6861
+ setOtpStatus("idle");
6885
6862
  const normalizedHandle = isSignUp && handle.trim() ? handle.trim() : void 0;
6886
6863
  try {
6887
6864
  const demoResult = await onDemoLogin?.(email, normalizedHandle ?? null);
@@ -6902,11 +6879,15 @@ var LoginPopup = ({
6902
6879
  setView("otp");
6903
6880
  setOtp(Array(OTP_INPUT_LENGTH).fill(""));
6904
6881
  } catch (err) {
6905
- const message = err instanceof Error ? err.message : "Failed to send verification code";
6882
+ const message = friendlyError(err, "We couldn't send a code to that email. Please double-check it and try again.");
6906
6883
  console.error("[LoginTrace][Popup] onSendEmailCode threw", err);
6907
6884
  if (!isSignUp && message.includes("No account found")) {
6885
+ if (signInMode) {
6886
+ setError("No account found for this email. Go back and enter your access code to create one.");
6887
+ setLoading(false);
6888
+ return;
6889
+ }
6908
6890
  setIsSignUp(true);
6909
- setView("email");
6910
6891
  setError("");
6911
6892
  setLoading(false);
6912
6893
  return;
@@ -6918,7 +6899,7 @@ var LoginPopup = ({
6918
6899
  setView("otp");
6919
6900
  setOtp(Array(OTP_INPUT_LENGTH).fill(""));
6920
6901
  } catch (retryErr) {
6921
- setError(retryErr instanceof Error ? retryErr.message : "Failed to send verification code");
6902
+ setError(friendlyError(retryErr, "We couldn't send a code to that email. Please try again."));
6922
6903
  }
6923
6904
  setLoading(false);
6924
6905
  return;
@@ -7012,6 +6993,30 @@ var LoginPopup = ({
7012
6993
  setLoading(false);
7013
6994
  }
7014
6995
  };
6996
+ const runGateOtpVerify = React5__default.useCallback(async (code) => {
6997
+ if (!onVerifyEmailCode) return;
6998
+ setOtpStatus("checking");
6999
+ setError("");
7000
+ suppressAutoCloseRef.current = true;
7001
+ try {
7002
+ await onVerifyEmailCode({ code, email });
7003
+ if (await finishGate()) {
7004
+ setOtpStatus("valid");
7005
+ setTimeout(() => onClose(), 750);
7006
+ }
7007
+ } catch {
7008
+ suppressAutoCloseRef.current = false;
7009
+ setOtpStatus("invalid");
7010
+ }
7011
+ }, [onVerifyEmailCode, email, finishGate, onClose]);
7012
+ useEffect(() => {
7013
+ if (!gate || view !== "otp") return;
7014
+ if (otp.join("").length < OTP_INPUT_LENGTH) {
7015
+ if (otpStatus === "invalid") setOtpStatus("idle");
7016
+ return;
7017
+ }
7018
+ if (otpStatus === "idle") void runGateOtpVerify(otp.join(""));
7019
+ }, [gate, view, otp, otpStatus, runGateOtpVerify]);
7015
7020
  const handleKycWidgetResult = (result) => {
7016
7021
  setShowKycWidget(false);
7017
7022
  if (result.passed) {
@@ -7108,35 +7113,36 @@ var LoginPopup = ({
7108
7113
  setError("");
7109
7114
  };
7110
7115
  if (view === "main") {
7111
- return /* @__PURE__ */ jsx(Overlay2, { onClick: onClose, children: /* @__PURE__ */ jsxs(PopupContainer, { onClick: (event) => event.stopPropagation(), children: [
7116
+ return /* @__PURE__ */ jsx(Overlay2, { $transparent: gate, onClick: onClose, children: /* @__PURE__ */ jsxs(PopupContainer, { onClick: (event) => event.stopPropagation(), children: [
7112
7117
  /* @__PURE__ */ jsx(CloseButton, { onClick: onClose, "aria-label": "Close login popup", children: /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }) }) }),
7113
- /* @__PURE__ */ jsx(MainHeading, { children: loginTitle?.title ?? "Welcome to Loaf" }),
7114
- (loginTitle ? loginTitle.subtitle : "Sign in to claim 100k in test USD.") && /* @__PURE__ */ jsx(MainSubtitle, { children: loginTitle ? loginTitle.subtitle : "Sign in to claim 100k in test USD." }),
7115
- /* @__PURE__ */ jsxs(GateForm, { onSubmit: handleSendCode, children: [
7116
- /* @__PURE__ */ jsxs(GateInputRow, { children: [
7117
- /* @__PURE__ */ jsx(
7118
- GateInput,
7119
- {
7120
- type: "email",
7121
- placeholder: "Enter your email",
7122
- value: email,
7123
- onChange: (event) => setEmail(event.target.value),
7124
- autoFocus: true
7125
- }
7126
- ),
7127
- /* @__PURE__ */ jsx(GateSubmit, { type: "submit", disabled: loading || !email, "aria-label": "Continue with email", children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M5 12h14M13 6l6 6-6 6" }) }) })
7118
+ /* @__PURE__ */ jsxs(Title, { children: [
7119
+ /* @__PURE__ */ jsxs(LogoContainer3, { children: [
7120
+ /* @__PURE__ */ jsx(LogoImage, { src: logoSrc, alt: logoAlt }),
7121
+ /* @__PURE__ */ jsx(LogoBeta, { children: "Private Beta" })
7128
7122
  ] }),
7129
- error && /* @__PURE__ */ jsx(StatusMessage, { $error: true, children: error })
7123
+ /* @__PURE__ */ jsx(TitleText, { children: "Welcome to Loaf" })
7130
7124
  ] }),
7131
- /* @__PURE__ */ jsx(GateOr, { children: "or" }),
7132
- /* @__PURE__ */ jsxs(LoginButton, { onClick: handleWalletLogin, children: [
7133
- /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M21 18v1c0 1.1-.9 2-2 2H5c-1.11 0-2-.9-2-2V5c0-1.1.89-2 2-2h14c1.1 0 2 .9 2 2v1h-9c-1.11 0-2 .9-2 2v8c0 1.1.89 2 2 2h9zm-9-2h10V8H12v8zm4-2.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z" }) }),
7134
- "Join with wallet"
7125
+ /* @__PURE__ */ jsx(Subtitle, { children: "Sign in to claim 100k in test USD." }),
7126
+ /* @__PURE__ */ jsxs(ButtonsContainer, { children: [
7127
+ /* @__PURE__ */ jsxs(LoginButton, { onClick: () => handleEmailClick(false), children: [
7128
+ /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z" }) }),
7129
+ "Sign in with Email"
7130
+ ] }),
7131
+ /* @__PURE__ */ jsx("div", { style: { textAlign: "center", color: "var(--color-text-secondary, #848e9c)", fontSize: "0.875rem" }, children: "or" }),
7132
+ /* @__PURE__ */ jsxs(LoginButton, { onClick: handleWalletLogin, children: [
7133
+ /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M21 18v1c0 1.1-.9 2-2 2H5c-1.11 0-2-.9-2-2V5c0-1.1.89-2 2-2h14c1.1 0 2 .9 2 2v1h-9c-1.11 0-2 .9-2 2v8c0 1.1.89 2 2 2h9zm-9-2h10V8H12v8zm4-2.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z" }) }),
7134
+ "Sign in with Wallet"
7135
+ ] })
7136
+ ] }),
7137
+ /* @__PURE__ */ jsxs(SignUpPrompt, { children: [
7138
+ "New to Loaf?",
7139
+ " ",
7140
+ /* @__PURE__ */ jsx(SignUpLink, { type: "button", onClick: () => handleEmailClick(true), children: "Sign up" })
7135
7141
  ] })
7136
7142
  ] }) });
7137
7143
  }
7138
7144
  if (view === "email") {
7139
- return /* @__PURE__ */ jsx(Overlay2, { onClick: onClose, children: /* @__PURE__ */ jsxs(PopupContainer, { onClick: (event) => event.stopPropagation(), children: [
7145
+ return /* @__PURE__ */ jsx(Overlay2, { $transparent: gate, onClick: onClose, children: /* @__PURE__ */ jsxs(PopupContainer, { onClick: (event) => event.stopPropagation(), children: [
7140
7146
  /* @__PURE__ */ jsx(CloseButton, { onClick: onClose, "aria-label": "Close login popup", children: /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }) }) }),
7141
7147
  /* @__PURE__ */ jsxs(BackButton, { onClick: handleBack, children: [
7142
7148
  /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" }) }),
@@ -7175,7 +7181,7 @@ var LoginPopup = ({
7175
7181
  ] }) });
7176
7182
  }
7177
7183
  if (view === "wallet-handle") {
7178
- return /* @__PURE__ */ jsx(Overlay2, { onClick: loading ? void 0 : onClose, children: /* @__PURE__ */ jsxs(PopupContainer, { onClick: (event) => event.stopPropagation(), children: [
7184
+ return /* @__PURE__ */ jsx(Overlay2, { $transparent: gate, onClick: loading ? void 0 : onClose, children: /* @__PURE__ */ jsxs(PopupContainer, { onClick: (event) => event.stopPropagation(), children: [
7179
7185
  !loading && /* @__PURE__ */ jsx(CloseButton, { onClick: onClose, "aria-label": "Close login popup", children: /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }) }) }),
7180
7186
  /* @__PURE__ */ jsxs(Title, { children: [
7181
7187
  /* @__PURE__ */ jsx(LogoContainer3, { children: /* @__PURE__ */ jsx(LogoImage, { src: logoSrc, alt: logoAlt }) }),
@@ -7200,7 +7206,71 @@ var LoginPopup = ({
7200
7206
  ] }) });
7201
7207
  }
7202
7208
  if (view === "otp") {
7203
- return /* @__PURE__ */ jsx(Overlay2, { onClick: loading ? void 0 : onClose, children: /* @__PURE__ */ jsxs(PopupContainer, { onClick: (event) => event.stopPropagation(), children: [
7209
+ if (gate) {
7210
+ return /* @__PURE__ */ jsxs(GateShell, { children: [
7211
+ /* @__PURE__ */ jsx(GateTint, { $reveal: otpStatus === "valid" ? 3 : 2 }),
7212
+ /* @__PURE__ */ jsxs(GateForm, { children: [
7213
+ /* @__PURE__ */ jsxs(GateBrand, { children: [
7214
+ /* @__PURE__ */ jsx(GateLogoLink, { href: "https://loafmarkets.com", "aria-label": "Go to loafmarkets.com", children: /* @__PURE__ */ jsx(GateLogo, { src: logoSrc, alt: logoAlt }) }),
7215
+ /* @__PURE__ */ jsx(GateBrandDivider, {}),
7216
+ /* @__PURE__ */ jsx(GateBetaTag, { children: "Private Beta" })
7217
+ ] }),
7218
+ /* @__PURE__ */ jsxs(GateOtpText, { children: [
7219
+ "We sent a code to",
7220
+ /* @__PURE__ */ jsx("br", {}),
7221
+ /* @__PURE__ */ jsx("strong", { children: email })
7222
+ ] }),
7223
+ /* @__PURE__ */ jsx(OTPContainer, { children: otp.map((digit, index) => /* @__PURE__ */ jsx(
7224
+ OTPInput,
7225
+ {
7226
+ id: `otp-${index}`,
7227
+ type: "text",
7228
+ inputMode: "numeric",
7229
+ maxLength: 1,
7230
+ $status: otpStatus,
7231
+ readOnly: otpStatus === "valid" || otpStatus === "checking",
7232
+ value: digit,
7233
+ onChange: (event) => handleOTPChange(index, event.target.value),
7234
+ onKeyDown: (event) => handleOTPKeyDown(index, event),
7235
+ onInput: (event) => handleOTPInput(index, event),
7236
+ onPaste: handleOTPPaste,
7237
+ autoComplete: index === 0 ? "one-time-code" : "off",
7238
+ autoFocus: index === 0
7239
+ },
7240
+ index
7241
+ )) }),
7242
+ otpStatus === "invalid" && /* @__PURE__ */ jsx(StatusMessage, { $error: true, children: "That code isn't right. Try again." }),
7243
+ /* @__PURE__ */ jsxs(GateResendText, { children: [
7244
+ /* @__PURE__ */ jsx(
7245
+ "button",
7246
+ {
7247
+ type: "button",
7248
+ onClick: (event) => {
7249
+ event.preventDefault();
7250
+ void handleSendCode(event);
7251
+ },
7252
+ children: "Resend code"
7253
+ }
7254
+ ),
7255
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: " \xB7 " }),
7256
+ /* @__PURE__ */ jsx(
7257
+ "button",
7258
+ {
7259
+ type: "button",
7260
+ onClick: () => {
7261
+ setOtp(Array(OTP_INPUT_LENGTH).fill(""));
7262
+ setOtpStatus("idle");
7263
+ setError("");
7264
+ setView("referral");
7265
+ },
7266
+ children: "Back"
7267
+ }
7268
+ )
7269
+ ] })
7270
+ ] })
7271
+ ] });
7272
+ }
7273
+ return /* @__PURE__ */ jsx(Overlay2, { $transparent: gate, onClick: loading ? void 0 : onClose, children: /* @__PURE__ */ jsxs(PopupContainer, { onClick: (event) => event.stopPropagation(), children: [
7204
7274
  !loading && /* @__PURE__ */ jsx(CloseButton, { onClick: onClose, "aria-label": "Close login popup", children: /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }) }) }),
7205
7275
  loading ? /* @__PURE__ */ jsxs(AccountCreationLoader, { children: [
7206
7276
  /* @__PURE__ */ jsx(SpinnerRing, {}),
@@ -7289,8 +7359,8 @@ var LoginPopup = ({
7289
7359
  /* @__PURE__ */ jsx("rect", { x: "3", y: "11", width: "18", height: "11", rx: "2", ry: "2" }),
7290
7360
  /* @__PURE__ */ jsx("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })
7291
7361
  ] }) }),
7292
- /* @__PURE__ */ jsx(KycHeading, { children: "Verify Your Identity" }),
7293
- /* @__PURE__ */ jsx(KycSubline, { children: "Initial offerings are only available to verified investors" }),
7362
+ /* @__PURE__ */ jsx(KycHeading, { children: "Verify Wholesale Status" }),
7363
+ /* @__PURE__ */ jsx(KycSubline, { children: "Initial offerings are only available to accredited or wholesale investors" }),
7294
7364
  /* @__PURE__ */ jsx(KycStartButton, { type: "button", onClick: handleKycStart, disabled: kycLoading, children: kycLoading ? "Verifying\u2026" : kycStatusProp === "PENDING" || kycStatusProp === "ON_HOLD" ? "Resume Verification" : "Start Verification" }),
7295
7365
  /* @__PURE__ */ jsx(KycDuration, { children: "Takes about 2 minutes" }),
7296
7366
  /* @__PURE__ */ jsx(KycDivider, {}),
@@ -7376,7 +7446,7 @@ var LoginPopup = ({
7376
7446
  /* @__PURE__ */ jsx("div", { style: { color: "rgba(255,255,255,0.5)", fontSize: "0.8rem" }, children: "Deposit USDC from an exchange or wallet" })
7377
7447
  ] })
7378
7448
  ] }),
7379
- /* @__PURE__ */ jsxs(ModalOptionCard, { onClick: handleFiatFund, disabled: fiatFundingLoading, children: [
7449
+ /* @__PURE__ */ jsxs(ModalOptionCard, { onClick: handleFiatFund, disabled: true, children: [
7380
7450
  /* @__PURE__ */ jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", style: { color: "rgba(255,255,255,0.6)", flexShrink: 0 }, children: [
7381
7451
  /* @__PURE__ */ jsx("line", { x1: "3", y1: "22", x2: "21", y2: "22" }),
7382
7452
  /* @__PURE__ */ jsx("line", { x1: "6", y1: "18", x2: "6", y2: "11" }),
@@ -7386,7 +7456,10 @@ var LoginPopup = ({
7386
7456
  /* @__PURE__ */ jsx("polygon", { points: "12 2 20 7 4 7" })
7387
7457
  ] }),
7388
7458
  /* @__PURE__ */ jsxs("div", { style: { flex: 1 }, children: [
7389
- /* @__PURE__ */ jsx("div", { style: { color: "#fff", fontWeight: 600, fontSize: "0.95rem", marginBottom: "0.25rem" }, children: fiatFundingLoading ? "Loading widget\u2026" : "Bank Deposit" }),
7459
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem", marginBottom: "0.25rem" }, children: [
7460
+ /* @__PURE__ */ jsx("span", { style: { color: "#fff", fontWeight: 600, fontSize: "0.95rem" }, children: fiatFundingLoading ? "Loading widget\u2026" : "Bank Deposit" }),
7461
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "0.6rem", fontWeight: 700, color: "#E6C87E", background: "rgba(230,198,86,0.12)", padding: "0.12rem 0.4rem", borderRadius: "4px", textTransform: "uppercase", letterSpacing: "0.04em", whiteSpace: "nowrap" }, children: "Coming Soon" })
7462
+ ] }),
7390
7463
  /* @__PURE__ */ jsx("div", { style: { color: "rgba(255,255,255,0.5)", fontSize: "0.8rem" }, children: "Deposit to your account via card or bank transfer" })
7391
7464
  ] })
7392
7465
  ] })
@@ -7417,84 +7490,162 @@ var LoginPopup = ({
7417
7490
  setReferralLoading(false);
7418
7491
  }
7419
7492
  };
7420
- const handleJoinWaitlistSkip = async () => {
7421
- if (!onJoinWaitlist || !email) {
7422
- onClose();
7423
- return;
7424
- }
7493
+ const codeSuffix = referralCode.startsWith(CODE_PREFIX) ? referralCode.slice(CODE_PREFIX.length) : referralCode.replace(/^LOAF-?/i, "");
7494
+ const setCodeSuffix = (suffix) => {
7495
+ setReferralCode(CODE_PREFIX + suffix);
7425
7496
  setReferralError("");
7426
- setWaitlistLoading(true);
7427
- try {
7428
- const message = await onJoinWaitlist(email);
7429
- setWaitlistMessage(message || "You're on the waitlist.");
7430
- setTimeout(() => onClose(), 1400);
7431
- } catch (err) {
7432
- setReferralError(err instanceof Error ? err.message : "Couldn't join the waitlist. Please try again.");
7433
- setWaitlistLoading(false);
7497
+ setError("");
7498
+ if (gate) {
7499
+ if (suffix.length === 5) void runCodeValidation(CODE_PREFIX + suffix);
7500
+ else {
7501
+ validatedCodeRef.current = null;
7502
+ setCodeStatus("idle");
7503
+ }
7434
7504
  }
7435
7505
  };
7436
- return /* @__PURE__ */ jsx(Overlay2, { onClick: onClose, children: /* @__PURE__ */ jsxs(PopupContainer, { onClick: (event) => event.stopPropagation(), children: [
7437
- /* @__PURE__ */ jsx(CloseButton, { onClick: onClose, "aria-label": "Close login popup", children: /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }) }) }),
7438
- /* @__PURE__ */ jsxs(OnboardingStepContainer, { children: [
7439
- /* @__PURE__ */ jsx("div", { style: {
7440
- width: 52,
7441
- height: 52,
7442
- borderRadius: 14,
7443
- background: "linear-gradient(135deg, rgba(230,200,126,0.15) 0%, rgba(230,200,126,0.08) 100%)",
7444
- border: "1px solid rgba(230,200,126,0.25)",
7445
- display: "flex",
7446
- alignItems: "center",
7447
- justifyContent: "center",
7448
- marginBottom: "1.1rem"
7449
- }, children: /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "#e6c87e", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", children: [
7450
- /* @__PURE__ */ jsx("path", { d: "M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" }),
7451
- /* @__PURE__ */ jsx("circle", { cx: "9", cy: "7", r: "4" }),
7452
- /* @__PURE__ */ jsx("path", { d: "M23 21v-2a4 4 0 0 0-3-3.87" }),
7453
- /* @__PURE__ */ jsx("path", { d: "M16 3.13a4 4 0 0 1 0 7.75" })
7454
- ] }) }),
7455
- /* @__PURE__ */ jsx(OnboardingHeading, { children: "Enter code to access Private Beta trading" }),
7456
- /* @__PURE__ */ jsx(OnboardingSubtext, { style: { marginBottom: "1.5rem" }, children: "Enter code to trade 100k in USDC, or skip to continue." }),
7457
- /* @__PURE__ */ jsxs(EmailFormContainer, { style: { width: "100%", marginBottom: 0 }, children: [
7506
+ const handleCodeChange = (e) => {
7507
+ setCodeSuffix(e.target.value.toUpperCase().replace(/[^A-Z0-9]/g, "").slice(0, 5));
7508
+ };
7509
+ const handleCodePaste = (e) => {
7510
+ e.preventDefault();
7511
+ const text = (e.clipboardData.getData("text") || "").toUpperCase();
7512
+ setCodeSuffix(text.replace(/^LOAF-?/, "").replace(/[^A-Z0-9]/g, "").slice(0, 5));
7513
+ };
7514
+ const codeComplete = codeSuffix.length === 5;
7515
+ const codeInput = /* @__PURE__ */ jsx(
7516
+ CodeSuffixInput,
7517
+ {
7518
+ type: "text",
7519
+ inputMode: "text",
7520
+ autoComplete: "off",
7521
+ autoCapitalize: "characters",
7522
+ spellCheck: false,
7523
+ maxLength: 5,
7524
+ "aria-label": "Access code",
7525
+ readOnly: codeStatus === "valid",
7526
+ value: codeSuffix,
7527
+ onChange: handleCodeChange,
7528
+ onPaste: handleCodePaste,
7529
+ onKeyDown: (e) => {
7530
+ if (e.key !== "Enter" || !codeComplete) return;
7531
+ if (gate) void runCodeValidation(referralCode);
7532
+ else void handleReferralSubmit();
7533
+ },
7534
+ autoFocus: true
7535
+ }
7536
+ );
7537
+ const signInStep = /* @__PURE__ */ jsxs(GateReveal, { children: [
7538
+ /* @__PURE__ */ jsxs("form", { onSubmit: handleSendCode, style: { width: "100%" }, children: [
7539
+ /* @__PURE__ */ jsxs(CodeInputWrapper, { children: [
7458
7540
  /* @__PURE__ */ jsx(
7459
- EmailInput,
7541
+ GateEmailInput,
7460
7542
  {
7461
- type: "text",
7462
- placeholder: "e.g. LOAF-XXXX",
7463
- value: referralCode,
7464
- onChange: (e) => {
7465
- setReferralCode(e.target.value.toUpperCase());
7466
- setReferralError("");
7467
- },
7468
- onKeyDown: (e) => {
7469
- if (e.key === "Enter") void handleReferralSubmit();
7470
- },
7543
+ type: "email",
7544
+ inputMode: "email",
7545
+ autoComplete: "email",
7546
+ placeholder: "Enter your email",
7547
+ value: email,
7548
+ onChange: (event) => setEmail(event.target.value),
7471
7549
  autoFocus: true
7472
7550
  }
7473
7551
  ),
7474
- /* @__PURE__ */ jsx(
7475
- SubmitButton,
7476
- {
7477
- type: "button",
7478
- onClick: () => void handleReferralSubmit(),
7479
- disabled: referralLoading || !referralCode.trim(),
7480
- children: referralLoading ? "Verifying\u2026" : "Apply Code"
7481
- }
7482
- ),
7483
- referralError && /* @__PURE__ */ jsx(StatusMessage, { $error: true, children: referralError })
7552
+ /* @__PURE__ */ jsx(GateSubmit, { type: "submit", "aria-label": "Continue", disabled: loading || !email, children: loading ? /* @__PURE__ */ jsx(GateSpinner, {}) : /* @__PURE__ */ jsx("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M5 12h14M13 6l6 6-6 6" }) }) })
7484
7553
  ] }),
7485
- waitlistMessage ? /* @__PURE__ */ jsxs(StatusMessage, { style: { marginTop: "0.75rem" }, children: [
7486
- "\u2713 ",
7487
- waitlistMessage
7488
- ] }) : /* @__PURE__ */ jsx(
7489
- OnboardingSkipButton,
7554
+ error && /* @__PURE__ */ jsx(StatusMessage, { $error: true, children: error })
7555
+ ] }),
7556
+ /* @__PURE__ */ jsxs(Fragment, { children: [
7557
+ /* @__PURE__ */ jsx(GateOr, { children: "or" }),
7558
+ /* @__PURE__ */ jsxs(GateWalletButton, { type: "button", onClick: handleWalletLogin, children: [
7559
+ /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M21 18v1c0 1.1-.9 2-2 2H5c-1.11 0-2-.9-2-2V5c0-1.1.89-2 2-2h14c1.1 0 2 .9 2 2v1h-9c-1.11 0-2 .9-2 2v8c0 1.1.89 2 2 2h9zm-9-2h10V8H12v8zm4-2.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z" }) }),
7560
+ signInMode ? "Sign in with Wallet" : "Connect Wallet"
7561
+ ] })
7562
+ ] })
7563
+ ] });
7564
+ if (gate) {
7565
+ const codeAccepted = codeStatus === "valid";
7566
+ const showSignIn = signInMode || signInRevealed;
7567
+ return /* @__PURE__ */ jsxs(GateShell, { children: [
7568
+ /* @__PURE__ */ jsx(GateTint, { $reveal: showSignIn ? 1 : 0 }),
7569
+ /* @__PURE__ */ jsxs(GateForm, { children: [
7570
+ /* @__PURE__ */ jsxs(GateBrand, { children: [
7571
+ /* @__PURE__ */ jsx(GateLogoLink, { href: "https://loafmarkets.com", "aria-label": "Go to loafmarkets.com", children: /* @__PURE__ */ jsx(GateLogo, { src: logoSrc, alt: logoAlt }) }),
7572
+ /* @__PURE__ */ jsx(GateBrandDivider, {}),
7573
+ /* @__PURE__ */ jsx(GateBetaTag, { children: "Private Beta" })
7574
+ ] }),
7575
+ signInMode ? /* @__PURE__ */ jsxs(Fragment, { children: [
7576
+ signInStep,
7577
+ /* @__PURE__ */ jsx(GateResendText, { children: /* @__PURE__ */ jsx(
7578
+ "button",
7579
+ {
7580
+ type: "button",
7581
+ onClick: () => {
7582
+ setError("");
7583
+ setSignInMode(false);
7584
+ },
7585
+ children: "\u2190 Enter access code instead"
7586
+ }
7587
+ ) })
7588
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
7589
+ /* @__PURE__ */ jsxs(CodeInputWrapper, { $status: codeStatus, children: [
7590
+ /* @__PURE__ */ jsx(CodePrefix, { children: CODE_PREFIX }),
7591
+ codeInput,
7592
+ /* @__PURE__ */ jsx(
7593
+ GateSubmit,
7594
+ {
7595
+ type: "button",
7596
+ "aria-label": "Submit access code",
7597
+ $status: codeStatus,
7598
+ disabled: codeStatus === "checking" || !codeComplete,
7599
+ onClick: () => void runCodeValidation(referralCode),
7600
+ children: codeAccepted ? /* @__PURE__ */ jsx("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M20 6L9 17l-5-5" }) }) : codeStatus === "checking" ? /* @__PURE__ */ jsx(GateSpinner, {}) : /* @__PURE__ */ jsx("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M5 12h14M13 6l6 6-6 6" }) })
7601
+ }
7602
+ )
7603
+ ] }),
7604
+ (codeStatus === "invalid" || error && !signInRevealed) && /* @__PURE__ */ jsx(StatusMessage, { $error: true, children: error || "That code isn't valid. Double-check and try again." }),
7605
+ signInRevealed ? signInStep : /* @__PURE__ */ jsxs(GateResendText, { children: [
7606
+ "Already have an account?",
7607
+ " ",
7608
+ /* @__PURE__ */ jsx(
7609
+ "button",
7610
+ {
7611
+ type: "button",
7612
+ onClick: () => {
7613
+ setError("");
7614
+ setIsSignUp(false);
7615
+ setSignInMode(true);
7616
+ },
7617
+ children: "Sign in"
7618
+ }
7619
+ )
7620
+ ] })
7621
+ ] })
7622
+ ] })
7623
+ ] });
7624
+ }
7625
+ return /* @__PURE__ */ jsx(Overlay2, { onClick: onClose, children: /* @__PURE__ */ jsxs(PopupContainer, { onClick: (event) => event.stopPropagation(), children: [
7626
+ /* @__PURE__ */ jsx(CloseButton, { onClick: onClose, "aria-label": "Close login popup", children: /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }) }) }),
7627
+ /* @__PURE__ */ jsxs(Title, { children: [
7628
+ /* @__PURE__ */ jsxs(LogoContainer3, { children: [
7629
+ /* @__PURE__ */ jsx(LogoImage, { src: logoSrc, alt: logoAlt }),
7630
+ /* @__PURE__ */ jsx(LogoBeta, { children: "Private Beta" })
7631
+ ] }),
7632
+ /* @__PURE__ */ jsx(TitleText, { children: "Enter your access code" })
7633
+ ] }),
7634
+ /* @__PURE__ */ jsxs(EmailFormContainer, { style: { width: "100%", marginTop: "1.5rem", marginBottom: 0 }, children: [
7635
+ /* @__PURE__ */ jsxs(CodeInputWrapper, { children: [
7636
+ /* @__PURE__ */ jsx(CodePrefix, { children: CODE_PREFIX }),
7637
+ codeInput
7638
+ ] }),
7639
+ /* @__PURE__ */ jsx(
7640
+ SubmitButton,
7490
7641
  {
7491
7642
  type: "button",
7492
- onClick: () => void handleJoinWaitlistSkip(),
7493
- disabled: waitlistLoading,
7494
- style: { marginTop: "0.75rem" },
7495
- children: waitlistLoading ? "Joining\u2026" : email ? "Skip for now & join Waitlist" : "Skip for now"
7643
+ onClick: () => void handleReferralSubmit(),
7644
+ disabled: referralLoading || !codeComplete,
7645
+ children: referralLoading ? "Verifying\u2026" : "Continue"
7496
7646
  }
7497
- )
7647
+ ),
7648
+ referralError && /* @__PURE__ */ jsx(StatusMessage, { $error: true, children: referralError })
7498
7649
  ] })
7499
7650
  ] }) });
7500
7651
  }
@@ -7614,8 +7765,9 @@ var Overlay2 = styled10.div`
7614
7765
  left: 0;
7615
7766
  right: 0;
7616
7767
  bottom: 0;
7617
- background-color: rgba(0, 0, 0, 0.8);
7618
- backdrop-filter: blur(4px);
7768
+ background-color: ${(props) => props.$gate ? "var(--color-background, #0a0a0a)" : props.$transparent ? "rgba(0, 0, 0, 0.45)" : "rgba(0, 0, 0, 0.8)"};
7769
+ backdrop-filter: ${(props) => props.$gate ? "none" : props.$transparent ? "blur(2px)" : "blur(4px)"};
7770
+ transition: background-color 0.4s ease, backdrop-filter 0.4s ease;
7619
7771
  display: flex;
7620
7772
  justify-content: center;
7621
7773
  align-items: center;
@@ -7632,15 +7784,15 @@ var Overlay2 = styled10.div`
7632
7784
  }
7633
7785
  `;
7634
7786
  var PopupContainer = styled10.div`
7635
- background-color: var(--color-background, #0a0a0a);
7636
- border: 1px solid rgba(230, 198, 86, 0.3);
7787
+ background-color: ${(props) => props.$gate ? "transparent" : "var(--color-background, #0a0a0a)"};
7788
+ border: ${(props) => props.$gate ? "none" : "1px solid rgba(230, 198, 86, 0.3)"};
7637
7789
  border-radius: var(--border-radius, 12px);
7638
7790
  padding: 2.5rem;
7639
7791
  max-width: 440px;
7640
7792
  width: 90%;
7641
7793
  position: relative;
7642
7794
  animation: slideUp 0.3s ease-out;
7643
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
7795
+ box-shadow: ${(props) => props.$gate ? "none" : "0 10px 30px rgba(0, 0, 0, 0.3)"};
7644
7796
 
7645
7797
  @keyframes slideUp {
7646
7798
  from {
@@ -7827,29 +7979,27 @@ var Title = styled10.h2`
7827
7979
  flex-direction: column;
7828
7980
  align-items: center;
7829
7981
  justify-content: center;
7830
- gap: 0.4rem;
7982
+ gap: 1rem;
7831
7983
  `;
7832
- styled10.p`
7833
- font-size: 0.9rem;
7834
- font-weight: 400;
7835
- color: var(--color-text-secondary, #848e9c);
7984
+ var TitleText = styled10.span`
7836
7985
  text-align: center;
7837
- margin: 0 0 2rem;
7986
+ line-height: 1.2;
7838
7987
  `;
7839
- var MainHeading = styled10.h2`
7840
- font-size: 1.5rem;
7841
- font-weight: 700;
7842
- color: var(--color-text, #eaecef);
7843
- text-align: left;
7844
- margin: 0 0 0.35rem;
7988
+ var LogoBeta = styled10.span`
7989
+ color: var(--color-accent, #E6C87E);
7990
+ font-size: 0.6rem;
7991
+ font-weight: 600;
7992
+ letter-spacing: 0.14em;
7993
+ text-transform: uppercase;
7994
+ white-space: nowrap;
7995
+ padding-bottom: 0.95rem;
7845
7996
  `;
7846
- var MainSubtitle = styled10.p`
7997
+ var Subtitle = styled10.p`
7847
7998
  font-size: 0.9rem;
7848
7999
  font-weight: 400;
7849
8000
  color: var(--color-text-secondary, #848e9c);
7850
- text-align: left;
7851
- margin: 0 0 1.5rem;
7852
- line-height: 1.5;
8001
+ text-align: center;
8002
+ margin: 0 0 2rem;
7853
8003
  `;
7854
8004
  var LogoContainer3 = styled10.div`
7855
8005
  display: flex;
@@ -7859,148 +8009,349 @@ var LogoContainer3 = styled10.div`
7859
8009
  margin-bottom: 0.5rem;
7860
8010
  `;
7861
8011
  var LogoImage = styled10.img`
7862
- height: 40px;
8012
+ height: 60px;
7863
8013
  `;
7864
- styled10.div`
8014
+ var ButtonsContainer = styled10.div`
7865
8015
  display: flex;
7866
8016
  flex-direction: column;
7867
8017
  gap: 1rem;
7868
8018
  margin-bottom: 1.5rem;
7869
8019
  `;
7870
- styled10.div`
8020
+ var SignUpPrompt = styled10.div`
7871
8021
  text-align: center;
7872
8022
  font-size: 0.875rem;
7873
8023
  color: var(--color-text-secondary, #848e9c);
7874
8024
  margin-bottom: 0.5rem;
7875
8025
  `;
7876
- styled10.button`
7877
- background: none;
7878
- border: none;
7879
- padding: 0;
7880
- font: inherit;
7881
- color: var(--color-accent, #E6C87E);
7882
- text-decoration: underline;
7883
- cursor: pointer;
8026
+ var SignUpLink = styled10.button`
8027
+ background: none;
8028
+ border: none;
8029
+ padding: 0;
8030
+ font: inherit;
8031
+ color: var(--color-accent, #E6C87E);
8032
+ text-decoration: underline;
8033
+ cursor: pointer;
8034
+
8035
+ &:hover {
8036
+ opacity: 0.85;
8037
+ }
8038
+ `;
8039
+ var LoginButton = styled10.button`
8040
+ display: flex;
8041
+ align-items: center;
8042
+ justify-content: center;
8043
+ gap: 0.75rem;
8044
+ padding: 1rem 1.5rem;
8045
+ background-color: var(--color-background-light, #1a1a1a);
8046
+ border: 1px solid rgba(230, 198, 86, 0.2);
8047
+ border-radius: var(--border-radius, 8px);
8048
+ color: var(--color-text, #eaecef);
8049
+ font-size: 1rem;
8050
+ font-weight: 500;
8051
+ cursor: pointer;
8052
+ transition: all 0.2s ease;
8053
+
8054
+ &:hover {
8055
+ background-color: rgba(230, 198, 86, 0.1);
8056
+ border-color: var(--color-accent, #E6C87E);
8057
+ }
8058
+
8059
+ svg {
8060
+ width: 20px;
8061
+ height: 20px;
8062
+ color: var(--color-accent, #E6C87E);
8063
+ }
8064
+ `;
8065
+ var InfoText = styled10.p`
8066
+ color: var(--color-text-secondary, #848e9c);
8067
+ font-size: 0.875rem;
8068
+ text-align: center;
8069
+ line-height: 1.5;
8070
+ `;
8071
+ var EmailFormContainer = styled10.div`
8072
+ display: flex;
8073
+ flex-direction: column;
8074
+ gap: 1rem;
8075
+ margin-bottom: 1.5rem;
8076
+ `;
8077
+ var EmailInput = styled10.input`
8078
+ width: 100%;
8079
+ padding: 1rem 1.25rem;
8080
+ background-color: var(--color-background-light, #1a1a1a);
8081
+ border: 1px solid rgba(230, 198, 86, 0.2);
8082
+ border-radius: var(--border-radius, 8px);
8083
+ color: var(--color-text, #eaecef);
8084
+ font-size: 1rem;
8085
+ transition: all 0.2s ease;
8086
+
8087
+ &:focus {
8088
+ outline: none;
8089
+ border-color: var(--color-accent, #E6C87E);
8090
+ }
8091
+
8092
+ &::placeholder {
8093
+ color: var(--color-text-secondary, #848e9c);
8094
+ }
8095
+ `;
8096
+ var codeBorderColor = (status, focused) => {
8097
+ if (status === "valid") return "var(--color-positive, #00C076)";
8098
+ if (status === "invalid") return "var(--color-negative, #FF5757)";
8099
+ return focused ? "var(--color-accent, #E6C87E)" : "rgba(230, 198, 86, 0.2)";
8100
+ };
8101
+ var CodeInputWrapper = styled10.div`
8102
+ display: flex;
8103
+ align-items: center;
8104
+ width: 100%;
8105
+ padding: 1rem 1.25rem;
8106
+ background-color: var(--color-background-light, #1a1a1a);
8107
+ border: 1px solid ${(props) => codeBorderColor(props.$status, false)};
8108
+ border-radius: var(--border-radius, 8px);
8109
+ box-shadow: ${(props) => props.$status === "valid" ? "0 0 0 1px var(--color-positive, #00C076)" : props.$status === "invalid" ? "0 0 0 1px var(--color-negative, #FF5757)" : "none"};
8110
+ transition: all 0.2s ease;
8111
+
8112
+ &:focus-within {
8113
+ border-color: ${(props) => codeBorderColor(props.$status, true)};
8114
+ }
8115
+ `;
8116
+ var CodePrefix = styled10.span`
8117
+ color: var(--color-text-secondary, #848e9c);
8118
+ font-size: 1.1rem;
8119
+ font-weight: 600;
8120
+ letter-spacing: 0.22em;
8121
+ font-family: 'Space Grotesk', monospace;
8122
+ user-select: none;
8123
+ `;
8124
+ var CodeSuffixInput = styled10.input`
8125
+ flex: 1;
8126
+ min-width: 0;
8127
+ border: none;
8128
+ background: transparent;
8129
+ color: var(--color-text, #eaecef);
8130
+ font-size: 1.1rem;
8131
+ font-weight: 600;
8132
+ letter-spacing: 0.22em;
8133
+ text-transform: uppercase;
8134
+ font-family: 'Space Grotesk', monospace;
8135
+ /* Explicit line-height so iOS in-app WebViews size the caret to the text
8136
+ box instead of rendering a short, top-aligned caret (line-height: normal
8137
+ + webfont + letter-spacing miscomputes it in some WebViews). */
8138
+ line-height: 1.5;
8139
+
8140
+ &:focus {
8141
+ outline: none;
8142
+ }
8143
+
8144
+ &::placeholder {
8145
+ color: rgba(132, 142, 156, 0.45);
8146
+ letter-spacing: 0.22em;
8147
+ }
8148
+ `;
8149
+ var GateBrand = styled10.div`
8150
+ display: flex;
8151
+ align-items: center;
8152
+ justify-content: center;
8153
+ gap: 0.7rem;
8154
+ margin-bottom: 0.6rem;
8155
+ `;
8156
+ var GateLogoLink = styled10.a`
8157
+ display: inline-flex;
8158
+ align-items: center;
8159
+ cursor: pointer;
8160
+ transition: opacity 0.15s ease;
8161
+
8162
+ &:hover {
8163
+ opacity: 0.85;
8164
+ }
8165
+ `;
8166
+ var GateLogo = styled10.img`
8167
+ height: 44px;
8168
+ display: block;
8169
+ `;
8170
+ var GateBrandDivider = styled10.span`
8171
+ width: 1px;
8172
+ height: 26px;
8173
+ background: rgba(255, 255, 255, 0.18);
8174
+ `;
8175
+ var GateBetaTag = styled10.span`
8176
+ color: var(--color-text-secondary, #848e9c);
8177
+ font-size: 0.8rem;
8178
+ font-weight: 500;
8179
+ letter-spacing: 0.28em;
8180
+ text-transform: uppercase;
8181
+ `;
8182
+ var GateForm = styled10.div`
8183
+ display: flex;
8184
+ flex-direction: column;
8185
+ align-items: center;
8186
+ gap: 0.9rem;
8187
+ width: 100%;
8188
+ max-width: 360px;
8189
+ position: relative;
8190
+ z-index: 1;
8191
+ `;
8192
+ var GateShell = styled10.div`
8193
+ position: fixed;
8194
+ inset: 0;
8195
+ display: flex;
8196
+ align-items: center;
8197
+ justify-content: center;
8198
+ z-index: 10000;
8199
+ background-color: rgba(10, 10, 12, 0.66);
8200
+ `;
8201
+ var GateTint = styled10.div`
8202
+ position: absolute;
8203
+ left: 0;
8204
+ right: 0;
8205
+ bottom: 0;
8206
+ height: calc(${(props) => (3 - Math.max(0, Math.min(3, props.$reveal))) / 3 * 100}% + 90px);
8207
+ background: linear-gradient(
8208
+ to top,
8209
+ var(--color-background, #0a0a0c) calc(100% - 90px),
8210
+ rgba(10, 10, 12, 0) 100%
8211
+ );
8212
+ transition: height 0.65s ease;
8213
+ pointer-events: none;
8214
+ `;
8215
+ var GateOtpText = styled10.p`
8216
+ margin: 0;
8217
+ text-align: center;
8218
+ font-size: 0.9rem;
8219
+ line-height: 1.5;
8220
+ color: var(--color-text-secondary, #848e9c);
8221
+
8222
+ strong {
8223
+ color: var(--color-text, #eaecef);
8224
+ font-weight: 600;
8225
+ }
8226
+ `;
8227
+ var GateResendText = styled10.p`
8228
+ margin: 0.2rem 0 0;
8229
+ font-size: 0.85rem;
8230
+ color: var(--color-text-secondary, #848e9c);
7884
8231
 
7885
- &:hover {
7886
- opacity: 0.85;
8232
+ button {
8233
+ background: none;
8234
+ border: none;
8235
+ color: var(--color-accent, #E6C87E);
8236
+ cursor: pointer;
8237
+ text-decoration: underline;
8238
+ font-size: inherit;
7887
8239
  }
7888
8240
  `;
7889
- var LoginButton = styled10.button`
8241
+ var GateSubmit = styled10.button`
7890
8242
  display: flex;
7891
- width: 100%;
7892
- box-sizing: border-box;
7893
8243
  align-items: center;
7894
8244
  justify-content: center;
7895
- gap: 0.55rem;
7896
- padding: 0.55rem 1.1rem;
7897
- background-color: var(--color-background-light, #1a1a1a);
7898
- border: 1px solid rgba(230, 198, 86, 0.2);
7899
- border-radius: var(--border-radius, 8px);
7900
- color: var(--color-text, #eaecef);
7901
- font-size: 0.82rem;
7902
- font-weight: 500;
8245
+ flex-shrink: 0;
8246
+ width: 28px;
8247
+ height: 28px;
8248
+ margin-left: 0.5rem;
8249
+ padding: 0;
8250
+ background: none;
8251
+ border: none;
7903
8252
  cursor: pointer;
7904
- transition: all 0.2s ease;
8253
+ color: ${(props) => props.$status === "valid" ? "var(--color-positive, #00C076)" : props.$status === "invalid" ? "var(--color-negative, #FF5757)" : "var(--color-accent, #E6C87E)"};
8254
+ transition: color 0.15s ease, transform 0.15s ease, opacity 0.15s ease;
7905
8255
 
7906
- svg { width: 16px; height: 16px; }
7907
-
7908
- &:hover {
7909
- background-color: rgba(230, 198, 86, 0.1);
7910
- border-color: var(--color-accent, #E6C87E);
8256
+ &:not(:disabled):hover {
8257
+ transform: translateX(2px);
7911
8258
  }
7912
8259
 
7913
- svg {
7914
- width: 20px;
7915
- height: 20px;
7916
- color: var(--color-accent, #E6C87E);
8260
+ &:disabled {
8261
+ opacity: ${(props) => props.$status === "valid" ? 1 : 0.35};
8262
+ color: ${(props) => props.$status === "valid" ? "var(--color-positive, #00C076)" : "var(--color-text-secondary, #848e9c)"};
8263
+ cursor: default;
7917
8264
  }
7918
8265
  `;
7919
- var InfoText = styled10.p`
7920
- color: var(--color-text-secondary, #848e9c);
7921
- font-size: 0.875rem;
7922
- text-align: center;
7923
- line-height: 1.5;
7924
- `;
7925
- var EmailFormContainer = styled10.div`
7926
- display: flex;
7927
- flex-direction: column;
7928
- gap: 1rem;
7929
- margin-bottom: 1.5rem;
7930
- `;
7931
- var EmailInput = styled10.input`
7932
- width: 100%;
7933
- padding: 1rem 1.25rem;
7934
- background-color: var(--color-background-light, #1a1a1a);
7935
- border: 1px solid rgba(230, 198, 86, 0.2);
7936
- border-radius: var(--border-radius, 8px);
8266
+ var GateEmailInput = styled10.input`
8267
+ flex: 1;
8268
+ min-width: 0;
8269
+ border: none;
8270
+ background: transparent;
7937
8271
  color: var(--color-text, #eaecef);
7938
8272
  font-size: 1rem;
7939
- transition: all 0.2s ease;
7940
8273
 
7941
8274
  &:focus {
7942
8275
  outline: none;
7943
- border-color: var(--color-accent, #E6C87E);
7944
8276
  }
7945
8277
 
7946
8278
  &::placeholder {
7947
- color: var(--color-text-secondary, #848e9c);
8279
+ color: rgba(132, 142, 156, 0.5);
8280
+ }
8281
+ `;
8282
+ var GateSpinner = styled10.span`
8283
+ width: 18px;
8284
+ height: 18px;
8285
+ border: 2px solid rgba(230, 198, 86, 0.3);
8286
+ border-top-color: var(--color-accent, #E6C87E);
8287
+ border-radius: 50%;
8288
+ animation: gateSpin 0.7s linear infinite;
8289
+
8290
+ @keyframes gateSpin {
8291
+ to { transform: rotate(360deg); }
7948
8292
  }
7949
8293
  `;
7950
- var GateForm = styled10.form`
8294
+ var GateOr = styled10.div`
7951
8295
  display: flex;
7952
- flex-direction: column;
8296
+ align-items: center;
7953
8297
  gap: 0.6rem;
7954
8298
  width: 100%;
7955
- `;
7956
- var GateInputRow = styled10.div`
7957
- position: relative;
7958
- width: 100%;
7959
- `;
7960
- var GateInput = styled10.input`
7961
- width: 100%;
7962
- box-sizing: border-box;
7963
- padding: 1rem 3.25rem 1rem 1.25rem;
7964
- background-color: var(--color-background-light, #1a1a1a);
7965
- border: 1px solid rgba(230, 198, 86, 0.2);
7966
- border-radius: var(--border-radius, 8px);
7967
- color: var(--color-text, #eaecef);
7968
- font-size: 1rem;
7969
- transition: all 0.2s ease;
8299
+ color: var(--color-text-secondary, #848e9c);
8300
+ font-size: 0.78rem;
8301
+ text-transform: lowercase;
7970
8302
 
7971
- &:focus { outline: none; border-color: var(--color-accent, #E6C87E); }
7972
- &::placeholder { color: var(--color-text-secondary, #848e9c); }
8303
+ &::before,
8304
+ &::after {
8305
+ content: "";
8306
+ flex: 1;
8307
+ height: 1px;
8308
+ background: rgba(255, 255, 255, 0.1);
8309
+ }
7973
8310
  `;
7974
- var GateSubmit = styled10.button`
7975
- position: absolute;
7976
- top: 50%;
7977
- right: 0.55rem;
7978
- transform: translateY(-50%);
7979
- width: 34px;
7980
- height: 34px;
7981
- display: flex;
8311
+ var GateWalletButton = styled10.button`
8312
+ display: inline-flex;
7982
8313
  align-items: center;
7983
8314
  justify-content: center;
7984
- background: none;
7985
- border: none;
7986
- border-radius: 8px;
8315
+ gap: 0.5rem;
8316
+ margin-top: 0.1rem;
8317
+ padding: 0.55rem 1.1rem;
8318
+ background: transparent;
8319
+ border: 1px solid rgba(255, 255, 255, 0.12);
8320
+ border-radius: var(--border-radius, 8px);
7987
8321
  color: var(--color-text-secondary, #848e9c);
8322
+ font-size: 0.85rem;
8323
+ font-weight: 500;
7988
8324
  cursor: pointer;
7989
- transition: color 0.2s ease, background 0.2s ease;
8325
+ transition: border-color 0.15s ease, color 0.15s ease;
8326
+
8327
+ svg {
8328
+ width: 16px;
8329
+ height: 16px;
8330
+ }
7990
8331
 
7991
- svg { width: 18px; height: 18px; }
7992
- &:hover:not(:disabled) { color: var(--color-accent, #E6C87E); background: rgba(230, 198, 86, 0.1); }
7993
- &:disabled { opacity: 0.4; cursor: not-allowed; }
8332
+ &:hover {
8333
+ border-color: rgba(230, 198, 86, 0.4);
8334
+ color: var(--color-text, #eaecef);
8335
+ }
7994
8336
  `;
7995
- var GateOr = styled10.div`
8337
+ var GateReveal = styled10.div`
7996
8338
  display: flex;
8339
+ flex-direction: column;
7997
8340
  align-items: center;
7998
- gap: 0.75rem;
7999
- color: var(--color-text-secondary, #848e9c);
8000
- font-size: 0.8rem;
8001
- margin: 0.4rem 0;
8341
+ gap: 0.9rem;
8342
+ width: 100%;
8343
+ animation: gateReveal 0.3s ease-out;
8002
8344
 
8003
- &::before, &::after { content: ""; flex: 1; height: 1px; background: rgba(255, 255, 255, 0.08); }
8345
+ @keyframes gateReveal {
8346
+ from {
8347
+ opacity: 0;
8348
+ transform: translateY(-6px);
8349
+ }
8350
+ to {
8351
+ opacity: 1;
8352
+ transform: translateY(0);
8353
+ }
8354
+ }
8004
8355
  `;
8005
8356
  var OTPContainer = styled10.div`
8006
8357
  display: flex;
@@ -8014,15 +8365,17 @@ var OTPInput = styled10.input`
8014
8365
  font-size: 1.5rem;
8015
8366
  font-weight: 600;
8016
8367
  background-color: var(--color-background-light, #1a1a1a);
8017
- border: 1px solid rgba(230, 198, 86, 0.2);
8368
+ border: 1px solid ${(props) => codeBorderColor(props.$status, false)};
8018
8369
  border-radius: var(--border-radius, 8px);
8019
- color: var(--color-accent, #E6C87E);
8370
+ color: ${(props) => props.$status === "valid" ? "var(--color-positive, #00C076)" : props.$status === "invalid" ? "var(--color-negative, #FF5757)" : "var(--color-accent, #E6C87E)"};
8371
+ box-shadow: ${(props) => props.$status === "valid" ? "0 0 0 1px var(--color-positive, #00C076)" : props.$status === "invalid" ? "0 0 0 1px var(--color-negative, #FF5757)" : "none"};
8372
+ opacity: ${(props) => props.$status === "checking" ? 0.6 : 1};
8020
8373
  transition: all 0.2s ease;
8021
8374
 
8022
8375
  &:focus {
8023
8376
  outline: none;
8024
- border-color: var(--color-accent, #E6C87E);
8025
- box-shadow: 0 0 0 2px rgba(230, 198, 86, 0.2);
8377
+ border-color: ${(props) => codeBorderColor(props.$status, true)};
8378
+ box-shadow: 0 0 0 2px ${(props) => props.$status === "valid" ? "rgba(0, 192, 118, 0.25)" : props.$status === "invalid" ? "rgba(255, 87, 87, 0.25)" : "rgba(230, 198, 86, 0.2)"};
8026
8379
  }
8027
8380
  `;
8028
8381
  var SubmitButton = styled10.button`
@@ -8390,15 +8743,24 @@ var SpinnerRing = styled10.div`
8390
8743
  }
8391
8744
  `;
8392
8745
  var PREPARING_FACTS = [
8393
- "Property is the world's largest asset class \u2014 worth over US$300 trillion.",
8394
- "Loaf settles trades in about 1 second. Traditional property takes ~90 days.",
8746
+ // Exchange
8395
8747
  "Our matching engine processes orders in 30 microseconds.",
8396
- "Every token is backed 1:1 by land-title-registered property.",
8397
- "Own a slice of premium real estate \u2014 no mortgage, no paperwork.",
8748
+ "Loaf settles in 1 second. Traditional property takes 30-90 days.",
8398
8749
  "Buy and sell property as easily as a stock, on a live orderbook.",
8399
- "Earn rental yield on the exact fraction of property you own.",
8400
- "Title-backed by the NSW Land Registry \u2014 real ownership, on-chain.",
8401
- "Loaf ran the world's first property IPO."
8750
+ "Continuous price discovery, not a single buyer negotiating in a back room.",
8751
+ "Algo-trade property on our API. Build a bot with Claude using our docs.",
8752
+ // What you're holding
8753
+ "Hold shares secured 1:1 by the underlying property, as regulated securities.",
8754
+ "The same economic exposure as owning the property. Without the friction, gatekeeping, or illiquidity.",
8755
+ "Securitisation of property brings a new dimension of flexibility and tradability.",
8756
+ // Frictionless
8757
+ "Settle in seconds, then sell again whenever you want. No waiting days or weeks.",
8758
+ "99% of the frictions in property transactions can be, and have been, reduced.",
8759
+ // Vision
8760
+ "A family office can rebalance a billion dollars in an afternoon, but couldn't sell a $50M property in under six months.",
8761
+ "Family offices and HNWIs prize capital flexibility. Loaf makes property flexible.",
8762
+ "There's no law of physics that says the world's oldest asset class must stay illiquid.",
8763
+ "What if property underwent an IPO?"
8402
8764
  ];
8403
8765
  var FactCard = styled10.div`
8404
8766
  margin-top: 1.5rem;
@@ -8506,7 +8868,7 @@ var LogoImage2 = styled10.img`
8506
8868
  height: 30px;
8507
8869
  width: auto;
8508
8870
  `;
8509
- var LogoBeta = styled10.span`
8871
+ var LogoBeta2 = styled10.span`
8510
8872
  font-size: 0.6rem;
8511
8873
  font-weight: 600;
8512
8874
  letter-spacing: 0.08em;
@@ -8664,7 +9026,7 @@ function OnboardingGuide({
8664
9026
  /* @__PURE__ */ jsx(CloseButton2, { onClick: onClose, "aria-label": "Skip", children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 6l12 12M18 6L6 18" }) }) }),
8665
9027
  /* @__PURE__ */ jsxs(LogoRow, { children: [
8666
9028
  /* @__PURE__ */ jsx(LogoImage2, { src: logoSrc, alt: "Loaf" }),
8667
- /* @__PURE__ */ jsx(LogoBeta, { children: "Private Beta" })
9029
+ /* @__PURE__ */ jsx(LogoBeta2, { children: "Private Beta" })
8668
9030
  ] }),
8669
9031
  /* @__PURE__ */ jsx(Title2, { children: "You're in \u{1F389}" }),
8670
9032
  /* @__PURE__ */ jsx(Subtitle2, { children: "Here's everything you need to make your first trade." }),
@@ -12034,7 +12396,8 @@ function AssetSelectorBar({
12034
12396
  onSelect,
12035
12397
  trailing,
12036
12398
  imageUrl,
12037
- badgeLabel
12399
+ badgeLabel,
12400
+ tickerPrimary
12038
12401
  }) {
12039
12402
  const [isDropdownOpen, setIsDropdownOpen] = useState(false);
12040
12403
  const hasItems = selectorItems && selectorItems.length > 0;
@@ -12045,9 +12408,14 @@ function AssetSelectorBar({
12045
12408
  ];
12046
12409
  return /* @__PURE__ */ jsx(AssetSelectorWrapper, { children: /* @__PURE__ */ jsxs(IPOAssetSelector, { children: [
12047
12410
  imageUrl && /* @__PURE__ */ jsx(AssetThumbnail, { src: imageUrl, alt: propertyName }),
12048
- /* @__PURE__ */ jsx(AssetSelectorDropdown, { onClick: () => hasItems && setIsDropdownOpen((prev) => !prev), children: /* @__PURE__ */ jsxs(AssetName, { children: [
12049
- propertyName,
12050
- currentTicker ? ` (${currentTicker.toUpperCase()})` : "",
12411
+ /* @__PURE__ */ jsx(AssetSelectorDropdown, { $flush: tickerPrimary, onClick: () => hasItems && setIsDropdownOpen((prev) => !prev), children: /* @__PURE__ */ jsxs(AssetName, { children: [
12412
+ tickerPrimary && currentTicker ? /* @__PURE__ */ jsxs(AssetIdentity, { children: [
12413
+ /* @__PURE__ */ jsx(AssetTicker, { children: currentTicker.toUpperCase() }),
12414
+ /* @__PURE__ */ jsx(AssetSubName, { children: propertyName })
12415
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
12416
+ propertyName,
12417
+ currentTicker ? ` (${currentTicker.toUpperCase()})` : ""
12418
+ ] }),
12051
12419
  badgeLabel && /* @__PURE__ */ jsx(CompetitionBadge, { children: badgeLabel }),
12052
12420
  hasItems && /* @__PURE__ */ jsx(
12053
12421
  "svg",
@@ -12076,7 +12444,7 @@ function AssetSelectorBar({
12076
12444
  ] })
12077
12445
  ] })
12078
12446
  ] }, m.label)) }),
12079
- trailing != null && /* @__PURE__ */ jsx(TrailingWrapper, { children: /* @__PURE__ */ jsx(TrailingInner, { children: trailing }) }),
12447
+ trailing != null && /* @__PURE__ */ jsx(TrailingWrapper, { className: "asset-bar-trailing", children: /* @__PURE__ */ jsx(TrailingInner, { children: trailing }) }),
12080
12448
  isDropdownOpen && hasItems && /* @__PURE__ */ jsx(IPODropdown, { children: selectorItems.map((item) => {
12081
12449
  const isCurrent = item.tokenName === currentTokenName;
12082
12450
  const status = item.status?.toUpperCase();
@@ -12167,13 +12535,40 @@ var AssetSelectorDropdown = styled10.div`
12167
12535
  display: flex;
12168
12536
  align-items: center;
12169
12537
  cursor: pointer;
12170
- padding: 0.5rem;
12538
+ /* $flush (tickerPrimary): no horizontal padding / no hover box, so opening the
12539
+ selector doesn't make the ticker appear shoved right by the highlight box. */
12540
+ padding: ${(p) => p.$flush ? "0.25rem 0" : "0.5rem"};
12171
12541
  border-radius: 8px;
12172
- transition: all 0.2s ease;
12542
+ transition: ${(p) => p.$flush ? "none" : "all 0.2s ease"};
12173
12543
  flex: 0 1 auto;
12174
12544
  min-width: 0;
12175
12545
  overflow: hidden;
12176
- &:hover { background-color: rgba(255,255,255,0.05); }
12546
+ &:hover { background-color: ${(p) => p.$flush ? "transparent" : "rgba(255,255,255,0.05)"}; }
12547
+ `;
12548
+ var AssetIdentity = styled10.div`
12549
+ display: flex;
12550
+ flex-direction: column;
12551
+ min-width: 0;
12552
+ line-height: 1.2;
12553
+ `;
12554
+ var AssetTicker = styled10.span`
12555
+ font-weight: 700;
12556
+ font-size: 1.35rem;
12557
+ color: var(--color-text);
12558
+ letter-spacing: -0.01em;
12559
+ white-space: nowrap;
12560
+ overflow: hidden;
12561
+ text-overflow: ellipsis;
12562
+
12563
+ @media (max-width: 480px) { font-size: 1.2rem; }
12564
+ `;
12565
+ var AssetSubName = styled10.span`
12566
+ font-size: 0.8rem;
12567
+ font-weight: 500;
12568
+ color: rgba(255, 255, 255, 0.4);
12569
+ white-space: nowrap;
12570
+ overflow: hidden;
12571
+ text-overflow: ellipsis;
12177
12572
  `;
12178
12573
  var AssetName = styled10.div`
12179
12574
  display: flex;
@@ -14447,6 +14842,7 @@ function PortfolioActivityPanel({
14447
14842
  showPositionsTab = false,
14448
14843
  showSubscriptionsTab = true,
14449
14844
  onPositionClick,
14845
+ onSharePosition,
14450
14846
  onAssetClick,
14451
14847
  onClosePosition,
14452
14848
  offeringOrders = [],
@@ -14738,17 +15134,23 @@ function PortfolioActivityPanel({
14738
15134
  ] }),
14739
15135
  /* @__PURE__ */ jsxs(CPnlCell, { children: [
14740
15136
  /* @__PURE__ */ jsx(CCellLabel, { children: "PNL (%)" }),
14741
- /* @__PURE__ */ jsxs(CPnlLine, { children: [
14742
- /* @__PURE__ */ jsxs(CPnlValue, { $positive: isPositive, children: [
14743
- isPositive ? "+" : "-",
14744
- formatCurrency4(Math.abs(pnl))
15137
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.4rem" }, children: [
15138
+ /* @__PURE__ */ jsxs(CPnlLine, { children: [
15139
+ /* @__PURE__ */ jsxs(CPnlValue, { $positive: isPositive, children: [
15140
+ isPositive ? "+" : "-",
15141
+ formatCurrency4(Math.abs(pnl))
15142
+ ] }),
15143
+ /* @__PURE__ */ jsxs(CPnlPct, { $positive: isPositive, children: [
15144
+ "(",
15145
+ isPositive ? "+" : "",
15146
+ pnlPercent.toFixed(1),
15147
+ "%)"
15148
+ ] })
14745
15149
  ] }),
14746
- /* @__PURE__ */ jsxs(CPnlPct, { $positive: isPositive, children: [
14747
- "(",
14748
- isPositive ? "+" : "",
14749
- pnlPercent.toFixed(1),
14750
- "%)"
14751
- ] })
15150
+ onSharePosition && !pos.settling && /* @__PURE__ */ jsx(CShareIconBtn, { role: "button", "aria-label": "Share position", onClick: (e) => {
15151
+ e.stopPropagation();
15152
+ onSharePosition(pos);
15153
+ }, children: /* @__PURE__ */ jsx(ShareIcon, {}) })
14752
15154
  ] })
14753
15155
  ] }),
14754
15156
  /* @__PURE__ */ jsx(CChevronWrap, { $open: posExpanded, children: /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "rgba(255,255,255,0.5)", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("polyline", { points: "6 9 12 15 18 9" }) }) })
@@ -14768,16 +15170,16 @@ function PortfolioActivityPanel({
14768
15170
  /* @__PURE__ */ jsx(CDValue, { children: formatCurrency4(pos.marketPrice) })
14769
15171
  ] })
14770
15172
  ] }),
14771
- onClosePosition && !pos.settling && /* @__PURE__ */ jsxs(CActionRow, { children: [
14772
- /* @__PURE__ */ jsx(CCloseBtn, { onClick: () => onClosePosition(pos.tokenName, "market", pos.quantity, pos.marketPrice), children: "Market Sell" }),
14773
- /* @__PURE__ */ jsx(CCloseBtn, { onClick: () => onClosePosition(pos.tokenName, "limit", pos.quantity, pos.marketPrice), children: "Limit Sell" })
15173
+ (onClosePosition || onSharePosition) && !pos.settling && /* @__PURE__ */ jsxs(CActionRow, { children: [
15174
+ onClosePosition && /* @__PURE__ */ jsx(CCloseBtn, { onClick: () => onClosePosition(pos.tokenName, "market", pos.quantity, pos.marketPrice), children: "Market Sell" }),
15175
+ onClosePosition && /* @__PURE__ */ jsx(CCloseBtn, { onClick: () => onClosePosition(pos.tokenName, "limit", pos.quantity, pos.marketPrice), children: "Limit Sell" })
14774
15176
  ] })
14775
15177
  ] })
14776
15178
  ] }, pos.tokenName);
14777
15179
  })
14778
15180
  ] }),
14779
15181
  positions.length > 0 && !compactPositions && /* @__PURE__ */ jsxs(Fragment, { children: [
14780
- /* @__PURE__ */ jsxs(PositionsHeader, { $hasClose: !!onClosePosition, children: [
15182
+ /* @__PURE__ */ jsxs(PositionsHeader, { $hasClose: !!(onClosePosition || onSharePosition), children: [
14781
15183
  /* @__PURE__ */ jsxs(SortableHeaderCell, { $active: posSortKey === "asset", onClick: () => handlePosSort("asset"), children: [
14782
15184
  "Asset ",
14783
15185
  posSortKey === "asset" && (posSortAsc ? "\u2191" : "\u2193")
@@ -14810,7 +15212,7 @@ function PortfolioActivityPanel({
14810
15212
  const pnl = pos.propertyPnl ?? currentValue - costBasis;
14811
15213
  const pnlPercent = pos.propertyPnlPercent ?? pos.percentChange ?? 0;
14812
15214
  const isPositive = pnl >= 0;
14813
- return /* @__PURE__ */ jsxs(PositionsRow, { $hasClose: !!onClosePosition, children: [
15215
+ return /* @__PURE__ */ jsxs(PositionsRow, { $hasClose: !!(onClosePosition || onSharePosition), children: [
14814
15216
  /* @__PURE__ */ jsx(PositionsCell, { children: /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
14815
15217
  /* @__PURE__ */ jsx(HoldingsPropertyLink, { onClick: () => onPositionClick?.(pos.tokenName, pos.isIpoAllocation), children: pos.tokenName }),
14816
15218
  pos.settling && /* @__PURE__ */ jsx(SettlingTip, { children: /* @__PURE__ */ jsx(SettlingSpinner, {}) })
@@ -14827,23 +15229,29 @@ function PortfolioActivityPanel({
14827
15229
  ] }) }),
14828
15230
  /* @__PURE__ */ jsx(PositionsCell, { children: /* @__PURE__ */ jsx("span", { style: { color: "rgba(255,255,255,0.7)", fontSize: "0.8rem" }, children: formatCurrency4(pos.averageEntryPrice) }) }),
14829
15231
  /* @__PURE__ */ jsx(PositionsCell, { children: /* @__PURE__ */ jsx("span", { style: { color: "#fff", fontSize: "0.8rem" }, children: formatCurrency4(pos.marketPrice) }) }),
14830
- /* @__PURE__ */ jsxs(PositionsCell, { children: [
14831
- /* @__PURE__ */ jsxs(PnLAmount, { $positive: isPositive, children: [
14832
- isPositive ? "+" : "-",
14833
- formatCurrency4(Math.abs(pnl))
15232
+ /* @__PURE__ */ jsx(PositionsCell, { children: /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: "0.4rem" }, children: [
15233
+ /* @__PURE__ */ jsxs("span", { style: { display: "flex", flexDirection: "column" }, children: [
15234
+ /* @__PURE__ */ jsxs(PnLAmount, { $positive: isPositive, children: [
15235
+ isPositive ? "+" : "-",
15236
+ formatCurrency4(Math.abs(pnl))
15237
+ ] }),
15238
+ /* @__PURE__ */ jsxs(PnLPercentage, { $positive: isPositive, children: [
15239
+ isPositive ? "+" : "",
15240
+ pnlPercent.toFixed(1),
15241
+ "%"
15242
+ ] })
14834
15243
  ] }),
14835
- /* @__PURE__ */ jsxs(PnLPercentage, { $positive: isPositive, children: [
14836
- isPositive ? "+" : "",
14837
- pnlPercent.toFixed(1),
14838
- "%"
14839
- ] })
14840
- ] }),
14841
- onClosePosition && /* @__PURE__ */ jsx(PositionsCell, { children: !pos.settling && /* @__PURE__ */ jsxs(CloseActions, { children: [
14842
- /* @__PURE__ */ jsx(CloseBtn, { onClick: (e) => {
15244
+ onSharePosition && !pos.settling && /* @__PURE__ */ jsx(CShareIconBtn, { role: "button", "aria-label": "Share position", onClick: (e) => {
15245
+ e.stopPropagation();
15246
+ onSharePosition(pos);
15247
+ }, children: /* @__PURE__ */ jsx(ShareIcon, {}) })
15248
+ ] }) }),
15249
+ (onClosePosition || onSharePosition) && /* @__PURE__ */ jsx(PositionsCell, { children: !pos.settling && /* @__PURE__ */ jsxs(CloseActions, { children: [
15250
+ onClosePosition && /* @__PURE__ */ jsx(CloseBtn, { onClick: (e) => {
14843
15251
  e.stopPropagation();
14844
15252
  onClosePosition(pos.tokenName, "market", pos.quantity, pos.marketPrice);
14845
15253
  }, children: "Market Sell" }),
14846
- /* @__PURE__ */ jsx(CloseBtn, { onClick: (e) => {
15254
+ onClosePosition && /* @__PURE__ */ jsx(CloseBtn, { onClick: (e) => {
14847
15255
  e.stopPropagation();
14848
15256
  onClosePosition(pos.tokenName, "limit", pos.quantity, pos.marketPrice);
14849
15257
  }, children: "Limit Sell" })
@@ -14851,27 +15259,30 @@ function PortfolioActivityPanel({
14851
15259
  ] }, pos.tokenName);
14852
15260
  })
14853
15261
  ] }),
14854
- positions.length > 0 && closeAllOpen && onClosePosition && /* @__PURE__ */ jsx(CloseAllOverlay, { onClick: () => setCloseAllOpen(false), children: /* @__PURE__ */ jsxs(CloseAllModal, { onClick: (e) => e.stopPropagation(), children: [
14855
- /* @__PURE__ */ jsx(CloseAllModalX, { onClick: () => setCloseAllOpen(false), children: "\xD7" }),
14856
- /* @__PURE__ */ jsx(CloseAllTitle, { children: "Confirm Sell All" }),
14857
- /* @__PURE__ */ jsx(CloseAllDesc, { children: "This will sell all your positions and cancel their associated TP/SL orders." }),
14858
- /* @__PURE__ */ jsxs(CloseAllOption, { onClick: () => setCloseAllType("market"), children: [
14859
- /* @__PURE__ */ jsx(CloseAllRadio, { $selected: closeAllType === "market" }),
14860
- /* @__PURE__ */ jsx("span", { children: "Market Sell" })
14861
- ] }),
14862
- /* @__PURE__ */ jsxs(CloseAllOption, { onClick: () => setCloseAllType("limit"), children: [
14863
- /* @__PURE__ */ jsx(CloseAllRadio, { $selected: closeAllType === "limit" }),
14864
- /* @__PURE__ */ jsx("span", { children: "Limit Sell at Mid Price" })
14865
- ] }),
14866
- /* @__PURE__ */ jsxs(CloseAllConfirmBtn, { onClick: () => {
14867
- positions.forEach((pos) => onClosePosition(pos.tokenName, closeAllType, pos.quantity, pos.marketPrice));
14868
- setCloseAllOpen(false);
14869
- }, children: [
14870
- "Confirm ",
14871
- closeAllType === "market" ? "Market" : "Limit",
14872
- " Sell"
14873
- ] })
14874
- ] }) })
15262
+ positions.length > 0 && closeAllOpen && onClosePosition && typeof document !== "undefined" && createPortal(
15263
+ /* @__PURE__ */ jsx(CloseAllOverlay, { onClick: () => setCloseAllOpen(false), children: /* @__PURE__ */ jsxs(CloseAllModal, { onClick: (e) => e.stopPropagation(), children: [
15264
+ /* @__PURE__ */ jsx(CloseAllModalX, { onClick: () => setCloseAllOpen(false), children: "\xD7" }),
15265
+ /* @__PURE__ */ jsx(CloseAllTitle, { children: "Confirm Sell All" }),
15266
+ /* @__PURE__ */ jsx(CloseAllDesc, { children: "This will sell all your positions and cancel their associated TP/SL orders." }),
15267
+ /* @__PURE__ */ jsxs(CloseAllOption, { onClick: () => setCloseAllType("market"), children: [
15268
+ /* @__PURE__ */ jsx(CloseAllRadio, { $selected: closeAllType === "market" }),
15269
+ /* @__PURE__ */ jsx("span", { children: "Market Sell" })
15270
+ ] }),
15271
+ /* @__PURE__ */ jsxs(CloseAllOption, { onClick: () => setCloseAllType("limit"), children: [
15272
+ /* @__PURE__ */ jsx(CloseAllRadio, { $selected: closeAllType === "limit" }),
15273
+ /* @__PURE__ */ jsx("span", { children: "Limit Sell at Mid Price" })
15274
+ ] }),
15275
+ /* @__PURE__ */ jsxs(CloseAllConfirmBtn, { onClick: () => {
15276
+ positions.forEach((pos) => onClosePosition(pos.tokenName, closeAllType, pos.quantity, pos.marketPrice));
15277
+ setCloseAllOpen(false);
15278
+ }, children: [
15279
+ "Confirm ",
15280
+ closeAllType === "market" ? "Market" : "Limit",
15281
+ " Sell"
15282
+ ] })
15283
+ ] }) }),
15284
+ document.body
15285
+ )
14875
15286
  ] }),
14876
15287
  activeTab === "subscriptions" && /* @__PURE__ */ jsxs(Fragment, { children: [
14877
15288
  offeringOrders.length === 0 && /* @__PURE__ */ jsx(EmptyState, { children: "IPO allocations will appear here once you subscribe." }),
@@ -15328,57 +15739,63 @@ function PortfolioActivityPanel({
15328
15739
  ] }),
15329
15740
  /* @__PURE__ */ jsx(PaginationBtn, { disabled: currentPage >= totalPages - 1, onClick: () => setCurrentPage((p) => p + 1), children: "Next \u203A" })
15330
15741
  ] }),
15331
- cancelAllConfirmOpen && /* @__PURE__ */ jsx(CancelConfirmOverlay, { onClick: () => setCancelAllConfirmOpen(false), children: /* @__PURE__ */ jsxs(CancelConfirmModal, { onClick: (e) => e.stopPropagation(), children: [
15332
- /* @__PURE__ */ jsx(CancelConfirmTitle, { children: "Cancel All Orders" }),
15333
- /* @__PURE__ */ jsxs(CancelConfirmBody, { children: [
15334
- "Are you sure you want to cancel all ",
15335
- openOrdersCount,
15336
- " open orders? This action cannot be undone."
15337
- ] }),
15338
- /* @__PURE__ */ jsxs(CancelConfirmActions, { children: [
15339
- /* @__PURE__ */ jsx(CancelConfirmDismiss, { type: "button", onClick: () => setCancelAllConfirmOpen(false), children: "Keep Orders" }),
15340
- /* @__PURE__ */ jsx(
15341
- CancelConfirmProceed,
15342
- {
15343
- type: "button",
15344
- disabled: cancellingAll,
15345
- onClick: async () => {
15346
- if (onCancelAllOrders) {
15347
- setCancellingAll(true);
15348
- try {
15349
- await onCancelAllOrders();
15350
- } finally {
15351
- setCancellingAll(false);
15742
+ cancelAllConfirmOpen && typeof document !== "undefined" && createPortal(
15743
+ /* @__PURE__ */ jsx(CancelConfirmOverlay, { onClick: () => setCancelAllConfirmOpen(false), children: /* @__PURE__ */ jsxs(CancelConfirmModal, { onClick: (e) => e.stopPropagation(), children: [
15744
+ /* @__PURE__ */ jsx(CancelConfirmTitle, { children: "Cancel All Orders" }),
15745
+ /* @__PURE__ */ jsxs(CancelConfirmBody, { children: [
15746
+ "Are you sure you want to cancel all ",
15747
+ openOrdersCount,
15748
+ " open orders? This action cannot be undone."
15749
+ ] }),
15750
+ /* @__PURE__ */ jsxs(CancelConfirmActions, { children: [
15751
+ /* @__PURE__ */ jsx(CancelConfirmDismiss, { type: "button", onClick: () => setCancelAllConfirmOpen(false), children: "Keep Orders" }),
15752
+ /* @__PURE__ */ jsx(
15753
+ CancelConfirmProceed,
15754
+ {
15755
+ type: "button",
15756
+ disabled: cancellingAll,
15757
+ onClick: async () => {
15758
+ if (onCancelAllOrders) {
15759
+ setCancellingAll(true);
15760
+ try {
15761
+ await onCancelAllOrders();
15762
+ } finally {
15763
+ setCancellingAll(false);
15764
+ }
15352
15765
  }
15353
- }
15354
- setCancelAllConfirmOpen(false);
15355
- },
15356
- children: cancellingAll ? "Cancelling\u2026" : "Yes, Cancel All"
15357
- }
15358
- )
15359
- ] })
15360
- ] }) }),
15361
- pendingCancelOrderId !== null && /* @__PURE__ */ jsx(CancelConfirmOverlay, { onClick: () => setPendingCancelOrderId(null), children: /* @__PURE__ */ jsxs(CancelConfirmModal, { onClick: (e) => e.stopPropagation(), children: [
15362
- /* @__PURE__ */ jsx(CancelConfirmTitle, { children: "Cancel Order" }),
15363
- /* @__PURE__ */ jsx(CancelConfirmBody, { children: "Are you sure you want to cancel this order? This action cannot be undone." }),
15364
- /* @__PURE__ */ jsxs(CancelConfirmActions, { children: [
15365
- /* @__PURE__ */ jsx(CancelConfirmDismiss, { type: "button", onClick: () => setPendingCancelOrderId(null), children: "Keep Order" }),
15366
- /* @__PURE__ */ jsx(
15367
- CancelConfirmProceed,
15368
- {
15369
- type: "button",
15370
- disabled: cancellingOrderId != null,
15371
- onClick: async () => {
15372
- if (onCancelOrder && pendingCancelOrderId !== null) {
15373
- await onCancelOrder(pendingCancelOrderId);
15374
- }
15375
- setPendingCancelOrderId(null);
15376
- },
15377
- children: cancellingOrderId === pendingCancelOrderId ? "Cancelling\u2026" : "Yes, Cancel"
15378
- }
15379
- )
15380
- ] })
15381
- ] }) })
15766
+ setCancelAllConfirmOpen(false);
15767
+ },
15768
+ children: cancellingAll ? "Cancelling\u2026" : "Yes, Cancel All"
15769
+ }
15770
+ )
15771
+ ] })
15772
+ ] }) }),
15773
+ document.body
15774
+ ),
15775
+ pendingCancelOrderId !== null && typeof document !== "undefined" && createPortal(
15776
+ /* @__PURE__ */ jsx(CancelConfirmOverlay, { onClick: () => setPendingCancelOrderId(null), children: /* @__PURE__ */ jsxs(CancelConfirmModal, { onClick: (e) => e.stopPropagation(), children: [
15777
+ /* @__PURE__ */ jsx(CancelConfirmTitle, { children: "Cancel Order" }),
15778
+ /* @__PURE__ */ jsx(CancelConfirmBody, { children: "Are you sure you want to cancel this order? This action cannot be undone." }),
15779
+ /* @__PURE__ */ jsxs(CancelConfirmActions, { children: [
15780
+ /* @__PURE__ */ jsx(CancelConfirmDismiss, { type: "button", onClick: () => setPendingCancelOrderId(null), children: "Keep Order" }),
15781
+ /* @__PURE__ */ jsx(
15782
+ CancelConfirmProceed,
15783
+ {
15784
+ type: "button",
15785
+ disabled: cancellingOrderId != null,
15786
+ onClick: async () => {
15787
+ if (onCancelOrder && pendingCancelOrderId !== null) {
15788
+ await onCancelOrder(pendingCancelOrderId);
15789
+ }
15790
+ setPendingCancelOrderId(null);
15791
+ },
15792
+ children: cancellingOrderId === pendingCancelOrderId ? "Cancelling\u2026" : "Yes, Cancel"
15793
+ }
15794
+ )
15795
+ ] })
15796
+ ] }) }),
15797
+ document.body
15798
+ )
15382
15799
  ] });
15383
15800
  }
15384
15801
  var Container2 = styled10.div`
@@ -15449,7 +15866,7 @@ var Tab = styled10.button`
15449
15866
  }
15450
15867
  `;
15451
15868
  var TabCount = styled10.span`
15452
- color: #E6C656;
15869
+ color: var(--color-accent, #E6C87E);
15453
15870
  font-weight: 600;
15454
15871
  margin-left: 2px;
15455
15872
  `;
@@ -15634,7 +16051,8 @@ var CancelConfirmOverlay = styled10.div`
15634
16051
  display: flex;
15635
16052
  align-items: center;
15636
16053
  justify-content: center;
15637
- z-index: 1000;
16054
+ /* Above the trade page's fullscreen/sticky layers (TradePage.css uses up to 10001). */
16055
+ z-index: 10050;
15638
16056
  `;
15639
16057
  var CancelConfirmModal = styled10.div`
15640
16058
  background: #1a1a1a;
@@ -15724,6 +16142,29 @@ var CloseBtn = styled10.button`
15724
16142
  border-color: rgba(230, 200, 126, 0.5);
15725
16143
  }
15726
16144
  `;
16145
+ var CShareIconBtn = styled10.span`
16146
+ display: inline-flex;
16147
+ align-items: center;
16148
+ justify-content: center;
16149
+ width: 26px;
16150
+ height: 26px;
16151
+ border-radius: 6px;
16152
+ color: #E6C87E;
16153
+ cursor: pointer;
16154
+ pointer-events: auto;
16155
+ transition: color 0.15s ease, background 0.15s ease;
16156
+ &:hover {
16157
+ color: #F2D898;
16158
+ background: rgba(230, 200, 126, 0.14);
16159
+ }
16160
+ &:active { background: rgba(230, 200, 126, 0.22); }
16161
+ svg { width: 15px; height: 15px; }
16162
+ `;
16163
+ var ShareIcon = () => /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
16164
+ /* @__PURE__ */ jsx("path", { d: "M4 12v7a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-7" }),
16165
+ /* @__PURE__ */ jsx("polyline", { points: "16 6 12 2 8 6" }),
16166
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "2", x2: "12", y2: "15" })
16167
+ ] });
15727
16168
  var CloseAllHeaderBtn = styled10.button`
15728
16169
  background: rgba(230, 200, 126, 0.1);
15729
16170
  border: 1px solid rgba(230, 200, 126, 0.3);
@@ -15748,7 +16189,8 @@ var CloseAllOverlay = styled10.div`
15748
16189
  display: flex;
15749
16190
  align-items: center;
15750
16191
  justify-content: center;
15751
- z-index: 1000;
16192
+ /* Above the trade page's fullscreen/sticky layers (TradePage.css uses up to 10001). */
16193
+ z-index: 10050;
15752
16194
  `;
15753
16195
  var CloseAllModal = styled10.div`
15754
16196
  background: #1a1a1a;