@loafmarkets/ui 0.1.384 → 0.1.385

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)`;
@@ -5363,7 +5370,7 @@ var Header = ({
5363
5370
  return /* @__PURE__ */ jsxs(Fragment, { children: [
5364
5371
  /* @__PURE__ */ jsx(SafeAreaCover, {}),
5365
5372
  /* @__PURE__ */ jsx(Overlay, { $isOpen: isMobileMenuOpen, onClick: () => setIsMobileMenuOpen(false) }),
5366
- /* @__PURE__ */ jsxs(HeaderContainer, { id: "loaf-header", $bgOpacity: headerBgOpacity, children: [
5373
+ /* @__PURE__ */ jsxs(HeaderContainer, { id: "loaf-header", $bgOpacity: headerBgOpacity, $menuOpen: isMobileMenuOpen, children: [
5367
5374
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center" }, children: [
5368
5375
  /* @__PURE__ */ jsxs(Logo, { children: [
5369
5376
  /* @__PURE__ */ jsx(LogoLink, { href: logoHref ?? resolvedHomePath, onClick: handleLogoNavigation, children: /* @__PURE__ */ jsx("img", { src: Loaf_logo_Banner_default, alt: "LOAF Logo" }) }),
@@ -5839,7 +5846,8 @@ var Overlay = styled10.div`
5839
5846
  height: 100%;
5840
5847
  background-color: rgba(0, 0, 0, 0.7);
5841
5848
  backdrop-filter: blur(3px);
5842
- z-index: 970;
5849
+ /* Above the trade page's sticky/console layers (z 2000) + close modal (10001). */
5850
+ z-index: 10070;
5843
5851
  `;
5844
5852
  var MobileOnlyButton = styled10.button.attrs({ type: "button" })`
5845
5853
  display: none;
@@ -5887,7 +5895,11 @@ var HeaderContainer = styled10.header`
5887
5895
  top: 0;
5888
5896
  left: 0;
5889
5897
  right: 0;
5890
- z-index: 1000;
5898
+ /* Normally 1000. When the mobile menu is open, lift the whole header
5899
+ (and the Nav drawer trapped inside its stacking context) above the
5900
+ backdrop Overlay (10070) AND the trade page's modal layers (10001) so
5901
+ the drawer renders on top instead of being dimmed by the backdrop. */
5902
+ z-index: ${({ $menuOpen }) => $menuOpen ? 10075 : 1e3};
5891
5903
  width: 100%;
5892
5904
  /* Fallback min-height for browsers that can't evaluate calc+env */
5893
5905
  min-height: 56px;
@@ -5981,7 +5993,8 @@ var Nav = styled10.nav`
5981
5993
  max-width: 100%;
5982
5994
  height: calc(100dvh - var(--loaf-menu-top, calc(56px + max(env(safe-area-inset-top, 0px), var(--telegram-safe-top, 0px)))));
5983
5995
  background: rgb(17, 17, 19);
5984
- z-index: 980;
5996
+ /* Above the trade page's sticky/console layers (z 2000) + close modal (10001). */
5997
+ z-index: 10080;
5985
5998
  /* Gentle fade + slide-down instead of an abrupt slide-in. */
5986
5999
  opacity: ${(props) => props.$isOpen ? "1" : "0"};
5987
6000
  transform: ${(props) => props.$isOpen ? "translateY(0)" : "translateY(-14px)"};
