@loafmarkets/ui 0.1.408 → 0.1.410

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
@@ -712,6 +712,11 @@ type HeaderProps = {
712
712
  * without DOM-level click interception. */
713
713
  onReferClick?: () => void;
714
714
  portfolioSummary?: HeaderPortfolioData | null;
715
+ /**
716
+ * Cumulative competition points the user has earned across all rounds up to now.
717
+ * When provided, the account dropdown leads with a "Total Points" summary row.
718
+ */
719
+ totalPoints?: number | null;
715
720
  /**
716
721
  * True while a brand-new user's test funds (100k mock USD) are being deposited
717
722
  * on-chain (~10s). Replaces the portfolio metrics with a "funds arriving" banner.
@@ -768,9 +773,12 @@ interface OnboardingGuideProps {
768
773
  onEnterCode?: () => void;
769
774
  /** Dismiss / skip (✕, "Skip for now", or after Browse). */
770
775
  onClose: () => void;
776
+ /** Save the chosen handle (optional step). Reject to surface an error
777
+ * (e.g. handle taken). If omitted, the handle step is skipped entirely. */
778
+ onClaimHandle?: (handle: string) => Promise<void> | void;
771
779
  logoSrc?: string;
772
780
  }
773
- declare function OnboardingGuide({ open, fundsReady, cashLabel, onBrowseProperties, onEnterCode, onClose, logoSrc, }: OnboardingGuideProps): react_jsx_runtime.JSX.Element | null;
781
+ declare function OnboardingGuide({ open, fundsReady, cashLabel, onBrowseProperties, onEnterCode, onClose, onClaimHandle, logoSrc, }: OnboardingGuideProps): react_jsx_runtime.JSX.Element | null;
774
782
 
775
783
  type PropertyAddressOption = {
776
784
  id: string;
package/dist/index.d.ts CHANGED
@@ -712,6 +712,11 @@ type HeaderProps = {
712
712
  * without DOM-level click interception. */
713
713
  onReferClick?: () => void;
714
714
  portfolioSummary?: HeaderPortfolioData | null;
715
+ /**
716
+ * Cumulative competition points the user has earned across all rounds up to now.
717
+ * When provided, the account dropdown leads with a "Total Points" summary row.
718
+ */
719
+ totalPoints?: number | null;
715
720
  /**
716
721
  * True while a brand-new user's test funds (100k mock USD) are being deposited
717
722
  * on-chain (~10s). Replaces the portfolio metrics with a "funds arriving" banner.
@@ -768,9 +773,12 @@ interface OnboardingGuideProps {
768
773
  onEnterCode?: () => void;
769
774
  /** Dismiss / skip (✕, "Skip for now", or after Browse). */
770
775
  onClose: () => void;
776
+ /** Save the chosen handle (optional step). Reject to surface an error
777
+ * (e.g. handle taken). If omitted, the handle step is skipped entirely. */
778
+ onClaimHandle?: (handle: string) => Promise<void> | void;
771
779
  logoSrc?: string;
772
780
  }
773
- declare function OnboardingGuide({ open, fundsReady, cashLabel, onBrowseProperties, onEnterCode, onClose, logoSrc, }: OnboardingGuideProps): react_jsx_runtime.JSX.Element | null;
781
+ declare function OnboardingGuide({ open, fundsReady, cashLabel, onBrowseProperties, onEnterCode, onClose, onClaimHandle, logoSrc, }: OnboardingGuideProps): react_jsx_runtime.JSX.Element | null;
774
782
 
775
783
  type PropertyAddressOption = {
776
784
  id: string;
package/dist/index.js CHANGED
@@ -2669,6 +2669,22 @@ var PropertyTour = React5__namespace.forwardRef(
2669
2669
  }
2670
2670
  );
2671
2671
  PropertyTour.displayName = "PropertyTour";
2672
+
2673
+ // src/lib/isSafeUrl.ts
2674
+ var CONTROL_CHARS = /[\u0000-\u001F\u007F]/g;
2675
+ function isSafeUrl(url) {
2676
+ if (!url) return false;
2677
+ const cleaned = url.replace(CONTROL_CHARS, "").replace(/\\/g, "/").trim();
2678
+ if (cleaned === "") return false;
2679
+ if (cleaned.startsWith("//")) return false;
2680
+ if (cleaned.startsWith("/")) return true;
2681
+ try {
2682
+ const { protocol } = new URL(cleaned);
2683
+ return protocol === "http:" || protocol === "https:";
2684
+ } catch {
2685
+ return false;
2686
+ }
2687
+ }
2672
2688
  var ITEMS_PER_PAGE = 5;
2673
2689
  var ITEMS_PER_PAGE_MOBILE = 5;
2674
2690
  var ensureAnimationsInjected = () => {
@@ -2864,7 +2880,7 @@ function NewsArticleModal({ item, onClose }) {
2864
2880
  ] }),
