@loafmarkets/ui 0.1.405 → 0.1.406

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.d.mts CHANGED
@@ -706,6 +706,11 @@ type HeaderProps = {
706
706
  onOrdersNavigate?: () => void;
707
707
  onWalletNavigate?: () => void;
708
708
  onSettingsClick?: () => void;
709
+ /** Fired by the "Refer" CTA button + user-menu "Refer" item. When omitted, both
710
+ * fall back to `onSettingsClick` (so existing consumers are unchanged). Lets a
711
+ * consumer route Refer somewhere other than Settings (e.g. an invite modal)
712
+ * without DOM-level click interception. */
713
+ onReferClick?: () => void;
709
714
  portfolioSummary?: HeaderPortfolioData | null;
710
715
  /**
711
716
  * True while a brand-new user's test funds (100k mock USD) are being deposited
@@ -716,7 +721,7 @@ type HeaderProps = {
716
721
  /**
717
722
  * The bar's authed CTA button. Defaults to "deposit" (the funding flow) so
718
723
  * existing consumers are unchanged. "refer" swaps it for a Refer button that
719
- * opens the Settings popup (same `onSettingsClick` as the user-menu Refer item).
724
+ * fires `onReferClick` (falling back to `onSettingsClick` when not provided).
720
725
  */
721
726
  headerCta?: "deposit" | "refer";
722
727
  /** Show the "Refer" user-menu item. Off by default — referral is feature-flagged
@@ -1093,7 +1098,7 @@ type ToastData = {
1093
1098
  amount?: string;
1094
1099
  /** On-chain tx hash — shown truncated with a block explorer link */
1095
1100
  txHash?: string;
1096
- /** Explorer base URL, defaults to Base Sepolia */
1101
+ /** Explorer base URL, defaults to Arbitrum Sepolia */
1097
1102
  explorerUrl?: string;
1098
1103
  /** Auto-dismiss after ms. 0 = never. Default 6000 */
1099
1104
  duration?: number;
@@ -1269,11 +1274,13 @@ type PropertyPhotoGalleryProps = {
1269
1274
  isOpen: boolean;
1270
1275
  onClose: () => void;
1271
1276
  startIndex?: number;
1277
+ startWithVideo?: boolean;
1272
1278
  images?: PropertyGalleryImage[];
1273
1279
  title?: string;
1274
1280
  subtitle?: string;
1281
+ videoUrl?: string;
1275
1282
  };
1276
- declare function PropertyPhotoGallery({ isOpen, onClose, startIndex, title, subtitle, images, }: PropertyPhotoGalleryProps): react_jsx_runtime.JSX.Element | null;
1283
+ declare function PropertyPhotoGallery({ isOpen, onClose, startIndex, startWithVideo, title, subtitle, images, videoUrl, }: PropertyPhotoGalleryProps): react_jsx_runtime.JSX.Element | null;
1277
1284
 
1278
1285
  type UseAdaptivePollingOptions = {
1279
1286
  enabled: boolean;
package/dist/index.d.ts CHANGED
@@ -706,6 +706,11 @@ type HeaderProps = {
706
706
  onOrdersNavigate?: () => void;
707
707
  onWalletNavigate?: () => void;
708
708
  onSettingsClick?: () => void;
709
+ /** Fired by the "Refer" CTA button + user-menu "Refer" item. When omitted, both
710
+ * fall back to `onSettingsClick` (so existing consumers are unchanged). Lets a
711
+ * consumer route Refer somewhere other than Settings (e.g. an invite modal)
712
+ * without DOM-level click interception. */
713
+ onReferClick?: () => void;
709
714
  portfolioSummary?: HeaderPortfolioData | null;
710
715
  /**
711
716
  * True while a brand-new user's test funds (100k mock USD) are being deposited
@@ -716,7 +721,7 @@ type HeaderProps = {
716
721
  /**
717
722
  * The bar's authed CTA button. Defaults to "deposit" (the funding flow) so
718
723
  * existing consumers are unchanged. "refer" swaps it for a Refer button that
719
- * opens the Settings popup (same `onSettingsClick` as the user-menu Refer item).
724
+ * fires `onReferClick` (falling back to `onSettingsClick` when not provided).
720
725
  */
721
726
  headerCta?: "deposit" | "refer";
722
727
  /** Show the "Refer" user-menu item. Off by default — referral is feature-flagged
@@ -1093,7 +1098,7 @@ type ToastData = {
1093
1098
  amount?: string;
1094
1099
  /** On-chain tx hash — shown truncated with a block explorer link */
1095
1100
  txHash?: string;
1096
- /** Explorer base URL, defaults to Base Sepolia */
1101
+ /** Explorer base URL, defaults to Arbitrum Sepolia */
1097
1102
  explorerUrl?: string;
1098
1103
  /** Auto-dismiss after ms. 0 = never. Default 6000 */
1099
1104
  duration?: number;
@@ -1269,11 +1274,13 @@ type PropertyPhotoGalleryProps = {
1269
1274
  isOpen: boolean;
1270
1275
  onClose: () => void;
1271
1276
  startIndex?: number;
1277
+ startWithVideo?: boolean;
1272
1278
  images?: PropertyGalleryImage[];
1273
1279
  title?: string;
1274
1280
  subtitle?: string;
1281
+ videoUrl?: string;
1275
1282
  };
1276
- declare function PropertyPhotoGallery({ isOpen, onClose, startIndex, title, subtitle, images, }: PropertyPhotoGalleryProps): react_jsx_runtime.JSX.Element | null;
1283
+ declare function PropertyPhotoGallery({ isOpen, onClose, startIndex, startWithVideo, title, subtitle, images, videoUrl, }: PropertyPhotoGalleryProps): react_jsx_runtime.JSX.Element | null;
1277
1284
 
1278
1285
  type UseAdaptivePollingOptions = {
1279
1286
  enabled: boolean;
package/dist/index.js CHANGED
@@ -5101,6 +5101,7 @@ var Header = ({
5101
5101
  onOrdersNavigate: _onOrdersNavigate,
5102
5102
  onWalletNavigate: _onWalletNavigate,
5103
5103
  onSettingsClick,
5104
+ onReferClick,
5104
5105
  portfolioSummary,
5105
5106
  fundsPending = false,
5106
5107
  transparentOnTop = false,
@@ -5539,7 +5540,7 @@ var Header = ({
5539
5540
  onClick: (event) => {
5540
5541
  event?.preventDefault();
5541
5542
  closeMenus();
5542
- onSettingsClick?.();
5543
+ (onReferClick ?? onSettingsClick)?.();
5543
5544
  },
5544
5545
  children: "Refer"
5545
5546
  }
@@ -5582,7 +5583,7 @@ var Header = ({
5582
5583
  onClick: (event) => {
5583
5584
  event?.preventDefault();
5584
5585
  closeMenus();
5585
- onSettingsClick?.();
5586
+ (onReferClick ?? onSettingsClick)?.();
5586
5587
  },
5587
5588
  children: /* @__PURE__ */ jsxRuntime.jsx(MenuItem, { children: "Refer" })
5588
5589
  }
@@ -7700,12 +7701,12 @@ var LoginPopup = ({
7700
7701
  marginBottom: "1.25rem"
7701
7702
  }, children: [
7702
7703
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: { width: 7, height: 7, borderRadius: "50%", background: "#0052FF", flexShrink: 0 } }),
7703
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.72rem", fontWeight: 600, color: "#6b9fff", letterSpacing: "0.02em" }, children: "Base Sepolia" })
7704
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.72rem", fontWeight: 600, color: "#6b9fff", letterSpacing: "0.02em" }, children: "Arbitrum Sepolia" })
7704
7705
  ] }),
7705
7706
  /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { fontSize: "0.82rem", color: "rgba(255,255,255,0.5)", textAlign: "center", marginBottom: "1.1rem", lineHeight: 1.5, maxWidth: 320 }, children: [
7706
7707
  "Send ",
7707
7708
  /* @__PURE__ */ jsxRuntime.jsx("strong", { style: { color: "rgba(255,255,255,0.8)" }, children: "USDC" }),
7708
- " on Base Sepolia to the address below. Only send on this network."
7709
+ " on Arbitrum Sepolia to the address below. Only send on this network."
7709
7710
  ] }),
7710
7711
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
7711
7712
  width: "100%",
@@ -10350,7 +10351,7 @@ function PropertyOverview({
10350
10351
  isLoading = false,
10351
10352
  ticker,
10352
10353
  contractAddress,
10353
- chain = "Base (Ethereum L2)",
10354
+ chain = "Arbitrum (Ethereum L2)",
10354
10355
  volume24h,
10355
10356
  priceHistory: priceHistoryProp,
10356
10357
  beta = false,
@@ -10389,13 +10390,13 @@ function PropertyOverview({
10389
10390
  const resolvedPropertyType = rawPropertyType?.toLowerCase() === "house" ? "Residential" : rawPropertyType;
10390
10391
  const galleryImages = images ?? [];
10391
10392
  const chainValue = /* @__PURE__ */ jsxRuntime.jsxs(ChainLogos, { children: [
10392
- /* @__PURE__ */ jsxRuntime.jsx(ChainLogoImg, { src: "/Base-Symbol.png", alt: "Base" }),
10393
+ /* @__PURE__ */ jsxRuntime.jsx(ChainLogoImg, { src: "/Arbitrum-Symbol.svg", alt: "Arbitrum" }),
10393
10394
  /* @__PURE__ */ jsxRuntime.jsx(ChainLogoImg, { src: "/Ethereum-Logo.png", alt: "Ethereum", style: { filter: "brightness(0) invert(1)" } }),
10394
10395
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: chain })
10395
10396
  ] });
10396
10397
  const tokenDetailsItems = [
10397
10398
  { label: "Chain", value: chainValue },
10398
- { label: "On-Chain Address", value: contractAddress ? truncateAddress(contractAddress) : "\u2014", mono: true, link: contractAddress ? `https://sepolia.basescan.org/address/${contractAddress}` : void 0, copyValue: contractAddress },
10399
+ { label: "On-Chain Address", value: contractAddress ? truncateAddress(contractAddress) : "\u2014", mono: true, link: contractAddress ? `https://sepolia.arbiscan.io/address/${contractAddress}` : void 0, copyValue: contractAddress },
10399
10400
  { label: "Token Ticker", value: ticker ?? "\u2014" },
10400
10401
  { label: "Total Circulating Tokens", value: isLoading && resolvedTokensIssued == null ? loadingSkeleton : resolvedTokensIssued?.toLocaleString() ?? "\u2014" },
10401
10402
  { label: "Legal Structuring", value: "See Documents", docLink: true },
@@ -10415,7 +10416,9 @@ function PropertyOverview({
10415
10416
  ];
10416
10417
  const growthPct = overviewData?.growth5Yr;
10417
10418
  const propertyStats = [
10418
- { label: "Property Value", value: isLoading && tokenMarketCap == null ? loadingSkeleton : tokenMarketCap ? fmtDollar(tokenMarketCap) : "\u2014" },
10419
+ // Property Value = the API offeringValuation (OverviewResponse.offeringValuation), NOT
10420
+ // midPrice×tokensIssued market cap (fixed 2026-07-22). null → '—', never a fabricated cap.
10421
+ { label: "Property Value", value: isLoading && overviewData?.offeringValuation == null ? loadingSkeleton : overviewData?.offeringValuation ? fmtDollar(overviewData.offeringValuation) : "\u2014" },
10419
10422
  ...beta ? [] : [{ label: "Offers", value: "3" }],
10420
10423
  { label: "Growth (5yr)", value: growthPct != null ? `${growthPct > 0 ? "+" : ""}${growthPct}%` : "\u2014" },
10421
10424
  { label: "Last Dividend", value: overviewData?.lastDividend != null ? `$${overviewData.lastDividend.toFixed(2)}` : "\u2014" },
@@ -14916,7 +14919,7 @@ var getOrderStatusMeta = (statusRaw) => {
14916
14919
  return { color: "#E6C87E", bg: "rgba(240,185,11,0.15)" };
14917
14920
  };
14918
14921
  var ChevronDown = () => /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx("polyline", { points: "6 9 12 15 18 9" }) });
14919
- var BaseScanIconLink = ({ url }) => /* @__PURE__ */ jsxRuntime.jsx(TxLink, { href: url, target: "_blank", rel: "noopener noreferrer", title: "View on BaseScan", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
14922
+ var ArbiscanIconLink = ({ url }) => /* @__PURE__ */ jsxRuntime.jsx(TxLink, { href: url, target: "_blank", rel: "noopener noreferrer", title: "View on Arbiscan", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
14920
14923
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }),
14921
14924
  /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "15 3 21 3 21 9" }),
14922
14925
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "10", y1: "14", x2: "21", y2: "3" })
@@ -14941,7 +14944,7 @@ function PortfolioActivityPanel({
14941
14944
  defaultTab,
14942
14945
  onTabChange,
14943
14946
  pageSize = ACTIVITY_PAGE_SIZE_DEFAULT,
14944
- blockExplorerBaseUrl: _blockExplorerBaseUrl = "https://sepolia.basescan.org/tx",
14947
+ blockExplorerBaseUrl: _blockExplorerBaseUrl = "https://sepolia.arbiscan.io/tx",
14945
14948
  className,
14946
14949
  style
14947
14950
  }) {
@@ -15385,7 +15388,7 @@ function PortfolioActivityPanel({
15385
15388
  /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.35rem" }, children: [
15386
15389
  formatNumber2(order.quantity),
15387
15390
  order.ticker && /* @__PURE__ */ jsxRuntime.jsx(TickerTag, { style: { marginLeft: 0 }, children: order.ticker.toUpperCase() }),
15388
- order.txHash && /* @__PURE__ */ jsxRuntime.jsx(BaseScanIconLink, { url: `${_blockExplorerBaseUrl}/${order.txHash}` })
15391
+ order.txHash && /* @__PURE__ */ jsxRuntime.jsx(ArbiscanIconLink, { url: `${_blockExplorerBaseUrl}/${order.txHash}` })
15389
15392
  ] })
15390
15393
  ] }),
15391
15394
  /* @__PURE__ */ jsxRuntime.jsxs(CUnitsCell, { children: [
@@ -15438,7 +15441,7 @@ function PortfolioActivityPanel({
15438
15441
  order.status === "REJECTED" && order.rejectionReason && /* @__PURE__ */ jsxRuntime.jsx(CellText, { $muted: true, style: { fontSize: "0.62rem", marginTop: "0.2rem", maxWidth: "120px", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, title: order.rejectionReason, children: order.rejectionReason })
15439
15442
  ] }),
15440
15443
  /* @__PURE__ */ jsxRuntime.jsx(GridCell, { children: /* @__PURE__ */ jsxRuntime.jsx(CellText, { $muted: true, children: formatTimestamp(order.createdAt) }) }),
15441
- /* @__PURE__ */ jsxRuntime.jsx(GridCell, { children: order.txHash && /* @__PURE__ */ jsxRuntime.jsx(TxLink, { href: `${_blockExplorerBaseUrl}/${order.txHash}`, target: "_blank", rel: "noopener noreferrer", title: "View on BaseScan", children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
15444
+ /* @__PURE__ */ jsxRuntime.jsx(GridCell, { children: order.txHash && /* @__PURE__ */ jsxRuntime.jsx(TxLink, { href: `${_blockExplorerBaseUrl}/${order.txHash}`, target: "_blank", rel: "noopener noreferrer", title: "View on Arbiscan", children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
15442
15445
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }),
15443
15446
  /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "15 3 21 3 21 9" }),
15444
15447
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "10", y1: "14", x2: "21", y2: "3" })
@@ -15682,7 +15685,7 @@ function PortfolioActivityPanel({
15682
15685
  /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.35rem" }, children: [
15683
15686
  formatNumber2(trade.quantity),
15684
15687
  trade.ticker && /* @__PURE__ */ jsxRuntime.jsx(TickerTag, { style: { marginLeft: 0 }, children: trade.ticker.toUpperCase() }),
15685
- trade.txHash && /* @__PURE__ */ jsxRuntime.jsx(BaseScanIconLink, { url: `${_blockExplorerBaseUrl}/${trade.txHash}` })
15688
+ trade.txHash && /* @__PURE__ */ jsxRuntime.jsx(ArbiscanIconLink, { url: `${_blockExplorerBaseUrl}/${trade.txHash}` })
15686
15689
  ] })
15687
15690
  ] }),
15688
15691
  /* @__PURE__ */ jsxRuntime.jsxs(CUnitsCell, { children: [
@@ -15749,7 +15752,7 @@ function PortfolioActivityPanel({
15749
15752
  /* @__PURE__ */ jsxRuntime.jsx(GridCell, { children: /* @__PURE__ */ jsxRuntime.jsx(CellText, { children: formatCurrency4(trade.price * trade.quantity) }) }),
15750
15753
  /* @__PURE__ */ jsxRuntime.jsx(GridCell, { children: settlementMeta ? /* @__PURE__ */ jsxRuntime.jsx(ActivityTag, { $color: settlementMeta.color, $bg: settlementMeta.bg, children: settlementLabel }) : /* @__PURE__ */ jsxRuntime.jsx(CellText, { $muted: true, children: "\u2014" }) }),
15751
15754
  /* @__PURE__ */ jsxRuntime.jsx(GridCell, { children: /* @__PURE__ */ jsxRuntime.jsx(CellText, { $muted: true, children: formatTimestamp(trade.executedAt) }) }),
15752
- /* @__PURE__ */ jsxRuntime.jsx(GridCell, { children: trade.txHash && /* @__PURE__ */ jsxRuntime.jsx(TxLink, { href: `${_blockExplorerBaseUrl}/${trade.txHash}`, target: "_blank", rel: "noopener noreferrer", title: "View on BaseScan", children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
15755
+ /* @__PURE__ */ jsxRuntime.jsx(GridCell, { children: trade.txHash && /* @__PURE__ */ jsxRuntime.jsx(TxLink, { href: `${_blockExplorerBaseUrl}/${trade.txHash}`, target: "_blank", rel: "noopener noreferrer", title: "View on Arbiscan", children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
15753
15756
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }),
15754
15757
  /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "15 3 21 3 21 9" }),
15755
15758
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "10", y1: "14", x2: "21", y2: "3" })
@@ -15796,7 +15799,7 @@ function PortfolioActivityPanel({
15796
15799
  /* @__PURE__ */ jsxRuntime.jsx(CDValue, { children: formatTimestamp(t.createdAt) })
15797
15800
  ] })
15798
15801
  ] }),
15799
- txUrl && /* @__PURE__ */ jsxRuntime.jsx(CExpandedLink, { href: txUrl, target: "_blank", rel: "noopener noreferrer", children: "View on BaseScan \u2197" })
15802
+ txUrl && /* @__PURE__ */ jsxRuntime.jsx(CExpandedLink, { href: txUrl, target: "_blank", rel: "noopener noreferrer", children: "View on Arbiscan \u2197" })
15800
15803
  ] })
15801
15804
  ] }, rowKey);