@@ -5999,9 +6012,12 @@ var Nav = styled10.nav`
5999
6012
  display: flex;
6000
6013
  flex-direction: column;
6001
6014
  align-items: center;
6002
- justify-content: center;
6015
+ /* safe-center keeps centering when the menu fits, but top-aligns (honouring the
6016
+ 2.5rem top padding) when the logged-in menu overflows — otherwise flex
6017
+ centering pushes the first item + padding above the scroll area and clips it. */
6018
+ justify-content: safe center;
6003
6019
  min-height: 100%;
6004
- padding: 2rem 1.5rem 2.5rem;
6020
+ padding: 2.5rem 1.5rem 2.5rem;
6005
6021
  gap: 0.4rem;
6006
6022
  }
6007
6023
  }
@@ -6711,6 +6727,17 @@ PropertySubheader.displayName = "PropertySubheader";
6711
6727
  var DEFAULT_LOGO_SRC = Loaf_logo_Banner_default;
6712
6728
  var DEFAULT_LOGO_ALT = "Loaf";
6713
6729
  var OTP_INPUT_LENGTH = 6;
6730
+ var CODE_PREFIX = "LOAF-";
6731
+ var CODE_SUFFIX_LEN = 5;
6732
+ var normalizeAccessCode = (raw) => CODE_PREFIX + raw.toUpperCase().replace(/^LOAF-?/, "").replace(/[^A-Z0-9]/g, "").slice(0, CODE_SUFFIX_LEN);
6733
+ var friendlyError = (err, fallback) => {
6734
+ const data = err?.response?.data;
6735
+ if (typeof data?.error === "string" && data.error.trim()) return data.error;
6736
+ if (typeof data?.message === "string" && data.message.trim()) return data.message;
6737
+ const msg = err instanceof Error ? err.message : "";
6738
+ if (msg && !/request failed with status code/i.test(msg) && !/network error/i.test(msg)) return msg;
6739
+ return fallback;
6740
+ };
6714
6741
  var LoginPopup = ({
6715
6742
  onClose,
6716
6743
  onOpenEarlyAccess,
@@ -6729,10 +6756,13 @@ var LoginPopup = ({
6729
6756
  renderKycWidget,
6730
6757
  onFundWallet,
6731
6758
  initialView,
6759
+ initialCode,
6732
6760
  kycStatus: kycStatusProp,
6733
6761
  walletAddress,
6734
6762
  onSubmitReferralCode,
6735
- onJoinWaitlist
6763
+ onValidateCode,
6764
+ onCheckAccess,
6765
+ gate
6736
6766
  }) => {
6737
6767
  const [view, setView] = useState(() => initialView ?? "main");
6738
6768
  const [email, setEmail] = useState("");
@@ -6740,19 +6770,17 @@ var LoginPopup = ({
6740
6770
  const [otp, setOtp] = useState(Array(OTP_INPUT_LENGTH).fill(""));
6741
6771
  const [error, setError] = useState("");
6742
6772
  const [copied, setCopied] = useState(false);
6743
- const [referralCode, setReferralCode] = useState("");
6773
+ const [referralCode, setReferralCode] = useState(
6774
+ () => gate && initialCode ? normalizeAccessCode(initialCode) : ""
6775
+ );
6744
6776
  const [referralLoading, setReferralLoading] = useState(false);
6745
6777
  const [referralError, setReferralError] = useState("");
6746
- const [waitlistLoading, setWaitlistLoading] = useState(false);
6747
- const [waitlistMessage, setWaitlistMessage] = useState("");
6778
+ const [codeStatus, setCodeStatus] = useState("idle");
6779
+ const [signInRevealed, setSignInRevealed] = useState(false);
6780
+ const [otpStatus, setOtpStatus] = useState("idle");
6781
+ const [signInMode, setSignInMode] = useState(false);
6748
6782
  const [loading, setLoading] = useState(false);
6749
6783
  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
6784
  const [fundingAmount] = useState("");
6757
6785
  const [kycLoading, setKycLoading] = useState(false);
6758
6786
  const [showKycWidget, setShowKycWidget] = useState(false);
@@ -6762,6 +6790,64 @@ var LoginPopup = ({
6762
6790
  const [fundingError, setFundingError] = useState("");
6763
6791
  const [transakWidgetUrl, setTransakWidgetUrl] = useState(null);
6764
6792
  const suppressAutoCloseRef = React5__default.useRef(false);
6793
+ const validatedCodeRef = React5__default.useRef(null);
6794
+ const finishGate = React5__default.useCallback(async () => {
6795
+ if (!gate) return true;
6796
+ const code = validatedCodeRef.current;
6797
+ if (code) {
6798
+ try {
6799
+ await onSubmitReferralCode?.(code);
6800
+ window.dispatchEvent(new CustomEvent("loaf:onboarding-start"));
6801
+ return true;
6802
+ } catch {
6803
+ }
6804
+ }
6805
+ const hasAccess = onCheckAccess ? await onCheckAccess() : true;
6806
+ if (hasAccess) return true;
6807
+ suppressAutoCloseRef.current = true;
6808
+ validatedCodeRef.current = null;
6809
+ setOtp(Array(OTP_INPUT_LENGTH).fill(""));
6810
+ setOtpStatus("idle");
6811
+ setSignInMode(false);
6812
+ setSignInRevealed(false);
6813
+ setCodeStatus(code ? "invalid" : "idle");
6814
+ setView("referral");
6815
+ setError(
6816
+ code ? "That code can't be redeemed anymore. Enter a different access code." : "Enter your access code to continue."
6817
+ );
6818
+ return false;
6819
+ }, [gate, onSubmitReferralCode, onCheckAccess]);
6820
+ const runCodeValidation = React5__default.useCallback(async (fullCode) => {
6821
+ setCodeStatus("checking");
6822
+ setError("");
6823
+ try {
6824
+ const ok = onValidateCode ? await onValidateCode(fullCode) : true;
6825
+ if (!ok) {
6826
+ validatedCodeRef.current = null;
6827
+ setCodeStatus("invalid");
6828
+ return;
6829
+ }
6830
+ validatedCodeRef.current = fullCode;
6831
+ setCodeStatus("valid");
6832
+ if (isAuthenticated) {
6833
+ if (await finishGate()) setTimeout(() => onClose(), 500);
6834
+ return;
6835
+ }
6836
+ setIsSignUp(true);
6837
+ setSignInRevealed(true);
6838
+ } catch {
6839
+ validatedCodeRef.current = null;
6840
+ setCodeStatus("invalid");
6841
+ }
6842
+ }, [onValidateCode, isAuthenticated, finishGate, onClose]);
6843
+ const autoCodeTriedRef = React5__default.useRef(false);
6844
+ useEffect(() => {
6845
+ if (autoCodeTriedRef.current || !gate || !initialCode) return;
6846
+ const full = normalizeAccessCode(initialCode);
6847
+ if (full.length !== CODE_PREFIX.length + CODE_SUFFIX_LEN) return;
6848
+ autoCodeTriedRef.current = true;
6849
+ void runCodeValidation(full);
6850
+ }, [gate, initialCode, runCodeValidation]);
6765
6851
  useEffect(() => {
6766
6852
  if (typeof initialView === "string") {
6767
6853
  setView(initialView);
@@ -6816,11 +6902,11 @@ var LoginPopup = ({
6816
6902
  }, [autoCloseOnAuth, currentUser, isAuthenticated, onClose, view]);
6817
6903
  useEffect(() => {
6818
6904
  const handleEsc = (e) => {
6819
- if (e.key === "Escape") onClose();
6905
+ if (e.key === "Escape" && !gate) onClose();
6820
6906
  };
6821
6907
  window.addEventListener("keydown", handleEsc);
6822
6908
  return () => window.removeEventListener("keydown", handleEsc);
6823
- }, [onClose]);
6909
+ }, [onClose, gate]);
6824
6910
  const handleWalletLogin = async () => {
6825
6911
  if (onWalletLogin) {
6826
6912
  suppressAutoCloseRef.current = true;
@@ -6832,12 +6918,16 @@ var LoginPopup = ({
6832
6918
  setView("wallet-handle");
6833
6919
  return;
6834
6920
  }
6835
- suppressAutoCloseRef.current = false;
6836
- onClose();
6921
+ if (await finishGate()) {
6922
+ suppressAutoCloseRef.current = false;
6923
+ onClose();
6924
+ }
6837
6925
  return;
6838
6926
  } catch (err) {
6839
6927
  console.error("[LoginTrace][Popup] Wallet login failed", err);
6840
6928
  suppressAutoCloseRef.current = false;
6929
+ setError(friendlyError(err, "Wallet login failed. Please try again."));
6930
+ return;
6841
6931
  }
6842
6932
  }
6843
6933
  onClose();
@@ -6859,6 +6949,13 @@ var LoginPopup = ({
6859
6949
  setError("");
6860
6950
  try {
6861
6951
  await onWalletSignup(handle.trim());
6952
+ if (gate) {
6953
+ if (await finishGate()) {
6954
+ suppressAutoCloseRef.current = false;
6955
+ onClose();
6956
+ }
6957
+ return;
6958
+ }
6862
6959
  if (onSubmitReferralCode) {
6863
6960
  setView("referral");
6864
6961
  } else {
@@ -6870,6 +6967,11 @@ var LoginPopup = ({
6870
6967
  setLoading(false);
6871
6968
  }
6872
6969
  };
6970
+ const handleEmailClick = (signUp = false) => {
6971
+ setIsSignUp(signUp);
6972
+ setView("email");
6973
+ setError("");
6974
+ };
6873
6975
  const handleSendCode = async (event) => {
6874
6976
  event?.preventDefault();
6875
6977
  if (!email || !email.includes("@")) {
@@ -6882,6 +6984,7 @@ var LoginPopup = ({
6882
6984
  }
6883
6985
  setLoading(true);
6884
6986
  setError("");
6987
+ setOtpStatus("idle");
6885
6988
  const normalizedHandle = isSignUp && handle.trim() ? handle.trim() : void 0;
6886
6989
  try {
6887
6990
  const demoResult = await onDemoLogin?.(email, normalizedHandle ?? null);
@@ -6902,11 +7005,15 @@ var LoginPopup = ({
6902
7005
  setView("otp");
6903
7006
  setOtp(Array(OTP_INPUT_LENGTH).fill(""));
6904
7007
  } catch (err) {
6905
- const message = err instanceof Error ? err.message : "Failed to send verification code";
7008
+ const message = friendlyError(err, "We couldn't send a code to that email. Please double-check it and try again.");
6906
7009
  console.error("[LoginTrace][Popup] onSendEmailCode threw", err);
6907
7010
  if (!isSignUp && message.includes("No account found")) {
7011
+ if (signInMode) {
7012
+ setError("No account found for this email. Go back and enter your access code to create one.");
7013
+ setLoading(false);
7014
+ return;
7015
+ }
6908
7016
  setIsSignUp(true);
6909
- setView("email");
6910
7017
  setError("");
6911
7018
  setLoading(false);
6912
7019
  return;
@@ -6918,7 +7025,7 @@ var LoginPopup = ({
6918
7025
  setView("otp");
6919
7026
  setOtp(Array(OTP_INPUT_LENGTH).fill(""));
6920
7027
  } catch (retryErr) {
6921
- setError(retryErr instanceof Error ? retryErr.message : "Failed to send verification code");
7028
+ setError(friendlyError(retryErr, "We couldn't send a code to that email. Please try again."));
6922
7029
  }
6923
7030
  setLoading(false);
6924
7031
  return;
@@ -7012,6 +7119,30 @@ var LoginPopup = ({
7012
7119
  setLoading(false);
7013
7120
  }
7014
7121
  };
7122
+ const runGateOtpVerify = React5__default.useCallback(async (code) => {
7123
+ if (!onVerifyEmailCode) return;
7124
+ setOtpStatus("checking");
7125
+ setError("");
7126
+ suppressAutoCloseRef.current = true;
7127
+ try {
7128
+ await onVerifyEmailCode({ code, email });
7129
+ if (await finishGate()) {
7130
+ setOtpStatus("valid");
7131
+ setTimeout(() => onClose(), 750);
7132
+ }
7133
+ } catch {
7134
+ suppressAutoCloseRef.current = false;
7135
+ setOtpStatus("invalid");
7136
+ }
7137
+ }, [onVerifyEmailCode, email, finishGate, onClose]);
7138
+ useEffect(() => {
7139
+ if (!gate || view !== "otp") return;
7140
+ if (otp.join("").length < OTP_INPUT_LENGTH) {
7141
+ if (otpStatus === "invalid") setOtpStatus("idle");
7142
+ return;
7143
+ }
7144
+ if (otpStatus === "idle") void runGateOtpVerify(otp.join(""));
7145
+ }, [gate, view, otp, otpStatus, runGateOtpVerify]);
7015
7146
  const handleKycWidgetResult = (result) => {
7016
7147
  setShowKycWidget(false);
7017
7148
  if (result.passed) {
@@ -7108,35 +7239,36 @@ var LoginPopup = ({
7108
7239
  setError("");
7109
7240
  };
7110
7241
  if (view === "main") {
7111
- return /* @__PURE__ */ jsx(Overlay2, { onClick: onClose, children: /* @__PURE__ */ jsxs(PopupContainer, { onClick: (event) => event.stopPropagation(), children: [
7242
+ return /* @__PURE__ */ jsx(Overlay2, { $transparent: gate, onClick: onClose, children: /* @__PURE__ */ jsxs(PopupContainer, { onClick: (event) => event.stopPropagation(), children: [
7112
7243
  /* @__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" }) }) })
7244
+ /* @__PURE__ */ jsxs(Title, { children: [
7245
+ /* @__PURE__ */ jsxs(LogoContainer3, { children: [
7246
+ /* @__PURE__ */ jsx(LogoImage, { src: logoSrc, alt: logoAlt }),
7247
+ /* @__PURE__ */ jsx(LogoBeta, { children: "Private Beta" })
7128
7248
  ] }),
7129
- error && /* @__PURE__ */ jsx(StatusMessage, { $error: true, children: error })
7249
+ /* @__PURE__ */ jsx(TitleText, { children: "Welcome to Loaf" })
7130
7250
  ] }),
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"
7251
+ /* @__PURE__ */ jsx(Subtitle, { children: "Sign in to claim 100k in test USD." }),
7252
+ /* @__PURE__ */ jsxs(ButtonsContainer, { children: [
7253
+ /* @__PURE__ */ jsxs(LoginButton, { onClick: () => handleEmailClick(false), children: [
7254
+ /* @__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" }) }),
7255
+ "Sign in with Email"
7256
+ ] }),
7257
+ /* @__PURE__ */ jsx("div", { style: { textAlign: "center", color: "var(--color-text-secondary, #848e9c)", fontSize: "0.875rem" }, children: "or" }),
7258
+ /* @__PURE__ */ jsxs(LoginButton, { onClick: handleWalletLogin, children: [
7259
+ /* @__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" }) }),
7260
+ "Sign in with Wallet"
7261
+ ] })
7262
+ ] }),
7263
+ /* @__PURE__ */ jsxs(SignUpPrompt, { children: [
7264
+ "New to Loaf?",
7265
+ " ",
7266
+ /* @__PURE__ */ jsx(SignUpLink, { type: "button", onClick: () => handleEmailClick(true), children: "Sign up" })
7135
7267
  ] })
7136
7268
  ] }) });
7137
7269
  }
7138
7270
  if (view === "email") {
7139
- return /* @__PURE__ */ jsx(Overlay2, { onClick: onClose, children: /* @__PURE__ */ jsxs(PopupContainer, { onClick: (event) => event.stopPropagation(), children: [
7271
+ return /* @__PURE__ */ jsx(Overlay2, { $transparent: gate, onClick: onClose, children: /* @__PURE__ */ jsxs(PopupContainer, { onClick: (event) => event.stopPropagation(), children: [
7140
7272
  /* @__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
7273
  /* @__PURE__ */ jsxs(BackButton, { onClick: handleBack, children: [
7142
7274
  /* @__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 +7307,7 @@ var LoginPopup = ({
7175
7307
  ] }) });
7176
7308
  }
7177
7309
  if (view === "wallet-handle") {
7178
- return /* @__PURE__ */ jsx(Overlay2, { onClick: loading ? void 0 : onClose, children: /* @__PURE__ */ jsxs(PopupContainer, { onClick: (event) => event.stopPropagation(), children: [
7310
+ return /* @__PURE__ */ jsx(Overlay2, { $transparent: gate, onClick: loading ? void 0 : onClose, children: /* @__PURE__ */ jsxs(PopupContainer, { onClick: (event) => event.stopPropagation(), children: [
7179
7311
  !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
7312
  /* @__PURE__ */ jsxs(Title, { children: [
7181
7313
  /* @__PURE__ */ jsx(LogoContainer3, { children: /* @__PURE__ */ jsx(LogoImage, { src: logoSrc, alt: logoAlt }) }),
@@ -7200,7 +7332,71 @@ var LoginPopup = ({
7200
7332
  ] }) });
7201
7333
  }
7202
7334
  if (view === "otp") {
7203
- return /* @__PURE__ */ jsx(Overlay2, { onClick: loading ? void 0 : onClose, children: /* @__PURE__ */ jsxs(PopupContainer, { onClick: (event) => event.stopPropagation(), children: [
7335
+ if (gate) {
7336
+ return /* @__PURE__ */ jsxs(GateShell, { children: [
7337
+ /* @__PURE__ */ jsx(GateTint, { $reveal: otpStatus === "valid" ? 3 : 2 }),
7338
+ /* @__PURE__ */ jsxs(GateForm, { children: [
7339
+ /* @__PURE__ */ jsxs(GateBrand, { children: [
7340
+ /* @__PURE__ */ jsx(GateLogoLink, { href: "https://loafmarkets.com", "aria-label": "Go to loafmarkets.com", children: /* @__PURE__ */ jsx(GateLogo, { src: logoSrc, alt: logoAlt }) }),
7341
+ /* @__PURE__ */ jsx(GateBrandDivider, {}),
7342
+ /* @__PURE__ */ jsx(GateBetaTag, { children: "Private Beta" })
7343
+ ] }),
7344
+ /* @__PURE__ */ jsxs(GateOtpText, { children: [
7345
+ "We sent a code to",
7346
+ /* @__PURE__ */ jsx("br", {}),
7347
+ /* @__PURE__ */ jsx("strong", { children: email })
7348
+ ] }),
7349
+ /* @__PURE__ */ jsx(OTPContainer, { children: otp.map((digit, index) => /* @__PURE__ */ jsx(
7350
+ OTPInput,
7351
+ {
7352
+ id: `otp-${index}`,
7353
+ type: "text",
7354
+ inputMode: "numeric",
7355
+ maxLength: 1,
7356
+ $status: otpStatus,
7357
+ readOnly: otpStatus === "valid" || otpStatus === "checking",
7358
+ value: digit,
7359
+ onChange: (event) => handleOTPChange(index, event.target.value),
7360
+ onKeyDown: (event) => handleOTPKeyDown(index, event),
7361
+ onInput: (event) => handleOTPInput(index, event),
7362
+ onPaste: handleOTPPaste,
7363
+ autoComplete: index === 0 ? "one-time-code" : "off",
7364
+ autoFocus: index === 0
7365
+ },
7366
+ index
7367
+ )) }),
7368
+ otpStatus === "invalid" && /* @__PURE__ */ jsx(StatusMessage, { $error: true, children: "That code isn't right. Try again." }),
7369
+ /* @__PURE__ */ jsxs(GateResendText, { children: [
7370
+ /* @__PURE__ */ jsx(
7371
+ "button",
7372
+ {
7373
+ type: "button",
7374
+ onClick: (event) => {
7375
+ event.preventDefault();
7376
+ void handleSendCode(event);
7377
+ },
7378
+ children: "Resend code"
7379
+ }
7380
+ ),
7381
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: " \xB7 " }),
7382
+ /* @__PURE__ */ jsx(
7383
+ "button",
7384
+ {
7385
+ type: "button",
7386
+ onClick: () => {
7387
+ setOtp(Array(OTP_INPUT_LENGTH).fill(""));
7388
+ setOtpStatus("idle");
7389
+ setError("");
7390
+ setView("referral");
7391
+ },
7392
+ children: "Back"
7393
+ }
7394
+ )
7395
+ ] })
7396
+ ] })
7397
+ ] });
7398
+ }
7399
+ return /* @__PURE__ */ jsx(Overlay2, { $transparent: gate, onClick: loading ? void 0 : onClose, children: /* @__PURE__ */ jsxs(PopupContainer, { onClick: (event) => event.stopPropagation(), children: [
7204
7400
  !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
7401
  loading ? /* @__PURE__ */ jsxs(AccountCreationLoader, { children: [
7206
7402
  /* @__PURE__ */ jsx(SpinnerRing, {}),
@@ -7289,8 +7485,8 @@ var LoginPopup = ({
7289
7485
  /* @__PURE__ */ jsx("rect", { x: "3", y: "11", width: "18", height: "11", rx: "2", ry: "2" }),
7290
7486
  /* @__PURE__ */ jsx("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })
7291
7487
  ] }) }),
7292
- /* @__PURE__ */ jsx(KycHeading, { children: "Verify Your Identity" }),
7293
- /* @__PURE__ */ jsx(KycSubline, { children: "Initial offerings are only available to verified investors" }),
7488
+ /* @__PURE__ */ jsx(KycHeading, { children: "Verify Wholesale Status" }),
7489
+ /* @__PURE__ */ jsx(KycSubline, { children: "Initial offerings are only available to accredited or wholesale investors" }),
7294
7490
  /* @__PURE__ */ jsx(KycStartButton, { type: "button", onClick: handleKycStart, disabled: kycLoading, children: kycLoading ? "Verifying\u2026" : kycStatusProp === "PENDING" || kycStatusProp === "ON_HOLD" ? "Resume Verification" : "Start Verification" }),
7295
7491
  /* @__PURE__ */ jsx(KycDuration, { children: "Takes about 2 minutes" }),
7296
7492
  /* @__PURE__ */ jsx(KycDivider, {}),
@@ -7376,7 +7572,7 @@ var LoginPopup = ({
7376
7572
  /* @__PURE__ */ jsx("div", { style: { color: "rgba(255,255,255,0.5)", fontSize: "0.8rem" }, children: "Deposit USDC from an exchange or wallet" })
7377
7573
  ] })
7378
7574
  ] }),
7379
- /* @__PURE__ */ jsxs(ModalOptionCard, { onClick: handleFiatFund, disabled: fiatFundingLoading, children: [
7575
+ /* @__PURE__ */ jsxs(ModalOptionCard, { onClick: handleFiatFund, disabled: true, children: [
7380
7576
  /* @__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
7577
  /* @__PURE__ */ jsx("line", { x1: "3", y1: "22", x2: "21", y2: "22" }),
7382
7578
  /* @__PURE__ */ jsx("line", { x1: "6", y1: "18", x2: "6", y2: "11" }),
@@ -7386,7 +7582,10 @@ var LoginPopup = ({
7386
7582
  /* @__PURE__ */ jsx("polygon", { points: "12 2 20 7 4 7" })
7387
7583
  ] }),
7388
7584
  /* @__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" }),
7585
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem", marginBottom: "0.25rem" }, children: [
7586
+ /* @__PURE__ */ jsx("span", { style: { color: "#fff", fontWeight: 600, fontSize: "0.95rem" }, children: fiatFundingLoading ? "Loading widget\u2026" : "Bank Deposit" }),
7587
+ /* @__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" })
7588
+ ] }),
7390
7589
  /* @__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
7590
  ] })
7392
7591
  ] })
@@ -7417,84 +7616,162 @@ var LoginPopup = ({
7417
7616
  setReferralLoading(false);
7418
7617
  }
7419
7618
  };
7420
- const handleJoinWaitlistSkip = async () => {
7421
- if (!onJoinWaitlist || !email) {
7422
- onClose();
7423
- return;
7424
- }
7619
+ const codeSuffix = referralCode.startsWith(CODE_PREFIX) ? referralCode.slice(CODE_PREFIX.length) : referralCode.replace(/^LOAF-?/i, "");
7620
+ const setCodeSuffix = (suffix) => {
7621
+ setReferralCode(CODE_PREFIX + suffix);
7425
7622
  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);
7623
+ setError("");
7624
+ if (gate) {
7625
+ if (suffix.length === 5) void runCodeValidation(CODE_PREFIX + suffix);
7626
+ else {
7627
+ validatedCodeRef.current = null;
7628
+ setCodeStatus("idle");
7629
+ }
7434
7630
  }
7435
7631
  };
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: [
7632
+ const handleCodeChange = (e) => {
7633
+ setCodeSuffix(e.target.value.toUpperCase().replace(/[^A-Z0-9]/g, "").slice(0, 5));
7634
+ };
7635
+ const handleCodePaste = (e) => {
7636
+ e.preventDefault();
7637
+ const text = (e.clipboardData.getData("text") || "").toUpperCase();
7638
+ setCodeSuffix(text.replace(/^LOAF-?/, "").replace(/[^A-Z0-9]/g, "").slice(0, 5));
7639
+ };
7640
+ const codeComplete = codeSuffix.length === 5;
7641
+ const codeInput = /* @__PURE__ */ jsx(
7642
+ CodeSuffixInput,
7643
+ {
7644
+ type: "text",
7645
+ inputMode: "text",
7646
+ autoComplete: "off",
7647
+ autoCapitalize: "characters",
7648
+ spellCheck: false,
7649
+ maxLength: 5,
7650
+ "aria-label": "Access code",
7651
+ readOnly: codeStatus === "valid",
7652
+ value: codeSuffix,
7653
+ onChange: handleCodeChange,
7654
+ onPaste: handleCodePaste,
7655
+ onKeyDown: (e) => {
7656
+ if (e.key !== "Enter" || !codeComplete) return;
7657
+ if (gate) void runCodeValidation(referralCode);
7658
+ else void handleReferralSubmit();
7659
+ },
7660
+ autoFocus: true
7661
+ }
7662
+ );
7663
+ const signInStep = /* @__PURE__ */ jsxs(GateReveal, { children: [
7664
+ /* @__PURE__ */ jsxs("form", { onSubmit: handleSendCode, style: { width: "100%" }, children: [
7665
+ /* @__PURE__ */ jsxs(CodeInputWrapper, { children: [
7458
7666
  /* @__PURE__ */ jsx(
7459
- EmailInput,
7667
+ GateEmailInput,
7460
7668
  {
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
- },
7669
+ type: "email",
7670
+ inputMode: "email",
7671
+ autoComplete: "email",
7672
+ placeholder: "Enter your email",
7673
+ value: email,
7674
+ onChange: (event) => setEmail(event.target.value),
7471
7675
  autoFocus: true
7472
7676
  }
7473
7677
  ),
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 })
7678
+ /* @__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" }) }) })
7679
+ ] }),
7680
+ error && /* @__PURE__ */ jsx(StatusMessage, { $error: true, children: error })
7681
+ ] }),
7682
+ /* @__PURE__ */ jsxs(Fragment, { children: [
7683
+ /* @__PURE__ */ jsx(GateOr, { children: "or" }),
7684
+ /* @__PURE__ */ jsxs(GateWalletButton, { type: "button", onClick: handleWalletLogin, children: [
7685
+ /* @__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" }) }),
7686
+ signInMode ? "Sign in with Wallet" : "Connect Wallet"
7687
+ ] })
7688
+ ] })
7689
+ ] });
7690
+ if (gate) {
7691
+ const codeAccepted = codeStatus === "valid";
7692
+ const showSignIn = signInMode || signInRevealed;
7693
+ return /* @__PURE__ */ jsxs(GateShell, { children: [
7694
+ /* @__PURE__ */ jsx(GateTint, { $reveal: showSignIn ? 1 : 0 }),
7695
+ /* @__PURE__ */ jsxs(GateForm, { children: [
7696
+ /* @__PURE__ */ jsxs(GateBrand, { children: [
7697
+ /* @__PURE__ */ jsx(GateLogoLink, { href: "https://loafmarkets.com", "aria-label": "Go to loafmarkets.com", children: /* @__PURE__ */ jsx(GateLogo, { src: logoSrc, alt: logoAlt }) }),
7698
+ /* @__PURE__ */ jsx(GateBrandDivider, {}),
7699
+ /* @__PURE__ */ jsx(GateBetaTag, { children: "Private Beta" })
7700
+ ] }),
7701
+ signInMode ? /* @__PURE__ */ jsxs(Fragment, { children: [
7702
+ signInStep,
7703
+ /* @__PURE__ */ jsx(GateResendText, { children: /* @__PURE__ */ jsx(
7704
+ "button",
7705
+ {
7706
+ type: "button",
7707
+ onClick: () => {
7708
+ setError("");
7709
+ setSignInMode(false);
7710
+ },
7711
+ children: "\u2190 Enter access code instead"
7712
+ }
7713
+ ) })
7714
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
7715
+ /* @__PURE__ */ jsxs(CodeInputWrapper, { $status: codeStatus, children: [
7716
+ /* @__PURE__ */ jsx(CodePrefix, { children: CODE_PREFIX }),
7717
+ codeInput,
7718
+ /* @__PURE__ */ jsx(
7719
+ GateSubmit,
7720
+ {
7721
+ type: "button",
7722
+ "aria-label": "Submit access code",
7723
+ $status: codeStatus,
7724
+ disabled: codeStatus === "checking" || !codeComplete,
7725
+ onClick: () => void runCodeValidation(referralCode),
7726
+ 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" }) })
7727
+ }
7728
+ )
7729
+ ] }),
7730
+ (codeStatus === "invalid" || error && !signInRevealed) && /* @__PURE__ */ jsx(StatusMessage, { $error: true, children: error || "That code isn't valid. Double-check and try again." }),
7731
+ signInRevealed ? signInStep : /* @__PURE__ */ jsxs(GateResendText, { children: [
7732
+ "Already have an account?",
7733
+ " ",
7734
+ /* @__PURE__ */ jsx(
7735
+ "button",
7736
+ {
7737
+ type: "button",
7738
+ onClick: () => {
7739
+ setError("");
7740
+ setIsSignUp(false);
7741
+ setSignInMode(true);
7742
+ },
7743
+ children: "Sign in"
7744
+ }
7745
+ )
7746
+ ] })
7747
+ ] })
7748
+ ] })
7749
+ ] });
7750
+ }
7751
+ return /* @__PURE__ */ jsx(Overlay2, { onClick: onClose, children: /* @__PURE__ */ jsxs(PopupContainer, { onClick: (event) => event.stopPropagation(), children: [
7752
+ /* @__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" }) }) }),
7753
+ /* @__PURE__ */ jsxs(Title, { children: [
7754
+ /* @__PURE__ */ jsxs(LogoContainer3, { children: [
7755
+ /* @__PURE__ */ jsx(LogoImage, { src: logoSrc, alt: logoAlt }),
7756
+ /* @__PURE__ */ jsx(LogoBeta, { children: "Private Beta" })
7757
+ ] }),
7758
+ /* @__PURE__ */ jsx(TitleText, { children: "Enter your access code" })
7759
+ ] }),
7760
+ /* @__PURE__ */ jsxs(EmailFormContainer, { style: { width: "100%", marginTop: "1.5rem", marginBottom: 0 }, children: [
7761
+ /* @__PURE__ */ jsxs(CodeInputWrapper, { children: [
7762
+ /* @__PURE__ */ jsx(CodePrefix, { children: CODE_PREFIX }),
7763
+ codeInput
7484
7764
  ] }),
7485
- waitlistMessage ? /* @__PURE__ */ jsxs(StatusMessage, { style: { marginTop: "0.75rem" }, children: [
7486
- "\u2713 ",
7487
- waitlistMessage
7488
- ] }) : /* @__PURE__ */ jsx(
7489
- OnboardingSkipButton,
7765
+ /* @__PURE__ */ jsx(
7766
+ SubmitButton,
7490
7767
  {
7491
7768
  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"
7769
+ onClick: () => void handleReferralSubmit(),
7770
+ disabled: referralLoading || !codeComplete,
7771
+ children: referralLoading ? "Verifying\u2026" : "Continue"
7496
7772
  }
7497
- )
7773
+ ),
7774
+ referralError && /* @__PURE__ */ jsx(StatusMessage, { $error: true, children: referralError })
7498
7775
  ] })
7499
7776
  ] }) });
7500
7777
  }
@@ -7614,8 +7891,9 @@ var Overlay2 = styled10.div`
7614
7891
  left: 0;