2865
2881
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: "1px", background: "rgba(255,255,255,0.08)", marginBottom: "1.25rem" } }),
2866
2882
  item.summary ? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.875rem", color: "rgba(255,255,255,0.8)", lineHeight: 1.75, margin: 0 }, children: item.summary }) : /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.8rem", color: "rgba(255,255,255,0.35)", fontStyle: "italic", margin: 0 }, children: "Summary not available for this article." }),
2867
- item.url && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: "1.25rem", paddingTop: "1rem", borderTop: "1px solid rgba(255,255,255,0.08)" }, children: /* @__PURE__ */ jsxRuntime.jsxs(
2883
+ item.url && isSafeUrl(item.url) && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: "1.25rem", paddingTop: "1rem", borderTop: "1px solid rgba(255,255,255,0.08)" }, children: /* @__PURE__ */ jsxRuntime.jsxs(
2868
2884
  "a",
2869
2885
  {
2870
2886
  href: item.url,
@@ -5103,6 +5119,7 @@ var Header = ({
5103
5119
  onSettingsClick,
5104
5120
  onReferClick,
5105
5121
  portfolioSummary,
5122
+ totalPoints,
5106
5123
  fundsPending = false,
5107
5124
  transparentOnTop = false,
5108
5125
  headerCta = "deposit",
@@ -5566,6 +5583,11 @@ var Header = ({
5566
5583
  }
5567
5584
  ),
5568
5585
  isUserMenuOpen && /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu, { className: "user-menu-dropdown", children: [
5586
+ totalPoints != null && /* @__PURE__ */ jsxRuntime.jsxs(PointsHeader, { children: [
5587
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pts-label", children: "Total Points" }),
5588
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pts-value", children: totalPoints.toLocaleString() }),
5589
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pts-note", children: "Cumulative points earned. More soon." })
5590
+ ] }),
5569
5591
  /* @__PURE__ */ jsxRuntime.jsx(
5570
5592
  "button",
5571
5593
  {
@@ -5623,7 +5645,7 @@ var Header = ({
5623
5645
  }
5624
5646
  )
5625
5647
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center" }, children: [
5626
- /* @__PURE__ */ jsxRuntime.jsx(Button2, { className: "signup", onClick: handleSignIn, children: "Connect" }),
5648
+ /* @__PURE__ */ jsxRuntime.jsx(Button2, { className: "signup", onClick: handleSignIn, children: "Join Competition" }),
5627
5649
  /* @__PURE__ */ jsxRuntime.jsx(
5628
5650
  MobileOnlyButton,
5629
5651
  {
@@ -6151,6 +6173,33 @@ var DropdownMenu = styled10__default.default.div`
6151
6173
  display: block;
6152
6174
  }
6153
6175
  `;
6176
+ var PointsHeader = styled10__default.default.div`
6177
+ padding: 0.7rem 1.25rem 0.8rem;
6178
+ margin-bottom: 0.35rem;
6179
+ border-bottom: 1px solid rgba(255, 255, 255, 0.08);
6180
+ .pts-note {
6181
+ margin-top: 0.4rem;
6182
+ font-size: 0.6rem;
6183
+ line-height: 1.4;
6184
+ color: rgba(255, 255, 255, 0.4);
6185
+ }
6186
+ .pts-label {
6187
+ font-family: 'SF Mono', 'Fira Code', ui-monospace, monospace;
6188
+ font-size: 0.56rem;
6189
+ font-weight: 700;
6190
+ letter-spacing: 0.16em;
6191
+ text-transform: uppercase;
6192
+ color: rgba(255, 255, 255, 0.45);
6193
+ }
6194
+ .pts-value {
6195
+ margin-top: 0.2rem;
6196
+ font-size: 1.15rem;
6197
+ font-weight: 800;
6198
+ color: #E6C87E;
6199
+ font-variant-numeric: tabular-nums;
6200
+ line-height: 1;
6201
+ }
6202
+ `;
6154
6203
  var MenuItem = styled10__default.default.div`
6155
6204
  padding: 0.75rem 1.25rem;
6156
6205
  cursor: pointer;
@@ -6607,6 +6656,16 @@ var PropertySubheader = React5__namespace.forwardRef(
6607
6656
  }
6608
6657
  );
6609
6658
  PropertySubheader.displayName = "PropertySubheader";
6659
+
6660
+ // src/lib/isTrustedTransakOrigin.ts
6661
+ function isTrustedTransakOrigin(eventOrigin, widgetUrl) {
6662
+ if (!widgetUrl) return false;
6663
+ try {
6664
+ return eventOrigin === new URL(widgetUrl).origin;
6665
+ } catch {
6666
+ return false;
6667
+ }
6668
+ }
6610
6669
  var DEFAULT_LOGO_SRC = Loaf_logo_Banner_default;
6611
6670
  var DEFAULT_LOGO_ALT = "Loaf";
6612
6671
  var OTP_INPUT_LENGTH = 6;
@@ -6747,7 +6806,7 @@ var LoginPopup = ({
6747
6806
  React5.useEffect(() => {
6748
6807
  if (!transakWidgetUrl) return;
6749
6808
  const handleTransakMessage = (event) => {
6750
- if (!event.origin.includes("transak.com") && !event.origin.includes("global.transak.com")) {
6809
+ if (!isTrustedTransakOrigin(event.origin, transakWidgetUrl)) {
6751
6810
  return;
6752
6811
  }
6753
6812
  try {
@@ -9053,6 +9112,48 @@ var CodeLink = styled10__default.default.button`
9053
9112
  text-underline-offset: 2px;
9054
9113
  }
9055
9114
  `;
9115
+ var HandleField = styled10__default.default.div`
9116
+ display: flex;
9117
+ align-items: center;
9118
+ gap: 0.1rem;
9119
+ width: 100%;
9120
+ margin-bottom: 0.4rem;
9121
+ padding: 0 0.9rem;
9122
+ background: rgba(255, 255, 255, 0.04);
9123
+ border: 1px solid rgba(255, 255, 255, 0.1);
9124
+ border-radius: 10px;
9125
+ transition: border-color 0.15s ease;
9126
+
9127
+ &:focus-within {
9128
+ border-color: rgba(230, 200, 126, 0.6);
9129
+ }
9130
+ `;
9131
+ var HandleAt = styled10__default.default.span`
9132
+ color: rgba(255, 255, 255, 0.4);
9133
+ font-size: 1rem;
9134
+ font-weight: 600;
9135
+ `;
9136
+ var HandleInput = styled10__default.default.input`
9137
+ flex: 1;
9138
+ min-width: 0;
9139
+ padding: 0.85rem 0.4rem;
9140
+ border: none;
9141
+ background: transparent;
9142
+ color: #fff;
9143
+ font-size: 1rem;
9144
+ font-family: inherit;
9145
+ outline: none;
9146
+
9147
+ &::placeholder {
9148
+ color: rgba(255, 255, 255, 0.35);
9149
+ }
9150
+ `;
9151
+ var FieldError = styled10__default.default.div`
9152
+ width: 100%;
9153
+ margin-bottom: 0.6rem;
9154
+ font-size: 0.78rem;
9155
+ color: #ff6b6b;
9156
+ `;
9056
9157
  function OnboardingGuide({
9057
9158
  open,
9058
9159
  fundsReady,
@@ -9060,8 +9161,21 @@ function OnboardingGuide({
9060
9161
  onBrowseProperties,
9061
9162
  onEnterCode,
9062
9163
  onClose,
9164
+ onClaimHandle,
9063
9165
  logoSrc = "/Loaf-logo-Banner.png"
9064
9166
  }) {
9167
+ const [step, setStep] = React5.useState(onClaimHandle ? "handle" : "funds");
9168
+ const [handle, setHandle] = React5.useState("");
9169
+ const [claiming, setClaiming] = React5.useState(false);
9170
+ const [handleError, setHandleError] = React5.useState("");
9171
+ React5.useEffect(() => {
9172
+ if (open) {
9173
+ setStep(onClaimHandle ? "handle" : "funds");
9174
+ setHandle("");
9175
+ setClaiming(false);
9176
+ setHandleError("");
9177
+ }
9178
+ }, [open, onClaimHandle]);
9065
9179
  React5.useEffect(() => {
9066
9180
  if (!open) return;
9067
9181
  const onKey = (e) => {
@@ -9071,6 +9185,58 @@ function OnboardingGuide({
9071
9185
  return () => window.removeEventListener("keydown", onKey);
9072
9186
  }, [open, onClose]);
9073
9187
  if (!open) return null;
9188
+ const handleClaimContinue = async () => {
9189
+ const trimmed = handle.trim().replace(/^@+/, "");
9190
+ if (trimmed && onClaimHandle) {
9191
+ setClaiming(true);
9192
+ setHandleError("");
9193
+ try {
9194
+ await onClaimHandle(trimmed);
9195
+ } catch (err) {
9196
+ setHandleError(err instanceof Error ? err.message : "Couldn't claim that handle. Try another or skip.");
9197
+ setClaiming(false);
9198
+ return;
9199
+ }
9200
+ setClaiming(false);
9201
+ }
9202
+ setStep("funds");
9203
+ };
9204
+ if (step === "handle") {
9205
+ return /* @__PURE__ */ jsxRuntime.jsx(Overlay3, { onClick: onClose, role: "dialog", "aria-modal": "true", "aria-label": "Claim your handle", children: /* @__PURE__ */ jsxRuntime.jsxs(Card2, { onClick: (e) => e.stopPropagation(), children: [
9206
+ /* @__PURE__ */ jsxRuntime.jsx(CloseButton2, { onClick: onClose, "aria-label": "Skip", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 6l12 12M18 6L6 18" }) }) }),
9207
+ /* @__PURE__ */ jsxRuntime.jsxs(LogoRow, { children: [
9208
+ /* @__PURE__ */ jsxRuntime.jsx(LogoImage2, { src: logoSrc, alt: "Loaf" }),
9209
+ /* @__PURE__ */ jsxRuntime.jsx(LogoBeta2, { children: "Private Beta" })
9210
+ ] }),
9211
+ /* @__PURE__ */ jsxRuntime.jsx(Title2, { children: "Claim your handle" }),
9212
+ /* @__PURE__ */ jsxRuntime.jsx(Subtitle2, { children: "Pick a username for your Loaf profile. It's optional \u2014 you can set or change it later." }),
9213
+ /* @__PURE__ */ jsxRuntime.jsxs(HandleField, { children: [
9214
+ /* @__PURE__ */ jsxRuntime.jsx(HandleAt, { children: "@" }),
9215
+ /* @__PURE__ */ jsxRuntime.jsx(
9216
+ HandleInput,
9217
+ {
9218
+ type: "text",
9219
+ autoComplete: "off",
9220
+ autoCapitalize: "none",
9221
+ spellCheck: false,
9222
+ placeholder: "username (optional)",
9223
+ value: handle,
9224
+ onChange: (e) => {
9225
+ setHandle(e.target.value.replace(/[^a-zA-Z0-9_]/g, ""));
9226
+ setHandleError("");
9227
+ },
9228
+ onKeyDown: (e) => {
9229
+ if (e.key === "Enter" && !claiming) void handleClaimContinue();
9230
+ },
9231
+ autoFocus: true
9232
+ }
9233
+ )
9234
+ ] }),
9235
+ handleError && /* @__PURE__ */ jsxRuntime.jsx(FieldError, { children: handleError }),
9236
+ /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: () => void handleClaimContinue(), disabled: claiming, children: claiming ? "Saving\u2026" : "Continue" }),
9237
+ /* @__PURE__ */ jsxRuntime.jsx(SkipButton, { onClick: () => setStep("funds"), disabled: claiming, children: "Skip for now" })
9238
+ ] }) });
9239
+ }
9074
9240
  return /* @__PURE__ */ jsxRuntime.jsx(Overlay3, { onClick: onClose, role: "dialog", "aria-modal": "true", "aria-label": "Welcome to Loaf", children: /* @__PURE__ */ jsxRuntime.jsxs(Card2, { onClick: (e) => e.stopPropagation(), children: [
9075
9241
  /* @__PURE__ */ jsxRuntime.jsx(CloseButton2, { onClick: onClose, "aria-label": "Skip", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 6l12 12M18 6L6 18" }) }) }),
9076
9242
  /* @__PURE__ */ jsxRuntime.jsxs(LogoRow, { children: [
@@ -12134,6 +12300,11 @@ var DocIcon = () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "20", height
12134
12300
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "16", y1: "17", x2: "8", y2: "17" }),
12135
12301
  /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "10 9 9 9 8 9" })
12136
12302
  ] });
12303
+ var DownloadIcon = () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
12304
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
12305
+ /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "7 10 12 15 17 10" }),
12306
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "15", x2: "12", y2: "3" })
12307
+ ] });
12137
12308
  var LIVE_TITLE_PATTERNS = [/term\s*sheet/i, /memo/i];
12138
12309
  var COMING_SOON_TITLES = [
12139
12310
  "Valuation Report",
@@ -12148,11 +12319,30 @@ var COMING_SOON_TITLES = [
12148
12319
  ];
12149
12320
  function PropertyDocuments({ documentsData, highlightDocument, onClearHighlight }) {
12150
12321
  const highlightRef = React5.useRef(null);
12151
- React5.useRef(null);
12322
+ const [previewUrl, setPreviewUrl] = React5.useState(null);
12323
+ const [previewTitle, setPreviewTitle] = React5.useState(null);
12324
+ const backendDocuments = Array.isArray(documentsData?.documents) ? documentsData.documents : [];
12325
+ const liveDocs = backendDocuments.filter(
12326
+ (d) => Boolean(d.documentUrl) && isSafeUrl(d.documentUrl) && LIVE_TITLE_PATTERNS.some((rx) => rx.test(d.title))
12327
+ );
12328
+ const comingSoonDocs = COMING_SOON_TITLES.filter(
12329
+ (title) => !liveDocs.some((d) => d.title.toLowerCase().includes(title.toLowerCase()) || title.toLowerCase().includes(d.title.toLowerCase()))
12330
+ ).map((title) => ({ title, documentUrl: "" }));
12331
+ const documents = [...liveDocs, ...comingSoonDocs];
12332
+ const activeUrl = previewUrl ?? (liveDocs[0]?.documentUrl || null);
12333
+ const activeTitle = previewUrl ? previewTitle : liveDocs[0]?.title || null;
12334
+ const selectPreview = (doc) => {
12335
+ setPreviewUrl(doc.documentUrl);
12336
+ setPreviewTitle(doc.title);
12337
+ };
12152
12338
  React5.useEffect(() => {
12153
12339
  if (highlightDocument && highlightRef.current) {
12154
12340
  highlightRef.current.scrollIntoView({ behavior: "smooth", block: "nearest" });
12155
12341
  }
12342
+ if (highlightDocument) {
12343
+ const hit = liveDocs.find((d) => d.title.toLowerCase().includes(highlightDocument.toLowerCase()));
12344
+ if (hit) selectPreview(hit);
12345
+ }
12156
12346
  }, [highlightDocument]);
12157
12347
  React5.useEffect(() => {
12158
12348
  if (!highlightDocument || !onClearHighlight) return;
@@ -12164,36 +12354,58 @@ function PropertyDocuments({ documentsData, highlightDocument, onClearHighlight
12164
12354
  document.addEventListener("click", handler, true);
12165
12355
  return () => document.removeEventListener("click", handler, true);
12166
12356
  }, [highlightDocument, onClearHighlight]);
12167
- const backendDocuments = Array.isArray(documentsData?.documents) ? documentsData.documents : [];
12168
- const liveDocs = backendDocuments.filter(
12169
- (d) => Boolean(d.documentUrl) && LIVE_TITLE_PATTERNS.some((rx) => rx.test(d.title))
12170
- );
12171
- const comingSoonDocs = COMING_SOON_TITLES.filter(
12172
- (title) => !liveDocs.some((d) => d.title.toLowerCase().includes(title.toLowerCase()) || title.toLowerCase().includes(d.title.toLowerCase()))
12173
- ).map((title) => ({ title, documentUrl: "" }));
12174
- const documents = [...liveDocs, ...comingSoonDocs];
12175
12357
  return /* @__PURE__ */ jsxRuntime.jsxs(Section2, { children: [
12176
12358
  /* @__PURE__ */ jsxRuntime.jsx(SectionHeading, { children: "Investment Documents" }),
12177
- /* @__PURE__ */ jsxRuntime.jsx(DocList, { children: documents.map(({ documentUrl, title }) => {
12178
- const isAvailable = Boolean(documentUrl);
12179
- const isHighlighted = highlightDocument != null && title.toLowerCase().includes(highlightDocument.toLowerCase());
12180
- return /* @__PURE__ */ jsxRuntime.jsx(
12181
- DocItem,
12359
+ /* @__PURE__ */ jsxRuntime.jsxs(SplitLayout, { children: [
12360
+ /* @__PURE__ */ jsxRuntime.jsx(DocList, { children: documents.map(({ documentUrl, title }) => {
12361
+ const isAvailable = Boolean(documentUrl);
12362
+ const isHighlighted = highlightDocument != null && title.toLowerCase().includes(highlightDocument.toLowerCase());
12363
+ const isActive = isAvailable && documentUrl === activeUrl;
12364
+ return /* @__PURE__ */ jsxRuntime.jsx(
12365
+ DocItem,
12366
+ {
12367
+ ref: isHighlighted ? highlightRef : void 0,
12368
+ $highlighted: isHighlighted,
12369
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
12370
+ DocRow,
12371
+ {
12372
+ $available: isAvailable,
12373
+ $active: isActive,
12374
+ onMouseEnter: isAvailable ? () => selectPreview({ documentUrl, title }) : void 0,
12375
+ onClick: isAvailable ? () => selectPreview({ documentUrl, title }) : void 0,
12376
+ children: [
12377
+ /* @__PURE__ */ jsxRuntime.jsxs(DocName, { $available: isAvailable, children: [
12378
+ /* @__PURE__ */ jsxRuntime.jsx(DocIconWrapper, { children: /* @__PURE__ */ jsxRuntime.jsx(DocIcon, {}) }),
12379
+ title
12380
+ ] }),
12381
+ isAvailable ? /* @__PURE__ */ jsxRuntime.jsx(
12382
+ DownloadBtn,
12383
+ {
12384
+ href: documentUrl,
12385
+ target: "_blank",
12386
+ rel: "noopener noreferrer",
12387
+ "aria-label": `Open ${title}`,
12388
+ title: "Open document",
12389
+ onClick: (e) => e.stopPropagation(),
12390
+ children: /* @__PURE__ */ jsxRuntime.jsx(DownloadIcon, {})
12391
+ }
12392
+ ) : /* @__PURE__ */ jsxRuntime.jsx(ComingSoonBadge, { children: "Coming Soon" })
12393
+ ]
12394
+ }
12395
+ )
12396
+ },
12397
+ `${title}-${documentUrl || "pending"}`
12398
+ );
12399
+ }) }),
12400
+ /* @__PURE__ */ jsxRuntime.jsx(PreviewPane, { children: activeUrl ? /* @__PURE__ */ jsxRuntime.jsx(
12401
+ PreviewFrame,
12182
12402
  {
12183
- ref: isHighlighted ? highlightRef : void 0,
12184
- $highlighted: isHighlighted,
12185
- children: isAvailable ? /* @__PURE__ */ jsxRuntime.jsxs(DocLink, { href: documentUrl, target: "_blank", rel: "noopener noreferrer", children: [
12186
- /* @__PURE__ */ jsxRuntime.jsx(DocIconWrapper, { children: /* @__PURE__ */ jsxRuntime.jsx(DocIcon, {}) }),
12187
- title
12188
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(DocItemDisabled, { children: [
12189
- /* @__PURE__ */ jsxRuntime.jsx(DocIconWrapper, { children: /* @__PURE__ */ jsxRuntime.jsx(DocIcon, {}) }),
12190
- title,
12191
- /* @__PURE__ */ jsxRuntime.jsx(ComingSoonBadge, { children: "Coming Soon" })
12192
- ] })
12403
+ src: `${activeUrl}#toolbar=0&navpanes=0&view=FitH`,
12404
+ title: activeTitle ?? "Document preview"
12193
12405
  },
12194
- `${title}-${documentUrl || "pending"}`
12195
- );
12196
- }) })
12406
+ activeUrl
12407
+ ) : /* @__PURE__ */ jsxRuntime.jsx(PreviewEmpty, { children: "Hover a document to preview it here" }) })
12408
+ ] })
12197
12409
  ] });
