@loafmarkets/ui 0.1.407 → 0.1.409

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
@@ -5103,6 +5103,7 @@ var Header = ({
5103
5103
  onSettingsClick,
5104
5104
  onReferClick,
5105
5105
  portfolioSummary,
5106
+ totalPoints,
5106
5107
  fundsPending = false,
5107
5108
  transparentOnTop = false,
5108
5109
  headerCta = "deposit",
@@ -5566,6 +5567,11 @@ var Header = ({
5566
5567
  }
5567
5568
  ),
5568
5569
  isUserMenuOpen && /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu, { className: "user-menu-dropdown", children: [
5570
+ totalPoints != null && /* @__PURE__ */ jsxRuntime.jsxs(PointsHeader, { children: [
5571
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pts-label", children: "Total Points" }),
5572
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pts-value", children: totalPoints.toLocaleString() }),
5573
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pts-note", children: "Cumulative points earned. More soon." })
5574
+ ] }),
5569
5575
  /* @__PURE__ */ jsxRuntime.jsx(
5570
5576
  "button",
5571
5577
  {
@@ -5623,7 +5629,7 @@ var Header = ({
5623
5629
  }
5624
5630
  )
5625
5631
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center" }, children: [
5626
- /* @__PURE__ */ jsxRuntime.jsx(Button2, { className: "signup", onClick: handleSignIn, children: "Connect" }),
5632
+ /* @__PURE__ */ jsxRuntime.jsx(Button2, { className: "signup", onClick: handleSignIn, children: "Join Competition" }),
5627
5633
  /* @__PURE__ */ jsxRuntime.jsx(
5628
5634
  MobileOnlyButton,
5629
5635
  {
@@ -6151,6 +6157,33 @@ var DropdownMenu = styled10__default.default.div`
6151
6157
  display: block;
6152
6158
  }
6153
6159
  `;
6160
+ var PointsHeader = styled10__default.default.div`
6161
+ padding: 0.7rem 1.25rem 0.8rem;
6162
+ margin-bottom: 0.35rem;
6163
+ border-bottom: 1px solid rgba(255, 255, 255, 0.08);
6164
+ .pts-note {
6165
+ margin-top: 0.4rem;
6166
+ font-size: 0.6rem;
6167
+ line-height: 1.4;
6168
+ color: rgba(255, 255, 255, 0.4);
6169
+ }
6170
+ .pts-label {
6171
+ font-family: 'SF Mono', 'Fira Code', ui-monospace, monospace;
6172
+ font-size: 0.56rem;
6173
+ font-weight: 700;
6174
+ letter-spacing: 0.16em;
6175
+ text-transform: uppercase;
6176
+ color: rgba(255, 255, 255, 0.45);
6177
+ }
6178
+ .pts-value {
6179
+ margin-top: 0.2rem;
6180
+ font-size: 1.15rem;
6181
+ font-weight: 800;
6182
+ color: #E6C87E;
6183
+ font-variant-numeric: tabular-nums;
6184
+ line-height: 1;
6185
+ }
6186
+ `;
6154
6187
  var MenuItem = styled10__default.default.div`
6155
6188
  padding: 0.75rem 1.25rem;
6156
6189
  cursor: pointer;
@@ -7576,7 +7609,6 @@ var LoginPopup = ({
7576
7609
  ] })
7577
7610
  ] });
7578
7611
  if (gate) {
7579
- const codeAccepted = codeStatus === "valid";
7580
7612
  const showSignIn = signInMode || signInRevealed;
7581
7613
  return /* @__PURE__ */ jsxRuntime.jsxs(GateShell, { children: [
7582
7614
  /* @__PURE__ */ jsxRuntime.jsx(GateTint, { $reveal: showSignIn ? 1 : 0 }),
@@ -7602,35 +7634,30 @@ var LoginPopup = ({
7602
7634
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
7603
7635
  /* @__PURE__ */ jsxRuntime.jsxs(CodeInputWrapper, { $status: codeStatus, children: [
7604
7636
  /* @__PURE__ */ jsxRuntime.jsx(CodePrefix, { children: CODE_PREFIX }),
7605
- codeInput,
7606
- /* @__PURE__ */ jsxRuntime.jsx(
7607
- GateSubmit,
7608
- {
7609
- type: "button",
7610
- "aria-label": "Submit access code",
7611
- $status: codeStatus,
7612
- disabled: codeStatus === "checking" || !codeComplete,
7613
- onClick: () => void runCodeValidation(referralCode),
7614
- children: codeAccepted ? /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 6L9 17l-5-5" }) }) : codeStatus === "checking" ? /* @__PURE__ */ jsxRuntime.jsx(GateSpinner, {}) : /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 12h14M13 6l6 6-6 6" }) })
7615
- }
7616
- )
7637
+ codeInput
7617
7638
  ] }),
7618
7639
  (codeStatus === "invalid" || error && !signInRevealed) && /* @__PURE__ */ jsxRuntime.jsx(StatusMessage, { $error: true, children: error || "That code isn't valid. Double-check and try again." }),
7619
- signInRevealed ? signInStep : /* @__PURE__ */ jsxRuntime.jsxs(GateResendText, { children: [
7620
- "Already have an account?",
7621
- " ",
7622
- /* @__PURE__ */ jsxRuntime.jsx(
7623
- "button",
7624
- {
7625
- type: "button",
7626
- onClick: () => {
7627
- setError("");
7628
- setIsSignUp(false);
7629
- setSignInMode(true);
7630
- },
7631
- children: "Sign in"
7632
- }
7633
- )
7640
+ signInRevealed ? signInStep : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
7641
+ /* @__PURE__ */ jsxRuntime.jsxs(GateResendText, { children: [
7642
+ "Already have an account?",
7643
+ " ",
7644
+ /* @__PURE__ */ jsxRuntime.jsx(
7645
+ "button",
7646
+ {
7647
+ type: "button",
7648
+ onClick: () => {
7649
+ setError("");
7650
+ setIsSignUp(false);
7651
+ setSignInMode(true);
7652
+ },
7653
+ children: "Sign in"
7654
+ }
7655
+ )
7656
+ ] }),
7657
+ /* @__PURE__ */ jsxRuntime.jsxs(GateEarlyAccess, { href: "https://loafmarkets.com/?waitlist=1", children: [
7658
+ "Signup for early access",
7659
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 12h14M13 6l6 6-6 6" }) })
7660
+ ] })
7634
7661
  ] })
7635
7662
  ] })
7636
7663
  ] })
@@ -8253,6 +8280,33 @@ var GateResendText = styled10__default.default.p`
8253
8280
  font-size: inherit;
8254
8281
  }
8255
8282
  `;
8283
+ var GateEarlyAccess = styled10__default.default.a`
8284
+ display: inline-flex;
8285
+ align-items: center;
8286
+ gap: 0.4rem;
8287
+ margin-top: 0.15rem;
8288
+ color: var(--color-text-secondary, #848e9c);
8289
+ font-size: 0.8rem;
8290
+ font-weight: 500;
8291
+ letter-spacing: 0.02em;
8292
+ text-decoration: none;
8293
+ cursor: pointer;
8294
+ transition: color 0.15s ease;
8295
+
8296
+ svg {
8297
+ width: 13px;
8298
+ height: 13px;
8299
+ transition: transform 0.15s ease;
8300
+ }
8301
+
8302
+ &:hover {
8303
+ color: var(--color-accent, #E6C87E);
8304
+ }
8305
+
8306
+ &:hover svg {
8307
+ transform: translateX(3px);
8308
+ }
8309
+ `;
8256
8310
  var GateSubmit = styled10__default.default.button`
8257
8311
  display: flex;
8258
8312
  align-items: center;
@@ -9032,6 +9086,48 @@ var CodeLink = styled10__default.default.button`
9032
9086
  text-underline-offset: 2px;
9033
9087
  }
9034
9088
  `;
9089
+ var HandleField = styled10__default.default.div`
9090
+ display: flex;
9091
+ align-items: center;
9092
+ gap: 0.1rem;
9093
+ width: 100%;
9094
+ margin-bottom: 0.4rem;
9095
+ padding: 0 0.9rem;
9096
+ background: rgba(255, 255, 255, 0.04);
9097
+ border: 1px solid rgba(255, 255, 255, 0.1);
9098
+ border-radius: 10px;
9099
+ transition: border-color 0.15s ease;
9100
+
9101
+ &:focus-within {
9102
+ border-color: rgba(230, 200, 126, 0.6);
9103
+ }
9104
+ `;
9105
+ var HandleAt = styled10__default.default.span`
9106
+ color: rgba(255, 255, 255, 0.4);
9107
+ font-size: 1rem;
9108
+ font-weight: 600;
9109
+ `;
9110
+ var HandleInput = styled10__default.default.input`
9111
+ flex: 1;
9112
+ min-width: 0;
9113
+ padding: 0.85rem 0.4rem;
9114
+ border: none;
9115
+ background: transparent;
9116
+ color: #fff;
9117
+ font-size: 1rem;
9118
+ font-family: inherit;
9119
+ outline: none;
9120
+
9121
+ &::placeholder {
9122
+ color: rgba(255, 255, 255, 0.35);
9123
+ }
9124
+ `;
9125
+ var FieldError = styled10__default.default.div`
9126
+ width: 100%;
9127
+ margin-bottom: 0.6rem;
9128
+ font-size: 0.78rem;
9129
+ color: #ff6b6b;
9130
+ `;
9035
9131
  function OnboardingGuide({
9036
9132
  open,
9037
9133
  fundsReady,
@@ -9039,8 +9135,21 @@ function OnboardingGuide({
9039
9135
  onBrowseProperties,
9040
9136
  onEnterCode,
9041
9137
  onClose,
9138
+ onClaimHandle,
9042
9139
  logoSrc = "/Loaf-logo-Banner.png"
9043
9140
  }) {
9141
+ const [step, setStep] = React5.useState(onClaimHandle ? "handle" : "funds");
9142
+ const [handle, setHandle] = React5.useState("");
9143
+ const [claiming, setClaiming] = React5.useState(false);
9144
+ const [handleError, setHandleError] = React5.useState("");
9145
+ React5.useEffect(() => {
9146
+ if (open) {
9147
+ setStep(onClaimHandle ? "handle" : "funds");
9148
+ setHandle("");
9149
+ setClaiming(false);
9150
+ setHandleError("");
9151
+ }
9152
+ }, [open, onClaimHandle]);
9044
9153
  React5.useEffect(() => {
9045
9154
  if (!open) return;
9046
9155
  const onKey = (e) => {
@@ -9050,6 +9159,58 @@ function OnboardingGuide({
9050
9159
  return () => window.removeEventListener("keydown", onKey);
9051
9160
  }, [open, onClose]);
9052
9161
  if (!open) return null;
9162
+ const handleClaimContinue = async () => {
9163
+ const trimmed = handle.trim().replace(/^@+/, "");
9164
+ if (trimmed && onClaimHandle) {
9165
+ setClaiming(true);
9166
+ setHandleError("");
9167
+ try {
9168
+ await onClaimHandle(trimmed);
9169
+ } catch (err) {
9170
+ setHandleError(err instanceof Error ? err.message : "Couldn't claim that handle. Try another or skip.");
9171
+ setClaiming(false);
9172
+ return;
9173
+ }
9174
+ setClaiming(false);
9175
+ }
9176
+ setStep("funds");
9177
+ };
9178
+ if (step === "handle") {
9179
+ 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: [
9180
+ /* @__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" }) }) }),
9181
+ /* @__PURE__ */ jsxRuntime.jsxs(LogoRow, { children: [
9182
+ /* @__PURE__ */ jsxRuntime.jsx(LogoImage2, { src: logoSrc, alt: "Loaf" }),
9183
+ /* @__PURE__ */ jsxRuntime.jsx(LogoBeta2, { children: "Private Beta" })
9184
+ ] }),
9185
+ /* @__PURE__ */ jsxRuntime.jsx(Title2, { children: "Claim your handle" }),
9186
+ /* @__PURE__ */ jsxRuntime.jsx(Subtitle2, { children: "Pick a username for your Loaf profile. It's optional \u2014 you can set or change it later." }),
9187
+ /* @__PURE__ */ jsxRuntime.jsxs(HandleField, { children: [
9188
+ /* @__PURE__ */ jsxRuntime.jsx(HandleAt, { children: "@" }),
9189
+ /* @__PURE__ */ jsxRuntime.jsx(
9190
+ HandleInput,
9191
+ {
9192
+ type: "text",
9193
+ autoComplete: "off",
9194
+ autoCapitalize: "none",
9195
+ spellCheck: false,
9196
+ placeholder: "username (optional)",
9197
+ value: handle,
9198
+ onChange: (e) => {
9199
+ setHandle(e.target.value.replace(/[^a-zA-Z0-9_]/g, ""));
9200
+ setHandleError("");
9201
+ },
9202
+ onKeyDown: (e) => {
9203
+ if (e.key === "Enter" && !claiming) void handleClaimContinue();
9204
+ },
9205
+ autoFocus: true
9206
+ }
9207
+ )
9208
+ ] }),
9209
+ handleError && /* @__PURE__ */ jsxRuntime.jsx(FieldError, { children: handleError }),
9210
+ /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: () => void handleClaimContinue(), disabled: claiming, children: claiming ? "Saving\u2026" : "Continue" }),
9211
+ /* @__PURE__ */ jsxRuntime.jsx(SkipButton, { onClick: () => setStep("funds"), disabled: claiming, children: "Skip for now" })
9212
+ ] }) });
9213
+ }
9053
9214
  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: [
9054
9215
  /* @__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" }) }) }),
9055
9216
  /* @__PURE__ */ jsxRuntime.jsxs(LogoRow, { children: [
@@ -12113,6 +12274,11 @@ var DocIcon = () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "20", height
12113
12274
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "16", y1: "17", x2: "8", y2: "17" }),
12114
12275
  /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "10 9 9 9 8 9" })
12115
12276
  ] });
12277
+ 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: [
12278
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
12279
+ /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "7 10 12 15 17 10" }),
12280
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "15", x2: "12", y2: "3" })
12281
+ ] });
12116
12282
  var LIVE_TITLE_PATTERNS = [/term\s*sheet/i, /memo/i];