7615
7892
  right: 0;
7616
7893
  bottom: 0;
7617
- background-color: rgba(0, 0, 0, 0.8);
7618
- backdrop-filter: blur(4px);
7894
+ background-color: ${(props) => props.$gate ? "var(--color-background, #0a0a0a)" : props.$transparent ? "rgba(0, 0, 0, 0.45)" : "rgba(0, 0, 0, 0.8)"};
7895
+ backdrop-filter: ${(props) => props.$gate ? "none" : props.$transparent ? "blur(2px)" : "blur(4px)"};
7896
+ transition: background-color 0.4s ease, backdrop-filter 0.4s ease;
7619
7897
  display: flex;
7620
7898
  justify-content: center;
7621
7899
  align-items: center;
@@ -7632,15 +7910,15 @@ var Overlay2 = styled10.div`
7632
7910
  }
7633
7911
  `;
7634
7912
  var PopupContainer = styled10.div`
7635
- background-color: var(--color-background, #0a0a0a);
7636
- border: 1px solid rgba(230, 198, 86, 0.3);
7913
+ background-color: ${(props) => props.$gate ? "transparent" : "var(--color-background, #0a0a0a)"};
7914
+ border: ${(props) => props.$gate ? "none" : "1px solid rgba(230, 198, 86, 0.3)"};
7637
7915
  border-radius: var(--border-radius, 12px);
7638
7916
  padding: 2.5rem;
7639
7917
  max-width: 440px;
7640
7918
  width: 90%;
7641
7919
  position: relative;
7642
7920
  animation: slideUp 0.3s ease-out;
7643
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
7921
+ box-shadow: ${(props) => props.$gate ? "none" : "0 10px 30px rgba(0, 0, 0, 0.3)"};
7644
7922
 
7645
7923
  @keyframes slideUp {
7646
7924
  from {
@@ -7827,29 +8105,27 @@ var Title = styled10.h2`
7827
8105
  flex-direction: column;