12198
12410
  }
12199
12411
  var Section2 = styled10__default.default.section`
@@ -12213,45 +12425,103 @@ var SectionHeading = styled10__default.default.h2`
12213
12425
  align-items: center;
12214
12426
  gap: 0.5rem;
12215
12427
  `;
12428
+ var SplitLayout = styled10__default.default.div`
12429
+ display: flex;
12430
+ gap: 1.25rem;
12431
+ align-items: stretch;
12432
+
12433
+ @media (max-width: 768px) {
12434
+ flex-direction: column;
12435
+ }
12436
+ `;
12216
12437
  var DocList = styled10__default.default.ul`
12217
12438
  list-style: none;
12218
12439
  padding: 0;
12219
12440
  margin: 0;
12441
+ flex: 1 1 50%;
12442
+ min-width: 0;
12220
12443
  `;
12221
12444
  var DocItem = styled10__default.default.li`
12222
- padding: 0.75rem 0;
12445
+ padding: 0.35rem 0;
12223
12446
  border-bottom: 1px solid var(--color-border, rgba(255, 255, 255, 0.1));
12224
12447
  border-radius: ${(p) => p.$highlighted ? "6px" : "0"};
12225
12448
  background: ${(p) => p.$highlighted ? "rgba(212, 175, 55, 0.08)" : "transparent"};