12117
12283
  var COMING_SOON_TITLES = [
12118
12284
  "Valuation Report",
@@ -12127,11 +12293,30 @@ var COMING_SOON_TITLES = [
12127
12293
  ];
12128
12294
  function PropertyDocuments({ documentsData, highlightDocument, onClearHighlight }) {
12129
12295
  const highlightRef = React5.useRef(null);
12130
- React5.useRef(null);
12296
+ const [previewUrl, setPreviewUrl] = React5.useState(null);
12297
+ const [previewTitle, setPreviewTitle] = React5.useState(null);
12298
+ const backendDocuments = Array.isArray(documentsData?.documents) ? documentsData.documents : [];
12299
+ const liveDocs = backendDocuments.filter(
12300
+ (d) => Boolean(d.documentUrl) && LIVE_TITLE_PATTERNS.some((rx) => rx.test(d.title))
12301
+ );
12302
+ const comingSoonDocs = COMING_SOON_TITLES.filter(
12303
+ (title) => !liveDocs.some((d) => d.title.toLowerCase().includes(title.toLowerCase()) || title.toLowerCase().includes(d.title.toLowerCase()))
12304
+ ).map((title) => ({ title, documentUrl: "" }));
12305
+ const documents = [...liveDocs, ...comingSoonDocs];
12306
+ const activeUrl = previewUrl ?? (liveDocs[0]?.documentUrl || null);
12307
+ const activeTitle = previewUrl ? previewTitle : liveDocs[0]?.title || null;
12308
+ const selectPreview = (doc) => {
12309
+ setPreviewUrl(doc.documentUrl);
12310
+ setPreviewTitle(doc.title);
12311
+ };
12131
12312
  React5.useEffect(() => {
12132
12313
  if (highlightDocument && highlightRef.current) {
12133
12314
  highlightRef.current.scrollIntoView({ behavior: "smooth", block: "nearest" });
12134
12315
  }
12316
+ if (highlightDocument) {
12317
+ const hit = liveDocs.find((d) => d.title.toLowerCase().includes(highlightDocument.toLowerCase()));
12318
+ if (hit) selectPreview(hit);
12319
+ }
12135
12320
  }, [highlightDocument]);
12136
12321
  React5.useEffect(() => {
12137
12322
  if (!highlightDocument || !onClearHighlight) return;
@@ -12143,36 +12328,58 @@ function PropertyDocuments({ documentsData, highlightDocument, onClearHighlight
12143
12328
  document.addEventListener("click", handler, true);
12144
12329
  return () => document.removeEventListener("click", handler, true);
12145
12330
  }, [highlightDocument, onClearHighlight]);
12146
- const backendDocuments = Array.isArray(documentsData?.documents) ? documentsData.documents : [];
12147
- const liveDocs = backendDocuments.filter(
12148
- (d) => Boolean(d.documentUrl) && LIVE_TITLE_PATTERNS.some((rx) => rx.test(d.title))
12149
- );
12150
- const comingSoonDocs = COMING_SOON_TITLES.filter(
12151
- (title) => !liveDocs.some((d) => d.title.toLowerCase().includes(title.toLowerCase()) || title.toLowerCase().includes(d.title.toLowerCase()))
12152
- ).map((title) => ({ title, documentUrl: "" }));
12153
- const documents = [...liveDocs, ...comingSoonDocs];
12154
12331
  return /* @__PURE__ */ jsxRuntime.jsxs(Section2, { children: [
12155
12332
  /* @__PURE__ */ jsxRuntime.jsx(SectionHeading, { children: "Investment Documents" }),
12156
- /* @__PURE__ */ jsxRuntime.jsx(DocList, { children: documents.map(({ documentUrl, title }) => {
12157
- const isAvailable = Boolean(documentUrl);
12158
- const isHighlighted = highlightDocument != null && title.toLowerCase().includes(highlightDocument.toLowerCase());
12159
- return /* @__PURE__ */ jsxRuntime.jsx(
12160
- DocItem,
12333
+ /* @__PURE__ */ jsxRuntime.jsxs(SplitLayout, { children: [
12334
+ /* @__PURE__ */ jsxRuntime.jsx(DocList, { children: documents.map(({ documentUrl, title }) => {
12335
+ const isAvailable = Boolean(documentUrl);
12336
+ const isHighlighted = highlightDocument != null && title.toLowerCase().includes(highlightDocument.toLowerCase());
12337
+ const isActive = isAvailable && documentUrl === activeUrl;
12338
+ return /* @__PURE__ */ jsxRuntime.jsx(
12339
+ DocItem,
12340
+ {
12341
+ ref: isHighlighted ? highlightRef : void 0,
12342
+ $highlighted: isHighlighted,
12343
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
12344
+ DocRow,
12345
+ {
12346
+ $available: isAvailable,
12347
+ $active: isActive,
12348
+ onMouseEnter: isAvailable ? () => selectPreview({ documentUrl, title }) : void 0,
12349
+ onClick: isAvailable ? () => selectPreview({ documentUrl, title }) : void 0,
12350
+ children: [
12351
+ /* @__PURE__ */ jsxRuntime.jsxs(DocName, { $available: isAvailable, children: [
12352
+ /* @__PURE__ */ jsxRuntime.jsx(DocIconWrapper, { children: /* @__PURE__ */ jsxRuntime.jsx(DocIcon, {}) }),
12353
+ title
12354
+ ] }),
12355
+ isAvailable ? /* @__PURE__ */ jsxRuntime.jsx(
12356
+ DownloadBtn,
12357
+ {
12358
+ href: documentUrl,
12359
+ target: "_blank",
12360
+ rel: "noopener noreferrer",
12361
+ "aria-label": `Open ${title}`,
12362
+ title: "Open document",
12363
+ onClick: (e) => e.stopPropagation(),
12364
+ children: /* @__PURE__ */ jsxRuntime.jsx(DownloadIcon, {})
12365
+ }
12366
+ ) : /* @__PURE__ */ jsxRuntime.jsx(ComingSoonBadge, { children: "Coming Soon" })
12367
+ ]
12368
+ }
12369
+ )
12370
+ },
12371
+ `${title}-${documentUrl || "pending"}`
12372
+ );
12373
+ }) }),
12374
+ /* @__PURE__ */ jsxRuntime.jsx(PreviewPane, { children: activeUrl ? /* @__PURE__ */ jsxRuntime.jsx(
12375
+ PreviewFrame,
12161
12376
  {
12162
- ref: isHighlighted ? highlightRef : void 0,
12163
- $highlighted: isHighlighted,
12164
- children: isAvailable ? /* @__PURE__ */ jsxRuntime.jsxs(DocLink, { href: documentUrl, target: "_blank", rel: "noopener noreferrer", children: [
12165
- /* @__PURE__ */ jsxRuntime.jsx(DocIconWrapper, { children: /* @__PURE__ */ jsxRuntime.jsx(DocIcon, {}) }),
12166
- title
12167
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(DocItemDisabled, { children: [
12168
- /* @__PURE__ */ jsxRuntime.jsx(DocIconWrapper, { children: /* @__PURE__ */ jsxRuntime.jsx(DocIcon, {}) }),
12169
- title,
12170
- /* @__PURE__ */ jsxRuntime.jsx(ComingSoonBadge, { children: "Coming Soon" })
12171
- ] })
12377
+ src: `${activeUrl}#toolbar=0&navpanes=0&view=FitH`,
12378
+ title: activeTitle ?? "Document preview"
12172
12379
  },
12173
- `${title}-${documentUrl || "pending"}`
12174
- );
12175
- }) })
12380
+ activeUrl
12381
+ ) : /* @__PURE__ */ jsxRuntime.jsx(PreviewEmpty, { children: "Hover a document to preview it here" }) })
12382
+ ] })
12176
12383
  ] });
12177
12384
  }
12178
12385
  var Section2 = styled10__default.default.section`
@@ -12192,45 +12399,103 @@ var SectionHeading = styled10__default.default.h2`
12192
12399
  align-items: center;
12193
12400
  gap: 0.5rem;
12194
12401
  `;
12402
+ var SplitLayout = styled10__default.default.div`
12403
+ display: flex;
12404
+ gap: 1.25rem;
12405
+ align-items: stretch;
12406
+
12407
+ @media (max-width: 768px) {
12408
+ flex-direction: column;
12409
+ }
12410
+ `;
12195
12411
  var DocList = styled10__default.default.ul`
12196
12412
  list-style: none;
12197
12413
  padding: 0;
12198
12414
  margin: 0;
12415
+ flex: 1 1 50%;
12416
+ min-width: 0;
12199
12417
  `;
12200
12418
  var DocItem = styled10__default.default.li`
12201
- padding: 0.75rem 0;
12419
+ padding: 0.35rem 0;
12202
12420
  border-bottom: 1px solid var(--color-border, rgba(255, 255, 255, 0.1));
12203
12421
  border-radius: ${(p) => p.$highlighted ? "6px" : "0"};
12204
12422
  background: ${(p) => p.$highlighted ? "rgba(212, 175, 55, 0.08)" : "transparent"};
12205
- ${(p) => p.$highlighted ? "padding: 0.75rem;" : ""}
12206
12423
  transition: background 0.3s;
12207
12424
 
12208
12425
  &:last-child {
12209
12426
  border-bottom: none;
12210
12427
  }
12211
12428
  `;
12212
- var DocLink = styled10__default.default.a`
12429
+ var DocRow = styled10__default.default.div`
12213
12430
  display: flex;
12214
12431
  align-items: center;
12215
- color: var(--color-text);
12216
- text-decoration: none;
12217
- transition: color 0.2s;
12432
+ gap: 0.5rem;
12433
+ padding: 0.4rem 0.5rem;
12434
+ border-radius: 6px;
12435
+ cursor: ${(p) => p.$available ? "pointer" : "not-allowed"};
12436
+ background: ${(p) => p.$active ? "rgba(230, 200, 126, 0.10)" : "transparent"};
12437
+ transition: background 0.2s, color 0.2s;
12218
12438
 
12219
- &:hover {
12220
- color: var(--color-accent);
12221
- }
12439
+ ${(p) => p.$available && !p.$active ? "&:hover { background: rgba(230, 200, 126, 0.06); }" : ""}
12222
12440
  `;
12223
- var DocItemDisabled = styled10__default.default.div`
12441
+ var DocName = styled10__default.default.span`
12224
12442
  display: flex;
12225
12443
  align-items: center;
12226
- color: var(--color-text-secondary);
12227
- cursor: not-allowed;
12228
- opacity: 0.6;
12444
+ flex: 1;
12445
+ min-width: 0;
12446
+ color: ${(p) => p.$available ? "var(--color-text)" : "var(--color-text-secondary)"};
12447
+ opacity: ${(p) => p.$available ? 1 : 0.6};
12229
12448
  `;
12230
12449
  var DocIconWrapper = styled10__default.default.span`
12231
12450
  margin-right: 0.75rem;
12232
12451
  flex-shrink: 0;
12233
12452
  color: var(--color-text-secondary);
12453
+ display: inline-flex;
12454
+ `;
12455
+ var DownloadBtn = styled10__default.default.a`
12456
+ flex-shrink: 0;
12457
+ display: inline-flex;
12458
+ align-items: center;
12459
+ justify-content: center;
12460
+ width: 30px;
12461
+ height: 30px;
12462
+ border-radius: 6px;
12463
+ color: var(--color-text-secondary);
12464
+ transition: color 0.2s, background 0.2s;
12465
+
12466
+ &:hover {
12467
+ color: var(--color-accent);
12468
+ background: rgba(230, 200, 126, 0.1);
12469
+ }
12470
+ `;
12471
+ var PreviewPane = styled10__default.default.div`
12472
+ flex: 1 1 50%;
12473
+ min-width: 0;
12474
+ min-height: 360px;
12475
+ display: flex;
12476
+ border: 1px solid var(--color-border, rgba(255, 255, 255, 0.1));
12477
+ border-radius: 10px;
12478
+ overflow: hidden;
12479
+ background: rgba(0, 0, 0, 0.25);
12480
+
12481
+ /* No hover on touch devices — fall back to the list + download icon. */
12482
+ @media (max-width: 768px) {
12483
+ display: none;
12484
+ }
12485
+ `;
12486
+ var PreviewFrame = styled10__default.default.iframe`
12487
+ width: 100%;
12488
+ height: 100%;
12489
+ min-height: 360px;
12490
+ border: 0;
12491
+ background: #fff;
12492
+ `;
12493
+ var PreviewEmpty = styled10__default.default.div`
12494
+ margin: auto;
12495
+ padding: 1rem;
12496
+ text-align: center;
12497
+ color: var(--color-text-secondary);
12498
+ font-size: 0.85rem;
12234
12499
  `;
12235
12500
  var ComingSoonBadge = styled10__default.default.span`
12236
12501
  margin-left: auto;
@@ -12242,6 +12507,7 @@ var ComingSoonBadge = styled10__default.default.span`
12242
12507
  font-weight: 500;
12243
12508
  text-transform: uppercase;
12244
12509
  letter-spacing: 0.05em;
12510
+ flex-shrink: 0;
12245
12511
  `;
12246
12512
  var formatIsoDate = (value) => {
12247
12513
  const parsed = new Date(value);
@@ -19879,7 +20145,7 @@ var OpenMailButton = styled10__default.default.a`
19879
20145
  `;
19880
20146
  var ContactPopup = ({ onClose }) => {
19881
20147
  const [copied, setCopied] = React5.useState(false);
19882
- const email = "Gavin@loafmarkets.com";
20148
+ const email = "admin@loafmarkets.com";
19883
20149
  const handleCopy = async () => {
19884
20150
  try {
19885
20151
  await navigator.clipboard.writeText(email);
@@ -20106,7 +20372,17 @@ var SiteFooter = () => {
20106
20372
  /* @__PURE__ */ jsxRuntime.jsx(FooterLinkItem, { children: /* @__PURE__ */ jsxRuntime.jsx(FooterLink, { href: "/trade", children: "Trade" }) }),
20107
20373
  /* @__PURE__ */ jsxRuntime.jsx(FooterLinkItem, { children: /* @__PURE__ */ jsxRuntime.jsx(FooterLink, { href: "/offerings", children: "Initial Offerings" }) }),
20108
20374
  /* @__PURE__ */ jsxRuntime.jsx(FooterLinkItem, { children: /* @__PURE__ */ jsxRuntime.jsx(FooterLink, { href: "/map", children: "Property Map" }) }),
20109
- /* @__PURE__ */ jsxRuntime.jsx(FooterLinkItem, { children: /* @__PURE__ */ jsxRuntime.jsx(FooterLink, { href: "/loaf-liquidity", children: "Loaf Liquidity" }) })
20375
+ /* @__PURE__ */ jsxRuntime.jsx(FooterLinkItem, { children: /* @__PURE__ */ jsxRuntime.jsx(
20376
+ FooterLink,
20377
+ {
20378
+ href: "/loaf-liquidity",
20379
+ onClick: (e) => {
20380
+ e.preventDefault();
20381
+ if (typeof window !== "undefined") window.dispatchEvent(new CustomEvent("loaf:open-liquidity-popup"));
20382
+ },
20383
+ children: "Loaf Liquidity"
20384
+ }
20385
+ ) })
20110
20386
  ] })
20111
20387
  ] }),
20112
20388
  /* @__PURE__ */ jsxRuntime.jsxs(FooterColumn, { children: [
@@ -20142,7 +20418,7 @@ var SiteFooter = () => {
20142
20418
  /* @__PURE__ */ jsxRuntime.jsx(LegalLink, { href: "/cookies", children: "Cookie Policy" })
20143
20419
  ] })
20144
20420
  ] }),
20145
- /* @__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." }) })
20421
+ /* @__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." }) })
20146
20422
  ] }) }),
20147
20423
  showContactPopup && /* @__PURE__ */ jsxRuntime.jsx(ContactPopup, { onClose: () => setShowContactPopup(false) })
20148
20424
  ] });