7828
8106
  align-items: center;
7829
8107
  justify-content: center;
7830
- gap: 0.4rem;
8108
+ gap: 1rem;
7831
8109
  `;
7832
- styled10.p`
7833
- font-size: 0.9rem;
7834
- font-weight: 400;
7835
- color: var(--color-text-secondary, #848e9c);
8110
+ var TitleText = styled10.span`
7836
8111
  text-align: center;
7837
- margin: 0 0 2rem;
8112
+ line-height: 1.2;
7838
8113
  `;
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;
8114
+ var LogoBeta = styled10.span`
8115
+ color: var(--color-accent, #E6C87E);
8116
+ font-size: 0.6rem;
8117
+ font-weight: 600;
8118
+ letter-spacing: 0.14em;
8119
+ text-transform: uppercase;
8120
+ white-space: nowrap;
8121
+ padding-bottom: 0.95rem;
7845
8122
  `;
7846
- var MainSubtitle = styled10.p`
8123
+ var Subtitle = styled10.p`
7847
8124
  font-size: 0.9rem;
7848
8125
  font-weight: 400;
7849
8126
  color: var(--color-text-secondary, #848e9c);
7850
- text-align: left;
7851
- margin: 0 0 1.5rem;
7852
- line-height: 1.5;
8127
+ text-align: center;
8128
+ margin: 0 0 2rem;
7853
8129
  `;
7854
8130
  var LogoContainer3 = styled10.div`
7855
8131
  display: flex;
@@ -7859,21 +8135,21 @@ var LogoContainer3 = styled10.div`
7859
8135
  margin-bottom: 0.5rem;
7860
8136
  `;
7861
8137
  var LogoImage = styled10.img`
7862
- height: 40px;
8138
+ height: 60px;
7863
8139
  `;
7864
- styled10.div`
8140
+ var ButtonsContainer = styled10.div`
7865
8141
  display: flex;
7866
8142
  flex-direction: column;
7867
8143
  gap: 1rem;
7868
8144
  margin-bottom: 1.5rem;
7869
8145
  `;
7870
- styled10.div`
8146
+ var SignUpPrompt = styled10.div`
7871
8147
  text-align: center;
7872
8148
  font-size: 0.875rem;
7873
8149
  color: var(--color-text-secondary, #848e9c);
7874
8150
  margin-bottom: 0.5rem;
7875
8151
  `;
7876
- styled10.button`
8152
+ var SignUpLink = styled10.button`
7877
8153
  background: none;
7878
8154
  border: none;
7879
8155
  padding: 0;
@@ -7888,23 +8164,19 @@ styled10.button`
7888
8164
  `;
7889
8165
  var LoginButton = styled10.button`
7890
8166
  display: flex;
7891
- width: 100%;
7892
- box-sizing: border-box;
7893
8167
  align-items: center;
7894
8168
  justify-content: center;
7895
- gap: 0.55rem;
7896
- padding: 0.55rem 1.1rem;
8169
+ gap: 0.75rem;
8170
+ padding: 1rem 1.5rem;
7897
8171
  background-color: var(--color-background-light, #1a1a1a);
7898
8172
  border: 1px solid rgba(230, 198, 86, 0.2);
7899
8173
  border-radius: var(--border-radius, 8px);
7900
8174
  color: var(--color-text, #eaecef);
7901
- font-size: 0.82rem;
8175
+ font-size: 1rem;
7902
8176
  font-weight: 500;
7903
8177
  cursor: pointer;
7904
8178
  transition: all 0.2s ease;
7905
8179
 
7906
- svg { width: 16px; height: 16px; }
7907
-
7908
8180
  &:hover {
7909
8181
  background-color: rgba(230, 198, 86, 0.1);
7910
8182
  border-color: var(--color-accent, #E6C87E);
@@ -7947,60 +8219,265 @@ var EmailInput = styled10.input`
7947
8219
  color: var(--color-text-secondary, #848e9c);
7948
8220
  }
7949
8221
  `;
7950
- var GateForm = styled10.form`
8222
+ var codeBorderColor = (status, focused) => {
8223
+ if (status === "valid") return "var(--color-positive, #00C076)";
8224
+ if (status === "invalid") return "var(--color-negative, #FF5757)";
8225
+ return focused ? "var(--color-accent, #E6C87E)" : "rgba(230, 198, 86, 0.2)";
8226
+ };
8227
+ var CodeInputWrapper = styled10.div`
7951
8228
  display: flex;
7952
- flex-direction: column;
7953
- gap: 0.6rem;
7954
- width: 100%;
7955
- `;
7956
- var GateInputRow = styled10.div`
7957
- position: relative;
7958
- width: 100%;
7959
- `;
7960
- var GateInput = styled10.input`
8229
+ align-items: center;
7961
8230
  width: 100%;
7962
- box-sizing: border-box;
7963
- padding: 1rem 3.25rem 1rem 1.25rem;
8231
+ padding: 1rem 1.25rem;
7964
8232
  background-color: var(--color-background-light, #1a1a1a);
7965
- border: 1px solid rgba(230, 198, 86, 0.2);
8233
+ border: 1px solid ${(props) => codeBorderColor(props.$status, false)};
7966
8234
  border-radius: var(--border-radius, 8px);
7967
- color: var(--color-text, #eaecef);
7968
- font-size: 1rem;
8235
+ 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"};
7969
8236
  transition: all 0.2s ease;
7970
8237
 
7971
- &:focus { outline: none; border-color: var(--color-accent, #E6C87E); }
7972
- &::placeholder { color: var(--color-text-secondary, #848e9c); }
8238
+ &:focus-within {
8239
+ border-color: ${(props) => codeBorderColor(props.$status, true)};
8240
+ }
7973
8241
  `;
7974
- var GateSubmit = styled10.button`
8242
+ var CodePrefix = styled10.span`
8243
+ color: var(--color-text-secondary, #848e9c);
8244
+ font-size: 1.1rem;
8245
+ font-weight: 600;
8246
+ letter-spacing: 0.22em;
8247
+ font-family: 'Space Grotesk', monospace;
8248
+ user-select: none;
8249
+ `;
8250
+ var CodeSuffixInput = styled10.input`
8251
+ flex: 1;
8252
+ min-width: 0;
8253
+ border: none;
8254
+ background: transparent;
8255
+ color: var(--color-text, #eaecef);
8256
+ font-size: 1.1rem;
8257
+ font-weight: 600;
8258
+ letter-spacing: 0.22em;
8259
+ text-transform: uppercase;
8260
+ font-family: 'Space Grotesk', monospace;
8261
+ /* Explicit line-height so iOS in-app WebViews size the caret to the text
8262
+ box instead of rendering a short, top-aligned caret (line-height: normal
8263
+ + webfont + letter-spacing miscomputes it in some WebViews). */
8264
+ line-height: 1.5;
8265
+
8266
+ &:focus {
8267
+ outline: none;
8268
+ }
8269
+
8270
+ &::placeholder {
8271
+ color: rgba(132, 142, 156, 0.45);
8272
+ letter-spacing: 0.22em;
8273
+ }
8274
+ `;
8275
+ var GateBrand = styled10.div`
8276
+ display: flex;
8277
+ align-items: center;
8278
+ justify-content: center;
8279
+ gap: 0.7rem;
8280
+ margin-bottom: 0.6rem;
8281
+ `;
8282
+ var GateLogoLink = styled10.a`
8283
+ display: inline-flex;
8284
+ align-items: center;
8285
+ cursor: pointer;
8286
+ transition: opacity 0.15s ease;
8287
+
8288
+ &:hover {
8289
+ opacity: 0.85;
8290
+ }
8291
+ `;
8292
+ var GateLogo = styled10.img`
8293
+ height: 44px;
8294
+ display: block;
8295
+ `;
8296
+ var GateBrandDivider = styled10.span`
8297
+ width: 1px;
8298
+ height: 26px;
8299
+ background: rgba(255, 255, 255, 0.18);
8300
+ `;
8301
+ var GateBetaTag = styled10.span`
8302
+ color: var(--color-text-secondary, #848e9c);
8303
+ font-size: 0.8rem;
8304
+ font-weight: 500;
8305
+ letter-spacing: 0.28em;
8306
+ text-transform: uppercase;
8307
+ `;
8308
+ var GateForm = styled10.div`
8309
+ display: flex;
8310
+ flex-direction: column;
8311
+ align-items: center;
8312
+ gap: 0.9rem;
8313
+ width: 100%;
8314
+ max-width: 360px;
8315
+ position: relative;
8316
+ z-index: 1;
8317
+ `;
8318
+ var GateShell = styled10.div`
8319
+ position: fixed;
8320
+ inset: 0;
8321
+ display: flex;
8322
+ align-items: center;
8323
+ justify-content: center;
8324
+ z-index: 10000;
8325
+ background-color: rgba(10, 10, 12, 0.66);
8326
+ `;
8327
+ var GateTint = styled10.div`
7975
8328
  position: absolute;
7976
- top: 50%;
7977
- right: 0.55rem;
7978
- transform: translateY(-50%);
7979
- width: 34px;
7980
- height: 34px;
8329
+ left: 0;
8330
+ right: 0;
8331
+ bottom: 0;
8332
+ height: calc(${(props) => (3 - Math.max(0, Math.min(3, props.$reveal))) / 3 * 100}% + 90px);
8333
+ background: linear-gradient(
8334
+ to top,
8335
+ var(--color-background, #0a0a0c) calc(100% - 90px),
8336
+ rgba(10, 10, 12, 0) 100%
8337
+ );
8338
+ transition: height 0.65s ease;
8339
+ pointer-events: none;
8340
+ `;
8341
+ var GateOtpText = styled10.p`
8342
+ margin: 0;
8343
+ text-align: center;
8344
+ font-size: 0.9rem;
8345
+ line-height: 1.5;
8346
+ color: var(--color-text-secondary, #848e9c);
8347
+
8348
+ strong {
8349
+ color: var(--color-text, #eaecef);
8350
+ font-weight: 600;
8351
+ }
8352
+ `;
8353
+ var GateResendText = styled10.p`
8354
+ margin: 0.2rem 0 0;
8355
+ font-size: 0.85rem;
8356
+ color: var(--color-text-secondary, #848e9c);
8357
+
8358
+ button {
8359
+ background: none;
8360
+ border: none;
8361
+ color: var(--color-accent, #E6C87E);
8362
+ cursor: pointer;
8363
+ text-decoration: underline;
8364
+ font-size: inherit;
8365
+ }
8366
+ `;
8367
+ var GateSubmit = styled10.button`
7981
8368
  display: flex;
7982
8369
  align-items: center;
7983
8370
  justify-content: center;
8371
+ flex-shrink: 0;
8372
+ width: 28px;
8373
+ height: 28px;
8374
+ margin-left: 0.5rem;
8375
+ padding: 0;
7984
8376
  background: none;
7985
8377
  border: none;
7986
- border-radius: 8px;
7987
- color: var(--color-text-secondary, #848e9c);
7988
8378
  cursor: pointer;
7989
- transition: color 0.2s ease, background 0.2s ease;
8379
+ color: ${(props) => props.$status === "valid" ? "var(--color-positive, #00C076)" : props.$status === "invalid" ? "var(--color-negative, #FF5757)" : "var(--color-accent, #E6C87E)"};
8380
+ transition: color 0.15s ease, transform 0.15s ease, opacity 0.15s ease;
8381
+
8382
+ &:not(:disabled):hover {
8383
+ transform: translateX(2px);
8384
+ }
7990
8385
 
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; }
8386
+ &:disabled {
8387
+ opacity: ${(props) => props.$status === "valid" ? 1 : 0.35};
8388
+ color: ${(props) => props.$status === "valid" ? "var(--color-positive, #00C076)" : "var(--color-text-secondary, #848e9c)"};
8389
+ cursor: default;
8390
+ }
8391
+ `;
8392
+ var GateEmailInput = styled10.input`
8393
+ flex: 1;
8394
+ min-width: 0;
8395
+ border: none;
8396
+ background: transparent;
8397
+ color: var(--color-text, #eaecef);
8398
+ font-size: 1rem;
8399
+
8400
+ &:focus {
8401
+ outline: none;
8402
+ }
8403
+
8404
+ &::placeholder {
8405
+ color: rgba(132, 142, 156, 0.5);
8406
+ }
8407
+ `;
8408
+ var GateSpinner = styled10.span`
8409
+ width: 18px;
8410
+ height: 18px;
8411
+ border: 2px solid rgba(230, 198, 86, 0.3);
8412
+ border-top-color: var(--color-accent, #E6C87E);
8413
+ border-radius: 50%;
8414
+ animation: gateSpin 0.7s linear infinite;
8415
+
8416
+ @keyframes gateSpin {
8417
+ to { transform: rotate(360deg); }
8418
+ }
7994
8419
  `;
7995
8420
  var GateOr = styled10.div`
7996
8421
  display: flex;
7997
8422
  align-items: center;
7998
- gap: 0.75rem;
8423
+ gap: 0.6rem;
8424
+ width: 100%;
7999
8425
  color: var(--color-text-secondary, #848e9c);
8000
- font-size: 0.8rem;
8001
- margin: 0.4rem 0;
8426
+ font-size: 0.78rem;
8427
+ text-transform: lowercase;
8002
8428
 
8003
- &::before, &::after { content: ""; flex: 1; height: 1px; background: rgba(255, 255, 255, 0.08); }
8429
+ &::before,
8430
+ &::after {
8431
+ content: "";
8432
+ flex: 1;
8433
+ height: 1px;
8434
+ background: rgba(255, 255, 255, 0.1);
8435
+ }
8436
+ `;
8437
+ var GateWalletButton = styled10.button`
8438
+ display: inline-flex;
8439
+ align-items: center;
8440
+ justify-content: center;
8441
+ gap: 0.5rem;
8442
+ margin-top: 0.1rem;
8443
+ padding: 0.55rem 1.1rem;
8444
+ background: transparent;
8445
+ border: 1px solid rgba(255, 255, 255, 0.12);
8446
+ border-radius: var(--border-radius, 8px);
8447
+ color: var(--color-text-secondary, #848e9c);
8448
+ font-size: 0.85rem;
8449
+ font-weight: 500;
8450
+ cursor: pointer;
8451
+ transition: border-color 0.15s ease, color 0.15s ease;
8452
+
8453
+ svg {
8454
+ width: 16px;
8455
+ height: 16px;
8456
+ }
8457
+
8458
+ &:hover {
8459
+ border-color: rgba(230, 198, 86, 0.4);
8460
+ color: var(--color-text, #eaecef);
8461
+ }
8462
+ `;
8463
+ var GateReveal = styled10.div`
8464
+ display: flex;
8465
+ flex-direction: column;
8466
+ align-items: center;
8467
+ gap: 0.9rem;
8468
+ width: 100%;
8469
+ animation: gateReveal 0.3s ease-out;
8470
+
8471
+ @keyframes gateReveal {
8472
+ from {
8473
+ opacity: 0;
8474
+ transform: translateY(-6px);
8475
+ }
8476
+ to {
8477
+ opacity: 1;
8478
+ transform: translateY(0);
8479
+ }
8480
+ }
8004
8481
  `;
8005
8482
  var OTPContainer = styled10.div`
8006
8483
  display: flex;
@@ -8014,15 +8491,17 @@ var OTPInput = styled10.input`
8014
8491
  font-size: 1.5rem;
8015
8492
  font-weight: 600;
8016
8493
  background-color: var(--color-background-light, #1a1a1a);
8017
- border: 1px solid rgba(230, 198, 86, 0.2);
8494
+ border: 1px solid ${(props) => codeBorderColor(props.$status, false)};
8018
8495
  border-radius: var(--border-radius, 8px);
8019
- color: var(--color-accent, #E6C87E);
8496
+ color: ${(props) => props.$status === "valid" ? "var(--color-positive, #00C076)" : props.$status === "invalid" ? "var(--color-negative, #FF5757)" : "var(--color-accent, #E6C87E)"};
8497
+ 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"};
8498
+ opacity: ${(props) => props.$status === "checking" ? 0.6 : 1};
8020
8499
  transition: all 0.2s ease;
8021
8500
 
8022
8501
  &:focus {
8023
8502
  outline: none;
8024
- border-color: var(--color-accent, #E6C87E);
8025
- box-shadow: 0 0 0 2px rgba(230, 198, 86, 0.2);
8503
+ border-color: ${(props) => codeBorderColor(props.$status, true)};
8504
+ 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
8505
  }
8027
8506
  `;
8028
8507
  var SubmitButton = styled10.button`
@@ -8390,15 +8869,24 @@ var SpinnerRing = styled10.div`
8390
8869
  }
8391
8870
  `;
8392
8871
  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.",
8872
+ // Exchange
8395
8873
  "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.",
8874
+ "Loaf settles in 1 second. Traditional property takes 30-90 days.",
8398
8875
  "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."
8876
+ "Continuous price discovery, not a single buyer negotiating in a back room.",
8877
+ "Algo-trade property on our API. Build a bot with Claude using our docs.",
8878
+ // What you're holding
8879
+ "Hold shares secured 1:1 by the underlying property, as regulated securities.",
8880
+ "The same economic exposure as owning the property. Without the friction, gatekeeping, or illiquidity.",
8881
+ "Securitisation of property brings a new dimension of flexibility and tradability.",
8882
+ // Frictionless
8883
+ "Settle in seconds, then sell again whenever you want. No waiting days or weeks.",
8884
+ "99% of the frictions in property transactions can be, and have been, reduced.",
8885
+ // Vision
8886
+ "A family office can rebalance a billion dollars in an afternoon, but couldn't sell a $50M property in under six months.",
8887
+ "Family offices and HNWIs prize capital flexibility. Loaf makes property flexible.",
8888
+ "There's no law of physics that says the world's oldest asset class must stay illiquid.",
8889
+ "What if property underwent an IPO?"
8402
8890
  ];
8403
8891
  var FactCard = styled10.div`
8404
8892
  margin-top: 1.5rem;
@@ -8506,7 +8994,7 @@ var LogoImage2 = styled10.img`
8506
8994
  height: 30px;
8507
8995
  width: auto;
8508
8996
  `;
8509
- var LogoBeta = styled10.span`
8997
+ var LogoBeta2 = styled10.span`
8510
8998
  font-size: 0.6rem;
8511
8999
  font-weight: 600;
8512
9000
  letter-spacing: 0.08em;
@@ -8664,7 +9152,7 @@ function OnboardingGuide({
8664
9152
  /* @__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
9153
  /* @__PURE__ */ jsxs(LogoRow, { children: [
8666
9154
  /* @__PURE__ */ jsx(LogoImage2, { src: logoSrc, alt: "Loaf" }),
8667
- /* @__PURE__ */ jsx(LogoBeta, { children: "Private Beta" })
9155
+ /* @__PURE__ */ jsx(LogoBeta2, { children: "Private Beta" })
8668
9156
  ] }),
8669
9157
  /* @__PURE__ */ jsx(Title2, { children: "You're in \u{1F389}" }),
8670
9158
  /* @__PURE__ */ jsx(Subtitle2, { children: "Here's everything you need to make your first trade." }),
@@ -12034,7 +12522,8 @@ function AssetSelectorBar({
12034
12522
  onSelect,
12035
12523
  trailing,
12036
12524
  imageUrl,
12037
- badgeLabel
12525
+ badgeLabel,
12526
+ tickerPrimary
12038
12527
  }) {
12039
12528
  const [isDropdownOpen, setIsDropdownOpen] = useState(false);
12040
12529
  const hasItems = selectorItems && selectorItems.length > 0;
@@ -12045,9 +12534,14 @@ function AssetSelectorBar({
12045
12534
  ];
12046
12535
  return /* @__PURE__ */ jsx(AssetSelectorWrapper, { children: /* @__PURE__ */ jsxs(IPOAssetSelector, { children: [
12047
12536
  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()})` : "",
12537
+ /* @__PURE__ */ jsx(AssetSelectorDropdown, { $flush: tickerPrimary, onClick: () => hasItems && setIsDropdownOpen((prev) => !prev), children: /* @__PURE__ */ jsxs(AssetName, { children: [
12538
+ tickerPrimary && currentTicker ? /* @__PURE__ */ jsxs(AssetIdentity, { children: [
12539
+ /* @__PURE__ */ jsx(AssetTicker, { children: currentTicker.toUpperCase() }),
12540
+ /* @__PURE__ */ jsx(AssetSubName, { children: propertyName })
12541
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
12542
+ propertyName,
12543
+ currentTicker ? ` (${currentTicker.toUpperCase()})` : ""
12544
+ ] }),
12051
12545
  badgeLabel && /* @__PURE__ */ jsx(CompetitionBadge, { children: badgeLabel }),
12052
12546
  hasItems && /* @__PURE__ */ jsx(
12053
12547
  "svg",
@@ -12076,7 +12570,7 @@ function AssetSelectorBar({
12076
12570
  ] })
12077
12571
  ] })
12078
12572
  ] }, m.label)) }),
12079
- trailing != null && /* @__PURE__ */ jsx(TrailingWrapper, { children: /* @__PURE__ */ jsx(TrailingInner, { children: trailing }) }),
12573
+ trailing != null && /* @__PURE__ */ jsx(TrailingWrapper, { className: "asset-bar-trailing", children: /* @__PURE__ */ jsx(TrailingInner, { children: trailing }) }),
12080
12574
  isDropdownOpen && hasItems && /* @__PURE__ */ jsx(IPODropdown, { children: selectorItems.map((item) => {
12081
12575
  const isCurrent = item.tokenName === currentTokenName;
12082
12576
  const status = item.status?.toUpperCase();
@@ -12167,13 +12661,40 @@ var AssetSelectorDropdown = styled10.div`
12167
12661
  display: flex;
12168
12662
  align-items: center;
12169
12663
  cursor: pointer;
12170
- padding: 0.5rem;
12664
+ /* $flush (tickerPrimary): no horizontal padding / no hover box, so opening the
12665
+ selector doesn't make the ticker appear shoved right by the highlight box. */
12666
+ padding: ${(p) => p.$flush ? "0.25rem 0" : "0.5rem"};
12171
12667
  border-radius: 8px;
12172
- transition: all 0.2s ease;
12668
+ transition: ${(p) => p.$flush ? "none" : "all 0.2s ease"};
12173
12669
  flex: 0 1 auto;
12174
12670
  min-width: 0;
12175
12671
  overflow: hidden;
12176
- &:hover { background-color: rgba(255,255,255,0.05); }
12672
+ &:hover { background-color: ${(p) => p.$flush ? "transparent" : "rgba(255,255,255,0.05)"}; }
12673
+ `;
12674
+ var AssetIdentity = styled10.div`
12675
+ display: flex;
12676
+ flex-direction: column;
12677
+ min-width: 0;
12678
+ line-height: 1.2;
12679
+ `;
12680
+ var AssetTicker = styled10.span`
12681
+ font-weight: 700;
12682
+ font-size: 1.35rem;
12683
+ color: var(--color-text);
12684
+ letter-spacing: -0.01em;
12685
+ white-space: nowrap;
12686
+ overflow: hidden;
12687
+ text-overflow: ellipsis;
12688
+
12689
+ @media (max-width: 480px) { font-size: 1.2rem; }
12690
+ `;
12691
+ var AssetSubName = styled10.span`
12692
+ font-size: 0.8rem;
12693
+ font-weight: 500;
12694
+ color: rgba(255, 255, 255, 0.4);
12695
+ white-space: nowrap;
12696
+ overflow: hidden;
12697
+ text-overflow: ellipsis;
12177
12698
  `;
12178
12699
  var AssetName = styled10.div`
12179
12700
  display: flex;
@@ -14851,27 +15372,30 @@ function PortfolioActivityPanel({
14851
15372
  ] }, pos.tokenName);
14852
15373
  })
14853
15374
  ] }),
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
- ] }) })
15375
+ positions.length > 0 && closeAllOpen && onClosePosition && typeof document !== "undefined" && createPortal(
15376
+ /* @__PURE__ */ jsx(CloseAllOverlay, { onClick: () => setCloseAllOpen(false), children: /* @__PURE__ */ jsxs(CloseAllModal, { onClick: (e) => e.stopPropagation(), children: [
15377
+ /* @__PURE__ */ jsx(CloseAllModalX, { onClick: () => setCloseAllOpen(false), children: "\xD7" }),
15378
+ /* @__PURE__ */ jsx(CloseAllTitle, { children: "Confirm Sell All" }),
15379
+ /* @__PURE__ */ jsx(CloseAllDesc, { children: "This will sell all your positions and cancel their associated TP/SL orders." }),
15380
+ /* @__PURE__ */ jsxs(CloseAllOption, { onClick: () => setCloseAllType("market"), children: [
15381
+ /* @__PURE__ */ jsx(CloseAllRadio, { $selected: closeAllType === "market" }),
15382
+ /* @__PURE__ */ jsx("span", { children: "Market Sell" })
15383
+ ] }),
15384
+ /* @__PURE__ */ jsxs(CloseAllOption, { onClick: () => setCloseAllType("limit"), children: [
15385
+ /* @__PURE__ */ jsx(CloseAllRadio, { $selected: closeAllType === "limit" }),
15386
+ /* @__PURE__ */ jsx("span", { children: "Limit Sell at Mid Price" })
15387
+ ] }),
15388
+ /* @__PURE__ */ jsxs(CloseAllConfirmBtn, { onClick: () => {
15389
+ positions.forEach((pos) => onClosePosition(pos.tokenName, closeAllType, pos.quantity, pos.marketPrice));
15390
+ setCloseAllOpen(false);
15391
+ }, children: [
15392
+ "Confirm ",
15393
+ closeAllType === "market" ? "Market" : "Limit",
15394
+ " Sell"
15395
+ ] })
15396
+ ] }) }),
15397
+ document.body
15398
+ )
14875
15399
  ] }),
14876
15400
  activeTab === "subscriptions" && /* @__PURE__ */ jsxs(Fragment, { children: [
14877
15401
  offeringOrders.length === 0 && /* @__PURE__ */ jsx(EmptyState, { children: "IPO allocations will appear here once you subscribe." }),
@@ -15328,57 +15852,63 @@ function PortfolioActivityPanel({
15328
15852
  ] }),
15329
15853
  /* @__PURE__ */ jsx(PaginationBtn, { disabled: currentPage >= totalPages - 1, onClick: () => setCurrentPage((p) => p + 1), children: "Next \u203A" })
15330
15854
  ] }),
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);
15855
+ cancelAllConfirmOpen && typeof document !== "undefined" && createPortal(
15856
+ /* @__PURE__ */ jsx(CancelConfirmOverlay, { onClick: () => setCancelAllConfirmOpen(false), children: /* @__PURE__ */ jsxs(CancelConfirmModal, { onClick: (e) => e.stopPropagation(), children: [
15857
+ /* @__PURE__ */ jsx(CancelConfirmTitle, { children: "Cancel All Orders" }),
15858
+ /* @__PURE__ */ jsxs(CancelConfirmBody, { children: [
15859
+ "Are you sure you want to cancel all ",
15860
+ openOrdersCount,
15861
+ " open orders? This action cannot be undone."
15862
+ ] }),
15863
+ /* @__PURE__ */ jsxs(CancelConfirmActions, { children: [
15864
+ /* @__PURE__ */ jsx(CancelConfirmDismiss, { type: "button", onClick: () => setCancelAllConfirmOpen(false), children: "Keep Orders" }),
15865
+ /* @__PURE__ */ jsx(
15866
+ CancelConfirmProceed,
15867
+ {
15868
+ type: "button",
15869
+ disabled: cancellingAll,
15870
+ onClick: async () => {
15871
+ if (onCancelAllOrders) {
15872
+ setCancellingAll(true);
15873
+ try {
15874
+ await onCancelAllOrders();
15875
+ } finally {
15876
+ setCancellingAll(false);
15877
+ }
15352
15878
  }
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
- ] }) })
15879
+ setCancelAllConfirmOpen(false);
15880
+ },
15881
+ children: cancellingAll ? "Cancelling\u2026" : "Yes, Cancel All"
15882
+ }
15883
+ )
15884
+ ] })
15885
+ ] }) }),
15886
+ document.body
15887
+ ),
15888
+ pendingCancelOrderId !== null && typeof document !== "undefined" && createPortal(
15889
+ /* @__PURE__ */ jsx(CancelConfirmOverlay, { onClick: () => setPendingCancelOrderId(null), children: /* @__PURE__ */ jsxs(CancelConfirmModal, { onClick: (e) => e.stopPropagation(), children: [
15890
+ /* @__PURE__ */ jsx(CancelConfirmTitle, { children: "Cancel Order" }),
15891
+ /* @__PURE__ */ jsx(CancelConfirmBody, { children: "Are you sure you want to cancel this order? This action cannot be undone." }),
15892
+ /* @__PURE__ */ jsxs(CancelConfirmActions, { children: [
15893
+ /* @__PURE__ */ jsx(CancelConfirmDismiss, { type: "button", onClick: () => setPendingCancelOrderId(null), children: "Keep Order" }),
15894
+ /* @__PURE__ */ jsx(
15895
+ CancelConfirmProceed,
15896
+ {
15897
+ type: "button",
15898
+ disabled: cancellingOrderId != null,
15899
+ onClick: async () => {
15900
+ if (onCancelOrder && pendingCancelOrderId !== null) {
15901
+ await onCancelOrder(pendingCancelOrderId);
15902
+ }
15903
+ setPendingCancelOrderId(null);
15904
+ },
15905
+ children: cancellingOrderId === pendingCancelOrderId ? "Cancelling\u2026" : "Yes, Cancel"
15906
+ }
15907
+ )
15908
+ ] })
15909
+ ] }) }),
15910
+ document.body
15911
+ )
15382
15912
  ] });