12226
- ${(p) => p.$highlighted ? "padding: 0.75rem;" : ""}
12227
12449
  transition: background 0.3s;
12228
12450
 
12229
12451
  &:last-child {
12230
12452
  border-bottom: none;
12231
12453
  }
12232
12454
  `;
12233
- var DocLink = styled10__default.default.a`
12455
+ var DocRow = styled10__default.default.div`
12234
12456
  display: flex;
12235
12457
  align-items: center;
12236
- color: var(--color-text);
12237
- text-decoration: none;
12238
- transition: color 0.2s;
12458
+ gap: 0.5rem;
12459
+ padding: 0.4rem 0.5rem;
12460
+ border-radius: 6px;
12461
+ cursor: ${(p) => p.$available ? "pointer" : "not-allowed"};
12462
+ background: ${(p) => p.$active ? "rgba(230, 200, 126, 0.10)" : "transparent"};
12463
+ transition: background 0.2s, color 0.2s;
12239
12464
 
12240
- &:hover {
12241
- color: var(--color-accent);
12242
- }
12465
+ ${(p) => p.$available && !p.$active ? "&:hover { background: rgba(230, 200, 126, 0.06); }" : ""}
12243
12466
  `;
12244
- var DocItemDisabled = styled10__default.default.div`
12467
+ var DocName = styled10__default.default.span`
12245
12468
  display: flex;