15802
15805
  }),
@@ -15822,7 +15825,7 @@ function PortfolioActivityPanel({
15822
15825
  /* @__PURE__ */ jsxRuntime.jsx(GridCell, { children: /* @__PURE__ */ jsxRuntime.jsx(CellText, { $muted: true, children: formatAddress(t.address) }) }),
15823
15826
  /* @__PURE__ */ jsxRuntime.jsx(GridCell, { children: /* @__PURE__ */ jsxRuntime.jsx(ActivityTag, { $color: statusMeta.color, $bg: statusMeta.bg, children: prettyLabel(t.status) }) }),
15824
15827
  /* @__PURE__ */ jsxRuntime.jsx(GridCell, { children: /* @__PURE__ */ jsxRuntime.jsx(CellText, { $muted: true, children: formatTimestamp(t.createdAt) }) }),
15825
- /* @__PURE__ */ jsxRuntime.jsx(GridCell, { children: txUrl && /* @__PURE__ */ jsxRuntime.jsx(TxLink, { href: txUrl, target: "_blank", rel: "noopener noreferrer", title: "View on BaseScan", children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
15828
+ /* @__PURE__ */ jsxRuntime.jsx(GridCell, { children: txUrl && /* @__PURE__ */ jsxRuntime.jsx(TxLink, { href: txUrl, target: "_blank", rel: "noopener noreferrer", title: "View on Arbiscan", children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
15826
15829
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }),
15827
15830
  /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "15 3 21 3 21 9" }),
15828
15831
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "10", y1: "14", x2: "21", y2: "3" })
@@ -18925,13 +18928,14 @@ var CloseButton3 = styled10__default.default.button`
18925
18928
  var MainImageContainer = styled10__default.default.div`
18926
18929
  position: relative;
18927
18930
  flex: 1;
18931
+ min-height: 0;
18928
18932
  display: flex;
18929
18933
  align-items: center;
18930
18934
  justify-content: center;
18931
- padding: 80px 20px 140px;
18935
+ padding: 20px 20px 160px;
18932
18936
 
18933
18937
  @media (max-width: 768px) {
18934
- padding: 60px 0 110px;
18938
+ padding: 10px 0 130px;
18935
18939
  }
18936
18940
  `;
18937
18941
  var MainImage = styled10__default.default.img`
@@ -18943,8 +18947,11 @@ var MainImage = styled10__default.default.img`
18943
18947
  opacity: ${(p) => p.$visible ? 1 : 0};
18944
18948
  pointer-events: ${(p) => p.$visible ? "auto" : "none"};
18945
18949
  transition: opacity 300ms ease;
18946
- /* Inset box excludes the bottom thumbnail strip so margin:auto centres the image in the
18947
- space ABOVE the thumbnails (absolute ignores the parent's padding). */
18950
+ /* All slides share the same absolute box so switching only crossfades opacity
18951
+ no relative↔absolute size jump (the "grow then shrink" on navigation).
18952
+ The inset box excludes the bottom thumbnail strip so margin:auto centres the image
18953
+ in the space ABOVE the thumbnails (absolute positioning ignores the parent's padding,
18954
+ which otherwise let the image sink behind the thumbnails / "lean down"). */
18948
18955
  position: absolute;
18949
18956
  top: 24px;
18950
18957
  left: 24px;
@@ -19138,9 +19145,11 @@ function PropertyPhotoGallery({
19138
19145
  isOpen,
19139
19146
  onClose,
19140
19147
  startIndex = 0,
19148
+ startWithVideo = false,
19141
19149
  title = "28 Derby Street, Vaucluse",
19142
19150
  subtitle = "Elara \u2014 Luxury Harbour Residence",
19143
- images
19151
+ images,
19152
+ videoUrl
19144
19153
  }) {
19145
19154
  const galleryImages = images?.length ? images : DEFAULT_IMAGES;
19146
19155
  if (!isOpen) return null;
@@ -19149,16 +19158,19 @@ function PropertyPhotoGallery({
19149
19158
  {
19150
19159
  galleryImages,
19151
19160
  startIndex,
19161
+ startWithVideo,
19152
19162
  title,
19153
19163
  subtitle,
19154
- onClose
19164
+ onClose,
19165
+ videoUrl
19155
19166
  },
19156
- `${startIndex}-${galleryImages.length}`
19167
+ `${startIndex}-${galleryImages.length}-${startWithVideo}`
19157
19168
  );
19158
19169
  }
19159
- function GalleryContent({ galleryImages, startIndex, title, subtitle, onClose }) {
19170
+ function GalleryContent({ galleryImages, startIndex, startWithVideo, title, subtitle, onClose, videoUrl }) {
19160
19171
  const [currentIndex, setCurrentIndex] = React5.useState(() => clampIndex(startIndex, galleryImages.length));
19161
19172
  const [activeHotspotId, setActiveHotspotId] = React5.useState(null);
19173
+ const [showVideo, setShowVideo] = React5.useState(startWithVideo ?? false);
19162
19174
  const sliderRef = React5.useRef(null);
19163
19175
  const [isDragging, setIsDragging] = React5.useState(false);
19164
19176
  const wasDraggingRef = React5.useRef(false);
@@ -19208,7 +19220,7 @@ function GalleryContent({ galleryImages, startIndex, title, subtitle, onClose })
19208
19220
  ] }),
19209
19221
  /* @__PURE__ */ jsxRuntime.jsx(CloseButton3, { onClick: onClose, "aria-label": "Close gallery", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", width: "22", height: "22", fill: "none", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 6l12 12M6 18L18 6" }) }) })
19210
19222
  ] }),
19211
- /* @__PURE__ */ jsxRuntime.jsxs(SliderContainer, { onClick: (event) => event.stopPropagation(), children: [
19223
+ !showVideo && /* @__PURE__ */ jsxRuntime.jsxs(SliderContainer, { onClick: (event) => event.stopPropagation(), children: [
19212
19224
  /* @__PURE__ */ jsxRuntime.jsxs(
19213
19225
  SliderTrack,
19214
19226
  {
@@ -19271,7 +19283,21 @@ function GalleryContent({ galleryImages, startIndex, title, subtitle, onClose })
19271
19283
  children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", width: "22", height: "22", fill: "none", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15 18l-6-6 6-6" }) })
19272
19284
  }
19273
19285
  ),
19274
- galleryImages.map((image, idx) => /* @__PURE__ */ jsxRuntime.jsx(MainImage, { src: image.src, alt: image.alt, $visible: idx === currentIndex }, image.alt)),
19286
+ showVideo && videoUrl ? /* @__PURE__ */ jsxRuntime.jsx(GalleryVideoWrap, { children: /* @__PURE__ */ jsxRuntime.jsx(
19287
+ "video",
19288
+ {
19289
+ autoPlay: true,
19290
+ loop: true,
19291
+ playsInline: true,
19292
+ muted: true,
19293
+ controls: true,
19294
+ onVolumeChange: (e) => {
19295
+ if (!e.currentTarget.muted) e.currentTarget.muted = true;
19296
+ },
19297
+ style: { objectFit: "contain", borderRadius: 12, boxShadow: "0 30px 80px rgba(0,0,0,0.55)" },
19298
+ children: /* @__PURE__ */ jsxRuntime.jsx("source", { src: videoUrl, type: "video/mp4" })
19299
+ }
19300
+ ) }) : galleryImages.map((image, idx) => /* @__PURE__ */ jsxRuntime.jsx(MainImage, { src: image.src, alt: image.alt, $visible: idx === currentIndex }, image.alt)),
19275
19301
  galleryImages[currentIndex]?.hotspots?.map((hotspot) => /* @__PURE__ */ jsxRuntime.jsx(
19276
19302
  Hotspot,
19277
19303
  {
@@ -19316,11 +19342,83 @@ function GalleryContent({ galleryImages, startIndex, title, subtitle, onClose })
19316
19342
  ]
19317
19343
  }
19318
19344
  ),
19319
- /* @__PURE__ */ jsxRuntime.jsx(ThumbnailStrip, { onClick: (event) => event.stopPropagation(), children: galleryImages.map((image, idx) => /* @__PURE__ */ jsxRuntime.jsx(Thumbnail3, { $active: idx === currentIndex, onClick: () => setCurrentIndex(idx), "aria-label": `Preview ${image.label}`, children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: image.src, alt: image.alt }) }, image.alt)) })
19345
+ /* @__PURE__ */ jsxRuntime.jsxs(ThumbnailStrip, { onClick: (event) => event.stopPropagation(), children: [
19346
+ galleryImages.map((image, idx) => /* @__PURE__ */ jsxRuntime.jsx(Thumbnail3, { $active: !showVideo && idx === currentIndex, onClick: () => {
19347
+ setShowVideo(false);
19348
+ setCurrentIndex(idx);
19349
+ }, "aria-label": `Preview ${image.label}`, children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: image.src, alt: image.alt }) }, image.alt)),
19350
+ videoUrl && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
19351
+ /* @__PURE__ */ jsxRuntime.jsx(ThumbnailDivider, {}),
19352
+ /* @__PURE__ */ jsxRuntime.jsxs(VideoBtn, { $active: showVideo, onClick: () => setShowVideo(true), children: [
19353
+ /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
19354
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "2", y: "4", width: "14", height: "16", rx: "2" }),
19355
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16 8l6-4v16l-6-4z" })
19356
+ ] }),
19357
+ "Video"
19358
+ ] })
19359
+ ] })
19360
+ ] })
19320
19361
  ]
19321
19362
  }
19322
19363
  );
19323
19364
  }
19365
+ var GalleryVideoWrap = styled10__default.default.div`
19366
+ position: absolute;
19367
+ inset: 0;
19368
+ display: flex;
19369
+ align-items: center;
19370
+ justify-content: center;
19371
+ padding: 20px 20px 160px;
19372
+
19373
+ video {
19374
+ max-width: 90%;
19375
+ max-height: 100%;
19376
+ }
19377
+
19378
+ /* Property videos stay permanently muted — remove the volume + mute buttons
19379
+ from the native player (Chrome/Edge/Safari/iOS/Android). Firefox ignores
19380
+ these and is covered by the onVolumeChange force-mute fallback. */
19381
+ video::-webkit-media-controls-mute-button,
19382
+ video::-webkit-media-controls-volume-slider,
19383
+ video::-webkit-media-controls-volume-control-container {
19384
+ display: none !important;
19385
+ }
19386
+
19387
+ @media (max-width: 768px) {
19388
+ padding: 10px 0 130px;
19389
+
19390
+ video {
19391
+ max-width: 100%;
19392
+ }
19393
+ }
19394
+ `;
19395
+ var ThumbnailDivider = styled10__default.default.div`
19396
+ width: 1px;
19397
+ height: 48px;
19398
+ background: rgba(255, 255, 255, 0.15);
19399
+ flex-shrink: 0;
19400
+ align-self: center;
19401
+ `;
19402
+ var VideoBtn = styled10__default.default.button`
19403
+ display: flex;
19404
+ align-items: center;
19405
+ gap: 6px;
19406
+ padding: 8px 14px;
19407
+ border-radius: 8px;
19408
+ border: 1px solid ${(p) => p.$active ? "var(--color-accent, #E6C87E)" : "rgba(255, 255, 255, 0.2)"};
19409
+ background: ${(p) => p.$active ? "rgba(240, 185, 11, 0.15)" : "rgba(255, 255, 255, 0.08)"};
19410
+ color: ${(p) => p.$active ? "var(--color-accent, #E6C87E)" : "#fff"};
19411
+ font-size: 0.82rem;
19412
+ font-weight: 500;
19413
+ cursor: pointer;
19414
+ flex-shrink: 0;
19415
+ transition: all 0.2s ease;
19416
+
19417
+ &:hover {
19418
+ background: rgba(255, 255, 255, 0.15);
19419
+ border-color: rgba(255, 255, 255, 0.3);
19420
+ }
19421
+ `;
19324
19422
  var slideIn2 = styled10.keyframes`
19325
19423
  from { transform: translateX(110%); opacity: 0; }
19326
19424
  to { transform: translateX(0); opacity: 1; }
@@ -19489,7 +19587,7 @@ var Container3 = styled10__default.default.div`
19489
19587
  gap: 8px;
19490
19588
  }
19491
19589
  `;
19492
- var DEFAULT_EXPLORER = "https://sepolia.basescan.org/tx/";
19590
+ var DEFAULT_EXPLORER = "https://sepolia.arbiscan.io/tx/";
19493
19591
  function truncateHash(hash) {
19494
19592
  return `${hash.slice(0, 6)}\u2026${hash.slice(-4)}`;
19495
19593
  }
@@ -20056,8 +20154,7 @@ var SiteFooter = () => {
20056
20154
  /* @__PURE__ */ jsxRuntime.jsxs(LegalLinks, { children: [
20057
20155
  /* @__PURE__ */ jsxRuntime.jsx(LegalLink, { href: "/privacy", children: "Privacy Policy" }),
20058
20156
  /* @__PURE__ */ jsxRuntime.jsx(LegalLink, { href: "/terms", children: "Terms of Use" }),
20059
- /* @__PURE__ */ jsxRuntime.jsx(LegalLink, { href: "/cookies", children: "Cookie Policy" }),
20060
- /* @__PURE__ */ jsxRuntime.jsx(LegalLink, { href: "/risk", children: "Risk Warning" })
20157
+ /* @__PURE__ */ jsxRuntime.jsx(LegalLink, { href: "/cookies", children: "Cookie Policy" })
20061
20158
  ] })
20062
20159
  ] }),
20063
20160
  /* @__PURE__ */ jsxRuntime.jsx(Disclaimer2, { children: /* @__PURE__ */ jsxRuntime.jsx(DisclaimerText, { children: "Warning: Digital asset prices can be volatile. The value of your investment can go down or up and you may not get back the amount invested. Past gains are not indicative of future returns. You are solely responsible for your investment decisions and Loaf is not liable for any losses you may incur. The information here should not be regarded as financial or investment advice. For more information, see our Terms of Use and Risk Warning." }) })