15383
15913
  }
15384
15914
  var Container2 = styled10.div`
@@ -15449,7 +15979,7 @@ var Tab = styled10.button`
15449
15979
  }
15450
15980
  `;
15451
15981
  var TabCount = styled10.span`
15452
- color: #E6C656;
15982
+ color: var(--color-accent, #E6C87E);
15453
15983
  font-weight: 600;
15454
15984
  margin-left: 2px;
15455
15985
  `;
@@ -15634,7 +16164,8 @@ var CancelConfirmOverlay = styled10.div`
15634
16164
  display: flex;
15635
16165
  align-items: center;
15636
16166
  justify-content: center;
15637
- z-index: 1000;
16167
+ /* Above the trade page's fullscreen/sticky layers (TradePage.css uses up to 10001). */
16168
+ z-index: 10050;
15638
16169
  `;
15639
16170
  var CancelConfirmModal = styled10.div`
15640
16171
  background: #1a1a1a;
@@ -15748,7 +16279,8 @@ var CloseAllOverlay = styled10.div`
15748
16279
  display: flex;
15749
16280
  align-items: center;
15750
16281
  justify-content: center;
15751
- z-index: 1000;
16282
+ /* Above the trade page's fullscreen/sticky layers (TradePage.css uses up to 10001). */
16283
+ z-index: 10050;
15752
16284
  `;
15753
16285
  var CloseAllModal = styled10.div`
15754
16286
  background: #1a1a1a;