12246
12469
  align-items: center;
12247
- color: var(--color-text-secondary);
12248
- cursor: not-allowed;
12249
- opacity: 0.6;
12470
+ flex: 1;
12471
+ min-width: 0;
12472
+ color: ${(p) => p.$available ? "var(--color-text)" : "var(--color-text-secondary)"};
12473
+ opacity: ${(p) => p.$available ? 1 : 0.6};
12250
12474
  `;
12251
12475
  var DocIconWrapper = styled10__default.default.span`
12252
12476
  margin-right: 0.75rem;
12253
12477
  flex-shrink: 0;
12254
12478
  color: var(--color-text-secondary);
12479
+ display: inline-flex;
12480
+ `;
12481
+ var DownloadBtn = styled10__default.default.a`
12482
+ flex-shrink: 0;
12483
+ display: inline-flex;
12484
+ align-items: center;
12485
+ justify-content: center;
12486
+ width: 30px;
12487
+ height: 30px;
12488
+ border-radius: 6px;
12489
+ color: var(--color-text-secondary);
12490
+ transition: color 0.2s, background 0.2s;
12491
+
12492
+ &:hover {
12493
+ color: var(--color-accent);
12494
+ background: rgba(230, 200, 126, 0.1);
12495
+ }
12496
+ `;
12497
+ var PreviewPane = styled10__default.default.div`
12498
+ flex: 1 1 50%;
12499
+ min-width: 0;
12500
+ min-height: 360px;
12501
+ display: flex;
12502
+ border: 1px solid var(--color-border, rgba(255, 255, 255, 0.1));
12503
+ border-radius: 10px;
12504
+ overflow: hidden;
12505
+ background: rgba(0, 0, 0, 0.25);
12506
+
12507
+ /* No hover on touch devices — fall back to the list + download icon. */
12508
+ @media (max-width: 768px) {
12509
+ display: none;
12510
+ }
12511
+ `;
12512
+ var PreviewFrame = styled10__default.default.iframe`
12513
+ width: 100%;
12514
+ height: 100%;
12515
+ min-height: 360px;
12516
+ border: 0;
12517
+ background: #fff;
12518
+ `;
12519
+ var PreviewEmpty = styled10__default.default.div`
12520
+ margin: auto;
12521
+ padding: 1rem;
12522
+ text-align: center;
12523
+ color: var(--color-text-secondary);
12524
+ font-size: 0.85rem;
12255
12525
  `;
12256
12526
  var ComingSoonBadge = styled10__default.default.span`
12257
12527
  margin-left: auto;
@@ -12263,6 +12533,7 @@ var ComingSoonBadge = styled10__default.default.span`
12263
12533
  font-weight: 500;
12264
12534
  text-transform: uppercase;
12265
12535
  letter-spacing: 0.05em;
12536
+ flex-shrink: 0;
12266
12537
  `;
12267
12538
  var formatIsoDate = (value) => {
12268
12539
  const parsed = new Date(value);
@@ -19900,7 +20171,7 @@ var OpenMailButton = styled10__default.default.a`
19900
20171
  `;
19901
20172
  var ContactPopup = ({ onClose }) => {
19902
20173
  const [copied, setCopied] = React5.useState(false);
19903
- const email = "Gavin@loafmarkets.com";
20174
+ const email = "admin@loafmarkets.com";
19904
20175
  const handleCopy = async () => {
19905
20176
  try {
19906
20177
  await navigator.clipboard.writeText(email);
@@ -20127,7 +20398,17 @@ var SiteFooter = () => {
20127
20398
  /* @__PURE__ */ jsxRuntime.jsx(FooterLinkItem, { children: /* @__PURE__ */ jsxRuntime.jsx(FooterLink, { href: "/trade", children: "Trade" }) }),
20128
20399
  /* @__PURE__ */ jsxRuntime.jsx(FooterLinkItem, { children: /* @__PURE__ */ jsxRuntime.jsx(FooterLink, { href: "/offerings", children: "Initial Offerings" }) }),
20129
20400
  /* @__PURE__ */ jsxRuntime.jsx(FooterLinkItem, { children: /* @__PURE__ */ jsxRuntime.jsx(FooterLink, { href: "/map", children: "Property Map" }) }),
20130
- /* @__PURE__ */ jsxRuntime.jsx(FooterLinkItem, { children: /* @__PURE__ */ jsxRuntime.jsx(FooterLink, { href: "/loaf-liquidity", children: "Loaf Liquidity" }) })
20401
+ /* @__PURE__ */ jsxRuntime.jsx(FooterLinkItem, { children: /* @__PURE__ */ jsxRuntime.jsx(
20402
+ FooterLink,
20403
+ {
20404
+ href: "/loaf-liquidity",
20405
+ onClick: (e) => {
20406
+ e.preventDefault();
20407
+ if (typeof window !== "undefined") window.dispatchEvent(new CustomEvent("loaf:open-liquidity-popup"));
20408
+ },
20409
+ children: "Loaf Liquidity"
20410
+ }
20411
+ ) })
20131
20412
  ] })
20132
20413
  ] }),
20133
20414
  /* @__PURE__ */ jsxRuntime.jsxs(FooterColumn, { children: [
@@ -20163,7 +20444,7 @@ var SiteFooter = () => {
20163
20444
  /* @__PURE__ */ jsxRuntime.jsx(LegalLink, { href: "/cookies", children: "Cookie Policy" })
20164
20445
  ] })
20165
20446
  ] }),
20166
- /* @__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." }) })
20447
+ /* @__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." }) })
20167
20448
  ] }) }),
20168
20449
  showContactPopup && /* @__PURE__ */ jsxRuntime.jsx(ContactPopup, { onClose: () => setShowContactPopup(false) })
20169
20450
  ] });