@loafmarkets/ui 0.1.415 → 0.1.417

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.js CHANGED
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
 
3
- var React5 = require('react');
3
+ var React9 = require('react');
4
4
  var reactSlot = require('@radix-ui/react-slot');
5
5
  var classVarianceAuthority = require('class-variance-authority');
6
6
  var clsx = require('clsx');
7
7
  var tailwindMerge = require('tailwind-merge');
8
8
  var jsxRuntime = require('react/jsx-runtime');
9
- var styled10 = require('styled-components');
10
9
  var ReactDOM = require('react-dom');
10
+ var styled10 = require('styled-components');
11
11
  var lucideReact = require('lucide-react');
12
12
  var LightweightCharts = require('lightweight-charts');
13
13
  var md = require('react-icons/md');
@@ -36,9 +36,9 @@ function _interopNamespace(e) {
36
36
  return Object.freeze(n);
37
37
  }
38
38
 
39
- var React5__namespace = /*#__PURE__*/_interopNamespace(React5);
40
- var styled10__default = /*#__PURE__*/_interopDefault(styled10);
39
+ var React9__namespace = /*#__PURE__*/_interopNamespace(React9);
41
40
  var ReactDOM__namespace = /*#__PURE__*/_interopNamespace(ReactDOM);
41
+ var styled10__default = /*#__PURE__*/_interopDefault(styled10);
42
42
  var LightweightCharts__namespace = /*#__PURE__*/_interopNamespace(LightweightCharts);
43
43
 
44
44
  // src/components/button.tsx
@@ -79,7 +79,7 @@ var buttonVariants = classVarianceAuthority.cva(
79
79
  }
80
80
  }
81
81
  );
82
- var Button = React5__namespace.forwardRef(
82
+ var Button = React9__namespace.forwardRef(
83
83
  ({ className, variant, size, radius, asChild = false, ...props }, ref) => {
84
84
  const Comp = asChild ? reactSlot.Slot : "button";
85
85
  const coercedRadius = radius ?? (variant === "onboarding" || variant === "onboardingOutline" ? "square" : void 0);
@@ -118,11 +118,11 @@ var badgeVariants = classVarianceAuthority.cva(
118
118
  }
119
119
  }
120
120
  );
121
- var Badge = React5__namespace.forwardRef(
121
+ var Badge = React9__namespace.forwardRef(
122
122
  ({ className, variant, size, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("span", { ref, className: cn(badgeVariants({ variant, size }), className), ...props })
123
123
  );
124
124
  Badge.displayName = "Badge";
125
- var Card = React5__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
125
+ var Card = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
126
126
  "div",
127
127
  {
128
128
  ref,
@@ -134,11 +134,11 @@ var Card = React5__namespace.forwardRef(({ className, ...props }, ref) => /* @__
134
134
  }
135
135
  ));
136
136
  Card.displayName = "Card";
137
- var CardHeader = React5__namespace.forwardRef(
137
+ var CardHeader = React9__namespace.forwardRef(
138
138
  ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("flex flex-col space-y-1.5 p-6", className), ...props })
139
139
  );
140
140
  CardHeader.displayName = "CardHeader";
141
- var CardTitle = React5__namespace.forwardRef(
141
+ var CardTitle = React9__namespace.forwardRef(
142
142
  ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
143
143
  "h3",
144
144
  {
@@ -149,15 +149,15 @@ var CardTitle = React5__namespace.forwardRef(
149
149
  )
150
150
  );
151
151
  CardTitle.displayName = "CardTitle";
152
- var CardDescription = React5__namespace.forwardRef(
152
+ var CardDescription = React9__namespace.forwardRef(
153
153
  ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("p", { ref, className: cn("text-sm text-slate-500", className), ...props })
154
154
  );
155
155
  CardDescription.displayName = "CardDescription";
156
- var CardContent = React5__namespace.forwardRef(
156
+ var CardContent = React9__namespace.forwardRef(
157
157
  ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("px-6 pb-6 pt-2 text-sm text-slate-600", className), ...props })
158
158
  );
159
159
  CardContent.displayName = "CardContent";
160
- var CardFooter = React5__namespace.forwardRef(
160
+ var CardFooter = React9__namespace.forwardRef(
161
161
  ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("flex items-center border-t border-slate-100 px-6 py-4", className), ...props })
162
162
  );
163
163
  CardFooter.displayName = "CardFooter";
@@ -175,7 +175,7 @@ var defaultFormatSignedCurrency = (value) => {
175
175
  const sign = value >= 0 ? "+" : "-";
176
176
  return `${sign}${defaultFormatCurrency(Math.abs(value))}`;
177
177
  };
178
- var PortfolioSummary = React5__namespace.forwardRef(
178
+ var PortfolioSummary = React9__namespace.forwardRef(
179
179
  ({
180
180
  availableCash,
181
181
  portfolioValue,
@@ -325,23 +325,23 @@ function HousePositionSlider({
325
325
  className,
326
326
  ...props
327
327
  }) {
328
- const [orderMode, setOrderMode] = React5__namespace.useState("none");
329
- const [buyTrackingMode, setBuyTrackingMode] = React5__namespace.useState("dollars");
330
- const [deltaDollars, setDeltaDollars] = React5__namespace.useState(0);
331
- const [deltaTokensBuy, setDeltaTokensBuy] = React5__namespace.useState(0);
332
- const [deltaTokensSell, setDeltaTokensSell] = React5__namespace.useState(0);
333
- const [_isDragging, setIsDragging] = React5__namespace.useState(false);
334
- const [visualTargetPct, setVisualTargetPct] = React5__namespace.useState(null);
335
- const [orderType, setOrderType] = React5__namespace.useState(defaultOrderType);
336
- const [limitPrice, setLimitPrice] = React5__namespace.useState(currentPrice);
337
- const [limitPriceInput, setLimitPriceInput] = React5__namespace.useState(currentPrice.toFixed(2));
338
- const [limitPriceDirty, setLimitPriceDirty] = React5__namespace.useState(false);
339
- const [ownershipInput, setOwnershipInput] = React5__namespace.useState("");
340
- const [tokenAmountInput, setTokenAmountInput] = React5__namespace.useState("");
341
- const houseRef = React5__namespace.useRef(null);
328
+ const [orderMode, setOrderMode] = React9__namespace.useState("none");
329
+ const [buyTrackingMode, setBuyTrackingMode] = React9__namespace.useState("dollars");
330
+ const [deltaDollars, setDeltaDollars] = React9__namespace.useState(0);
331
+ const [deltaTokensBuy, setDeltaTokensBuy] = React9__namespace.useState(0);
332
+ const [deltaTokensSell, setDeltaTokensSell] = React9__namespace.useState(0);
333
+ const [_isDragging, setIsDragging] = React9__namespace.useState(false);
334
+ const [visualTargetPct, setVisualTargetPct] = React9__namespace.useState(null);
335
+ const [orderType, setOrderType] = React9__namespace.useState(defaultOrderType);
336
+ const [limitPrice, setLimitPrice] = React9__namespace.useState(currentPrice);
337
+ const [limitPriceInput, setLimitPriceInput] = React9__namespace.useState(currentPrice.toFixed(2));
338
+ const [limitPriceDirty, setLimitPriceDirty] = React9__namespace.useState(false);
339
+ const [ownershipInput, setOwnershipInput] = React9__namespace.useState("");
340
+ const [tokenAmountInput, setTokenAmountInput] = React9__namespace.useState("");
341
+ const houseRef = React9__namespace.useRef(null);
342
342
  const asks = orderbook?.asks ?? [];
343
343
  const bids = orderbook?.bids ?? [];
344
- React5__namespace.useEffect(() => {
344
+ React9__namespace.useEffect(() => {
345
345
  if (orderType !== "limit") return;
346
346
  if (limitPriceDirty) return;
347
347
  setLimitPrice(currentPrice);
@@ -416,7 +416,7 @@ function HousePositionSlider({
416
416
  const impliedDisplayTargetOwnership = clamp(targetOwnership + ownershipShift, 0, 100);
417
417
  const displayTargetOwnership = visualTargetPct ?? impliedDisplayTargetOwnership;
418
418
  const estFeeTokens = Math.abs(deltaValue) * 5e-3 / (effectivePrice || 1);
419
- const resetOrder = React5__namespace.useCallback(() => {
419
+ const resetOrder = React9__namespace.useCallback(() => {
420
420
  setOrderMode("none");
421
421
  setBuyTrackingMode("dollars");
422
422
  setDeltaDollars(0);
@@ -424,7 +424,7 @@ function HousePositionSlider({
424
424
  setDeltaTokensSell(0);
425
425
  setVisualTargetPct(null);
426
426
  }, []);
427
- const updateOrderFromTargetValue = React5__namespace.useCallback(
427
+ const updateOrderFromTargetValue = React9__namespace.useCallback(
428
428
  (newTargetValue) => {
429
429
  const newDeltaValue = newTargetValue - holdingsValue;
430
430
  if (newDeltaValue > 0) {
@@ -448,7 +448,7 @@ function HousePositionSlider({
448
448
  },
449
449
  [effectiveAvailableCash, effectivePrice, effectiveTokensHeld, holdingsValue, resetOrder, tokensHeld]
450
450
  );
451
- const updateOrderFromOwnership = React5__namespace.useCallback(
451
+ const updateOrderFromOwnership = React9__namespace.useCallback(
452
452
  (newOwnershipPercent) => {
453
453
  const nextOwnership = clamp(newOwnershipPercent, 0, 100);
454
454
  const newTargetTokens = nextOwnership / 100 * totalTokens;
@@ -457,7 +457,7 @@ function HousePositionSlider({
457
457
  },
458
458
  [effectivePrice, totalTokens, updateOrderFromTargetValue]
459
459
  );
460
- const updateOrderFromTokenAmount = React5__namespace.useCallback(
460
+ const updateOrderFromTokenAmount = React9__namespace.useCallback(
461
461
  (tokenAmountSigned) => {
462
462
  if (tokenAmountSigned > 0) {
463
463
  const maxTokens = effectiveAvailableCash / (effectivePrice || 1);
@@ -480,7 +480,7 @@ function HousePositionSlider({
480
480
  },
481
481
  [effectiveAvailableCash, effectivePrice, effectiveTokensHeld, resetOrder]
482
482
  );
483
- const updateOrderFromSlider = React5__namespace.useCallback(
483
+ const updateOrderFromSlider = React9__namespace.useCallback(
484
484
  (pct) => {
485
485
  const normalized = (pct - 50) / 50;
486
486
  const magnitude = Math.min(Math.abs(normalized), 1);
@@ -524,7 +524,7 @@ function HousePositionSlider({
524
524
  },
525
525
  [effectiveAvailableCash, effectiveTokensHeld, resetOrder]
526
526
  );
527
- const handleDragAtClientY = React5__namespace.useCallback(
527
+ const handleDragAtClientY = React9__namespace.useCallback(
528
528
  (clientY) => {
529
529
  if (!houseRef.current) return;
530
530
  const rect = houseRef.current.getBoundingClientRect();
@@ -1040,18 +1040,18 @@ function HousePositionSliderMobile({
1040
1040
  className,
1041
1041
  ...props
1042
1042
  }) {
1043
- const [orderMode, setOrderMode] = React5__namespace.useState("none");
1044
- const [deltaDollars, setDeltaDollars] = React5__namespace.useState(0);
1045
- const [deltaTokensSell, setDeltaTokensSell] = React5__namespace.useState(0);
1046
- const [deltaTokensBuy, setDeltaTokensBuy] = React5__namespace.useState(0);
1047
- const [buyTrackingMode, setBuyTrackingMode] = React5__namespace.useState("dollars");
1048
- const [orderType, setOrderType] = React5__namespace.useState(defaultOrderType);
1049
- const [limitPrice, setLimitPrice] = React5__namespace.useState(currentPrice);
1050
- const [limitPriceInput, setLimitPriceInput] = React5__namespace.useState(currentPrice.toFixed(2));
1051
- const [limitPriceDirty, setLimitPriceDirty] = React5__namespace.useState(false);
1052
- const [tokenAmountInput, setTokenAmountInput] = React5__namespace.useState("");
1053
- const houseRef = React5__namespace.useRef(null);
1054
- React5__namespace.useEffect(() => {
1043
+ const [orderMode, setOrderMode] = React9__namespace.useState("none");
1044
+ const [deltaDollars, setDeltaDollars] = React9__namespace.useState(0);
1045
+ const [deltaTokensSell, setDeltaTokensSell] = React9__namespace.useState(0);
1046
+ const [deltaTokensBuy, setDeltaTokensBuy] = React9__namespace.useState(0);
1047
+ const [buyTrackingMode, setBuyTrackingMode] = React9__namespace.useState("dollars");
1048
+ const [orderType, setOrderType] = React9__namespace.useState(defaultOrderType);
1049
+ const [limitPrice, setLimitPrice] = React9__namespace.useState(currentPrice);
1050
+ const [limitPriceInput, setLimitPriceInput] = React9__namespace.useState(currentPrice.toFixed(2));
1051
+ const [limitPriceDirty, setLimitPriceDirty] = React9__namespace.useState(false);
1052
+ const [tokenAmountInput, setTokenAmountInput] = React9__namespace.useState("");
1053
+ const houseRef = React9__namespace.useRef(null);
1054
+ React9__namespace.useEffect(() => {
1055
1055
  if (orderType !== "limit") return;
1056
1056
  if (limitPriceDirty) return;
1057
1057
  setLimitPrice(currentPrice);
@@ -1093,7 +1093,7 @@ function HousePositionSliderMobile({
1093
1093
  const hasChange = orderMode !== "none" && (Math.abs(deltaTokens) > 1e-3 || Math.abs(deltaValue) > 0.01);
1094
1094
  const targetOwnership = totalTokens > 0 ? targetTokens / totalTokens * 100 : 0;
1095
1095
  const estFeeTokens = effectivePrice > 0 ? Math.abs(deltaValue) * 5e-3 / effectivePrice : 0;
1096
- const updateOrderFromTargetValue = React5__namespace.useCallback(
1096
+ const updateOrderFromTargetValue = React9__namespace.useCallback(
1097
1097
  (newTargetValue) => {
1098
1098
  const newDeltaValue = newTargetValue - holdingsValue;
1099
1099
  if (newDeltaValue > 0.01) {
@@ -1121,7 +1121,7 @@ function HousePositionSliderMobile({
1121
1121
  },
1122
1122
  [holdingsValue, availableCash, effectivePrice, tokensHeld]
1123
1123
  );
1124
- const updateOrderFromTokenAmount = React5__namespace.useCallback(
1124
+ const updateOrderFromTokenAmount = React9__namespace.useCallback(
1125
1125
  (tokenAmount) => {
1126
1126
  if (tokenAmount > 0) {
1127
1127
  const maxTokens = effectivePrice > 0 ? availableCash / effectivePrice : 0;
@@ -1148,14 +1148,14 @@ function HousePositionSliderMobile({
1148
1148
  },
1149
1149
  [effectivePrice, availableCash, tokensHeld]
1150
1150
  );
1151
- const handleMarkerClick = React5__namespace.useCallback(
1151
+ const handleMarkerClick = React9__namespace.useCallback(
1152
1152
  (pct) => {
1153
1153
  const newTargetValue = pct / 100 * totalCapacity;
1154
1154
  updateOrderFromTargetValue(newTargetValue);
1155
1155
  },
1156
1156
  [totalCapacity, updateOrderFromTargetValue]
1157
1157
  );
1158
- const onMouseDown = React5__namespace.useCallback(
1158
+ const onMouseDown = React9__namespace.useCallback(
1159
1159
  (e) => {
1160
1160
  e.preventDefault();
1161
1161
  const move = (ev) => {
@@ -1175,7 +1175,7 @@ function HousePositionSliderMobile({
1175
1175
  },
1176
1176
  [totalCapacity, updateOrderFromTargetValue]
1177
1177
  );
1178
- const onTouchStart = React5__namespace.useCallback(
1178
+ const onTouchStart = React9__namespace.useCallback(
1179
1179
  (e) => {
1180
1180
  e.preventDefault();
1181
1181
  e.stopPropagation();
@@ -1655,7 +1655,7 @@ var StatusDot = styled10__default.default.span`
1655
1655
  background: rgba(255, 255, 255, 0.3);
1656
1656
  `}
1657
1657
  `;
1658
- var LoafLiquidityBadge = React5__namespace.forwardRef(
1658
+ var LoafLiquidityBadge = React9__namespace.forwardRef(
1659
1659
  ({ className, isGlowing, status = "unknown", onClick, children, ...props }, ref) => {
1660
1660
  return /* @__PURE__ */ jsxRuntime.jsxs(
1661
1661
  LogoContainer,
@@ -1679,7 +1679,7 @@ var LoafLiquidityBadge = React5__namespace.forwardRef(
1679
1679
  );
1680
1680
  LoafLiquidityBadge.displayName = "LoafLiquidityBadge";
1681
1681
  var toSize = (v, fallback) => v == null ? fallback : typeof v === "number" ? `${v}px` : v;
1682
- var Skeleton = React5__namespace.forwardRef(
1682
+ var Skeleton = React9__namespace.forwardRef(
1683
1683
  ({ width, height, radius, className, style, ...props }, ref) => {
1684
1684
  return /* @__PURE__ */ jsxRuntime.jsx(
1685
1685
  "div",
@@ -1703,9 +1703,17 @@ var Skeleton = React5__namespace.forwardRef(
1703
1703
  }
1704
1704
  );
1705
1705
  Skeleton.displayName = "Skeleton";
1706
+ var DEFAULT_PRICE_LABEL = "Price (USD)";
1707
+ var PRICE_UNIT_MIN_WIDTH_PX = 320;
1708
+ function priceHeaderLabel(label, panelWidth) {
1709
+ if (label === DEFAULT_PRICE_LABEL && panelWidth != null && panelWidth > 0 && panelWidth < PRICE_UNIT_MIN_WIDTH_PX) {
1710
+ return "Price";
1711
+ }
1712
+ return label;
1713
+ }
1706
1714
  function useViewportCompact(breakpoint) {
1707
- const [isCompact, setIsCompact] = React5__namespace.useState(false);
1708
- React5__namespace.useEffect(() => {
1715
+ const [isCompact, setIsCompact] = React9__namespace.useState(false);
1716
+ React9__namespace.useEffect(() => {
1709
1717
  if (typeof window === "undefined") return;
1710
1718
  const check = () => setIsCompact(window.innerWidth <= breakpoint);
1711
1719
  check();
@@ -1720,9 +1728,14 @@ var formatTradeTime = (time) => {
1720
1728
  if (Number.isNaN(d.getTime())) return time;
1721
1729
  return d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false });
1722
1730
  };
1723
- var FLASH_DURATION_MS = 450;
1724
- var FLASH_UP_COLOR = "rgba(14, 203, 129, 0.35)";
1725
- var FLASH_DOWN_COLOR = "rgba(246, 70, 93, 0.35)";
1731
+ var FLASH_DURATION_MS = 900;
1732
+ var FLASH_UP_COLOR = "rgba(14, 203, 129, 0.22)";
1733
+ var FLASH_DOWN_COLOR = "rgba(246, 70, 93, 0.22)";
1734
+ var FLASH_KEYFRAMES = (color) => [
1735
+ { backgroundColor: "rgba(0,0,0,0)" },
1736
+ { backgroundColor: color, offset: 0.3 },
1737
+ { backgroundColor: "rgba(0,0,0,0)" }
1738
+ ];
1726
1739
  function getTradeKey(trade, fallbackIndex) {
1727
1740
  if (trade.tradeId != null) return `id-${trade.tradeId}`;
1728
1741
  if (trade.time != null) return `t-${trade.time}-${trade.type}-${trade.price}-${trade.amount}`;
@@ -1731,14 +1744,11 @@ function getTradeKey(trade, fallbackIndex) {
1731
1744
  function flashElement(node, side) {
1732
1745
  if (typeof node.animate !== "function") return;
1733
1746
  const color = side === "bid" ? FLASH_UP_COLOR : FLASH_DOWN_COLOR;
1734
- node.animate(
1735
- [{ backgroundColor: color }, { backgroundColor: "transparent" }],
1736
- { duration: FLASH_DURATION_MS, easing: "ease-out" }
1737
- );
1747
+ node.animate(FLASH_KEYFRAMES(color), { duration: FLASH_DURATION_MS, easing: "ease-in-out" });
1738
1748
  }
1739
1749
  function useAmountChangeFlash(ref, amount, side, flashOnMount) {
1740
- const prevAmountRef = React5__namespace.useRef(flashOnMount ? null : amount);
1741
- React5__namespace.useEffect(() => {
1750
+ const prevAmountRef = React9__namespace.useRef(flashOnMount ? null : amount);
1751
+ React9__namespace.useEffect(() => {
1742
1752
  const prev = prevAmountRef.current;
1743
1753
  const node = ref.current;
1744
1754
  if (node && prev !== amount) {
@@ -1759,18 +1769,15 @@ function TradeRow({
1759
1769
  compact,
1760
1770
  explorerUrl
1761
1771
  }) {
1762
- const rowRef = React5__namespace.useRef(null);
1772
+ const rowRef = React9__namespace.useRef(null);
1763
1773
  const { type } = trade;
1764
- React5__namespace.useEffect(() => {
1774
+ React9__namespace.useEffect(() => {
1765
1775
  if (seenTradeKeysRef.current.has(tradeKey)) return;
1766
1776
  seenTradeKeysRef.current.add(tradeKey);
1767
1777
  const node = rowRef.current;
1768
1778
  if (!node || typeof node.animate !== "function") return;
1769
1779
  const color = type === "buy" ? FLASH_UP_COLOR : FLASH_DOWN_COLOR;
1770
- node.animate(
1771
- [{ backgroundColor: color }, { backgroundColor: "transparent" }],
1772
- { duration: FLASH_DURATION_MS, easing: "ease-out" }
1773
- );
1780
+ node.animate(FLASH_KEYFRAMES(color), { duration: FLASH_DURATION_MS, easing: "ease-in-out" });
1774
1781
  }, [tradeKey, seenTradeKeysRef, type]);
1775
1782
  if (compact) {
1776
1783
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -1837,6 +1844,27 @@ function TradeRow({
1837
1844
  }
1838
1845
  );
1839
1846
  }
1847
+ function BreadSliceGlyph() {
1848
+ return /* @__PURE__ */ jsxRuntime.jsx(
1849
+ "svg",
1850
+ {
1851
+ width: "10",
1852
+ height: "10",
1853
+ viewBox: "0 0 10 10",
1854
+ "aria-hidden": "true",
1855
+ style: { display: "block", filter: "drop-shadow(0 0 4px rgba(201,162,39,0.6))" },
1856
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1857
+ "path",
1858
+ {
1859
+ d: "M1 4.2 C1 1.9 2.7 1 5 1 C7.3 1 9 1.9 9 4.2 L9 8.2 C9 8.6 8.6 9 8.2 9 L1.8 9 C1.4 9 1 8.6 1 8.2 Z",
1860
+ fill: "none",
1861
+ stroke: "#C9A227",
1862
+ strokeWidth: "1.3"
1863
+ }
1864
+ )
1865
+ }
1866
+ );
1867
+ }
1840
1868
  function DepthRow({
1841
1869
  side,
1842
1870
  price,
@@ -1851,12 +1879,14 @@ function DepthRow({
1851
1879
  fillHeight
1852
1880
  }) {
1853
1881
  const isAsk = side === "ask";
1854
- const rowRef = React5__namespace.useRef(null);
1882
+ const rowRef = React9__namespace.useRef(null);
1855
1883
  useAmountChangeFlash(rowRef, amount, side, flashOnMount);
1856
1884
  return /* @__PURE__ */ jsxRuntime.jsxs(
1857
1885
  "div",
1858
1886
  {
1859
1887
  ref: rowRef,
1888
+ "data-depth-row": true,
1889
+ "data-price": `$${formatNumber(price, precision)}`,
1860
1890
  className: "relative grid grid-cols-3 items-center gap-3 px-3 text-[0.8rem]",
1861
1891
  style: fillHeight ? { flex: "1 1 0", minHeight: 0, maxHeight: `${DEPTH_ROW_HEIGHT_PX}px`, cursor: onPriceClick ? "pointer" : void 0 } : { height: `${DEPTH_ROW_HEIGHT_PX}px`, flexShrink: 0, cursor: onPriceClick ? "pointer" : void 0 },
1862
1892
  onClick: onPriceClick ? () => onPriceClick(price) : void 0,
@@ -1864,14 +1894,20 @@ function DepthRow({
1864
1894
  /* @__PURE__ */ jsxRuntime.jsx(
1865
1895
  "div",
1866
1896
  {
1897
+ "data-depth-bar": true,
1867
1898
  className: cn(isAsk ? "bg-rose-900/30" : "bg-emerald-900/30"),
1868
1899
  style: {
1869
1900
  position: "absolute",
1870
- top: 0,
1871
- bottom: 0,
1901
+ top: 1,
1902
+ bottom: 1,
1872
1903
  left: 0,
1873
1904
  width: `${clamp3(depthPct, 0, 100)}%`,
1874
- transition: "width 300ms ease-out"
1905
+ transition: "width 300ms ease-out",
1906
+ borderRadius: "0 4px 4px 0",
1907
+ // backgroundImage LONGHAND, not the `background` shorthand — jsdom's
1908
+ // cssstyle drops gradients from the shorthand (tests assert this).
1909
+ backgroundColor: "transparent",
1910
+ backgroundImage: isAsk ? "linear-gradient(90deg, rgba(246,70,93,0.11), rgba(246,70,93,0.15))" : "linear-gradient(90deg, rgba(14,203,129,0.09), rgba(14,203,129,0.13))"
1875
1911
  }
1876
1912
  }
1877
1913
  ),
@@ -1879,33 +1915,35 @@ function DepthRow({
1879
1915
  "span",
1880
1916
  {
1881
1917
  "aria-label": "Your order at this price",
1882
- className: "absolute left-0 top-1/2 -translate-y-1/2 z-[2] h-[6px] w-[6px] rounded-full bg-[#C9A227] shadow-[0_0_6px_rgba(201,162,39,0.7)]"
1918
+ className: "absolute left-0 top-1/2 -translate-y-1/2 z-[2]",
1919
+ children: /* @__PURE__ */ jsxRuntime.jsx(BreadSliceGlyph, {})
1883
1920
  }
1884
1921
  ) : null,
1885
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative z-[1] tabular-nums", isAsk ? "text-[#f6465d]" : "text-[#0ecb81]"), children: [
1922
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cell": "price", className: cn("relative z-[1] tabular-nums", isAsk ? "text-[#f6465d]" : "text-[#0ecb81]"), children: [
1886
1923
  "$",
1887
1924
  formatNumber(price, precision)
1888
1925
  ] }),
1889
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative z-[1] text-right tabular-nums text-white/90", children: formatNumber(amount, amountPrecision) }),
1890
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative z-[1] text-right tabular-nums text-white/50", children: formatNumber(cumDepth, amountPrecision) })
1926
+ /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cell": "amt", className: "relative z-[1] text-right tabular-nums text-white/90", children: formatNumber(amount, amountPrecision) }),
1927
+ /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cell": "tot", className: "relative z-[1] text-right tabular-nums text-white/50", children: formatNumber(cumDepth, amountPrecision) })
1891
1928
  ]
1892
1929
  }
1893
1930
  );
1894
1931
  }
1895
1932
  var clamp3 = (value, min, max) => Math.min(max, Math.max(min, value));
1933
+ var fitLevelCount = (availableHeightPx, max) => clamp3(Math.floor(availableHeightPx / DEPTH_ROW_HEIGHT_PX), 1, max);
1896
1934
  var LEVEL_ROWS_VISIBLE = 8;
1897
1935
  var DEPTH_ROW_HEIGHT_PX = 28;
1898
1936
  var COMPACT_ROWS_VISIBLE = 5;
1899
1937
  var COMPACT_ROW_HEIGHT_PX = 30;
1900
1938
  var COMPACT_BREAKPOINT_PX = 1024;
1901
- var Orderbook = React5__namespace.forwardRef(
1939
+ var Orderbook = React9__namespace.forwardRef(
1902
1940
  ({
1903
1941
  asks,
1904
1942
  bids,
1905
1943
  midPrice,
1906
1944
  midChangePercent,
1907
1945
  trades = [],
1908
- priceLabel = "Price (USD)",
1946
+ priceLabel = DEFAULT_PRICE_LABEL,
1909
1947
  amountLabel = "Amount",
1910
1948
  precision = 2,
1911
1949
  amountPrecision = 2,
@@ -1933,12 +1971,12 @@ var Orderbook = React5__namespace.forwardRef(
1933
1971
  status: liquidityStatus
1934
1972
  }
1935
1973
  );
1936
- const [tab, setTabState] = React5__namespace.useState(defaultTab);
1974
+ const [tab, setTabState] = React9__namespace.useState(defaultTab);
1937
1975
  const setTab = setTabState;
1938
1976
  const effectiveTab = showTradesTab ? tab : "orderbook";
1939
- const [tradeFilter, setTradeFilter] = React5__namespace.useState("all");
1977
+ const [tradeFilter, setTradeFilter] = React9__namespace.useState("all");
1940
1978
  const viewportCompact = useViewportCompact(COMPACT_BREAKPOINT_PX);
1941
- const { userAskPrices, userBidPrices } = React5__namespace.useMemo(() => {
1979
+ const { userAskPrices, userBidPrices } = React9__namespace.useMemo(() => {
1942
1980
  const ask = /* @__PURE__ */ new Set();
1943
1981
  const bid = /* @__PURE__ */ new Set();
1944
1982
  if (!userOrderPrices) return { userAskPrices: ask, userBidPrices: bid };
@@ -1948,15 +1986,15 @@ var Orderbook = React5__namespace.forwardRef(
1948
1986
  }
1949
1987
  return { userAskPrices: ask, userBidPrices: bid };
1950
1988
  }, [userOrderPrices]);
1951
- const seenTradeKeysRef = React5__namespace.useRef(/* @__PURE__ */ new Set());
1952
- const initializedRef = React5__namespace.useRef(false);
1989
+ const seenTradeKeysRef = React9__namespace.useRef(/* @__PURE__ */ new Set());
1990
+ const initializedRef = React9__namespace.useRef(false);
1953
1991
  if (!initializedRef.current && trades.length > 0) {
1954
1992
  for (let i = 0; i < trades.length; i++) {
1955
1993
  seenTradeKeysRef.current.add(getTradeKey(trades[i], i));
1956
1994
  }
1957
1995
  initializedRef.current = true;
1958
1996
  }
1959
- React5__namespace.useEffect(() => {
1997
+ React9__namespace.useEffect(() => {
1960
1998
  if (!initializedRef.current) return;
1961
1999
  const live = /* @__PURE__ */ new Set();
1962
2000
  for (let i = 0; i < trades.length; i++) {
@@ -1967,7 +2005,7 @@ var Orderbook = React5__namespace.forwardRef(
1967
2005
  if (!live.has(key)) seen.delete(key);
1968
2006
  }
1969
2007
  }, [trades]);
1970
- React5__namespace.useEffect(() => {
2008
+ React9__namespace.useEffect(() => {
1971
2009
  setTab(defaultTab);
1972
2010
  }, [defaultTab]);
1973
2011
  const handleTab = (next) => {
@@ -1975,10 +2013,49 @@ var Orderbook = React5__namespace.forwardRef(
1975
2013
  onTabChange?.(next);
1976
2014
  };
1977
2015
  const isCompact = variant === "compact" || variant === "auto" && viewportCompact;
2016
+ const rootRef = React9__namespace.useRef(null);
2017
+ const setRootRef = React9__namespace.useCallback(
2018
+ (node) => {
2019
+ rootRef.current = node;
2020
+ if (typeof ref === "function") ref(node);
2021
+ else if (ref) ref.current = node;
2022
+ },
2023
+ [ref]
2024
+ );
2025
+ const [panelWidth, setPanelWidth] = React9__namespace.useState(null);
2026
+ React9__namespace.useEffect(() => {
2027
+ if (typeof ResizeObserver === "undefined") return;
2028
+ const el = rootRef.current;
2029
+ if (!el) return;
2030
+ const ro = new ResizeObserver((entries) => {
2031
+ const w = entries[0]?.contentRect?.width;
2032
+ if (typeof w === "number") setPanelWidth((prev) => prev === w ? prev : w);
2033
+ });
2034
+ ro.observe(el);
2035
+ return () => ro.disconnect();
2036
+ }, []);
2037
+ const effectivePriceLabel = priceHeaderLabel(priceLabel, panelWidth);
1978
2038
  const effectiveLevels = maxLevels ?? LEVEL_ROWS_VISIBLE;
1979
2039
  const effectiveCompactLevels = maxLevels ?? COMPACT_ROWS_VISIBLE;
1980
2040
  const sectionHeight = isCompact ? effectiveCompactLevels * COMPACT_ROW_HEIGHT_PX : effectiveLevels * DEPTH_ROW_HEIGHT_PX;
1981
- const rowCount = isCompact ? effectiveCompactLevels : effectiveLevels;
2041
+ const asksSectionRef = React9__namespace.useRef(null);
2042
+ const [depthSectionH, setDepthSectionH] = React9__namespace.useState(null);
2043
+ React9__namespace.useEffect(() => {
2044
+ if (!fillHeight || isCompact || effectiveTab !== "orderbook") return;
2045
+ if (typeof ResizeObserver === "undefined") return;
2046
+ const el = asksSectionRef.current;
2047
+ if (!el) return;
2048
+ const ro = new ResizeObserver((entries) => {
2049
+ const h = entries[0]?.contentRect?.height;
2050
+ if (typeof h === "number") {
2051
+ setDepthSectionH((prev) => prev === h ? prev : h);
2052
+ }
2053
+ });
2054
+ ro.observe(el);
2055
+ return () => ro.disconnect();
2056
+ }, [fillHeight, isCompact, effectiveTab]);
2057
+ const fitRows = fillHeight && !isCompact && depthSectionH != null ? fitLevelCount(depthSectionH, effectiveLevels) : null;
2058
+ const rowCount = isCompact ? effectiveCompactLevels : fitRows ?? effectiveLevels;
1982
2059
  const askVisibleLevels = asks.slice(-rowCount);
1983
2060
  const bidVisibleLevels = bids.slice(0, rowCount);
1984
2061
  const askCumDepths = new Array(askVisibleLevels.length);
@@ -1999,9 +2076,9 @@ var Orderbook = React5__namespace.forwardRef(
1999
2076
  }
2000
2077
  const combinedMaxCumDepth = Math.max(1, ...askCumDepths, ...bidCumDepths);
2001
2078
  const askMaxCumDepth = combinedMaxCumDepth;
2002
- const prevAskPricesRef = React5__namespace.useRef(null);
2003
- const prevBidPricesRef = React5__namespace.useRef(null);
2004
- const newAskPrices = React5__namespace.useMemo(() => {
2079
+ const prevAskPricesRef = React9__namespace.useRef(null);
2080
+ const prevBidPricesRef = React9__namespace.useRef(null);
2081
+ const newAskPrices = React9__namespace.useMemo(() => {
2005
2082
  const current = new Set(askVisibleLevels.map((l) => l.price));
2006
2083
  const prev = prevAskPricesRef.current;
2007
2084
  if (!prev) return /* @__PURE__ */ new Set();
@@ -2011,7 +2088,7 @@ var Orderbook = React5__namespace.forwardRef(
2011
2088
  }
2012
2089
  return added;
2013
2090
  }, [askVisibleLevels]);
2014
- const newBidPrices = React5__namespace.useMemo(() => {
2091
+ const newBidPrices = React9__namespace.useMemo(() => {
2015
2092
  const current = new Set(bidVisibleLevels.map((l) => l.price));
2016
2093
  const prev = prevBidPricesRef.current;
2017
2094
  if (!prev) return /* @__PURE__ */ new Set();
@@ -2021,12 +2098,16 @@ var Orderbook = React5__namespace.forwardRef(
2021
2098
  }
2022
2099
  return added;
2023
2100
  }, [bidVisibleLevels]);
2024
- React5__namespace.useEffect(() => {
2101
+ React9__namespace.useEffect(() => {
2025
2102
  prevAskPricesRef.current = new Set(askVisibleLevels.map((l) => l.price));
2026
2103
  prevBidPricesRef.current = new Set(bidVisibleLevels.map((l) => l.price));
2027
2104
  }, [askVisibleLevels, bidVisibleLevels]);
2028
2105
  const bidMaxCumDepth = combinedMaxCumDepth;
2029
2106
  const midClass = midChangePercent == null ? "text-white" : midChangePercent >= 0 ? "text-[#0ecb81]" : "text-[#f6465d]";
2107
+ const bestAskPrice = asks.length > 0 ? asks[asks.length - 1].price : null;
2108
+ const bestBidPrice = bids.length > 0 ? bids[0].price : null;
2109
+ const spread = bestAskPrice != null && bestBidPrice != null ? bestAskPrice - bestBidPrice : null;
2110
+ const spreadPct = spread != null && midPrice > 0 ? spread / midPrice * 100 : null;
2030
2111
  const tradeFiltered = trades.filter((t) => tradeFilter === "all" || t.type === tradeFilter);
2031
2112
  const layoutProps = {
2032
2113
  tab: effectiveTab,
@@ -2035,7 +2116,7 @@ var Orderbook = React5__namespace.forwardRef(
2035
2116
  tradeFilter,
2036
2117
  setTradeFilter,
2037
2118
  rightHeader: resolvedRightHeader,
2038
- priceLabel,
2119
+ priceLabel: effectivePriceLabel,
2039
2120
  amountLabel,
2040
2121
  tradeFiltered,
2041
2122
  precision,
@@ -2062,12 +2143,15 @@ var Orderbook = React5__namespace.forwardRef(
2062
2143
  compactLevelCount: effectiveCompactLevels,
2063
2144
  fillHeight,
2064
2145
  newAskPrices,
2065
- newBidPrices
2146
+ newBidPrices,
2147
+ spread,
2148
+ spreadPct,
2149
+ asksSectionRef
2066
2150
  };
2067
2151
  return /* @__PURE__ */ jsxRuntime.jsx(
2068
2152
  Card,
2069
2153
  {
2070
- ref,
2154
+ ref: setRootRef,
2071
2155
  className: cn(
2072
2156
  "w-full max-w-[520px] overflow-hidden rounded-[12px] border border-white/10 bg-[#111111] text-white shadow-md flex flex-col",
2073
2157
  isCompact && "max-w-none",
@@ -2081,6 +2165,170 @@ var Orderbook = React5__namespace.forwardRef(
2081
2165
  }
2082
2166
  );
2083
2167
  Orderbook.displayName = "Orderbook";
2168
+ var MID_ROW_GOLD_STYLE = {
2169
+ backgroundImage: "linear-gradient(90deg, rgba(230,200,126,0.07), rgba(230,200,126,0.015))",
2170
+ borderTop: "1px solid rgba(230,200,126,0.22)",
2171
+ borderBottom: "1px solid rgba(230,200,126,0.22)"
2172
+ };
2173
+ function SpreadReadout({
2174
+ spread,
2175
+ spreadPct,
2176
+ precision: _precision,
2177
+ compact
2178
+ }) {
2179
+ if (spread == null || spreadPct == null) return null;
2180
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2181
+ "div",
2182
+ {
2183
+ style: {
2184
+ display: "flex",
2185
+ alignItems: "baseline",
2186
+ gap: 5,
2187
+ whiteSpace: "nowrap",
2188
+ lineHeight: 1
2189
+ },
2190
+ children: [
2191
+ /* @__PURE__ */ jsxRuntime.jsx(
2192
+ "span",
2193
+ {
2194
+ style: {
2195
+ fontSize: compact ? "0.48rem" : "0.52rem",
2196
+ letterSpacing: "0.14em",
2197
+ textTransform: "uppercase",
2198
+ color: "rgba(255,255,255,0.45)",
2199
+ fontWeight: 600
2200
+ },
2201
+ children: "Spread"
2202
+ }
2203
+ ),
2204
+ /* @__PURE__ */ jsxRuntime.jsxs(
2205
+ "span",
2206
+ {
2207
+ className: "tabular-nums",
2208
+ style: { fontSize: compact ? "0.56rem" : "0.62rem", color: "rgba(255,255,255,0.45)" },
2209
+ children: [
2210
+ spreadPct.toFixed(2),
2211
+ "%"
2212
+ ]
2213
+ }
2214
+ )
2215
+ ]
2216
+ }
2217
+ );
2218
+ }
2219
+ function useGlidePill() {
2220
+ const sectionRef = React9__namespace.useRef(null);
2221
+ const pillRef = React9__namespace.useRef(null);
2222
+ const priceSpanRef = React9__namespace.useRef(null);
2223
+ const boldedCellRef = React9__namespace.useRef(null);
2224
+ const [pillPrice, setPillPrice] = React9__namespace.useState(null);
2225
+ const motion = React9__namespace.useRef({ x: 0, targetX: 0, y: 0, targetY: 0, amp: 0, on: false, raf: 0 });
2226
+ const reducedMotion = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
2227
+ const unbold = () => {
2228
+ if (boldedCellRef.current) {
2229
+ boldedCellRef.current.style.fontWeight = "";
2230
+ boldedCellRef.current = null;
2231
+ }
2232
+ };
2233
+ const frame = React9__namespace.useCallback(() => {
2234
+ const m = motion.current;
2235
+ const section = sectionRef.current;
2236
+ const pill = pillRef.current;
2237
+ m.raf = 0;
2238
+ if (!section || !pill) return;
2239
+ const k = reducedMotion ? 1 : 0.12;
2240
+ m.x += (m.targetX - m.x) * k;
2241
+ m.y += (m.targetY - m.y) * k;
2242
+ pill.style.transform = `translate(${m.x.toFixed(1)}px, ${m.y.toFixed(1)}px)`;
2243
+ const ampTarget = m.on && !reducedMotion ? 1 : 0;
2244
+ m.amp += (ampTarget - m.amp) * 0.14;
2245
+ const crest = m.y + pill.offsetHeight / 2;
2246
+ const rows = section.querySelectorAll("[data-depth-row]");
2247
+ for (const row of rows) {
2248
+ const rect = row.getBoundingClientRect();
2249
+ const center = rect.top + rect.height / 2;
2250
+ const d = center - crest;
2251
+ const inf = Math.exp(-(d * d) / (2 * 42 * 42)) * m.amp;
2252
+ const bar = row.querySelector("[data-depth-bar]");
2253
+ const priceCell = row.querySelector('[data-cell="price"]');
2254
+ if (inf < 0.01) {
2255
+ if (row.dataset.waved) {
2256
+ delete row.dataset.waved;
2257
+ row.style.zIndex = "";
2258
+ row.style.background = "";
2259
+ row.style.boxShadow = "";
2260
+ if (bar) bar.style.filter = "";
2261
+ if (priceCell) priceCell.style.transform = "";
2262
+ }
2263
+ continue;
2264
+ }
2265
+ row.dataset.waved = "1";
2266
+ row.style.zIndex = String(3 + Math.round(inf * 10));
2267
+ row.style.background = `rgba(19,20,24,${(inf * 0.92).toFixed(2)})`;
2268
+ row.style.boxShadow = `0 ${(inf * 3).toFixed(1)}px ${(inf * 12).toFixed(1)}px rgba(0,0,0,${(inf * 0.45).toFixed(2)})`;
2269
+ if (bar) bar.style.filter = `brightness(${(1 + inf * 0.45).toFixed(2)})`;
2270
+ if (priceCell) {
2271
+ priceCell.style.transformOrigin = "left center";
2272
+ priceCell.style.transform = `scale(${(1 + inf * 0.09).toFixed(3)})`;
2273
+ }
2274
+ }
2275
+ if (m.on || Math.abs(m.targetX - m.x) + Math.abs(m.targetY - m.y) > 0.5 || m.amp > 5e-3) {
2276
+ m.raf = requestAnimationFrame(frame);
2277
+ }
2278
+ }, [reducedMotion]);
2279
+ const onMouseMove = React9__namespace.useCallback(
2280
+ (e) => {
2281
+ const target = e.target;
2282
+ const row = target?.closest?.("[data-depth-row]");
2283
+ const m = motion.current;
2284
+ if (!row || !row.dataset.price) {
2285
+ m.on = false;
2286
+ unbold();
2287
+ setPillPrice(null);
2288
+ return;
2289
+ }
2290
+ setPillPrice(row.dataset.price);
2291
+ const priceCell = row.querySelector('[data-cell="price"]');
2292
+ if (priceCell !== boldedCellRef.current) {
2293
+ unbold();
2294
+ if (priceCell) {
2295
+ priceCell.style.fontWeight = "600";
2296
+ boldedCellRef.current = priceCell;
2297
+ }
2298
+ }
2299
+ const pill = pillRef.current;
2300
+ if (pill && priceCell) {
2301
+ const rowRect = row.getBoundingClientRect();
2302
+ const cellRect = priceCell.getBoundingClientRect();
2303
+ const priceOffset = priceSpanRef.current?.offsetLeft || 48;
2304
+ const nextX = cellRect.left - priceOffset;
2305
+ const nextY = rowRect.top + (rowRect.height || 28) / 2 - pill.offsetHeight / 2;
2306
+ if (!m.on) {
2307
+ m.x = nextX;
2308
+ m.y = nextY;
2309
+ }
2310
+ m.targetX = nextX;
2311
+ m.targetY = nextY;
2312
+ }
2313
+ m.on = true;
2314
+ if (!m.raf && typeof requestAnimationFrame === "function") {
2315
+ m.raf = requestAnimationFrame(frame);
2316
+ }
2317
+ },
2318
+ [frame]
2319
+ );
2320
+ const onMouseLeave = React9__namespace.useCallback(() => {
2321
+ motion.current.on = false;
2322
+ unbold();
2323
+ setPillPrice(null);
2324
+ }, []);
2325
+ React9__namespace.useEffect(() => {
2326
+ return () => {
2327
+ if (motion.current.raf) cancelAnimationFrame(motion.current.raf);
2328
+ };
2329
+ }, []);
2330
+ return { sectionRef, pillRef, priceSpanRef, pillPrice, onMouseMove, onMouseLeave };
2331
+ }
2084
2332
  function SkeletonRow({ compact }) {
2085
2333
  if (compact) {
2086
2334
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -2137,8 +2385,12 @@ function DesktopOrderbookLayout({
2137
2385
  compactLevelCount: _compactLevelCount,
2138
2386
  fillHeight,
2139
2387
  newAskPrices,
2140
- newBidPrices
2388
+ newBidPrices,
2389
+ spread,
2390
+ spreadPct,
2391
+ asksSectionRef
2141
2392
  }) {
2393
+ const glide = useGlidePill();
2142
2394
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2143
2395
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-4 pt-4", children: [
2144
2396
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [
@@ -2148,8 +2400,8 @@ function DesktopOrderbookLayout({
2148
2400
  type: "button",
2149
2401
  onClick: () => handleTab("orderbook"),
2150
2402
  className: cn(
2151
- "relative pb-2 text-sm font-semibold",
2152
- tab === "orderbook" ? "text-white" : "text-white/60"
2403
+ "relative pb-2 text-[0.8rem] font-medium transition-colors",
2404
+ tab === "orderbook" ? "text-white" : "text-white/60 hover:text-white/85"
2153
2405
  ),
2154
2406
  children: [
2155
2407
  "Orderbook",
@@ -2163,8 +2415,8 @@ function DesktopOrderbookLayout({
2163
2415
  type: "button",
2164
2416
  onClick: () => handleTab("trades"),
2165
2417
  className: cn(
2166
- "relative pb-2 text-sm font-medium",
2167
- tab === "trades" ? "text-white" : "text-white/60"
2418
+ "relative pb-2 text-[0.8rem] font-medium transition-colors",
2419
+ tab === "trades" ? "text-white" : "text-white/60 hover:text-white/85"
2168
2420
  ),
2169
2421
  children: [
2170
2422
  "Trades",
@@ -2212,100 +2464,170 @@ function DesktopOrderbookLayout({
2212
2464
  )
2213
2465
  ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-3", children: rightHeader })
2214
2466
  ] }),
2215
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col min-h-0 px-4 pt-2", children: [
2216
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-3 gap-3 px-3 pb-1.5 pt-1 text-xs text-white/60", children: [
2217
- /* @__PURE__ */ jsxRuntime.jsx("div", { children: priceLabel }),
2218
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right", children: amountLabel }),
2219
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right", children: tab === "orderbook" ? "Total" : "Time" })
2220
- ] }),
2221
- tab === "trades" ? /* @__PURE__ */ jsxRuntime.jsx(
2222
- "div",
2223
- {
2224
- className: "max-h-[380px] overflow-y-auto overflow-x-hidden",
2225
- style: { scrollbarGutter: "stable" },
2226
- children: isLoading && tradeFiltered.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { children: Array.from({ length: effectiveLevels }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(SkeletonRow, {}, `trade-skel-${i}`)) }) : tradeFiltered.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-10 text-center text-sm text-white/50", children: "No trades" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { children: tradeFiltered.map((trade, i) => {
2227
- const tradeKey = getTradeKey(trade, i);
2228
- const explorerUrl = trade.txHash && tradeExplorerUrl ? tradeExplorerUrl(trade.txHash) : trade.txHash ? `https://sepolia.etherscan.io/tx/${trade.txHash}` : void 0;
2229
- return /* @__PURE__ */ jsxRuntime.jsx(
2230
- TradeRow,
2467
+ /* @__PURE__ */ jsxRuntime.jsxs(
2468
+ "div",
2469
+ {
2470
+ ref: glide.sectionRef,
2471
+ "data-glide-section": true,
2472
+ className: "relative flex flex-1 flex-col min-h-0 px-4 pt-2",
2473
+ onMouseMove: glide.onMouseMove,
2474
+ onMouseLeave: glide.onMouseLeave,
2475
+ children: [
2476
+ typeof document !== "undefined" ? ReactDOM.createPortal(
2477
+ /* @__PURE__ */ jsxRuntime.jsxs(
2478
+ "div",
2231
2479
  {
2232
- trade,
2233
- tradeKey,
2234
- precision,
2235
- amountPrecision,
2236
- seenTradeKeysRef,
2237
- explorerUrl
2238
- },
2239
- tradeKey
2240
- );
2241
- }) })
2242
- }
2243
- ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col min-h-0 overflow-hidden", fillHeight && "flex-1"), children: [
2244
- /* @__PURE__ */ jsxRuntime.jsx(
2245
- "div",
2246
- {
2247
- className: cn("flex flex-col justify-end overflow-hidden", !fillHeight && "flex-shrink-0"),
2248
- style: fillHeight ? { flex: 1, minHeight: 0 } : { height: `${sectionHeight}px` },
2249
- children: isLoading ? Array.from({ length: effectiveLevels }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(SkeletonRow, {}, `ask-skel-${i}`)) : askVisibleLevels.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center px-3 text-xs text-white/40", children: "No asks" }) : askVisibleLevels.map((l, i) => /* @__PURE__ */ jsxRuntime.jsx(
2250
- DepthRow,
2480
+ ref: glide.pillRef,
2481
+ "data-glide-pill": true,
2482
+ "data-visible": glide.pillPrice != null ? "true" : "false",
2483
+ style: {
2484
+ position: "fixed",
2485
+ left: 0,
2486
+ top: 0,
2487
+ zIndex: 60,
2488
+ pointerEvents: "none",
2489
+ display: "flex",
2490
+ alignItems: "center",
2491
+ gap: 8,
2492
+ whiteSpace: "nowrap",
2493
+ background: "#1b1d22",
2494
+ border: "1px solid rgba(255,255,255,0.14)",
2495
+ borderRadius: 999,
2496
+ padding: "4px 13px",
2497
+ boxShadow: "0 6px 20px rgba(0,0,0,0.55)",
2498
+ fontVariantNumeric: "tabular-nums",
2499
+ opacity: glide.pillPrice != null ? 1 : 0,
2500
+ transition: "opacity 160ms ease"
2501
+ },
2502
+ children: [
2503
+ /* @__PURE__ */ jsxRuntime.jsx(
2504
+ "span",
2505
+ {
2506
+ style: {
2507
+ fontSize: "0.6rem",
2508
+ fontWeight: 600,
2509
+ letterSpacing: "0.08em",
2510
+ textTransform: "uppercase",
2511
+ color: "#E6C87E",
2512
+ lineHeight: 1
2513
+ },
2514
+ children: "Limit"
2515
+ }
2516
+ ),
2517
+ /* @__PURE__ */ jsxRuntime.jsx(
2518
+ "span",
2519
+ {
2520
+ ref: glide.priceSpanRef,
2521
+ className: "tabular-nums",
2522
+ style: { fontSize: "0.8rem", fontWeight: 600, color: "#fff" },
2523
+ children: glide.pillPrice ?? ""
2524
+ }
2525
+ )
2526
+ ]
2527
+ }
2528
+ ),
2529
+ document.body
2530
+ ) : null,
2531
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-3 gap-3 px-3 pb-1.5 pt-1 text-xs text-white/60", children: [
2532
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children: priceLabel }),
2533
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right", children: amountLabel }),
2534
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right", children: tab === "orderbook" ? "Total" : "Time" })
2535
+ ] }),
2536
+ tab === "trades" ? /* @__PURE__ */ jsxRuntime.jsx(
2537
+ "div",
2538
+ {
2539
+ className: "max-h-[380px] overflow-y-auto overflow-x-hidden",
2540
+ style: { scrollbarGutter: "stable" },
2541
+ children: isLoading && tradeFiltered.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { children: Array.from({ length: effectiveLevels }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(SkeletonRow, {}, `trade-skel-${i}`)) }) : tradeFiltered.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-10 text-center text-sm text-white/50", children: "No trades" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { children: tradeFiltered.map((trade, i) => {
2542
+ const tradeKey = getTradeKey(trade, i);
2543
+ const explorerUrl = trade.txHash && tradeExplorerUrl ? tradeExplorerUrl(trade.txHash) : trade.txHash ? `https://sepolia.etherscan.io/tx/${trade.txHash}` : void 0;
2544
+ return /* @__PURE__ */ jsxRuntime.jsx(
2545
+ TradeRow,
2546
+ {
2547
+ trade,
2548
+ tradeKey,
2549
+ precision,
2550
+ amountPrecision,
2551
+ seenTradeKeysRef,
2552
+ explorerUrl
2553
+ },
2554
+ tradeKey
2555
+ );
2556
+ }) })
2557
+ }
2558
+ ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col min-h-0 overflow-hidden", fillHeight && "flex-1"), children: [
2559
+ /* @__PURE__ */ jsxRuntime.jsx(
2560
+ "div",
2251
2561
  {
2252
- side: "ask",
2253
- price: l.price,
2254
- amount: l.amount,
2255
- cumDepth: askCumDepths[i],
2256
- depthPct: askCumDepths[i] / askMaxCumDepth * 100,
2257
- precision,
2258
- amountPrecision,
2259
- hasUserOrder: userAskPrices.has(l.price),
2260
- onPriceClick,
2261
- flashOnMount: newAskPrices.has(l.price),
2262
- fillHeight
2263
- },
2264
- `ask-${l.price}`
2265
- ))
2266
- }
2267
- ),
2268
- /* @__PURE__ */ jsxRuntime.jsx(
2269
- "div",
2270
- {
2271
- className: "flex items-center gap-2 bg-[#0b1a24] px-3 py-2 flex-shrink-0 overflow-hidden orderbook-mid-row",
2272
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("text-lg font-semibold tabular-nums whitespace-nowrap orderbook-mid-price", midClass), children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { width: 110, height: 20 }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2273
- "$",
2274
- formatNumber(midPrice, precision),
2275
- midChangePercent == null ? null : /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("ml-2 text-sm font-semibold tabular-nums", midClass), children: [
2276
- midChangePercent >= 0 ? "+" : "",
2277
- midChangePercent.toFixed(2),
2278
- "%"
2279
- ] })
2280
- ] }) })
2281
- }
2282
- ),
2283
- /* @__PURE__ */ jsxRuntime.jsx(
2284
- "div",
2285
- {
2286
- className: cn("flex flex-col overflow-hidden", !fillHeight && "flex-shrink-0"),
2287
- style: fillHeight ? { flex: 1, minHeight: 0 } : { height: `${sectionHeight}px` },
2288
- children: isLoading ? Array.from({ length: effectiveLevels }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(SkeletonRow, {}, `bid-skel-${i}`)) : bidVisibleLevels.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center px-3 text-xs text-white/40", children: "No bids" }) : bidVisibleLevels.map((l, i) => /* @__PURE__ */ jsxRuntime.jsx(
2289
- DepthRow,
2562
+ ref: asksSectionRef,
2563
+ className: cn("flex flex-col justify-end overflow-hidden", !fillHeight && "flex-shrink-0"),
2564
+ style: fillHeight ? { flex: 1, minHeight: 0 } : { height: `${sectionHeight}px` },
2565
+ children: isLoading ? Array.from({ length: effectiveLevels }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(SkeletonRow, {}, `ask-skel-${i}`)) : askVisibleLevels.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center px-3 text-xs text-white/40", children: "No asks" }) : askVisibleLevels.map((l, i) => /* @__PURE__ */ jsxRuntime.jsx(
2566
+ DepthRow,
2567
+ {
2568
+ side: "ask",
2569
+ price: l.price,
2570
+ amount: l.amount,
2571
+ cumDepth: askCumDepths[i],
2572
+ depthPct: askCumDepths[i] / askMaxCumDepth * 100,
2573
+ precision,
2574
+ amountPrecision,
2575
+ hasUserOrder: userAskPrices.has(l.price),
2576
+ onPriceClick,
2577
+ flashOnMount: newAskPrices.has(l.price),
2578
+ fillHeight
2579
+ },
2580
+ `ask-${l.price}`
2581
+ ))
2582
+ }
2583
+ ),
2584
+ /* @__PURE__ */ jsxRuntime.jsxs(
2585
+ "div",
2290
2586
  {
2291
- side: "bid",
2292
- price: l.price,
2293
- amount: l.amount,
2294
- cumDepth: bidCumDepths[i],
2295
- depthPct: bidCumDepths[i] / bidMaxCumDepth * 100,
2296
- precision,
2297
- amountPrecision,
2298
- hasUserOrder: userBidPrices.has(l.price),
2299
- onPriceClick,
2300
- flashOnMount: newBidPrices.has(l.price),
2301
- fillHeight
2302
- },
2303
- `bid-${l.price}`
2304
- ))
2305
- }
2306
- )
2307
- ] })
2308
- ] })
2587
+ className: "flex items-center justify-between gap-2 px-3 py-2 flex-shrink-0 overflow-hidden orderbook-mid-row",
2588
+ style: { ...MID_ROW_GOLD_STYLE, alignItems: "baseline" },
2589
+ children: [
2590
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("text-lg font-semibold tabular-nums whitespace-nowrap orderbook-mid-price", midClass), children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { width: 110, height: 20 }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2591
+ "$",
2592
+ formatNumber(midPrice, precision),
2593
+ midChangePercent == null ? null : /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("ml-2 text-sm font-semibold tabular-nums", midClass), children: [
2594
+ midChangePercent >= 0 ? "+" : "",
2595
+ midChangePercent.toFixed(2),
2596
+ "%"
2597
+ ] })
2598
+ ] }) }),
2599
+ !isLoading && /* @__PURE__ */ jsxRuntime.jsx(SpreadReadout, { spread, spreadPct, precision })
2600
+ ]
2601
+ }
2602
+ ),
2603
+ /* @__PURE__ */ jsxRuntime.jsx(
2604
+ "div",
2605
+ {
2606
+ className: cn("flex flex-col overflow-hidden", !fillHeight && "flex-shrink-0"),
2607
+ style: fillHeight ? { flex: 1, minHeight: 0 } : { height: `${sectionHeight}px` },
2608
+ children: isLoading ? Array.from({ length: effectiveLevels }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(SkeletonRow, {}, `bid-skel-${i}`)) : bidVisibleLevels.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center px-3 text-xs text-white/40", children: "No bids" }) : bidVisibleLevels.map((l, i) => /* @__PURE__ */ jsxRuntime.jsx(
2609
+ DepthRow,
2610
+ {
2611
+ side: "bid",
2612
+ price: l.price,
2613
+ amount: l.amount,
2614
+ cumDepth: bidCumDepths[i],
2615
+ depthPct: bidCumDepths[i] / bidMaxCumDepth * 100,
2616
+ precision,
2617
+ amountPrecision,
2618
+ hasUserOrder: userBidPrices.has(l.price),
2619
+ onPriceClick,
2620
+ flashOnMount: newBidPrices.has(l.price),
2621
+ fillHeight
2622
+ },
2623
+ `bid-${l.price}`
2624
+ ))
2625
+ }
2626
+ )
2627
+ ] })
2628
+ ]
2629
+ }
2630
+ )
2309
2631
  ] });
2310
2632
  }
2311
2633
  function MobileOrderbookLayout({
@@ -2432,8 +2754,7 @@ function MobileOrderbookLayout({
2432
2754
  gridTemplateColumns: "1.2fr 0.8fr",
2433
2755
  padding: "0.3rem 0",
2434
2756
  fontSize: "0.8rem",
2435
- borderTop: "1px solid rgba(255,255,255,0.1)",
2436
- borderBottom: "1px solid rgba(255,255,255,0.1)"
2757
+ ...MID_ROW_GOLD_STYLE
2437
2758
  },
2438
2759
  children: [
2439
2760
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2515,7 +2836,7 @@ function MobileDepthRow({
2515
2836
  flashOnMount
2516
2837
  }) {
2517
2838
  const isAsk = side === "ask";
2518
- const rowRef = React5__namespace.useRef(null);
2839
+ const rowRef = React9__namespace.useRef(null);
2519
2840
  useAmountChangeFlash(rowRef, amount, side, flashOnMount);
2520
2841
  return /* @__PURE__ */ jsxRuntime.jsxs(
2521
2842
  "div",
@@ -2541,16 +2862,12 @@ function MobileDepthRow({
2541
2862
  left: "2px",
2542
2863
  top: "50%",
2543
2864
  transform: "translateY(-50%)",
2544
- width: "6px",
2545
- height: "6px",
2546
- borderRadius: "50%",
2547
- backgroundColor: "#C9A227",
2548
- boxShadow: "0 0 6px rgba(201,162,39,0.7)",
2549
2865
  zIndex: 2
2550
- }
2866
+ },
2867
+ children: /* @__PURE__ */ jsxRuntime.jsx(BreadSliceGlyph, {})
2551
2868
  }
2552
2869
  ) : null,
2553
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", zIndex: 1, color: isAsk ? "#f6465d" : "#0ecb81", paddingLeft: hasUserOrder ? "10px" : void 0 }, children: [
2870
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", zIndex: 1, color: isAsk ? "#f6465d" : "#0ecb81", paddingLeft: hasUserOrder ? "14px" : void 0 }, children: [
2554
2871
  "$",
2555
2872
  formatNumber(price, precision)
2556
2873
  ] }),
@@ -2560,12 +2877,12 @@ function MobileDepthRow({
2560
2877
  {
2561
2878
  style: {
2562
2879
  position: "absolute",
2563
- top: 0,
2880
+ top: 1,
2564
2881
  left: 0,
2565
- bottom: 0,
2882
+ bottom: 1,
2566
2883
  width: `${clamp3(depthPct, 0, 100)}%`,
2567
- backgroundColor: isAsk ? "#f6465d" : "#0ecb81",
2568
- opacity: 0.1,
2884
+ backgroundImage: isAsk ? "linear-gradient(90deg, rgba(246,70,93,0.11), rgba(246,70,93,0.15))" : "linear-gradient(90deg, rgba(14,203,129,0.09), rgba(14,203,129,0.13))",
2885
+ borderRadius: "0 4px 4px 0",
2569
2886
  zIndex: 0,
2570
2887
  transition: "width 300ms ease-out"
2571
2888
  }
@@ -2578,20 +2895,20 @@ function MobileDepthRow({
2578
2895
  var isHlsManifest = (url) => /\.m3u8(\?|$)/i.test(url);
2579
2896
  var HLS_MIME = "application/vnd.apple.mpegurl";
2580
2897
  var MANIFEST_FAILURES = /* @__PURE__ */ new Set(["manifestLoadError", "manifestLoadTimeOut", "manifestParsingError"]);
2581
- var HlsVideo = React5__namespace.forwardRef(
2898
+ var HlsVideo = React9__namespace.forwardRef(
2582
2899
  ({ src, capLevelToPlayerSize = true, onError, ...videoProps }, ref) => {
2583
- const videoRef = React5__namespace.useRef(null);
2584
- React5__namespace.useImperativeHandle(ref, () => videoRef.current, []);
2900
+ const videoRef = React9__namespace.useRef(null);
2901
+ React9__namespace.useImperativeHandle(ref, () => videoRef.current, []);
2585
2902
  const primary = typeof src === "string" ? src : src.src;
2586
2903
  const fallbackSrc = typeof src === "string" ? void 0 : src.fallbackSrc;
2587
- const [failedSrc, setFailedSrc] = React5__namespace.useState(null);
2904
+ const [failedSrc, setFailedSrc] = React9__namespace.useState(null);
2588
2905
  const usingFallback = fallbackSrc != null && failedSrc === primary;
2589
2906
  const activeSrc = usingFallback ? fallbackSrc : primary;
2590
- const failPrimary = React5__namespace.useCallback(() => {
2907
+ const failPrimary = React9__namespace.useCallback(() => {
2591
2908
  if (fallbackSrc == null) return;
2592
2909
  setFailedSrc(primary);
2593
2910
  }, [fallbackSrc, primary]);
2594
- React5__namespace.useEffect(() => {
2911
+ React9__namespace.useEffect(() => {
2595
2912
  const video = videoRef.current;
2596
2913
  if (!video || !activeSrc) return;
2597
2914
  if (!isHlsManifest(activeSrc) || video.canPlayType(HLS_MIME)) {
@@ -2624,7 +2941,7 @@ var HlsVideo = React5__namespace.forwardRef(
2624
2941
  hls?.destroy();
2625
2942
  };
2626
2943
  }, [activeSrc, capLevelToPlayerSize, failPrimary]);
2627
- const handleError = React5__namespace.useCallback(
2944
+ const handleError = React9__namespace.useCallback(
2628
2945
  (e) => {
2629
2946
  if (!usingFallback) failPrimary();
2630
2947
  onError?.(e);
@@ -2635,7 +2952,7 @@ var HlsVideo = React5__namespace.forwardRef(
2635
2952
  }
2636
2953
  );
2637
2954
  HlsVideo.displayName = "HlsVideo";
2638
- var PropertyTour = React5__namespace.forwardRef(
2955
+ var PropertyTour = React9__namespace.forwardRef(
2639
2956
  ({
2640
2957
  className,
2641
2958
  title,
@@ -2648,8 +2965,8 @@ var PropertyTour = React5__namespace.forwardRef(
2648
2965
  playsInline = true,
2649
2966
  ...props
2650
2967
  }, ref) => {
2651
- const videoRef = React5__namespace.useRef(null);
2652
- React5__namespace.useEffect(() => {
2968
+ const videoRef = React9__namespace.useRef(null);
2969
+ React9__namespace.useEffect(() => {
2653
2970
  const video = videoRef.current;
2654
2971
  if (!video) return;
2655
2972
  const handleFullscreenChange = () => {
@@ -2757,8 +3074,50 @@ function isSafeUrl(url) {
2757
3074
  return false;
2758
3075
  }
2759
3076
  }
3077
+ var BREAKING_WINDOW_HOURS = 6;
3078
+ var MAX_PINNED_BREAKING = 2;
3079
+ var BREAKING_IMPACT_FLOOR = 8;
3080
+ var breakingImpact = (it) => typeof it.impactScore === "number" && Number.isFinite(it.impactScore) ? it.impactScore : BREAKING_IMPACT_FLOOR;
3081
+ var dismissedBreakingKeys = /* @__PURE__ */ new Set();
3082
+ var __resetDismissedBreaking = () => dismissedBreakingKeys.clear();
3083
+ var breakingKey = (it) => String(it.id ?? it.title);
3084
+ var BREAKING_SWIPE_MIN_PX = 80;
2760
3085
  var ITEMS_PER_PAGE = 5;
2761
3086
  var ITEMS_PER_PAGE_MOBILE = 5;
3087
+ function formatAge(publishedAt, now) {
3088
+ const elapsed = Math.max(0, now - new Date(publishedAt).getTime());
3089
+ const secs = Math.floor(elapsed / 1e3);
3090
+ if (secs < 60) return `${secs}s ago`;
3091
+ const mins = Math.floor(secs / 60);
3092
+ if (mins < 60) return `${mins}m ago`;
3093
+ return `${Math.floor(mins / 60)}h ago`;
3094
+ }
3095
+ function rowAgeLabel(publishedAt, now) {
3096
+ if (!publishedAt) return null;
3097
+ const t = new Date(publishedAt).getTime();
3098
+ if (!Number.isFinite(t)) return null;
3099
+ const ageMs = Math.max(0, now - t);
3100
+ if (ageMs >= 24 * 36e5) return null;
3101
+ return { label: formatAge(publishedAt, now), fresh: ageMs < 36e5 };
3102
+ }
3103
+ function useTickingNow(active, youngestPublishedAt) {
3104
+ const [now, setNow] = React9__namespace.useState(() => Date.now());
3105
+ React9__namespace.useEffect(() => {
3106
+ if (!active || !youngestPublishedAt) return;
3107
+ let timer;
3108
+ const schedule = () => {
3109
+ const ageSecs = (Date.now() - new Date(youngestPublishedAt).getTime()) / 1e3;
3110
+ const delay = ageSecs < 60 ? 1e3 : ageSecs < 3600 ? 15e3 : 6e4;
3111
+ timer = setTimeout(() => {
3112
+ setNow(Date.now());
3113
+ schedule();
3114
+ }, delay);
3115
+ };
3116
+ schedule();
3117
+ return () => clearTimeout(timer);
3118
+ }, [active, youngestPublishedAt]);
3119
+ return now;
3120
+ }
2762
3121
  var ensureAnimationsInjected = () => {
2763
3122
  if (typeof document === "undefined") return;
2764
3123
  if (document.getElementById("property-news-updates-animations")) return;
@@ -2781,6 +3140,110 @@ var ensureAnimationsInjected = () => {
2781
3140
  from { opacity: 0; transform: translateY(12px) scale(0.98); }
2782
3141
  to { opacity: 1; transform: translateY(0) scale(1); }
2783
3142
  }
3143
+ /* The list scrolls but never shows a scrollbar (user call 2026-08-22: the
3144
+ grey track crowded the rows). Same treatment as the rest of the app. */
3145
+ .news-list-scroll { scrollbar-width: none; -ms-overflow-style: none; }
3146
+ .news-list-scroll::-webkit-scrollbar { display: none; width: 0; height: 0; }
3147
+ .property-news-breaking-dismiss {
3148
+ opacity: 0;
3149
+ pointer-events: none;
3150
+ transform: translateY(3px);
3151
+ transition: opacity 0.16s ease, transform 0.2s cubic-bezier(0.22, 1, 0.36, 1), color 0.15s ease;
3152
+ }
3153
+ .property-news-breaking-item:hover .property-news-breaking-dismiss,
3154
+ .property-news-breaking-item:focus-within .property-news-breaking-dismiss {
3155
+ opacity: 1;
3156
+ pointer-events: auto;
3157
+ transform: translateY(0);
3158
+ }
3159
+ .property-news-breaking-dismiss:hover,
3160
+ .property-news-breaking-dismiss:focus-visible {
3161
+ color: #fff !important;
3162
+ text-decoration: underline;
3163
+ text-underline-offset: 2px;
3164
+ outline: none;
3165
+ }
3166
+ .property-news-breaking-item[data-dismissing="true"] {
3167
+ pointer-events: none;
3168
+ animation: propertyNewsBreakingDismiss 0.22s cubic-bezier(0.4, 0, 1, 1) forwards;
3169
+ }
3170
+ .property-news-breaking-item[data-new-breaking="true"] {
3171
+ position: relative;
3172
+ overflow: hidden;
3173
+ animation: propertyNewsBreakingArrive 1.18s cubic-bezier(0.16, 1, 0.3, 1) both;
3174
+ }
3175
+ .property-news-breaking-item[data-new-breaking="true"]::after {
3176
+ content: "";
3177
+ position: absolute;
3178
+ inset: 0;
3179
+ z-index: 0;
3180
+ pointer-events: none;
3181
+ background: linear-gradient(100deg, transparent 8%, rgba(var(--color-accent-rgb, 230, 200, 126), 0.42) 48%, transparent 82%);
3182
+ transform: translateX(-120%);
3183
+ animation: propertyNewsBreakingSheen 1.18s 0.2s cubic-bezier(0.22, 1, 0.36, 1) both;
3184
+ }
3185
+ .property-news-breaking-item[data-new-breaking="true"] > span {
3186
+ position: relative;
3187
+ z-index: 1;
3188
+ }
3189
+ .property-news-breaking-item[data-new-breaking="true"][data-dismissing="true"] {
3190
+ animation: propertyNewsBreakingDismiss 0.22s cubic-bezier(0.4, 0, 1, 1) forwards;
3191
+ }
3192
+ @keyframes propertyNewsBreakingArrive {
3193
+ 0% { opacity: 0; transform: translateY(-14px) scale(0.985); box-shadow: 0 0 0 rgba(var(--color-accent-rgb, 230, 200, 126), 0); filter: brightness(1); }
3194
+ 24% { opacity: 1; transform: translateY(0) scale(1); box-shadow: 0 0 26px rgba(var(--color-accent-rgb, 230, 200, 126), 0.38); filter: brightness(1.16); }
3195
+ 58% { box-shadow: 0 0 16px rgba(var(--color-accent-rgb, 230, 200, 126), 0.22); filter: brightness(1.06); }
3196
+ 100% { opacity: 1; transform: translateY(0) scale(1); box-shadow: 0 0 0 rgba(var(--color-accent-rgb, 230, 200, 126), 0); }
3197
+ }
3198
+ @keyframes propertyNewsBreakingSheen {
3199
+ from { transform: translateX(-120%); }
3200
+ to { transform: translateX(120%); }
3201
+ }
3202
+ @keyframes propertyNewsBreakingDismiss {
3203
+ from { opacity: 1; transform: translateX(0); max-height: 96px; }
3204
+ to { opacity: 0; transform: translateX(24px); max-height: 0; padding-top: 0; padding-bottom: 0; }
3205
+ }
3206
+ @media (prefers-reduced-motion: reduce) {
3207
+ .property-news-breaking-dismiss { transition: none; }
3208
+ .property-news-breaking-item[data-new-breaking="true"],
3209
+ .property-news-breaking-item[data-new-breaking="true"]::after { animation-duration: 1ms; animation-delay: 0ms; }
3210
+ .property-news-breaking-item[data-dismissing="true"] { animation-duration: 1ms; }
3211
+ }
3212
+ /* Asset badge: the wrapper hosts the INSTANT CSS tooltip (native title is
3213
+ too slow) and the hover affordance that says "the icon itself is the
3214
+ target" \u2014 the box scales up with a gold ring while the tooltip shows
3215
+ "Name (TICKER)" immediately. */
3216
+ .news-badge-wrap { position: relative; display: inline-flex; flex-shrink: 0; }
3217
+ .news-badge-wrap > .news-badge-box { transition: transform 0.15s ease, box-shadow 0.15s ease; }
3218
+ .news-badge-wrap:hover > .news-badge-box {
3219
+ transform: scale(1.08);
3220
+ box-shadow: 0 0 0 1px rgba(var(--color-accent-rgb, 230, 200, 126), 0.7);
3221
+ }
3222
+ /* Instant element tooltip (an ::after can't carry a coloured % change).
3223
+ PORTALED to document.body with position:fixed \u2014 as an absolute child it
3224
+ was clipped by the scrolling news list at the top/right edges. Anchored
3225
+ above the badge by default (translateY(-100%)), flipped below via
3226
+ data-tip-below when the badge sits too close to the viewport top. */
3227
+ .news-badge-tipbox {
3228
+ position: fixed;
3229
+ display: flex;
3230
+ transform: translate(-50%, -100%);
3231
+ flex-direction: column;
3232
+ align-items: center;
3233
+ gap: 2px;
3234
+ background: #1a1d24;
3235
+ color: #fff;
3236
+ font-size: 0.62rem;
3237
+ font-weight: 600;
3238
+ line-height: 1.2;
3239
+ padding: 0.32rem 0.55rem;
3240
+ border-radius: 5px;
3241
+ border: 1px solid rgba(255, 255, 255, 0.12);
3242
+ white-space: nowrap;
3243
+ z-index: 10060;
3244
+ pointer-events: none;
3245
+ }
3246
+ .news-badge-tipbox[data-tip-below="true"] { transform: translate(-50%, 0); }
2784
3247
  `;
2785
3248
  document.head.appendChild(style);
2786
3249
  };
@@ -2817,7 +3280,8 @@ var formatDateShort = (value) => {
2817
3280
  if (diffS < 3600) return `${Math.floor(diffS / 60)}m ago`;
2818
3281
  if (diffS < 86400) return `${Math.floor(diffS / 3600)}h ago`;
2819
3282
  const time = d.toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit", hour12: true });
2820
- return `${time} \xB7 ${d.toLocaleDateString(void 0, { month: "short", day: "numeric" })}`;
3283
+ const date = d.toLocaleDateString(void 0, { month: "short", day: "numeric" });
3284
+ return `${date} \xB7 ${time}`;
2821
3285
  };
2822
3286
  var formatTimeAgo = (timestamp) => {
2823
3287
  const diff = (Date.now() - new Date(timestamp).getTime()) / 1e3;
@@ -2831,10 +3295,66 @@ function getSentimentInfo(score) {
2831
3295
  if (score < -0.15) return { arrow: "\u25BC", label: "Bearish", color: "#f6465d" };
2832
3296
  return { arrow: "", label: "Neutral", color: "#848e9c" };
2833
3297
  }
3298
+ function SentimentBar({ score, color }) {
3299
+ const clamped = Math.max(-1, Math.min(1, score));
3300
+ const widthPct = Math.abs(clamped) * 50;
3301
+ const direction = clamped > 0 ? "positive" : clamped < 0 ? "negative" : "neutral";
3302
+ const word = clamped > 0.15 ? "bullish" : clamped < -0.15 ? "bearish" : "neutral";
3303
+ const signed = `${clamped >= 0 ? "+" : "\u2212"}${Math.abs(clamped).toFixed(2)}`;
3304
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3305
+ "span",
3306
+ {
3307
+ "data-testid": "sentiment-bar",
3308
+ "data-score": String(score),
3309
+ role: "img",
3310
+ "aria-label": `Sentiment: ${word}, ${signed} of +1`,
3311
+ style: {
3312
+ position: "relative",
3313
+ display: "inline-block",
3314
+ width: "36px",
3315
+ height: "3px",
3316
+ borderRadius: "2px",
3317
+ background: "rgba(255,255,255,0.10)",
3318
+ flexShrink: 0
3319
+ },
3320
+ children: [
3321
+ /* @__PURE__ */ jsxRuntime.jsx(
3322
+ "span",
3323
+ {
3324
+ style: {
3325
+ position: "absolute",
3326
+ left: "50%",
3327
+ top: "-1px",
3328
+ width: "1px",
3329
+ height: "5px",
3330
+ background: "rgba(255,255,255,0.28)"
3331
+ }
3332
+ }
3333
+ ),
3334
+ /* @__PURE__ */ jsxRuntime.jsx(
3335
+ "span",
3336
+ {
3337
+ "data-testid": "sentiment-fill",
3338
+ "data-direction": direction,
3339
+ style: {
3340
+ position: "absolute",
3341
+ top: 0,
3342
+ height: "100%",
3343
+ borderRadius: "2px",
3344
+ background: color,
3345
+ width: `${widthPct}%`,
3346
+ left: `${clamped >= 0 ? 50 : 50 - widthPct}%`
3347
+ }
3348
+ }
3349
+ )
3350
+ ]
3351
+ }
3352
+ );
3353
+ }
2834
3354
  function NewsArticleModal({ item, onClose }) {
2835
3355
  const sentimentInfo = getSentimentInfo(item.sentimentScore);
2836
3356
  const catStyle = categoryStyles[item.type] ?? categoryStyles.market;
2837
- React5__namespace.useEffect(() => {
3357
+ React9__namespace.useEffect(() => {
2838
3358
  const handler = (e) => {
2839
3359
  if (e.key === "Escape") onClose();
2840
3360
  };
@@ -2895,7 +3415,7 @@ function NewsArticleModal({ item, onClose }) {
2895
3415
  children: /* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.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" }) })
2896
3416
  }
2897
3417
  ) }),
2898
- sentimentInfo && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginBottom: "0.6rem" }, children: /* @__PURE__ */ jsxRuntime.jsxs(
3418
+ sentimentInfo && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginBottom: "0.6rem" }, children: /* @__PURE__ */ jsxRuntime.jsx(
2899
3419
  "span",
2900
3420
  {
2901
3421
  style: {
@@ -2910,10 +3430,11 @@ function NewsArticleModal({ item, onClose }) {
2910
3430
  background: `${sentimentInfo.color}1a`,
2911
3431
  border: `1px solid ${sentimentInfo.color}40`
2912
3432
  },
2913
- children: [
3433
+ children: /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.3rem" }, children: [
2914
3434
  sentimentInfo.arrow ? `${sentimentInfo.arrow} ` : "",
2915
- sentimentInfo.label
2916
- ]
3435
+ sentimentInfo.label,
3436
+ item.sentimentScore != null && /* @__PURE__ */ jsxRuntime.jsx(SentimentBar, { score: item.sentimentScore, color: sentimentInfo.color })
3437
+ ] })
2917
3438
  }
2918
3439
  ) }),
2919
3440
  /* @__PURE__ */ jsxRuntime.jsx("h2", { style: { fontSize: "1rem", fontWeight: 600, color: "#fff", lineHeight: 1.5, margin: "0 0 1rem" }, children: item.title }),
@@ -2928,7 +3449,13 @@ function NewsArticleModal({ item, onClose }) {
2928
3449
  fontSize: "0.65rem",
2929
3450
  fontWeight: 500,
2930
3451
  background: "rgba(255,255,255,0.07)",
2931
- color: "#848e9c"
3452
+ color: "#848e9c",
3453
+ // Truncation guard moved here with the source itself — a long
3454
+ // publication name would otherwise stretch the meta row.
3455
+ maxWidth: "120px",
3456
+ overflow: "hidden",
3457
+ textOverflow: "ellipsis",
3458
+ whiteSpace: "nowrap"
2932
3459
  },
2933
3460
  children: item.source
2934
3461
  }
@@ -2990,7 +3517,7 @@ function NewsArticleModal({ item, onClose }) {
2990
3517
  }
2991
3518
  return modal;
2992
3519
  }
2993
- var PropertyNewsUpdates = React5__namespace.forwardRef(
3520
+ var PropertyNewsUpdates = React9__namespace.forwardRef(
2994
3521
  ({
2995
3522
  className,
2996
3523
  heading,
@@ -3004,49 +3531,120 @@ var PropertyNewsUpdates = React5__namespace.forwardRef(
3004
3531
  viewAllLabel = "View All News",
3005
3532
  connectionStatus = "live",
3006
3533
  disablePagination = false,
3534
+ hideHeading = false,
3535
+ hideCategoryTabs = false,
3536
+ category,
3537
+ onCategoryChange,
3538
+ pinBreaking = false,
3539
+ onAssetOpen,
3540
+ showAssetBadges = true,
3007
3541
  ...props
3008
3542
  }, ref) => {
3009
3543
  const isPurchaseVariant = variant === "purchases";
3010
3544
  const isHomeVariant = variant === "home";
3011
3545
  const resolvedHeading = heading ?? (isPurchaseVariant ? "Live Purchases" : "Property News & Headlines");
3012
- const [homeTab, setHomeTab] = React5__namespace.useState("all");
3013
- const [homePage, setHomePage] = React5__namespace.useState(0);
3546
+ const headerTrailing = Boolean(isHomeVariant ? viewAllHref : !isPurchaseVariant);
3547
+ const [homeTabInternal, setHomeTabInternal] = React9__namespace.useState("all");
3548
+ const homeTab = category ?? homeTabInternal;
3549
+ const setHomeTab = React9__namespace.useCallback(
3550
+ (next) => {
3551
+ setHomeTabInternal(next);
3552
+ onCategoryChange?.(next);
3553
+ },
3554
+ [onCategoryChange]
3555
+ );
3556
+ const [homePage, setHomePage] = React9__namespace.useState(0);
3014
3557
  const purchaseItems = purchasesProp ?? [];
3015
- const [page, setPage] = React5__namespace.useState(0);
3016
- const [selectedItem, setSelectedItem] = React5__namespace.useState(null);
3017
- const [isMobile, setIsMobile] = React5__namespace.useState(
3558
+ const [page, setPage] = React9__namespace.useState(0);
3559
+ const [selectedItem, setSelectedItem] = React9__namespace.useState(null);
3560
+ const [isMobile, setIsMobile] = React9__namespace.useState(
3018
3561
  () => typeof window !== "undefined" && window.innerWidth <= 768
3019
3562
  );
3020
- React5__namespace.useEffect(() => {
3563
+ React9__namespace.useEffect(() => {
3021
3564
  const check = () => setIsMobile(window.innerWidth <= 768);
3022
3565
  window.addEventListener("resize", check);
3023
3566
  return () => window.removeEventListener("resize", check);
3024
3567
  }, []);
3025
3568
  const homePageSize = isMobile ? ITEMS_PER_PAGE_MOBILE : ITEMS_PER_PAGE;
3026
- React5__namespace.useEffect(() => {
3569
+ React9__namespace.useEffect(() => {
3027
3570
  ensureAnimationsInjected();
3028
3571
  }, []);
3029
- React5__namespace.useEffect(() => {
3572
+ React9__namespace.useEffect(() => {
3030
3573
  setHomePage(0);
3031
3574
  }, [homeTab]);
3032
- const hasItems = Array.isArray(items) && items.length > 0;
3033
- const totalPages = React5__namespace.useMemo(
3034
- () => hasItems ? Math.max(1, Math.ceil(items.length / ITEMS_PER_PAGE)) : 1,
3035
- [hasItems, items.length]
3575
+ const breakingCandidates = React9__namespace.useMemo(() => {
3576
+ if (!pinBreaking || !Array.isArray(items)) return [];
3577
+ return items.filter((it) => it.breaking && it.publishedAt && Number.isFinite(new Date(it.publishedAt).getTime())).sort((a, b) => new Date(b.publishedAt).getTime() - new Date(a.publishedAt).getTime());
3578
+ }, [pinBreaking, items]);
3579
+ const breakingNow = useTickingNow(
3580
+ breakingCandidates.length > 0,
3581
+ breakingCandidates[0]?.publishedAt ?? null
3036
3582
  );
3037
- React5__namespace.useEffect(() => {
3583
+ const breakingEligible = React9__namespace.useMemo(() => {
3584
+ const cutoff = breakingNow - BREAKING_WINDOW_HOURS * 36e5;
3585
+ return breakingCandidates.filter((it) => {
3586
+ if (new Date(it.publishedAt).getTime() < cutoff) return false;
3587
+ return homeTab === "all" || it.type === homeTab;
3588
+ });
3589
+ }, [breakingCandidates, breakingNow, homeTab]);
3590
+ const [dismissedVersion, setDismissedVersion] = React9__namespace.useState(0);
3591
+ const [dismissingBreakingKey, setDismissingBreakingKey] = React9__namespace.useState(null);
3592
+ const [breakingSwipe, setBreakingSwipe] = React9__namespace.useState(null);
3593
+ const breakingSwipeStartXRef = React9__namespace.useRef(0);
3594
+ const suppressBreakingClickRef = React9__namespace.useRef(false);
3595
+ const breakingSwipeActiveKeyRef = React9__namespace.useRef(null);
3596
+ const breakingSwipeMaxRef = React9__namespace.useRef(0);
3597
+ const [badgeTip, setBadgeTip] = React9__namespace.useState(null);
3598
+ const BADGE_TIP_GAP = 7;
3599
+ const BADGE_TIP_FLIP_PX = 56;
3600
+ const showBadgeTip = (el, asset) => {
3601
+ const rect = el.getBoundingClientRect();
3602
+ const below = rect.top < BADGE_TIP_FLIP_PX;
3603
+ setBadgeTip({
3604
+ asset,
3605
+ left: rect.left + rect.width / 2,
3606
+ top: below ? rect.bottom + BADGE_TIP_GAP : rect.top - BADGE_TIP_GAP,
3607
+ below
3608
+ });
3609
+ };
3610
+ const pinnedBreaking = React9__namespace.useMemo(() => {
3611
+ return breakingEligible.filter((it) => !dismissedBreakingKeys.has(breakingKey(it))).sort((a, b) => breakingImpact(b) - breakingImpact(a)).slice(0, MAX_PINNED_BREAKING);
3612
+ }, [breakingEligible, dismissedVersion]);
3613
+ const breakingRowKeys = React9__namespace.useMemo(
3614
+ () => new Set(breakingEligible.map(breakingKey)),
3615
+ [breakingEligible]
3616
+ );
3617
+ const listItems = React9__namespace.useMemo(() => {
3618
+ if (pinnedBreaking.length === 0) return items;
3619
+ const pinned = new Set(pinnedBreaking);
3620
+ return items.filter((it) => !pinned.has(it));
3621
+ }, [items, pinnedBreaking]);
3622
+ const hasItems = Array.isArray(listItems) && listItems.length > 0;
3623
+ const totalPages = React9__namespace.useMemo(
3624
+ () => hasItems ? Math.max(1, Math.ceil(listItems.length / ITEMS_PER_PAGE)) : 1,
3625
+ [hasItems, listItems.length]
3626
+ );
3627
+ React9__namespace.useEffect(() => {
3038
3628
  setPage((prev) => Math.min(prev, totalPages - 1));
3039
3629
  }, [totalPages]);
3040
- const paginatedItems = hasItems ? items.slice(page * ITEMS_PER_PAGE, page * ITEMS_PER_PAGE + ITEMS_PER_PAGE) : [];
3041
- const homeFilteredItems = React5__namespace.useMemo(
3042
- () => hasItems ? items.filter((it) => homeTab === "all" || it.type === homeTab) : [],
3043
- [hasItems, items, homeTab]
3630
+ const paginatedItems = hasItems ? listItems.slice(page * ITEMS_PER_PAGE, page * ITEMS_PER_PAGE + ITEMS_PER_PAGE) : [];
3631
+ const homeFilteredItems = React9__namespace.useMemo(
3632
+ () => hasItems ? listItems.filter((it) => homeTab === "all" || it.type === homeTab) : [],
3633
+ [hasItems, listItems, homeTab]
3044
3634
  );
3045
- const homeTotalPages = React5__namespace.useMemo(
3635
+ const homeTotalPages = React9__namespace.useMemo(
3046
3636
  () => Math.max(1, Math.ceil(homeFilteredItems.length / homePageSize)),
3047
3637
  [homeFilteredItems.length, homePageSize]
3048
3638
  );
3049
3639
  const homePaginatedItems = disablePagination ? homeFilteredItems : homeFilteredItems.slice(homePage * homePageSize, (homePage + 1) * homePageSize);
3640
+ const youngestRowPublishedAt = React9__namespace.useMemo(() => {
3641
+ let youngest = null;
3642
+ for (const it of homePaginatedItems) {
3643
+ if (it.publishedAt && (!youngest || it.publishedAt > youngest)) youngest = it.publishedAt;
3644
+ }
3645
+ return youngest;
3646
+ }, [homePaginatedItems]);
3647
+ const rowNow = useTickingNow(isHomeVariant && youngestRowPublishedAt != null, youngestRowPublishedAt);
3050
3648
  return /* @__PURE__ */ jsxRuntime.jsxs(
3051
3649
  "div",
3052
3650
  {
@@ -3057,8 +3655,8 @@ var PropertyNewsUpdates = React5__namespace.forwardRef(
3057
3655
  ),
3058
3656
  ...props,
3059
3657
  children: [
3060
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", style: isHomeVariant ? { marginBottom: "0.5rem", minHeight: isMobile ? "32px" : "24px", gap: isMobile ? "0.5rem" : "1rem" } : void 0, children: [
3061
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
3658
+ (!hideHeading || headerTrailing) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", style: isHomeVariant ? { marginBottom: "0.5rem", minHeight: isMobile ? "32px" : "24px", gap: isMobile ? "0.5rem" : "1rem", justifyContent: hideHeading ? "flex-end" : void 0 } : hideHeading ? { justifyContent: "flex-end" } : void 0, children: [
3659
+ !hideHeading && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
3062
3660
  isHomeVariant && /* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: isMobile ? 14 : 16, height: isMobile ? 14 : 16, viewBox: "0 0 24 24", fill: "currentColor", style: { color: "var(--color-text, #fff)", flexShrink: 0, display: "block" }, children: /* @__PURE__ */ jsxRuntime.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-2zm-5 14H4v-4h11v4zm0-5H4V9h11v4zm5 5h-4V9h4v9z" }) }),
3063
3661
  isPurchaseVariant && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { display: "inline-block", width: "8px", height: "8px", borderRadius: "50%", backgroundColor: "#0ecb81", boxShadow: "0 0 8px rgba(14,203,129,0.8)", animation: "propertyNewsPulse 1.5s infinite" } }),
3064
3662
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
@@ -3077,7 +3675,7 @@ var PropertyNewsUpdates = React5__namespace.forwardRef(
3077
3675
  "LIVE"
3078
3676
  ] }) : null
3079
3677
  ] }),
3080
- isHomeVariant && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", gap: isMobile ? "1rem" : "1.5rem", marginTop: "0.25rem", marginBottom: "1rem", borderBottom: "1px solid rgba(255, 255, 255, 0.08)", paddingBottom: "0.5rem" }, children: ["all", "property", "market"].map((tab) => /* @__PURE__ */ jsxRuntime.jsx(
3678
+ isHomeVariant && !hideCategoryTabs && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", gap: isMobile ? "1rem" : "1.5rem", marginTop: "0.25rem", marginBottom: "1rem", borderBottom: "1px solid rgba(255, 255, 255, 0.08)", paddingBottom: "0.5rem" }, children: ["all", "property", "market"].map((tab) => /* @__PURE__ */ jsxRuntime.jsx(
3081
3679
  "button",
3082
3680
  {
3083
3681
  type: "button",
@@ -3098,10 +3696,229 @@ var PropertyNewsUpdates = React5__namespace.forwardRef(
3098
3696
  },
3099
3697
  tab
3100
3698
  )) }),
3699
+ pinnedBreaking.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
3700
+ "div",
3701
+ {
3702
+ "data-testid": "news-breaking",
3703
+ style: {
3704
+ display: "flex",
3705
+ flexDirection: "column",
3706
+ marginBottom: "0.5rem",
3707
+ // Clips the row while it's being swiped out to the right. The gold
3708
+ // band styling lives on each ITEM below, not here — the whole band
3709
+ // (rule, wash, hairlines) must travel with a swipe, not just the
3710
+ // text inside it (user bug report, 2026-08-20).
3711
+ overflow: "hidden"
3712
+ },
3713
+ children: pinnedBreaking.map((item, i) => {
3714
+ const sentiment = getSentimentInfo(item.sentimentScore);
3715
+ const k = breakingKey(item);
3716
+ const dx = breakingSwipe?.key === k ? breakingSwipe.dx : 0;
3717
+ const finishDismiss = () => {
3718
+ dismissedBreakingKeys.add(k);
3719
+ setDismissingBreakingKey(null);
3720
+ setDismissedVersion((v) => v + 1);
3721
+ };
3722
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3723
+ "button",
3724
+ {
3725
+ type: "button",
3726
+ className: "property-news-breaking-item",
3727
+ "data-new-breaking": item.isNew ? "true" : void 0,
3728
+ "data-dismissing": dismissingBreakingKey === k ? "true" : void 0,
3729
+ "data-testid": "news-breaking-item",
3730
+ onAnimationEnd: (e) => {
3731
+ if (e.animationName === "propertyNewsBreakingDismiss" && dismissingBreakingKey === k) {
3732
+ finishDismiss();
3733
+ }
3734
+ },
3735
+ onClick: () => {
3736
+ if (suppressBreakingClickRef.current) {
3737
+ suppressBreakingClickRef.current = false;
3738
+ return;
3739
+ }
3740
+ setSelectedItem(item);
3741
+ },
3742
+ onPointerDown: (e) => {
3743
+ breakingSwipeStartXRef.current = e.clientX;
3744
+ breakingSwipeActiveKeyRef.current = k;
3745
+ breakingSwipeMaxRef.current = 0;
3746
+ setBreakingSwipe({ key: k, dx: 0 });
3747
+ try {
3748
+ e.currentTarget.setPointerCapture?.(e.pointerId);
3749
+ } catch {
3750
+ }
3751
+ },
3752
+ onPointerMove: (e) => {
3753
+ if (breakingSwipeActiveKeyRef.current !== k) return;
3754
+ const delta = e.clientX - breakingSwipeStartXRef.current;
3755
+ breakingSwipeMaxRef.current = Math.max(breakingSwipeMaxRef.current, Math.abs(delta));
3756
+ setBreakingSwipe({ key: k, dx: Math.max(0, delta) });
3757
+ },
3758
+ onPointerUp: (e) => {
3759
+ if (breakingSwipeActiveKeyRef.current !== k) return;
3760
+ breakingSwipeActiveKeyRef.current = null;
3761
+ if (breakingSwipeMaxRef.current > 6) suppressBreakingClickRef.current = true;
3762
+ setBreakingSwipe(null);
3763
+ const drag = Math.max(0, e.clientX - breakingSwipeStartXRef.current);
3764
+ const width = e.currentTarget.offsetWidth || 0;
3765
+ if (drag >= Math.max(BREAKING_SWIPE_MIN_PX, width * 0.3)) {
3766
+ dismissedBreakingKeys.add(k);
3767
+ setDismissedVersion((v) => v + 1);
3768
+ }
3769
+ },
3770
+ onPointerCancel: () => {
3771
+ breakingSwipeActiveKeyRef.current = null;
3772
+ setBreakingSwipe(null);
3773
+ },
3774
+ style: {
3775
+ display: "block",
3776
+ width: "100%",
3777
+ padding: "0.4rem 0 0.5rem 0.6rem",
3778
+ textAlign: "left",
3779
+ background: "none",
3780
+ border: "none",
3781
+ cursor: "pointer",
3782
+ minWidth: 0,
3783
+ // Highlighted on purpose (user call, 2026-08-20): BREAKING is
3784
+ // the one thing on this page allowed to shout. The gold rule,
3785
+ // hairlines and wash live HERE on the item — the whole band
3786
+ // travels when the item is swiped away.
3787
+ borderLeft: "3px solid var(--color-accent, #E6C87E)",
3788
+ borderTop: "1px solid rgba(var(--color-accent-rgb, 230, 200, 126), 0.28)",
3789
+ borderBottom: "1px solid rgba(var(--color-accent-rgb, 230, 200, 126), 0.28)",
3790
+ backgroundImage: "linear-gradient(90deg, rgba(var(--color-accent-rgb, 230, 200, 126), 0.16) 0%, rgba(var(--color-accent-rgb, 230, 200, 126), 0.06) 45%, rgba(var(--color-accent-rgb, 230, 200, 126), 0.02) 100%)",
3791
+ // Horizontal drags are ours; vertical stays with the scroll.
3792
+ touchAction: "pan-y",
3793
+ transform: dx > 0 ? `translateX(${dx}px)` : void 0,
3794
+ opacity: dx > 0 ? Math.max(0.3, 1 - dx / 320) : 1,
3795
+ transition: dx > 0 ? "none" : "transform 0.15s ease, opacity 0.15s ease"
3796
+ },
3797
+ children: [
3798
+ /* @__PURE__ */ jsxRuntime.jsx(
3799
+ "span",
3800
+ {
3801
+ "data-testid": "news-breaking-title",
3802
+ style: {
3803
+ display: "-webkit-box",
3804
+ WebkitBoxOrient: "vertical",
3805
+ WebkitLineClamp: 2,
3806
+ fontSize: "0.8rem",
3807
+ fontWeight: 600,
3808
+ color: "#fff",
3809
+ lineHeight: 1.35,
3810
+ overflow: "hidden",
3811
+ overflowWrap: "anywhere"
3812
+ },
3813
+ children: item.title
3814
+ }
3815
+ ),
3816
+ /* @__PURE__ */ jsxRuntime.jsxs(
3817
+ "span",
3818
+ {
3819
+ style: {
3820
+ display: "flex",
3821
+ alignItems: "center",
3822
+ gap: "0.45rem",
3823
+ marginTop: "0.2rem"
3824
+ },
3825
+ children: [
3826
+ (() => {
3827
+ const age = rowAgeLabel(item.publishedAt, breakingNow);
3828
+ return age ? /* @__PURE__ */ jsxRuntime.jsx(
3829
+ "span",
3830
+ {
3831
+ "data-age-fresh": age.fresh ? "true" : void 0,
3832
+ style: {
3833
+ fontSize: "0.64rem",
3834
+ fontWeight: 600,
3835
+ fontVariantNumeric: "tabular-nums",
3836
+ color: age.fresh ? "var(--color-accent, #E6C87E)" : "rgba(255,255,255,0.65)"
3837
+ },
3838
+ children: age.label
3839
+ }
3840
+ ) : null;
3841
+ })(),
3842
+ /* @__PURE__ */ jsxRuntime.jsx(
3843
+ "span",
3844
+ {
3845
+ "data-testid": "breaking-tag",
3846
+ style: {
3847
+ fontSize: "0.55rem",
3848
+ fontWeight: 700,
3849
+ letterSpacing: "0.09em",
3850
+ color: "#0a0b0e",
3851
+ background: "var(--color-accent, #E6C87E)",
3852
+ padding: "0.16rem 0.34rem 0.13rem",
3853
+ borderRadius: "3px",
3854
+ lineHeight: 1
3855
+ },
3856
+ children: "BREAKING"
3857
+ }
3858
+ ),
3859
+ sentiment ? /* @__PURE__ */ jsxRuntime.jsxs(
3860
+ "span",
3861
+ {
3862
+ style: {
3863
+ display: "inline-flex",
3864
+ alignItems: "center",
3865
+ gap: "0.3rem",
3866
+ fontSize: "0.58rem",
3867
+ fontWeight: 600,
3868
+ color: sentiment.color
3869
+ },
3870
+ children: [
3871
+ sentiment.arrow ? `${sentiment.arrow} ` : "",
3872
+ sentiment.label,
3873
+ item.sentimentScore != null && /* @__PURE__ */ jsxRuntime.jsx(SentimentBar, { score: item.sentimentScore, color: sentiment.color })
3874
+ ]
3875
+ }
3876
+ ) : null,
3877
+ /* @__PURE__ */ jsxRuntime.jsx(
3878
+ "span",
3879
+ {
3880
+ className: "property-news-breaking-dismiss",
3881
+ "data-testid": "breaking-dismiss",
3882
+ role: "button",
3883
+ tabIndex: 0,
3884
+ "aria-label": `Dismiss breaking story: ${item.title}`,
3885
+ onPointerDown: (e) => e.stopPropagation(),
3886
+ onClick: (e) => {
3887
+ e.stopPropagation();
3888
+ setDismissingBreakingKey(k);
3889
+ },
3890
+ onKeyDown: (e) => {
3891
+ if (e.key !== "Enter" && e.key !== " ") return;
3892
+ e.preventDefault();
3893
+ e.stopPropagation();
3894
+ setDismissingBreakingKey(k);
3895
+ },
3896
+ style: {
3897
+ marginLeft: "auto",
3898
+ padding: "0.2rem 0.35rem 0.2rem 0.5rem",
3899
+ fontSize: "0.58rem",
3900
+ fontWeight: 600,
3901
+ color: "rgba(255,255,255,0.58)",
3902
+ cursor: "pointer",
3903
+ whiteSpace: "nowrap"
3904
+ },
3905
+ children: "Dismiss"
3906
+ }
3907
+ )
3908
+ ]
3909
+ }
3910
+ )
3911
+ ]
3912
+ },
3913
+ item.id ?? `breaking-${i}`
3914
+ );
3915
+ })
3916
+ }
3917
+ ),
3101
3918
  /* @__PURE__ */ jsxRuntime.jsx(
3102
3919
  "div",
3103
3920
  {
3104
- className: cn("flex flex-1 flex-col overflow-y-auto overflow-x-hidden", isHomeVariant ? "gap-0" : "mt-4 gap-3"),
3921
+ className: cn("news-list-scroll flex flex-1 flex-col overflow-y-auto overflow-x-hidden", isHomeVariant ? "gap-0" : "mt-4 gap-3"),
3105
3922
  style: !isPurchaseVariant && !isHomeVariant ? { minHeight: `${ITEMS_PER_PAGE * 86}px` } : void 0,
3106
3923
  children: isPurchaseVariant ? purchaseItems.length > 0 ? purchaseItems.slice(0, 7).map((purchase, index) => {
3107
3924
  const maxAmount = 6e4;
@@ -3132,10 +3949,12 @@ var PropertyNewsUpdates = React5__namespace.forwardRef(
3132
3949
  const key = item.displayId ?? item.id ?? `${item.title}-${index}`;
3133
3950
  const isProperty = item.type === "property";
3134
3951
  const sentimentInfo = getSentimentInfo(item.sentimentScore);
3952
+ const isBreakingRow = pinBreaking && Boolean(item.breaking) && breakingRowKeys.has(breakingKey(item));
3135
3953
  return /* @__PURE__ */ jsxRuntime.jsxs(
3136
3954
  "button",
3137
3955
  {
3138
3956
  type: "button",
3957
+ "data-breaking-row": isBreakingRow ? "true" : void 0,
3139
3958
  onClick: () => setSelectedItem(item),
3140
3959
  style: {
3141
3960
  width: "100%",
@@ -3151,6 +3970,10 @@ var PropertyNewsUpdates = React5__namespace.forwardRef(
3151
3970
  gap: isMobile ? "0.25rem" : "0.5rem",
3152
3971
  color: "inherit",
3153
3972
  transition: "background-color 0.15s"
3973
+ // No gold wash or left edge on a dismissed row: once swiped
3974
+ // out of the pin it is back in the ordinary feed, and the
3975
+ // BREAKING badge below is enough to find it again. Only the
3976
+ // PINNED strip keeps the gold treatment.
3154
3977
  },
3155
3978
  onMouseEnter: (e) => {
3156
3979
  e.currentTarget.style.backgroundColor = "rgba(255,255,255,0.04)";
@@ -3164,15 +3987,91 @@ var PropertyNewsUpdates = React5__namespace.forwardRef(
3164
3987
  },
3165
3988
  children: [
3166
3989
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1 }, children: [
3167
- /* @__PURE__ */ jsxRuntime.jsx("h3", { style: { fontSize: isMobile ? "0.88rem" : "0.95rem", fontWeight: 500, marginBottom: isMobile ? "0.25rem" : "0.35rem", color: "#f8f9fa", lineHeight: isMobile ? 1.35 : 1.4, transition: "color 0.15s" }, children: item.title }),
3990
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { style: { fontSize: "0.8rem", fontWeight: 500, marginBottom: isMobile ? "0.2rem" : "0.3rem", color: "#f8f9fa", lineHeight: 1.35, transition: "color 0.15s" }, children: item.title }),
3168
3991
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: isMobile ? "0.35rem" : "0.5rem", alignItems: "center", flexWrap: "wrap" }, children: [
3169
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#848e9c", fontSize: isMobile ? "0.55rem" : "0.7rem" }, children: formatDateShort(item.date) }),
3170
- item.source && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { padding: isMobile ? "0.08rem 0.35rem" : "0.1rem 0.4rem", borderRadius: "3px", fontSize: isMobile ? "0.52rem" : "0.62rem", fontWeight: 500, backgroundColor: "rgba(255,255,255,0.07)", color: "#848e9c", whiteSpace: "nowrap" }, children: item.source }),
3992
+ isBreakingRow && /* @__PURE__ */ jsxRuntime.jsx(
3993
+ "span",
3994
+ {
3995
+ "data-testid": "breaking-tag",
3996
+ style: {
3997
+ fontSize: "0.55rem",
3998
+ fontWeight: 700,
3999
+ letterSpacing: "0.09em",
4000
+ color: "#0a0b0e",
4001
+ background: "var(--color-accent, #E6C87E)",
4002
+ padding: "0.16rem 0.34rem 0.13rem",
4003
+ borderRadius: "3px",
4004
+ lineHeight: 1
4005
+ },
4006
+ children: "BREAKING"
4007
+ }
4008
+ ),
4009
+ (() => {
4010
+ const age = rowAgeLabel(item.publishedAt, rowNow);
4011
+ return age ? /* @__PURE__ */ jsxRuntime.jsx(
4012
+ "span",
4013
+ {
4014
+ "data-testid": "news-age",
4015
+ "data-age-fresh": age.fresh ? "true" : void 0,
4016
+ style: {
4017
+ // Sized against the 0.8rem row title (matches the
4018
+ // breaking strip's meta scale).
4019
+ fontSize: isMobile ? "0.58rem" : "0.64rem",
4020
+ fontWeight: 600,
4021
+ fontVariantNumeric: "tabular-nums",
4022
+ color: age.fresh ? "var(--color-accent, #E6C87E)" : "rgba(255,255,255,0.65)"
4023
+ },
4024
+ children: age.label
4025
+ }
4026
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#848e9c", fontSize: isMobile ? "0.55rem" : "0.7rem" }, children: formatDateShort(item.date) });
4027
+ })(),
4028
+ showAssetBadges && isProperty && (item.assets ?? (item.asset ? [item.asset] : [])).map((badgeAsset) => /* @__PURE__ */ jsxRuntime.jsx(
4029
+ "span",
4030
+ {
4031
+ className: "news-badge-wrap",
4032
+ onClick: onAssetOpen ? (e) => {
4033
+ e.stopPropagation();
4034
+ e.preventDefault();
4035
+ onAssetOpen(badgeAsset);
4036
+ } : void 0,
4037
+ onMouseEnter: (e) => showBadgeTip(e.currentTarget, badgeAsset),
4038
+ onMouseLeave: () => setBadgeTip(null),
4039
+ style: { cursor: onAssetOpen ? "pointer" : void 0 },
4040
+ children: /* @__PURE__ */ jsxRuntime.jsx(
4041
+ "span",
4042
+ {
4043
+ className: "news-badge-box",
4044
+ "data-testid": "news-asset-badge",
4045
+ style: {
4046
+ display: "inline-flex",
4047
+ alignItems: "center",
4048
+ justifyContent: "center",
4049
+ width: "20px",
4050
+ height: "20px",
4051
+ borderRadius: "5px",
4052
+ overflow: "hidden",
4053
+ background: "rgba(255,255,255,0.08)",
4054
+ flexShrink: 0
4055
+ },
4056
+ children: badgeAsset.imageUrl ? /* @__PURE__ */ jsxRuntime.jsx(
4057
+ "img",
4058
+ {
4059
+ src: badgeAsset.imageUrl,
4060
+ alt: badgeAsset.name ?? badgeAsset.ticker,
4061
+ style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
4062
+ }
4063
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.45rem", fontWeight: 700, letterSpacing: "0.02em", color: "rgba(255,255,255,0.75)" }, children: badgeAsset.ticker.slice(0, 4) })
4064
+ }
4065
+ )
4066
+ },
4067
+ badgeAsset.ticker
4068
+ )),
3171
4069
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: { padding: isMobile ? "0.1rem 0.35rem" : "0.15rem 0.4rem", borderRadius: "2px", fontSize: isMobile ? "0.52rem" : "0.65rem", fontWeight: 500, backgroundColor: isProperty ? "rgba(14,203,129,0.1)" : "rgba(240,185,11,0.1)", color: isProperty ? "#0ecb81" : "#E6C87E" }, children: isProperty ? "Property Update" : "Market News" }),
3172
- sentimentInfo && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontSize: isMobile ? "0.52rem" : "0.65rem", fontWeight: 600, color: sentimentInfo.color }, children: [
4070
+ sentimentInfo && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: isMobile ? "0.52rem" : "0.65rem", fontWeight: 600, color: sentimentInfo.color }, children: /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.3rem" }, children: [
3173
4071
  sentimentInfo.arrow ? `${sentimentInfo.arrow} ` : "",
3174
- sentimentInfo.label
3175
- ] })
4072
+ sentimentInfo.label,
4073
+ item.sentimentScore != null && /* @__PURE__ */ jsxRuntime.jsx(SentimentBar, { score: item.sentimentScore, color: sentimentInfo.color })
4074
+ ] }) })
3176
4075
  ] })
3177
4076
  ] }),
3178
4077
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#848e9c", paddingTop: "2px", flexShrink: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z" }) }) })
@@ -3221,15 +4120,13 @@ var PropertyNewsUpdates = React5__namespace.forwardRef(
3221
4120
  children: [
3222
4121
  /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.9375rem", fontWeight: 500, marginBottom: "0.4rem", color: "#fff", lineHeight: 1.4 }, children: item.title }),
3223
4122
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-end", gap: "0.5rem" }, children: [
3224
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem", minWidth: 0, flexWrap: "wrap" }, children: [
3225
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.75rem", color: isHighlighted ? "#0ecb81" : "#b5b8c5", whiteSpace: "nowrap" }, children: dateLabel }),
3226
- item.source && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { padding: "0.15rem 0.45rem", borderRadius: "3px", fontSize: "0.65rem", fontWeight: 500, backgroundColor: "rgba(255,255,255,0.07)", color: "#848e9c", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", maxWidth: "120px" }, children: item.source })
3227
- ] }),
4123
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem", minWidth: 0, flexWrap: "wrap" }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.75rem", color: isHighlighted ? "#0ecb81" : "#b5b8c5", whiteSpace: "nowrap" }, children: dateLabel }) }),
3228
4124
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "flex-end", gap: "0.3rem", flexShrink: 0 }, children: [
3229
- sentimentInfo && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontSize: "0.65rem", fontWeight: 700, color: sentimentInfo.color, whiteSpace: "nowrap" }, children: [
4125
+ sentimentInfo && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.65rem", fontWeight: 700, color: sentimentInfo.color, whiteSpace: "nowrap" }, children: /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.3rem" }, children: [
3230
4126
  sentimentInfo.arrow ? `${sentimentInfo.arrow} ` : "",
3231
- sentimentInfo.label
3232
- ] }),
4127
+ sentimentInfo.label,
4128
+ item.sentimentScore != null && /* @__PURE__ */ jsxRuntime.jsx(SentimentBar, { score: item.sentimentScore, color: sentimentInfo.color })
4129
+ ] }) }),
3233
4130
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: { padding: "0.25rem 0.6rem", borderRadius: "4px", border: `1px solid ${catStyle.borderColor}`, backgroundColor: catStyle.backgroundColor, color: catStyle.color, fontSize: "0.68rem", fontWeight: 600, textTransform: "uppercase", whiteSpace: "nowrap" }, children: item.type === "property" ? catStyle.label : "Market News" })
3234
4131
  ] })
3235
4132
  ] })
@@ -3302,7 +4199,21 @@ var PropertyNewsUpdates = React5__namespace.forwardRef(
3302
4199
  }
3303
4200
  )
3304
4201
  ] }),
3305
- selectedItem && !isPurchaseVariant && /* @__PURE__ */ jsxRuntime.jsx(NewsArticleModal, { item: selectedItem, onClose: () => setSelectedItem(null) })
4202
+ selectedItem && !isPurchaseVariant && /* @__PURE__ */ jsxRuntime.jsx(NewsArticleModal, { item: selectedItem, onClose: () => setSelectedItem(null) }),
4203
+ badgeTip && ReactDOM__namespace.createPortal(
4204
+ /* @__PURE__ */ jsxRuntime.jsx(
4205
+ "span",
4206
+ {
4207
+ className: "news-badge-tipbox",
4208
+ "data-testid": "news-asset-tip",
4209
+ "data-tip-below": badgeTip.below ? "true" : void 0,
4210
+ "aria-hidden": true,
4211
+ style: { position: "fixed", left: `${badgeTip.left}px`, top: `${badgeTip.top}px` },
4212
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { children: badgeTip.asset.name ? `${badgeTip.asset.name} (${badgeTip.asset.ticker})` : badgeTip.asset.ticker })
4213
+ }
4214
+ ),
4215
+ document.body
4216
+ )
3306
4217
  ]
3307
4218
  }
3308
4219
  );
@@ -3355,7 +4266,7 @@ var defaultFormat = (value) => new Intl.NumberFormat("en-US", {
3355
4266
  notation: value >= 1e5 ? "compact" : "standard",
3356
4267
  maximumFractionDigits: value >= 1e3 ? 0 : 2
3357
4268
  }).format(value);
3358
- var TradingSlider = React5__namespace.forwardRef(
4269
+ var TradingSlider = React9__namespace.forwardRef(
3359
4270
  ({
3360
4271
  label = "Trade size",
3361
4272
  helperText = "Drag to pick the desired notional.",
@@ -3373,7 +4284,7 @@ var TradingSlider = React5__namespace.forwardRef(
3373
4284
  ...rest
3374
4285
  }, ref) => {
3375
4286
  const isControlled = value !== void 0;
3376
- const [internalValue, setInternalValue] = React5__namespace.useState(
4287
+ const [internalValue, setInternalValue] = React9__namespace.useState(
3377
4288
  defaultValue ?? (typeof min === "number" ? min : 0)
3378
4289
  );
3379
4290
  const currentValue = isControlled ? Number(value) : internalValue;
@@ -3476,7 +4387,7 @@ var MobileToggleButton = styled10__default.default.button`
3476
4387
  padding: 0.6rem 0.5rem;
3477
4388
  }
3478
4389
  `;
3479
- var MobileTradeNav = React5__namespace.forwardRef(
4390
+ var MobileTradeNav = React9__namespace.forwardRef(
3480
4391
  ({ className, items, activeId, onChange, ...props }, ref) => {
3481
4392
  return /* @__PURE__ */ jsxRuntime.jsx(MobileToggleContainer, { ref, className: cn(className), ...props, children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(
3482
4393
  MobileToggleButton,
@@ -4002,7 +4913,7 @@ var formatPercent = (value, fractionDigits = 2) => {
4002
4913
  if (value == null || Number.isNaN(value)) return "\u2014";
4003
4914
  return `${value.toFixed(fractionDigits)}%`;
4004
4915
  };
4005
- var YourOrders = React5__namespace.forwardRef(
4916
+ var YourOrders = React9__namespace.forwardRef(
4006
4917
  ({
4007
4918
  className,
4008
4919
  title,
@@ -4015,10 +4926,10 @@ var YourOrders = React5__namespace.forwardRef(
4015
4926
  pageSize: pageSizeOverride,
4016
4927
  ...props
4017
4928
  }, ref) => {
4018
- const [internalActiveTab, setInternalActiveTab] = React5__namespace.useState(tabs?.[0]?.id ?? "portfolio");
4019
- const [page, setPage] = React5__namespace.useState(0);
4929
+ const [internalActiveTab, setInternalActiveTab] = React9__namespace.useState(tabs?.[0]?.id ?? "portfolio");
4930
+ const [page, setPage] = React9__namespace.useState(0);
4020
4931
  const effectiveActiveTabId = activeTabId ?? internalActiveTab;
4021
- React5__namespace.useEffect(() => {
4932
+ React9__namespace.useEffect(() => {
4022
4933
  setPage(0);
4023
4934
  }, [effectiveActiveTabId]);
4024
4935
  const handleTabChange = (tabId) => {
@@ -4400,7 +5311,7 @@ function createCandlestickSeries(chart, options) {
4400
5311
  }
4401
5312
  throw new Error("Candlestick series API is not available in the current lightweight-charts version.");
4402
5313
  }
4403
- var PriceChart = React5__namespace.forwardRef(
5314
+ var PriceChart = React9__namespace.forwardRef(
4404
5315
  ({
4405
5316
  className,
4406
5317
  title = "Price Chart",
@@ -4414,18 +5325,18 @@ var PriceChart = React5__namespace.forwardRef(
4414
5325
  height = 301.52,
4415
5326
  ...props
4416
5327
  }, ref) => {
4417
- const containerRef = React5__namespace.useRef(null);
4418
- const chartRef = React5__namespace.useRef(null);
4419
- const seriesRef = React5__namespace.useRef(null);
4420
- const priceLineRef = React5__namespace.useRef(null);
4421
- const [hoveredRange, setHoveredRange] = React5__namespace.useState(null);
4422
- const [dropdownOpen, setDropdownOpen] = React5__namespace.useState(false);
4423
- const dropdownRef = React5__namespace.useRef(null);
4424
- const isAutoScrollRef = React5__namespace.useRef(true);
5328
+ const containerRef = React9__namespace.useRef(null);
5329
+ const chartRef = React9__namespace.useRef(null);
5330
+ const seriesRef = React9__namespace.useRef(null);
5331
+ const priceLineRef = React9__namespace.useRef(null);
5332
+ const [hoveredRange, setHoveredRange] = React9__namespace.useState(null);
5333
+ const [dropdownOpen, setDropdownOpen] = React9__namespace.useState(false);
5334
+ const dropdownRef = React9__namespace.useRef(null);
5335
+ const isAutoScrollRef = React9__namespace.useRef(true);
4425
5336
  const visibleRanges = ranges.slice(0, VISIBLE_RANGE_COUNT);
4426
5337
  const dropdownRanges = ranges.slice(VISIBLE_RANGE_COUNT);
4427
5338
  const selectedInDropdown = dropdownRanges.includes(selectedRange);
4428
- React5__namespace.useEffect(() => {
5339
+ React9__namespace.useEffect(() => {
4429
5340
  const handleClickOutside = (e) => {
4430
5341
  if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
4431
5342
  setDropdownOpen(false);
@@ -4434,25 +5345,25 @@ var PriceChart = React5__namespace.forwardRef(
4434
5345
  document.addEventListener("mousedown", handleClickOutside);
4435
5346
  return () => document.removeEventListener("mousedown", handleClickOutside);
4436
5347
  }, []);
4437
- const resolvedPrice = React5__namespace.useMemo(() => {
5348
+ const resolvedPrice = React9__namespace.useMemo(() => {
4438
5349
  if (price != null) return price;
4439
5350
  const last = data.at(-1);
4440
5351
  return last?.close;
4441
5352
  }, [data, price]);
4442
- const inferredChangePercent = React5__namespace.useMemo(() => {
5353
+ const inferredChangePercent = React9__namespace.useMemo(() => {
4443
5354
  if (changePercent != null) return changePercent;
4444
5355
  const first = data[0]?.open;
4445
5356
  const last = data.at(-1)?.close;
4446
5357
  if (first == null || last == null || first === 0) return void 0;
4447
5358
  return (last - first) / first * 100;
4448
5359
  }, [changePercent, data]);
4449
- const dollarChange = React5__namespace.useMemo(() => {
5360
+ const dollarChange = React9__namespace.useMemo(() => {
4450
5361
  const first = data[0]?.open;
4451
5362
  const last = data.at(-1)?.close;
4452
5363
  if (first == null || last == null) return void 0;
4453
5364
  return last - first;
4454
5365
  }, [data]);
4455
- React5__namespace.useEffect(() => {
5366
+ React9__namespace.useEffect(() => {
4456
5367
  const el = containerRef.current;
4457
5368
  if (!el) return;
4458
5369
  const chart = LightweightCharts__namespace.createChart(el, {
@@ -4497,7 +5408,7 @@ var PriceChart = React5__namespace.forwardRef(
4497
5408
  chart.remove();
4498
5409
  };
4499
5410
  }, []);
4500
- React5__namespace.useEffect(() => {
5411
+ React9__namespace.useEffect(() => {
4501
5412
  const chart = chartRef.current;
4502
5413
  if (!chart) return;
4503
5414
  const effectiveRange = selectedRange ?? ranges?.[0] ?? "1D";
@@ -4505,7 +5416,7 @@ var PriceChart = React5__namespace.forwardRef(
4505
5416
  timeScale: getTimeScaleOptions(effectiveRange)
4506
5417
  });
4507
5418
  }, [selectedRange, ranges]);
4508
- React5__namespace.useEffect(() => {
5419
+ React9__namespace.useEffect(() => {
4509
5420
  const chart = chartRef.current;
4510
5421
  const series = seriesRef.current;
4511
5422
  if (!chart || !series) return;
@@ -4720,7 +5631,7 @@ var formatPrice3 = (value, currencySymbol, compact) => {
4720
5631
  maximumFractionDigits: 2
4721
5632
  })}`;
4722
5633
  };
4723
- var PropertyHeroHeader = React5__namespace.forwardRef(
5634
+ var PropertyHeroHeader = React9__namespace.forwardRef(
4724
5635
  ({
4725
5636
  className,
4726
5637
  imageUrl,
@@ -4754,12 +5665,12 @@ var PropertyHeroHeader = React5__namespace.forwardRef(
4754
5665
  const isPositive = changePercent == null ? void 0 : changePercent >= 0;
4755
5666
  const accentColor = "#e6c87e";
4756
5667
  const tradeHoverColor = "#f5dd9a";
4757
- const [isTradeInteracting, setIsTradeInteracting] = React5__namespace.useState(false);
4758
- const [isOfferInteracting, setIsOfferInteracting] = React5__namespace.useState(false);
4759
- const [isMobile, setIsMobile] = React5__namespace.useState(
5668
+ const [isTradeInteracting, setIsTradeInteracting] = React9__namespace.useState(false);
5669
+ const [isOfferInteracting, setIsOfferInteracting] = React9__namespace.useState(false);
5670
+ const [isMobile, setIsMobile] = React9__namespace.useState(
4760
5671
  () => typeof window !== "undefined" && window.innerWidth <= 768
4761
5672
  );
4762
- React5__namespace.useEffect(() => {
5673
+ React9__namespace.useEffect(() => {
4763
5674
  const onResize = () => setIsMobile(window.innerWidth <= 768);
4764
5675
  window.addEventListener("resize", onResize);
4765
5676
  return () => window.removeEventListener("resize", onResize);
@@ -5150,10 +6061,10 @@ function fmtCompactUsd(n) {
5150
6061
  }
5151
6062
  function useIsNarrow(maxWidthPx = 768) {
5152
6063
  const query = `(max-width:${maxWidthPx}px)`;
5153
- const [narrow, setNarrow] = React5.useState(
6064
+ const [narrow, setNarrow] = React9.useState(
5154
6065
  typeof window !== "undefined" ? window.matchMedia(query).matches : false
5155
6066
  );
5156
- React5.useEffect(() => {
6067
+ React9.useEffect(() => {
5157
6068
  if (typeof window === "undefined") return;
5158
6069
  const mq = window.matchMedia(query);
5159
6070
  const onChange = () => setNarrow(mq.matches);
@@ -5197,20 +6108,20 @@ var Header = ({
5197
6108
  headerCta = "deposit",
5198
6109
  showRefer = false
5199
6110
  }) => {
5200
- const [headerBgOpacity, setHeaderBgOpacity] = React5.useState(transparentOnTop ? 0 : 1);
5201
- const [isUserMenuOpen, setIsUserMenuOpen] = React5.useState(false);
5202
- const [isMobileMenuOpen, setIsMobileMenuOpen] = React5.useState(false);
6111
+ const [headerBgOpacity, setHeaderBgOpacity] = React9.useState(transparentOnTop ? 0 : 1);
6112
+ const [isUserMenuOpen, setIsUserMenuOpen] = React9.useState(false);
6113
+ const [isMobileMenuOpen, setIsMobileMenuOpen] = React9.useState(false);
5203
6114
  const isNarrow = useIsNarrow();
5204
- const [isMoreMenuOpen, setIsMoreMenuOpen] = React5.useState(false);
5205
- const [showLoginPopup, setShowLoginPopup] = React5.useState(false);
5206
- const [loginPopupInitialView, setLoginPopupInitialView] = React5.useState(void 0);
5207
- const [isPortfolioBarVisible, setIsPortfolioBarVisible] = React5.useState(() => {
6115
+ const [isMoreMenuOpen, setIsMoreMenuOpen] = React9.useState(false);
6116
+ const [showLoginPopup, setShowLoginPopup] = React9.useState(false);
6117
+ const [loginPopupInitialView, setLoginPopupInitialView] = React9.useState(void 0);
6118
+ const [isPortfolioBarVisible, setIsPortfolioBarVisible] = React9.useState(() => {
5208
6119
  if (typeof localStorage === "undefined") return false;
5209
6120
  return localStorage.getItem("portfolioBarVisible") !== "false";
5210
6121
  });
5211
- const portfolioPillRef = React5.useRef(null);
5212
- const [portfolioArrowLeft, setPortfolioArrowLeft] = React5.useState(null);
5213
- const togglePortfolioBar = React5.useCallback(() => {
6122
+ const portfolioPillRef = React9.useRef(null);
6123
+ const [portfolioArrowLeft, setPortfolioArrowLeft] = React9.useState(null);
6124
+ const togglePortfolioBar = React9.useCallback(() => {
5214
6125
  setIsPortfolioBarVisible((prev) => {
5215
6126
  const next = !prev;
5216
6127
  try {
@@ -5221,13 +6132,13 @@ var Header = ({
5221
6132
  });
5222
6133
  }, []);
5223
6134
  const SIMULATE_FUNDS = false;
5224
- const [simFundsPending, setSimFundsPending] = React5.useState(true);
5225
- React5.useEffect(() => {
6135
+ const [simFundsPending, setSimFundsPending] = React9.useState(true);
6136
+ React9.useEffect(() => {
5226
6137
  return;
5227
6138
  }, [simFundsPending]);
5228
6139
  const showFundsPending = (fundsPending || SIMULATE_FUNDS) && isAuthenticated && isPortfolioBarVisible;
5229
- const [fundsCountdown, setFundsCountdown] = React5.useState(10);
5230
- React5.useEffect(() => {
6140
+ const [fundsCountdown, setFundsCountdown] = React9.useState(10);
6141
+ React9.useEffect(() => {
5231
6142
  if (!showFundsPending) return;
5232
6143
  setFundsCountdown(10);
5233
6144
  const id = setInterval(() => {
@@ -5241,7 +6152,7 @@ var Header = ({
5241
6152
  }, 1e3);
5242
6153
  return () => clearInterval(id);
5243
6154
  }, [showFundsPending]);
5244
- React5.useEffect(() => {
6155
+ React9.useEffect(() => {
5245
6156
  const handler = () => {
5246
6157
  if (LoginPopupComponent) {
5247
6158
  setLoginPopupInitialView("funding");
@@ -5251,7 +6162,7 @@ var Header = ({
5251
6162
  window.addEventListener("loaf:open-funding", handler);
5252
6163
  return () => window.removeEventListener("loaf:open-funding", handler);
5253
6164
  }, [LoginPopupComponent]);
5254
- const updatePortfolioArrow = React5.useCallback(() => {
6165
+ const updatePortfolioArrow = React9.useCallback(() => {
5255
6166
  const eye = document.getElementById("portfolio-eye-toggle");
5256
6167
  const pill = portfolioPillRef.current;
5257
6168
  if (!eye || !pill) return;
@@ -5260,13 +6171,13 @@ var Header = ({
5260
6171
  const offset = eyeRect.left + eyeRect.width / 2 - pillRect.left - 7;
5261
6172
  setPortfolioArrowLeft(offset);
5262
6173
  }, []);
5263
- React5.useEffect(() => {
6174
+ React9.useEffect(() => {
5264
6175
  if (!isPortfolioBarVisible || !portfolioSummary) return;
5265
6176
  updatePortfolioArrow();
5266
6177
  window.addEventListener("resize", updatePortfolioArrow);
5267
6178
  return () => window.removeEventListener("resize", updatePortfolioArrow);
5268
6179
  }, [isPortfolioBarVisible, portfolioSummary, updatePortfolioArrow]);
5269
- React5.useEffect(() => {
6180
+ React9.useEffect(() => {
5270
6181
  if (typeof window === "undefined") return;
5271
6182
  const ua = navigator.userAgent;
5272
6183
  const isTelegram = /Telegram/i.test(ua);
@@ -5275,7 +6186,7 @@ var Header = ({
5275
6186
  document.documentElement.style.setProperty("--telegram-safe-top", "59px");
5276
6187
  }
5277
6188
  }, []);
5278
- React5.useEffect(() => {
6189
+ React9.useEffect(() => {
5279
6190
  if (!transparentOnTop) {
5280
6191
  setHeaderBgOpacity(1);
5281
6192
  return;
@@ -5288,7 +6199,7 @@ var Header = ({
5288
6199
  window.addEventListener("scroll", onScroll, { passive: true });
5289
6200
  return () => window.removeEventListener("scroll", onScroll);
5290
6201
  }, [transparentOnTop]);
5291
- React5.useEffect(() => {
6202
+ React9.useEffect(() => {
5292
6203
  const handleClickOutside = (event) => {
5293
6204
  const target = event.target;
5294
6205
  if (!target) return;
@@ -5308,7 +6219,7 @@ var Header = ({
5308
6219
  document.removeEventListener("mousedown", handleClickOutside);
5309
6220
  };
5310
6221
  }, [isUserMenuOpen, isMobileMenuOpen, isMoreMenuOpen]);
5311
- React5.useEffect(() => {
6222
+ React9.useEffect(() => {
5312
6223
  if (typeof window === "undefined") return;
5313
6224
  const handleExternalLoginPopup = (event) => {
5314
6225
  const customEvent = event;
@@ -5439,7 +6350,7 @@ var Header = ({
5439
6350
  setShowLoginPopup(true);
5440
6351
  }
5441
6352
  };
5442
- const handleLoginPopupClose = React5__namespace.default.useCallback(() => {
6353
+ const handleLoginPopupClose = React9__namespace.default.useCallback(() => {
5443
6354
  setShowLoginPopup(false);
5444
6355
  setLoginPopupInitialView(void 0);
5445
6356
  }, []);
@@ -6603,10 +7514,10 @@ var PBOperator = styled10__default.default.span`
6603
7514
  font-size: 0.55rem;
6604
7515
  }
6605
7516
  `;
6606
- var PropertySubheader = React5__namespace.forwardRef(
7517
+ var PropertySubheader = React9__namespace.forwardRef(
6607
7518
  ({ className, tabs, activeTabId, onTabChange, actions, ...props }, ref) => {
6608
- const tabsContainerRef = React5__namespace.useRef(null);
6609
- React5__namespace.useEffect(() => {
7519
+ const tabsContainerRef = React9__namespace.useRef(null);
7520
+ React9__namespace.useEffect(() => {
6610
7521
  const container = tabsContainerRef.current;
6611
7522
  if (!container) return;
6612
7523
  const isMobile = window.innerWidth <= 768;
@@ -6778,34 +7689,34 @@ var LoginPopup = ({
6778
7689
  onCheckAccess,
6779
7690
  gate
6780
7691
  }) => {
6781
- const [view, setView] = React5.useState(() => initialView ?? "main");
6782
- const [email, setEmail] = React5.useState("");
6783
- const [handle, setHandle] = React5.useState("");
6784
- const [otp, setOtp] = React5.useState(Array(OTP_INPUT_LENGTH).fill(""));
6785
- const [error, setError] = React5.useState("");
6786
- const [copied, setCopied] = React5.useState(false);
6787
- const [referralCode, setReferralCode] = React5.useState(
7692
+ const [view, setView] = React9.useState(() => initialView ?? "main");
7693
+ const [email, setEmail] = React9.useState("");
7694
+ const [handle, setHandle] = React9.useState("");
7695
+ const [otp, setOtp] = React9.useState(Array(OTP_INPUT_LENGTH).fill(""));
7696
+ const [error, setError] = React9.useState("");
7697
+ const [copied, setCopied] = React9.useState(false);
7698
+ const [referralCode, setReferralCode] = React9.useState(
6788
7699
  () => gate && initialCode ? normalizeAccessCode(initialCode) : ""
6789
7700
  );
6790
- const [referralLoading, setReferralLoading] = React5.useState(false);
6791
- const [referralError, setReferralError] = React5.useState("");
6792
- const [codeStatus, setCodeStatus] = React5.useState("idle");
6793
- const [signInRevealed, setSignInRevealed] = React5.useState(false);
6794
- const [otpStatus, setOtpStatus] = React5.useState("idle");
6795
- const [signInMode, setSignInMode] = React5.useState(false);
6796
- const [loading, setLoading] = React5.useState(false);
6797
- const [isSignUp, setIsSignUp] = React5.useState(false);
6798
- const [fundingAmount] = React5.useState("");
6799
- const [kycLoading, setKycLoading] = React5.useState(false);
6800
- const [showKycWidget, setShowKycWidget] = React5.useState(false);
6801
- const [showKycFooter, setShowKycFooter] = React5.useState(false);
6802
- const [cryptoFundingLoading, setCryptoFundingLoading] = React5.useState(false);
6803
- const [fiatFundingLoading, setFiatFundingLoading] = React5.useState(false);
6804
- const [fundingError, setFundingError] = React5.useState("");
6805
- const [transakWidgetUrl, setTransakWidgetUrl] = React5.useState(null);
6806
- const suppressAutoCloseRef = React5__namespace.default.useRef(false);
6807
- const validatedCodeRef = React5__namespace.default.useRef(null);
6808
- const finishGate = React5__namespace.default.useCallback(async () => {
7701
+ const [referralLoading, setReferralLoading] = React9.useState(false);
7702
+ const [referralError, setReferralError] = React9.useState("");
7703
+ const [codeStatus, setCodeStatus] = React9.useState("idle");
7704
+ const [signInRevealed, setSignInRevealed] = React9.useState(false);
7705
+ const [otpStatus, setOtpStatus] = React9.useState("idle");
7706
+ const [signInMode, setSignInMode] = React9.useState(false);
7707
+ const [loading, setLoading] = React9.useState(false);
7708
+ const [isSignUp, setIsSignUp] = React9.useState(false);
7709
+ const [fundingAmount] = React9.useState("");
7710
+ const [kycLoading, setKycLoading] = React9.useState(false);
7711
+ const [showKycWidget, setShowKycWidget] = React9.useState(false);
7712
+ const [showKycFooter, setShowKycFooter] = React9.useState(false);
7713
+ const [cryptoFundingLoading, setCryptoFundingLoading] = React9.useState(false);
7714
+ const [fiatFundingLoading, setFiatFundingLoading] = React9.useState(false);
7715
+ const [fundingError, setFundingError] = React9.useState("");
7716
+ const [transakWidgetUrl, setTransakWidgetUrl] = React9.useState(null);
7717
+ const suppressAutoCloseRef = React9__namespace.default.useRef(false);
7718
+ const validatedCodeRef = React9__namespace.default.useRef(null);
7719
+ const finishGate = React9__namespace.default.useCallback(async () => {
6809
7720
  if (!gate) return true;
6810
7721
  const code = validatedCodeRef.current;
6811
7722
  if (code) {
@@ -6831,7 +7742,7 @@ var LoginPopup = ({
6831
7742
  );
6832
7743
  return false;
6833
7744
  }, [gate, onSubmitReferralCode, onCheckAccess]);
6834
- const runCodeValidation = React5__namespace.default.useCallback(async (fullCode) => {
7745
+ const runCodeValidation = React9__namespace.default.useCallback(async (fullCode) => {
6835
7746
  setCodeStatus("checking");
6836
7747
  setError("");
6837
7748
  try {
@@ -6854,20 +7765,20 @@ var LoginPopup = ({
6854
7765
  setCodeStatus("invalid");
6855
7766
  }
6856
7767
  }, [onValidateCode, isAuthenticated, finishGate, onClose]);
6857
- const autoCodeTriedRef = React5__namespace.default.useRef(false);
6858
- React5.useEffect(() => {
7768
+ const autoCodeTriedRef = React9__namespace.default.useRef(false);
7769
+ React9.useEffect(() => {
6859
7770
  if (autoCodeTriedRef.current || !gate || !initialCode) return;
6860
7771
  const full = normalizeAccessCode(initialCode);
6861
7772
  if (full.length !== CODE_PREFIX.length + CODE_SUFFIX_LEN) return;
6862
7773
  autoCodeTriedRef.current = true;
6863
7774
  void runCodeValidation(full);
6864
7775
  }, [gate, initialCode, runCodeValidation]);
6865
- React5.useEffect(() => {
7776
+ React9.useEffect(() => {
6866
7777
  if (typeof initialView === "string") {
6867
7778
  setView(initialView);
6868
7779
  }
6869
7780
  }, [initialView]);
6870
- React5.useEffect(() => {
7781
+ React9.useEffect(() => {
6871
7782
  if (!showKycWidget) {
6872
7783
  setShowKycFooter(false);
6873
7784
  return;
@@ -6875,7 +7786,7 @@ var LoginPopup = ({
6875
7786
  const timer = setTimeout(() => setShowKycFooter(true), 5e3);
6876
7787
  return () => clearTimeout(timer);
6877
7788
  }, [showKycWidget]);
6878
- React5.useEffect(() => {
7789
+ React9.useEffect(() => {
6879
7790
  if (!transakWidgetUrl) return;
6880
7791
  const handleTransakMessage = (event) => {
6881
7792
  if (!isTrustedTransakOrigin(event.origin, transakWidgetUrl)) {
@@ -6900,7 +7811,7 @@ var LoginPopup = ({
6900
7811
  window.addEventListener("message", handleTransakMessage);
6901
7812
  return () => window.removeEventListener("message", handleTransakMessage);
6902
7813
  }, [transakWidgetUrl, onClose]);
6903
- React5.useEffect(() => {
7814
+ React9.useEffect(() => {
6904
7815
  if (!autoCloseOnAuth) {
6905
7816
  return;
6906
7817
  }
@@ -6914,7 +7825,7 @@ var LoginPopup = ({
6914
7825
  onClose();
6915
7826
  }
6916
7827
  }, [autoCloseOnAuth, currentUser, isAuthenticated, onClose, view]);
6917
- React5.useEffect(() => {
7828
+ React9.useEffect(() => {
6918
7829
  const handleEsc = (e) => {
6919
7830
  if (e.key === "Escape" && !gate) onClose();
6920
7831
  };
@@ -7137,7 +8048,7 @@ var LoginPopup = ({
7137
8048
  setLoading(false);
7138
8049
  }
7139
8050
  };
7140
- const runGateOtpVerify = React5__namespace.default.useCallback(async (code) => {
8051
+ const runGateOtpVerify = React9__namespace.default.useCallback(async (code) => {
7141
8052
  if (!onVerifyEmailCode) return;
7142
8053
  setOtpStatus("checking");
7143
8054
  setError("");
@@ -7153,7 +8064,7 @@ var LoginPopup = ({
7153
8064
  setOtpStatus("invalid");
7154
8065
  }
7155
8066
  }, [onVerifyEmailCode, email, finishGate, onClose]);
7156
- React5.useEffect(() => {
8067
+ React9.useEffect(() => {
7157
8068
  if (!gate || view !== "otp") return;
7158
8069
  if (otp.join("").length < OTP_INPUT_LENGTH) {
7159
8070
  if (otpStatus === "invalid") setOtpStatus("idle");
@@ -8728,8 +9639,8 @@ var TransakIframe = styled10__default.default.iframe`
8728
9639
  border-radius: var(--border-radius, 12px);
8729
9640
  `;
8730
9641
  var MiniLiveFeed = () => {
8731
- const [purchases, setPurchases] = React5.useState([]);
8732
- React5.useEffect(() => {
9642
+ const [purchases, setPurchases] = React9.useState([]);
9643
+ React9.useEffect(() => {
8733
9644
  const handles = [
8734
9645
  "Landlord",
8735
9646
  "PropertyKing",
@@ -8985,8 +9896,8 @@ var FactText = styled10__default.default.p`
8985
9896
  }
8986
9897
  `;
8987
9898
  var PreparingFacts = () => {
8988
- const [index, setIndex] = React5.useState(() => Math.floor(Math.random() * PREPARING_FACTS.length));
8989
- React5.useEffect(() => {
9899
+ const [index, setIndex] = React9.useState(() => Math.floor(Math.random() * PREPARING_FACTS.length));
9900
+ React9.useEffect(() => {
8990
9901
  const id = setInterval(() => {
8991
9902
  setIndex((i) => (i + 1) % PREPARING_FACTS.length);
8992
9903
  }, 4e3);
@@ -9249,11 +10160,11 @@ function OnboardingGuide({
9249
10160
  onClaimHandle,
9250
10161
  logoSrc = "/Loaf-logo-Banner.png"
9251
10162
  }) {
9252
- const [step, setStep] = React5.useState(onClaimHandle ? "handle" : "funds");
9253
- const [handle, setHandle] = React5.useState("");
9254
- const [claiming, setClaiming] = React5.useState(false);
9255
- const [handleError, setHandleError] = React5.useState("");
9256
- React5.useEffect(() => {
10163
+ const [step, setStep] = React9.useState(onClaimHandle ? "handle" : "funds");
10164
+ const [handle, setHandle] = React9.useState("");
10165
+ const [claiming, setClaiming] = React9.useState(false);
10166
+ const [handleError, setHandleError] = React9.useState("");
10167
+ React9.useEffect(() => {
9257
10168
  if (open) {
9258
10169
  setStep(onClaimHandle ? "handle" : "funds");
9259
10170
  setHandle("");
@@ -9261,7 +10172,7 @@ function OnboardingGuide({
9261
10172
  setHandleError("");
9262
10173
  }
9263
10174
  }, [open, onClaimHandle]);
9264
- React5.useEffect(() => {
10175
+ React9.useEffect(() => {
9265
10176
  if (!open) return;
9266
10177
  const onKey = (e) => {
9267
10178
  if (e.key === "Escape") onClose();
@@ -9361,7 +10272,7 @@ function OnboardingGuide({
9361
10272
  ] })
9362
10273
  ] }) });
9363
10274
  }
9364
- var PropertyCompareBar = React5__namespace.forwardRef(
10275
+ var PropertyCompareBar = React9__namespace.forwardRef(
9365
10276
  ({
9366
10277
  className,
9367
10278
  addresses,
@@ -9372,7 +10283,7 @@ var PropertyCompareBar = React5__namespace.forwardRef(
9372
10283
  price,
9373
10284
  ...props
9374
10285
  }, ref) => {
9375
- const normalizedAddresses = React5__namespace.useMemo(() => {
10286
+ const normalizedAddresses = React9__namespace.useMemo(() => {
9376
10287
  return addresses.map(
9377
10288
  (option) => typeof option === "string" ? { id: option, label: option } : option
9378
10289
  );
@@ -9380,11 +10291,11 @@ var PropertyCompareBar = React5__namespace.forwardRef(
9380
10291
  const hasAddresses = normalizedAddresses.length > 0;
9381
10292
  const firstAddressId = normalizedAddresses[0]?.id;
9382
10293
  const isControlled = selectedAddressId !== void 0;
9383
- const [internalSelectedId, setInternalSelectedId] = React5__namespace.useState(
10294
+ const [internalSelectedId, setInternalSelectedId] = React9__namespace.useState(
9384
10295
  () => isControlled ? void 0 : firstAddressId
9385
10296
  );
9386
10297
  const resolvedSelectedId = isControlled ? selectedAddressId : internalSelectedId;
9387
- React5__namespace.useEffect(() => {
10298
+ React9__namespace.useEffect(() => {
9388
10299
  if (!isControlled) {
9389
10300
  setInternalSelectedId((current) => {
9390
10301
  if (current != null && normalizedAddresses.some((option) => option.id === current)) {
@@ -9395,9 +10306,9 @@ var PropertyCompareBar = React5__namespace.forwardRef(
9395
10306
  }
9396
10307
  }, [firstAddressId, isControlled, normalizedAddresses]);
9397
10308
  const selectedOption = normalizedAddresses.find((option) => option.id === resolvedSelectedId) ?? normalizedAddresses[0];
9398
- const [isDropdownOpen, setIsDropdownOpen] = React5__namespace.useState(false);
9399
- const dropdownRef = React5__namespace.useRef(null);
9400
- React5__namespace.useEffect(() => {
10309
+ const [isDropdownOpen, setIsDropdownOpen] = React9__namespace.useState(false);
10310
+ const dropdownRef = React9__namespace.useRef(null);
10311
+ React9__namespace.useEffect(() => {
9401
10312
  if (!isDropdownOpen) return;
9402
10313
  const handleClick = (event) => {
9403
10314
  if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
@@ -9790,8 +10701,8 @@ var PriceChange = styled10__default.default.span`
9790
10701
  color: ${(props) => props.$isPositive == null ? "var(--color-text-secondary, rgba(255, 255, 255, 0.6))" : props.$isPositive ? "var(--color-positive, #0ecb81)" : "var(--color-negative, #f6465d)"};
9791
10702
  `;
9792
10703
  function useMaxWidth(maxWidth) {
9793
- const [match, setMatch] = React5.useState(() => typeof window !== "undefined" && window.matchMedia(`(max-width: ${maxWidth}px)`).matches);
9794
- React5.useEffect(() => {
10704
+ const [match, setMatch] = React9.useState(() => typeof window !== "undefined" && window.matchMedia(`(max-width: ${maxWidth}px)`).matches);
10705
+ React9.useEffect(() => {
9795
10706
  if (typeof window === "undefined") return;
9796
10707
  const mq = window.matchMedia(`(max-width: ${maxWidth}px)`);
9797
10708
  const onChange = () => setMatch(mq.matches);
@@ -9816,14 +10727,14 @@ function GalleryMapSection({
9816
10727
  hideSuburb = false,
9817
10728
  featuresSlot
9818
10729
  }) {
9819
- const [carouselIndex, setCarouselIndex] = React5.useState(0);
9820
- const [showVideo, setShowVideo] = React5.useState(false);
9821
- const [autoPlaying, setAutoPlaying] = React5.useState(autoPlay);
9822
- const [showFullMap, setShowFullMap] = React5.useState(false);
9823
- const [showFullVideo, setShowFullVideo] = React5.useState(false);
10730
+ const [carouselIndex, setCarouselIndex] = React9.useState(0);
10731
+ const [showVideo, setShowVideo] = React9.useState(false);
10732
+ const [autoPlaying, setAutoPlaying] = React9.useState(autoPlay);
10733
+ const [showFullMap, setShowFullMap] = React9.useState(false);
10734
+ const [showFullVideo, setShowFullVideo] = React9.useState(false);
9824
10735
  const isMobile = useMaxWidth(768);
9825
10736
  const resolvedMapUrl = mapUrl ?? (tokenName ? `/map/${tokenName}?embed=true&zoom=14&hideOthers=true` : "about:blank");
9826
- React5.useEffect(() => {
10737
+ React9.useEffect(() => {
9827
10738
  if (!autoPlaying || images.length <= 1) return;
9828
10739
  const interval = setInterval(() => {
9829
10740
  setCarouselIndex((p) => (p + 1) % images.length);
@@ -10010,10 +10921,10 @@ function GalleryMapSection({
10010
10921
  ] });
10011
10922
  }
10012
10923
  function StackedGallery({ images, onPhotoClick }) {
10013
- const [expanded, setExpanded] = React5.useState(false);
10014
- const [cardH, setCardH] = React5.useState(200);
10015
- const wrapRef = React5.useRef(null);
10016
- React5.useEffect(() => {
10924
+ const [expanded, setExpanded] = React9.useState(false);
10925
+ const [cardH, setCardH] = React9.useState(200);
10926
+ const wrapRef = React9.useRef(null);
10927
+ React9.useEffect(() => {
10017
10928
  const el = wrapRef.current;
10018
10929
  if (!el) return;
10019
10930
  const measure = () => {
@@ -10025,7 +10936,7 @@ function StackedGallery({ images, onPhotoClick }) {
10025
10936
  ro.observe(el);
10026
10937
  return () => ro.disconnect();
10027
10938
  }, []);
10028
- React5.useEffect(() => {
10939
+ React9.useEffect(() => {
10029
10940
  const el = wrapRef.current;
10030
10941
  if (!el) return;
10031
10942
  const update = () => {
@@ -10117,21 +11028,21 @@ function StackedGallery({ images, onPhotoClick }) {
10117
11028
  ] });
10118
11029
  }
10119
11030
  function FullMapPopup({ onClose, mapUrl }) {
10120
- React5.useEffect(() => {
11031
+ React9.useEffect(() => {
10121
11032
  const handleEsc = (e) => {
10122
11033
  if (e.key === "Escape") onClose();
10123
11034
  };
10124
11035
  window.addEventListener("keydown", handleEsc);
10125
11036
  return () => window.removeEventListener("keydown", handleEsc);
10126
11037
  }, [onClose]);
10127
- React5.useEffect(() => {
11038
+ React9.useEffect(() => {
10128
11039
  const handleMessage = (e) => {
10129
11040
  if (e.data === "loaf:close-embed") onClose();
10130
11041
  };
10131
11042
  window.addEventListener("message", handleMessage);
10132
11043
  return () => window.removeEventListener("message", handleMessage);
10133
11044
  }, [onClose]);
10134
- const iframeRef = React5.useCallback((node) => {
11045
+ const iframeRef = React9.useCallback((node) => {
10135
11046
  if (!node) return;
10136
11047
  const handleLoad = () => {
10137
11048
  try {
@@ -10608,10 +11519,10 @@ function PropertyOverview({
10608
11519
  beta = false,
10609
11520
  marketHours
10610
11521
  }) {
10611
- const [isDescExpanded, setDescExpanded] = React5.useState(false);
10612
- const [showDividendHistory, setShowDividendHistory] = React5.useState(false);
10613
- const [copiedAddress, setCopiedAddress] = React5.useState(false);
10614
- const [statsTab, setStatsTab] = React5.useState("token");
11522
+ const [isDescExpanded, setDescExpanded] = React9.useState(false);
11523
+ const [showDividendHistory, setShowDividendHistory] = React9.useState(false);
11524
+ const [copiedAddress, setCopiedAddress] = React9.useState(false);
11525
+ const [statsTab, setStatsTab] = React9.useState("token");
10615
11526
  const description = descriptionProp ?? overviewData?.description ?? DEFAULT_DESCRIPTION;
10616
11527
  const landSize = landProp ?? overviewData?.landSizeSqm ?? null;
10617
11528
  const buildingSize = buildingProp ?? overviewData?.buildingSizeSqm ?? null;
@@ -10920,8 +11831,8 @@ function DividendHistoryPopup({
10920
11831
  onClose
10921
11832
  }) {
10922
11833
  const { records, priceHistory } = generateDividendHistory(valuation ?? 0, totalTokens ?? 0, tokenPrice ?? 0, realPriceHistory);
10923
- const [highlightedIdx, setHighlightedIdx] = React5.useState(null);
10924
- React5.useEffect(() => {
11834
+ const [highlightedIdx, setHighlightedIdx] = React9.useState(null);
11835
+ React9.useEffect(() => {
10925
11836
  const handleEsc = (e) => {
10926
11837
  if (e.key === "Escape") onClose();
10927
11838
  };
@@ -11768,8 +12679,8 @@ var OfferPricePulse = styled10__default.default.span`
11768
12679
  `;
11769
12680
  var eventTypes = ["Leased", "Renovated", "Extended", "Rebuilt", "Rezoned", "DA Approval"];
11770
12681
  function PropertyHistory() {
11771
- const [historyFilter, setHistoryFilter] = React5.useState("all");
11772
- const pastSales = React5.useMemo(() => generateMockPastSales(), []);
12682
+ const [historyFilter, setHistoryFilter] = React9.useState("all");
12683
+ const pastSales = React9.useMemo(() => generateMockPastSales(), []);
11773
12684
  const loafListing = pastSales.find((sale) => sale.type === "Listed");
11774
12685
  const loafEvents = pastSales.filter(
11775
12686
  (sale) => sale.ownershipPeriod === "current" && sale.type !== "Listed" && (sale.id === "leased-current" || sale.id === "da-approval")
@@ -11906,7 +12817,7 @@ function PropertyHistory() {
11906
12817
  ] });
11907
12818
  }
11908
12819
  function EventDetails({ event }) {
11909
- const [expanded, setExpanded] = React5.useState(false);
12820
+ const [expanded, setExpanded] = React9.useState(false);
11910
12821
  const toggleExpand = () => {
11911
12822
  setExpanded((prev) => !prev);
11912
12823
  };
@@ -12403,9 +13314,9 @@ var COMING_SOON_TITLES = [
12403
13314
  "Risk Disclosure Statement"
12404
13315
  ];
12405
13316
  function PropertyDocuments({ documentsData, highlightDocument, onClearHighlight }) {
12406
- const highlightRef = React5.useRef(null);
12407
- const [previewUrl, setPreviewUrl] = React5.useState(null);
12408
- const [previewTitle, setPreviewTitle] = React5.useState(null);
13317
+ const highlightRef = React9.useRef(null);
13318
+ const [previewUrl, setPreviewUrl] = React9.useState(null);
13319
+ const [previewTitle, setPreviewTitle] = React9.useState(null);
12409
13320
  const backendDocuments = Array.isArray(documentsData?.documents) ? documentsData.documents : [];
12410
13321
  const liveDocs = backendDocuments.filter(
12411
13322
  (d) => Boolean(d.documentUrl) && isSafeUrl(d.documentUrl) && LIVE_TITLE_PATTERNS.some((rx) => rx.test(d.title))
@@ -12420,7 +13331,7 @@ function PropertyDocuments({ documentsData, highlightDocument, onClearHighlight
12420
13331
  setPreviewUrl(doc.documentUrl);
12421
13332
  setPreviewTitle(doc.title);
12422
13333
  };
12423
- React5.useEffect(() => {
13334
+ React9.useEffect(() => {
12424
13335
  if (highlightDocument && highlightRef.current) {
12425
13336
  highlightRef.current.scrollIntoView({ behavior: "smooth", block: "nearest" });
12426
13337
  }
@@ -12429,7 +13340,7 @@ function PropertyDocuments({ documentsData, highlightDocument, onClearHighlight
12429
13340
  if (hit) selectPreview(hit);
12430
13341
  }
12431
13342
  }, [highlightDocument]);
12432
- React5.useEffect(() => {
13343
+ React9.useEffect(() => {
12433
13344
  if (!highlightDocument || !onClearHighlight) return;
12434
13345
  const handler = (e) => {
12435
13346
  if (highlightRef.current && !highlightRef.current.contains(e.target)) {
@@ -12825,6 +13736,32 @@ var SignInButton = styled10__default.default.button`
12825
13736
  background: rgba(240, 185, 11, 0.1);
12826
13737
  }
12827
13738
  `;
13739
+ var SORT_COLUMNS = [
13740
+ { field: "price", label: "Price" },
13741
+ { field: "change", label: "24h Change" },
13742
+ { field: "volume", label: "24h Volume", hideOnPhone: true },
13743
+ { field: "value", label: "Asset Value", hideOnPhone: true }
13744
+ ];
13745
+ function sortValue(item, field) {
13746
+ switch (field) {
13747
+ case "volume":
13748
+ return item.volume24h ?? null;
13749
+ case "price":
13750
+ return item.price ?? null;
13751
+ case "change":
13752
+ return item.changePercent ?? null;
13753
+ case "value":
13754
+ return item.propertyValue ?? null;
13755
+ }
13756
+ }
13757
+ function fmtWholeUsd(value) {
13758
+ if (value == null) return null;
13759
+ return `$${value.toLocaleString(void 0, { maximumFractionDigits: 0 })}`;
13760
+ }
13761
+ function fmtPrice(value) {
13762
+ if (value == null) return null;
13763
+ return `$${value.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
13764
+ }
12828
13765
  function displayName(item) {
12829
13766
  if (item.name) return item.name;
12830
13767
  return item.tokenName.charAt(0).toUpperCase() + item.tokenName.slice(1);
@@ -12838,24 +13775,79 @@ function AssetSelectorBar({
12838
13775
  selectorItems,
12839
13776
  onSelect,
12840
13777
  trailing,
13778
+ afterName,
12841
13779
  imageUrl,
12842
13780
  badgeLabel,
12843
- tickerPrimary
13781
+ tickerPrimary,
13782
+ compactName
12844
13783
  }) {
12845
- const [isDropdownOpen, setIsDropdownOpen] = React5.useState(false);
13784
+ const [isDropdownOpen, setIsDropdownOpen] = React9.useState(false);
13785
+ const [nameTip, setNameTip] = React9.useState(null);
13786
+ const anchorRef = React9.useRef(null);
13787
+ const [dropdownPos, setDropdownPos] = React9.useState(null);
13788
+ React9.useLayoutEffect(() => {
13789
+ if (!isDropdownOpen) return;
13790
+ const measure = () => {
13791
+ const rect = anchorRef.current?.getBoundingClientRect();
13792
+ if (rect) setDropdownPos({ top: rect.bottom + 6, left: rect.left });
13793
+ };
13794
+ measure();
13795
+ window.addEventListener("resize", measure);
13796
+ window.addEventListener("scroll", measure, true);
13797
+ return () => {
13798
+ window.removeEventListener("resize", measure);
13799
+ window.removeEventListener("scroll", measure, true);
13800
+ };
13801
+ }, [isDropdownOpen]);
13802
+ const [sortField, setSortField] = React9.useState("volume");
13803
+ const [sortDir, setSortDir] = React9.useState("desc");
12846
13804
  const hasItems = selectorItems && selectorItems.length > 0;
13805
+ const hasMarketData = !!selectorItems?.some(
13806
+ (i) => i.price != null || i.changePercent != null || i.volume24h != null || i.propertyValue != null
13807
+ );
13808
+ const sortedItems = React9.useMemo(() => {
13809
+ if (!selectorItems) return [];
13810
+ if (!hasMarketData) return [...selectorItems];
13811
+ const dir = sortDir === "desc" ? -1 : 1;
13812
+ const missing = sortDir === "desc" ? -Infinity : Infinity;
13813
+ return [...selectorItems].sort((a, b) => {
13814
+ const av = sortValue(a, sortField) ?? missing;
13815
+ const bv = sortValue(b, sortField) ?? missing;
13816
+ return (av - bv) * dir || 0;
13817
+ });
13818
+ }, [selectorItems, hasMarketData, sortField, sortDir]);
13819
+ const handleSortClick = (field) => {
13820
+ if (field === sortField) {
13821
+ setSortDir((d) => d === "desc" ? "asc" : "desc");
13822
+ } else {
13823
+ setSortField(field);
13824
+ setSortDir("desc");
13825
+ }
13826
+ };
12847
13827
  const currentTicker = currentTokenName ? selectorItems?.find((i) => i.tokenName.toLowerCase() === currentTokenName.toLowerCase())?.ticker : void 0;
12848
13828
  const metrics = metricsProp ?? [
12849
13829
  ...tokenPrice != null ? [{ label: "Unit Price", value: `$${tokenPrice.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`, accent: true }] : [],
12850
13830
  ...offeringValuation != null ? [{ label: "Offering Valuation", value: `$${offeringValuation.toLocaleString()}` }] : []
12851
13831
  ];
12852
- return /* @__PURE__ */ jsxRuntime.jsx(AssetSelectorWrapper, { children: /* @__PURE__ */ jsxRuntime.jsxs(IPOAssetSelector, { children: [
13832
+ return /* @__PURE__ */ jsxRuntime.jsx(AssetSelectorWrapper, { children: /* @__PURE__ */ jsxRuntime.jsxs(IPOAssetSelector, { ref: anchorRef, children: [
12853
13833
  imageUrl && /* @__PURE__ */ jsxRuntime.jsx(AssetThumbnail, { src: imageUrl, alt: propertyName }),
12854
- /* @__PURE__ */ jsxRuntime.jsx(AssetSelectorDropdown, { $flush: tickerPrimary, onClick: () => hasItems && setIsDropdownOpen((prev) => !prev), children: /* @__PURE__ */ jsxRuntime.jsxs(AssetName, { children: [
13834
+ /* @__PURE__ */ jsxRuntime.jsx(AssetSelectorDropdown, { $flush: tickerPrimary, $noShrink: !!compactName && !!currentTicker && !tickerPrimary, onClick: () => hasItems && setIsDropdownOpen((prev) => !prev), children: /* @__PURE__ */ jsxRuntime.jsxs(AssetName, { children: [
12855
13835
  tickerPrimary && currentTicker ? /* @__PURE__ */ jsxRuntime.jsxs(AssetIdentity, { children: [
12856
13836
  /* @__PURE__ */ jsxRuntime.jsx(AssetTicker, { children: currentTicker.toUpperCase() }),
12857
13837
  /* @__PURE__ */ jsxRuntime.jsx(AssetSubName, { children: propertyName })
12858
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
13838
+ ] }) : compactName && currentTicker ? /* @__PURE__ */ jsxRuntime.jsx(
13839
+ CompactName,
13840
+ {
13841
+ className: "asset-bar-compact-name",
13842
+ "aria-label": propertyName,
13843
+ onMouseEnter: (e) => {
13844
+ const r = e.currentTarget.getBoundingClientRect();
13845
+ setNameTip({ left: r.left + r.width / 2, top: r.bottom + 10 });
13846
+ },
13847
+ onMouseLeave: () => setNameTip(null),
13848
+ children: currentTicker.toUpperCase()
13849
+ }
13850
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
12859
13851
  propertyName,
12860
13852
  currentTicker ? ` (${currentTicker.toUpperCase()})` : ""
12861
13853
  ] }),
@@ -12873,6 +13865,7 @@ function AssetSelectorBar({
12873
13865
  }
12874
13866
  )
12875
13867
  ] }) }),
13868
+ afterName != null && /* @__PURE__ */ jsxRuntime.jsx(AfterNameSlot, { className: "asset-bar-after-name", children: afterName }),
12876
13869
  metrics.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(SelectorMetrics, { children: metrics.map((m, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "contents" }, children: [
12877
13870
  i > 0 && /* @__PURE__ */ jsxRuntime.jsx(Separator, { children: "|" }),
12878
13871
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
@@ -12888,40 +13881,128 @@ function AssetSelectorBar({
12888
13881
  ] })
12889
13882
  ] }, m.label)) }),
12890
13883
  trailing != null && /* @__PURE__ */ jsxRuntime.jsx(TrailingWrapper, { className: "asset-bar-trailing", children: /* @__PURE__ */ jsxRuntime.jsx(TrailingInner, { children: trailing }) }),
12891
- isDropdownOpen && hasItems && /* @__PURE__ */ jsxRuntime.jsx(IPODropdown, { children: selectorItems.map((item) => {
12892
- const isCurrent = item.tokenName === currentTokenName;
12893
- const status = item.status?.toUpperCase();
12894
- const statusLabel = status === "PENDING" ? "Preparing" : status === "CLOSED" ? "Closed" : status === "CANCELLED" ? "Cancelled" : null;
12895
- return /* @__PURE__ */ jsxRuntime.jsx(
12896
- IPOOption,
13884
+ nameTip != null && typeof document !== "undefined" && ReactDOM.createPortal(
13885
+ /* @__PURE__ */ jsxRuntime.jsx(
13886
+ NameTip,
12897
13887
  {
12898
- onClick: () => {
12899
- if (!isCurrent && onSelect) {
12900
- onSelect(item.tokenName);
12901
- }
12902
- setIsDropdownOpen(false);
12903
- },
12904
- $selected: isCurrent,
12905
- style: { cursor: isCurrent ? "default" : "pointer" },
12906
- children: /* @__PURE__ */ jsxRuntime.jsxs(IPOOptionInner, { children: [
12907
- item.imageUrl && /* @__PURE__ */ jsxRuntime.jsx(IPOOptionThumb, { src: item.imageUrl, alt: displayName(item) }),
12908
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
12909
- /* @__PURE__ */ jsxRuntime.jsxs(IPOOptionName, { children: [
12910
- displayName(item),
12911
- item.ticker ? ` (${item.ticker})` : "",
12912
- item.badge && /* @__PURE__ */ jsxRuntime.jsx(CompetitionBadge, { children: item.badge })
12913
- ] }),
12914
- /* @__PURE__ */ jsxRuntime.jsxs(IPOOptionLocation, { children: [
12915
- item.streetAddress,
12916
- item.suburb ? `, ${item.suburb}` : "",
12917
- statusLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { children: statusLabel })
12918
- ] })
12919
- ] })
12920
- ] })
12921
- },
12922
- item.tokenName
12923
- );
12924
- }) })
13888
+ role: "tooltip",
13889
+ "data-testid": "asset-name-tip",
13890
+ style: { position: "fixed", left: nameTip.left, top: nameTip.top, transform: "translateX(-50%)", zIndex: 10050 },
13891
+ children: propertyName
13892
+ }
13893
+ ),
13894
+ document.body
13895
+ ),
13896
+ isDropdownOpen && hasItems && dropdownPos != null && typeof document !== "undefined" && ReactDOM.createPortal(
13897
+ /* @__PURE__ */ jsxRuntime.jsxs(
13898
+ IPODropdown,
13899
+ {
13900
+ $wide: hasMarketData,
13901
+ style: { position: "fixed", top: dropdownPos.top, left: dropdownPos.left, zIndex: 10050 },
13902
+ children: [
13903
+ hasMarketData && /* @__PURE__ */ jsxRuntime.jsxs(HeaderRow, { children: [
13904
+ /* @__PURE__ */ jsxRuntime.jsx(HeaderLabel, { children: "Asset" }),
13905
+ SORT_COLUMNS.map(({ field, label, hideOnPhone }) => /* @__PURE__ */ jsxRuntime.jsxs(
13906
+ SortButton,
13907
+ {
13908
+ type: "button",
13909
+ $active: sortField === field,
13910
+ $hideOnPhone: hideOnPhone,
13911
+ onClick: (e) => {
13912
+ e.stopPropagation();
13913
+ handleSortClick(field);
13914
+ },
13915
+ children: [
13916
+ label,
13917
+ sortField === field ? sortDir === "desc" ? " \u25BE" : " \u25B4" : ""
13918
+ ]
13919
+ },
13920
+ field
13921
+ ))
13922
+ ] }),
13923
+ sortedItems.map((item) => {
13924
+ const isCurrent = item.tokenName === currentTokenName;
13925
+ const status = item.status?.toUpperCase();
13926
+ const statusLabel = status === "PENDING" ? "Preparing" : status === "CLOSED" ? "Closed" : status === "CANCELLED" ? "Cancelled" : null;
13927
+ const price = fmtPrice(item.price);
13928
+ const change = item.changePercent;
13929
+ const vol = fmtWholeUsd(item.volume24h);
13930
+ const val = fmtWholeUsd(item.propertyValue);
13931
+ return /* @__PURE__ */ jsxRuntime.jsx(
13932
+ IPOOption,
13933
+ {
13934
+ "data-selector-row": item.tokenName,
13935
+ onClick: () => {
13936
+ if (!isCurrent && onSelect) {
13937
+ onSelect(item.tokenName);
13938
+ }
13939
+ setIsDropdownOpen(false);
13940
+ },
13941
+ $selected: isCurrent,
13942
+ style: {
13943
+ cursor: isCurrent ? "default" : "pointer",
13944
+ // Arena gold for competition assets — the same restrained cue as
13945
+ // the home Overview row (gold left crust + pooled spotlight, no
13946
+ // badge). backgroundImage LONGHAND: jsdom drops gradients from
13947
+ // the shorthand and UI6-AS-508 asserts it.
13948
+ ...item.isCompetition ? {
13949
+ // Straight crust bar — an inset box-shadow hugged the
13950
+ // row's rounded corners and read as a curved rim.
13951
+ // Left corners squared so the bar runs dead straight
13952
+ // like the home Overview arena row; right side keeps
13953
+ // the row rounding.
13954
+ borderLeft: "3px solid #E0B377",
13955
+ borderRadius: "0 7px 7px 0",
13956
+ backgroundImage: "radial-gradient(140% 90% at 0% 50%, rgba(224, 179, 119, 0.18) 0%, rgba(224, 179, 119, 0.06) 40%, transparent 68%)"
13957
+ } : {}
13958
+ },
13959
+ children: hasMarketData ? /* @__PURE__ */ jsxRuntime.jsxs(MarketRowInner, { children: [
13960
+ /* @__PURE__ */ jsxRuntime.jsxs(IdentityCell, { children: [
13961
+ item.imageUrl && /* @__PURE__ */ jsxRuntime.jsx(IPOOptionThumb, { src: item.imageUrl, alt: displayName(item) }),
13962
+ /* @__PURE__ */ jsxRuntime.jsxs(IPOOptionIdentity, { children: [
13963
+ /* @__PURE__ */ jsxRuntime.jsxs(IPOOptionName, { children: [
13964
+ /* @__PURE__ */ jsxRuntime.jsx(MarketTicker, { children: (item.ticker || displayName(item)).toUpperCase() }),
13965
+ item.typeBadge && /* @__PURE__ */ jsxRuntime.jsx(TypePill, { style: { color: item.typeBadge.color, background: item.typeBadge.background }, children: item.typeBadge.label }),
13966
+ item.badge && /* @__PURE__ */ jsxRuntime.jsx(CompetitionBadge, { children: item.badge })
13967
+ ] }),
13968
+ /* @__PURE__ */ jsxRuntime.jsxs(IPOOptionLocation, { children: [
13969
+ item.streetAddress,
13970
+ statusLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { children: statusLabel })
13971
+ ] })
13972
+ ] })
13973
+ ] }),
13974
+ /* @__PURE__ */ jsxRuntime.jsx(PriceCell, { children: price ?? "\u2014" }),
13975
+ change != null ? /* @__PURE__ */ jsxRuntime.jsxs(ChangeCell, { $positive: change >= 0, children: [
13976
+ change >= 0 ? "+" : "",
13977
+ change.toFixed(2),
13978
+ "%"
13979
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(MutedCell, { children: "\u2014" }),
13980
+ /* @__PURE__ */ jsxRuntime.jsx(MutedCell, { $hideOnPhone: true, children: vol ?? "\u2014" }),
13981
+ /* @__PURE__ */ jsxRuntime.jsx(MutedCell, { $hideOnPhone: true, children: val ?? "\u2014" })
13982
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(IPOOptionInner, { children: [
13983
+ item.imageUrl && /* @__PURE__ */ jsxRuntime.jsx(IPOOptionThumb, { src: item.imageUrl, alt: displayName(item) }),
13984
+ /* @__PURE__ */ jsxRuntime.jsxs(IPOOptionIdentity, { children: [
13985
+ /* @__PURE__ */ jsxRuntime.jsxs(IPOOptionName, { children: [
13986
+ displayName(item),
13987
+ item.ticker ? ` (${item.ticker})` : "",
13988
+ item.badge && /* @__PURE__ */ jsxRuntime.jsx(CompetitionBadge, { children: item.badge })
13989
+ ] }),
13990
+ /* @__PURE__ */ jsxRuntime.jsxs(IPOOptionLocation, { children: [
13991
+ item.streetAddress,
13992
+ item.suburb ? `, ${item.suburb}` : "",
13993
+ statusLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { children: statusLabel })
13994
+ ] })
13995
+ ] })
13996
+ ] })
13997
+ },
13998
+ item.tokenName
13999
+ );
14000
+ })
14001
+ ]
14002
+ }
14003
+ ),
14004
+ document.body
14005
+ )
12925
14006
  ] }) });
12926
14007
  }
12927
14008
  var AssetSelectorWrapper = styled10__default.default.div`
@@ -12983,7 +14064,9 @@ var AssetSelectorDropdown = styled10__default.default.div`
12983
14064
  padding: ${(p) => p.$flush ? "0.25rem 0" : "0.5rem"};
12984
14065
  border-radius: 8px;
12985
14066
  transition: ${(p) => p.$flush ? "none" : "all 0.2s ease"};
12986
- flex: 0 1 auto;
14067
+ /* $noShrink (compactName): a four-letter ticker is already the floor — it must
14068
+ never clip to "Y" when the bar is starved; the trailing scrolls instead. */
14069
+ flex: ${(p) => p.$noShrink ? "0 0 auto" : "0 1 auto"};
12987
14070
  min-width: 0;
12988
14071
  overflow: hidden;
12989
14072
  &:hover { background-color: ${(p) => p.$flush ? "transparent" : "rgba(255,255,255,0.05)"}; }
@@ -13040,6 +14123,35 @@ var AssetName = styled10__default.default.div`
13040
14123
  svg { margin-left: 0.4rem; width: 16px; height: 16px; }
13041
14124
  }
13042
14125
  `;
14126
+ var CompactName = styled10__default.default.span`
14127
+ white-space: nowrap;
14128
+ letter-spacing: 0.01em;
14129
+ `;
14130
+ var NameTip = styled10__default.default.div`
14131
+ background: #1a1d24;
14132
+ border: 1px solid rgba(255, 255, 255, 0.1);
14133
+ border-radius: 8px;
14134
+ padding: 0.45rem 0.7rem;
14135
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
14136
+ pointer-events: none;
14137
+ white-space: nowrap;
14138
+ color: #fff;
14139
+ font-size: 0.78rem;
14140
+ font-weight: 600;
14141
+ letter-spacing: -0.01em;
14142
+ &::before {
14143
+ content: '';
14144
+ position: absolute;
14145
+ top: -5px;
14146
+ left: 50%;
14147
+ transform: translateX(-50%) rotate(45deg);
14148
+ width: 8px;
14149
+ height: 8px;
14150
+ background: #1a1d24;
14151
+ border-left: 1px solid rgba(255, 255, 255, 0.1);
14152
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
14153
+ }
14154
+ `;
13043
14155
  var SelectorMetrics = styled10__default.default.div`
13044
14156
  display: flex;
13045
14157
  align-items: center;
@@ -13060,6 +14172,12 @@ var SelectorMetrics = styled10__default.default.div`
13060
14172
  padding: 0.4rem 0.5rem;
13061
14173
  }
13062
14174
  `;
14175
+ var AfterNameSlot = styled10__default.default.div`
14176
+ display: flex;
14177
+ align-items: center;
14178
+ flex-shrink: 0;
14179
+ min-width: 0;
14180
+ `;
13063
14181
  var TrailingWrapper = styled10__default.default.div`
13064
14182
  display: flex;
13065
14183
  align-items: center;
@@ -13131,14 +14249,16 @@ var IPODropdown = styled10__default.default.div`
13131
14249
  position: absolute;
13132
14250
  top: calc(100% + 6px);
13133
14251
  left: 0;
13134
- width: 320px;
14252
+ width: ${(p) => p.$wide ? "min(660px, calc(100vw - 24px))" : "320px"};
13135
14253
  background: #0d0d0f;
13136
14254
  border-radius: 10px;
13137
14255
  box-shadow: 0 12px 32px rgba(0,0,0,0.7), 0 0 0 1px rgba(255,255,255,0.06);
13138
14256
  z-index: 1000;
13139
- max-height: 320px;
14257
+ max-height: 360px;
13140
14258
  overflow-y: auto;
13141
- padding: 4px;
14259
+ /* Market mode: NO top padding — the sticky header must sit flush against the
14260
+ container's top edge so scrolling rows can never peek above/behind it. */
14261
+ padding: ${(p) => p.$wide ? "0 0 4px" : "4px"};
13142
14262
 
13143
14263
  scrollbar-width: thin;
13144
14264
  scrollbar-color: rgba(255,255,255,0.12) transparent;
@@ -13146,8 +14266,8 @@ var IPODropdown = styled10__default.default.div`
13146
14266
  &::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.12); border-radius: 2px; }
13147
14267
 
13148
14268
  @media (max-width: 480px) {
13149
- width: 280px;
13150
- max-height: 260px;
14269
+ width: ${(p) => p.$wide ? "min(360px, calc(100vw - 16px))" : "280px"};
14270
+ max-height: 300px;
13151
14271
  border-radius: 8px;
13152
14272
  }
13153
14273
  animation: slideDown 0.2s cubic-bezier(0.16,1,0.3,1);
@@ -13156,6 +14276,99 @@ var IPODropdown = styled10__default.default.div`
13156
14276
  to { opacity: 1; transform: translateY(0); }
13157
14277
  }
13158
14278
  `;
14279
+ var MARKET_GRID = "minmax(0, 1fr) 76px 92px 108px 118px";
14280
+ var MARKET_GRID_PHONE = "minmax(0, 1fr) 76px 92px";
14281
+ var HeaderRow = styled10__default.default.div`
14282
+ position: sticky;
14283
+ top: 0;
14284
+ z-index: 2;
14285
+ display: grid;
14286
+ grid-template-columns: ${MARKET_GRID};
14287
+ gap: 10px;
14288
+ align-items: center;
14289
+ padding: 0.5rem 0.85rem 0.45rem;
14290
+ background: #0d0d0f;
14291
+ border-bottom: 1px solid rgba(255,255,255,0.06);
14292
+
14293
+ @media (max-width: 480px) { grid-template-columns: ${MARKET_GRID_PHONE}; }
14294
+ `;
14295
+ var HeaderLabel = styled10__default.default.span`
14296
+ font-size: 0.6rem;
14297
+ font-weight: 600;
14298
+ letter-spacing: 0.08em;
14299
+ text-transform: uppercase;
14300
+ color: rgba(255,255,255,0.45);
14301
+ white-space: nowrap;
14302
+ `;
14303
+ var SortButton = styled10__default.default.button`
14304
+ background: none;
14305
+ border: none;
14306
+ padding: 0;
14307
+ cursor: pointer;
14308
+ font-family: inherit;
14309
+ font-size: 0.6rem;
14310
+ font-weight: 600;
14311
+ letter-spacing: 0.08em;
14312
+ text-transform: uppercase;
14313
+ white-space: nowrap;
14314
+ /* Left-aligned like every other column (user call, 2026-08-20). */
14315
+ text-align: left;
14316
+ justify-self: start;
14317
+ color: ${(p) => p.$active ? "var(--color-accent, #E6C87E)" : "rgba(255,255,255,0.45)"};
14318
+ transition: color 0.15s ease;
14319
+ &:hover { color: ${(p) => p.$active ? "var(--color-accent, #E6C87E)" : "rgba(255,255,255,0.75)"}; }
14320
+
14321
+ @media (max-width: 480px) {
14322
+ display: ${(p) => p.$hideOnPhone ? "none" : "inline"};
14323
+ }
14324
+ `;
14325
+ var IPOOptionIdentity = styled10__default.default.div`
14326
+ flex: 1 1 0;
14327
+ min-width: 0;
14328
+ overflow: hidden;
14329
+ `;
14330
+ var MarketRowInner = styled10__default.default.div`
14331
+ display: grid;
14332
+ grid-template-columns: ${MARKET_GRID};
14333
+ gap: 10px;
14334
+ align-items: center;
14335
+
14336
+ @media (max-width: 480px) { grid-template-columns: ${MARKET_GRID_PHONE}; }
14337
+ `;
14338
+ var IdentityCell = styled10__default.default.div`
14339
+ display: flex;
14340
+ align-items: center;
14341
+ gap: 0.65rem;
14342
+ min-width: 0;
14343
+ `;
14344
+ var PriceCell = styled10__default.default.span`
14345
+ font-size: 0.8rem;
14346
+ font-weight: 600;
14347
+ color: #fff;
14348
+ font-variant-numeric: tabular-nums;
14349
+ white-space: nowrap;
14350
+ text-align: left;
14351
+ `;
14352
+ var ChangeCell = styled10__default.default.span`
14353
+ font-size: 0.72rem;
14354
+ font-weight: 600;
14355
+ color: ${(p) => p.$positive ? "#0ecb81" : "#f6465d"};
14356
+ font-variant-numeric: tabular-nums;
14357
+ white-space: nowrap;
14358
+ text-align: left;
14359
+ `;
14360
+ var MutedCell = styled10__default.default.span`
14361
+ font-size: 0.72rem;
14362
+ /* Same ink as the price column (user call, 2026-08-20). */
14363
+ color: #fff;
14364
+ font-variant-numeric: tabular-nums;
14365
+ white-space: nowrap;
14366
+ text-align: left;
14367
+
14368
+ @media (max-width: 480px) {
14369
+ display: ${(p) => p.$hideOnPhone ? "none" : "inline"};
14370
+ }
14371
+ `;
13159
14372
  var IPOOption = styled10__default.default.div`
13160
14373
  padding: 0.6rem 0.75rem;
13161
14374
  transition: background 0.15s ease;
@@ -13181,6 +14394,25 @@ var IPOOptionName = styled10__default.default.div`
13181
14394
  font-size: 0.82rem;
13182
14395
  color: ${"#ebebeb"};
13183
14396
  line-height: 1.3;
14397
+ /* One line like the location — wrapped names made market rows uneven. */
14398
+ white-space: nowrap;
14399
+ overflow: hidden;
14400
+ text-overflow: ellipsis;
14401
+ `;
14402
+ var MarketTicker = styled10__default.default.span`
14403
+ font-weight: 700;
14404
+ letter-spacing: 0.01em;
14405
+ `;
14406
+ var TypePill = styled10__default.default.span`
14407
+ margin-left: 0.45rem;
14408
+ font-size: 0.55rem;
14409
+ font-weight: 700;
14410
+ letter-spacing: 0.08em;
14411
+ text-transform: uppercase;
14412
+ padding: 1px 5px;
14413
+ border-radius: 3px;
14414
+ vertical-align: middle;
14415
+ white-space: nowrap;
13184
14416
  `;
13185
14417
  var CompetitionBadge = styled10__default.default.span`
13186
14418
  margin-left: 0.4rem;
@@ -13199,6 +14431,11 @@ var IPOOptionLocation = styled10__default.default.div`
13199
14431
  font-size: 0.72rem;
13200
14432
  color: rgba(255,255,255,0.38);
13201
14433
  margin-top: 1px;
14434
+ /* One line, ellipsized — a long address must never wrap the row taller
14435
+ (matches the deployed dropdown's compact rows). */
14436
+ white-space: nowrap;
14437
+ overflow: hidden;
14438
+ text-overflow: ellipsis;
13202
14439
  span {
13203
14440
  margin-left: 0.4rem;
13204
14441
  color: var(--color-accent, #E6C87E);
@@ -13232,10 +14469,10 @@ var SlideOld = styled10__default.default.span`
13232
14469
  animation: ${slideOut} 0.35s cubic-bezier(0.4, 0, 0.2, 1) forwards;
13233
14470
  `;
13234
14471
  function SlideDigit({ value }) {
13235
- const prevRef = React5.useRef(value);
13236
- const [prev, setPrev] = React5.useState(null);
13237
- const [animKey, setAnimKey] = React5.useState(0);
13238
- React5.useEffect(() => {
14472
+ const prevRef = React9.useRef(value);
14473
+ const [prev, setPrev] = React9.useState(null);
14474
+ const [animKey, setAnimKey] = React9.useState(0);
14475
+ React9.useEffect(() => {
13239
14476
  if (value !== prevRef.current) {
13240
14477
  setPrev(prevRef.current);
13241
14478
  prevRef.current = value;
@@ -13275,15 +14512,15 @@ function OfferingProgressCard({
13275
14512
  style,
13276
14513
  className
13277
14514
  }) {
13278
- const [currentTime, setCurrentTime] = React5.useState(/* @__PURE__ */ new Date());
13279
- const [countdown, setCountdown] = React5.useState(null);
14515
+ const [currentTime, setCurrentTime] = React9.useState(/* @__PURE__ */ new Date());
14516
+ const [countdown, setCountdown] = React9.useState(null);
13280
14517
  const computedRaised = raisedAmount ?? totalSold * tokenPrice;
13281
14518
  const computedTarget = targetAmount ?? supplyToSell * tokenPrice;
13282
- React5.useEffect(() => {
14519
+ React9.useEffect(() => {
13283
14520
  const timer = setInterval(() => setCurrentTime(/* @__PURE__ */ new Date()), 1e3);
13284
14521
  return () => clearInterval(timer);
13285
14522
  }, []);
13286
- const [tradingCountdown, setTradingCountdown] = React5.useState(null);
14523
+ const [tradingCountdown, setTradingCountdown] = React9.useState(null);
13287
14524
  const calcCountdownFrom = (target) => {
13288
14525
  const now = Math.floor(Date.now() / 1e3);
13289
14526
  const diff = target - now;
@@ -13295,7 +14532,7 @@ function OfferingProgressCard({
13295
14532
  seconds: diff % 60
13296
14533
  };
13297
14534
  };
13298
- React5.useEffect(() => {
14535
+ React9.useEffect(() => {
13299
14536
  if (!opensAt) {
13300
14537
  setCountdown(null);
13301
14538
  return;
@@ -13304,7 +14541,7 @@ function OfferingProgressCard({
13304
14541
  const interval = setInterval(() => setCountdown(calcCountdownFrom(opensAt)), 1e3);
13305
14542
  return () => clearInterval(interval);
13306
14543
  }, [opensAt]);
13307
- React5.useEffect(() => {
14544
+ React9.useEffect(() => {
13308
14545
  if (!tradingStartsAt) {
13309
14546
  setTradingCountdown(null);
13310
14547
  return;
@@ -13798,8 +15035,8 @@ var HomeSpinner = styled10__default.default.span`
13798
15035
  }
13799
15036
  `;
13800
15037
  function useCountdown(opensAt) {
13801
- const [now, setNow] = React5.useState(() => Math.floor(Date.now() / 1e3));
13802
- React5.useEffect(() => {
15038
+ const [now, setNow] = React9.useState(() => Math.floor(Date.now() / 1e3));
15039
+ React9.useEffect(() => {
13803
15040
  if (opensAt == null) return;
13804
15041
  const id = setInterval(() => setNow(Math.floor(Date.now() / 1e3)), 1e3);
13805
15042
  return () => clearInterval(id);
@@ -13849,10 +15086,10 @@ function OrderPanel({
13849
15086
  toast
13850
15087
  }) {
13851
15088
  const countdown = useCountdown(opensAt);
13852
- const [payInputValue, setPayInputValue] = React5.useState("");
13853
- const [receiveInputValue, setReceiveInputValue] = React5.useState("");
13854
- const [isPayInputFocused, setIsPayInputFocused] = React5.useState(false);
13855
- const [isReceiveInputFocused, setIsReceiveInputFocused] = React5.useState(false);
15089
+ const [payInputValue, setPayInputValue] = React9.useState("");
15090
+ const [receiveInputValue, setReceiveInputValue] = React9.useState("");
15091
+ const [isPayInputFocused, setIsPayInputFocused] = React9.useState(false);
15092
+ const [isReceiveInputFocused, setIsReceiveInputFocused] = React9.useState(false);
13856
15093
  const handlePayBlur = () => {
13857
15094
  setIsPayInputFocused(false);
13858
15095
  const parsed = parseInt(payInputValue.replace(/,/g, "").replace(/[^0-9]/g, ""), 10) || 0;
@@ -14878,7 +16115,7 @@ function OrderConfirmationModal({
14878
16115
  isPlacing = false,
14879
16116
  placingLabel = "Placing order\u2026"
14880
16117
  }) {
14881
- const [dontShowAgain, setDontShowAgain] = React5.useState(false);
16118
+ const [dontShowAgain, setDontShowAgain] = React9.useState(false);
14882
16119
  if (!open) return null;
14883
16120
  const handleOverlayClose = () => {
14884
16121
  if (isPlacing) return;
@@ -15330,14 +16567,14 @@ function PortfolioActivityPanel({
15330
16567
  style
15331
16568
  }) {
15332
16569
  const resolvedDefaultTab = defaultTab ?? (showPositionsTab ? "positions" : showSubscriptionsTab ? "subscriptions" : "orders");
15333
- const [activeTab, setActiveTab] = React5.useState(resolvedDefaultTab);
15334
- const [currentPage, setCurrentPage] = React5.useState(0);
15335
- const [pendingCancelOrderId, setPendingCancelOrderId] = React5.useState(null);
15336
- const [cancelAllConfirmOpen, setCancelAllConfirmOpen] = React5.useState(false);
15337
- const [cancellingAll, setCancellingAll] = React5.useState(false);
15338
- const [closeAllOpen, setCloseAllOpen] = React5.useState(false);
15339
- const [closeAllType, setCloseAllType] = React5.useState("market");
15340
- const [closingAll, setClosingAll] = React5.useState(false);
16570
+ const [activeTab, setActiveTab] = React9.useState(resolvedDefaultTab);
16571
+ const [currentPage, setCurrentPage] = React9.useState(0);
16572
+ const [pendingCancelOrderId, setPendingCancelOrderId] = React9.useState(null);
16573
+ const [cancelAllConfirmOpen, setCancelAllConfirmOpen] = React9.useState(false);
16574
+ const [cancellingAll, setCancellingAll] = React9.useState(false);
16575
+ const [closeAllOpen, setCloseAllOpen] = React9.useState(false);
16576
+ const [closeAllType, setCloseAllType] = React9.useState("market");
16577
+ const [closingAll, setClosingAll] = React9.useState(false);
15341
16578
  const settlingPositionIds = positions.filter((p) => p.settling).map((p) => p.propertyId);
15342
16579
  const settlingKey = settlingPositionIds.join(",");
15343
16580
  const settlingTradeStartByProperty = /* @__PURE__ */ new Map();
@@ -15350,9 +16587,9 @@ function PortfolioActivityPanel({
15350
16587
  if (earliest === void 0 || startedAt < earliest) settlingTradeStartByProperty.set(trade.propertyId, startedAt);
15351
16588
  }
15352
16589
  const tradeDeadlineKey = settlingTradeDeadlines.join(",");
15353
- const settlingSinceRef = React5.useRef(/* @__PURE__ */ new Map());
15354
- const [now, setNow] = React5.useState(() => Date.now());
15355
- React5.useEffect(() => {
16590
+ const settlingSinceRef = React9.useRef(/* @__PURE__ */ new Map());
16591
+ const [now, setNow] = React9.useState(() => Date.now());
16592
+ React9.useEffect(() => {
15356
16593
  const since = settlingSinceRef.current;
15357
16594
  const seenAt = Date.now();
15358
16595
  for (const id of settlingPositionIds) if (!since.has(id)) since.set(id, seenAt);
@@ -15360,7 +16597,7 @@ function PortfolioActivityPanel({
15360
16597
  setNow(seenAt);
15361
16598
  }, [settlingKey]);
15362
16599
  const positionSettlingStart = (propertyId) => settlingTradeStartByProperty.get(propertyId) ?? settlingSinceRef.current.get(propertyId) ?? now;
15363
- React5.useEffect(() => {
16600
+ React9.useEffect(() => {
15364
16601
  const pending = [
15365
16602
  ...settlingPositionIds.map((id) => positionSettlingStart(id) + STUCK_SETTLING_MS),
15366
16603
  ...settlingTradeDeadlines
@@ -15373,8 +16610,8 @@ function PortfolioActivityPanel({
15373
16610
  const isPositionStuck = (pos) => !!pos.settling && now - positionSettlingStart(pos.propertyId) >= STUCK_SETTLING_MS;
15374
16611
  const isTradeStuck = (trade) => now - toMillis(trade.executedAt) >= STUCK_SETTLING_MS;
15375
16612
  const hasPositionActions = !!(onClosePosition || onSharePosition || onCloseAllPositions);
15376
- const [posSortKey, setPosSortKey] = React5.useState("asset");
15377
- const [posSortAsc, setPosSortAsc] = React5.useState(true);
16613
+ const [posSortKey, setPosSortKey] = React9.useState("asset");
16614
+ const [posSortAsc, setPosSortAsc] = React9.useState(true);
15378
16615
  const handlePosSort = (key) => {
15379
16616
  if (posSortKey === key) {
15380
16617
  setPosSortAsc((v) => !v);
@@ -15383,9 +16620,9 @@ function PortfolioActivityPanel({
15383
16620
  setPosSortAsc(key === "asset");
15384
16621
  }
15385
16622
  };
15386
- const [expandedPositions, setExpandedPositions] = React5.useState(/* @__PURE__ */ new Set());
15387
- const [allExpanded, setAllExpanded] = React5.useState(false);
15388
- const [sortDropdownOpen, setSortDropdownOpen] = React5.useState(false);
16623
+ const [expandedPositions, setExpandedPositions] = React9.useState(/* @__PURE__ */ new Set());
16624
+ const [allExpanded, setAllExpanded] = React9.useState(false);
16625
+ const [sortDropdownOpen, setSortDropdownOpen] = React9.useState(false);
15389
16626
  const togglePosition = (tokenName) => {
15390
16627
  setExpandedPositions((prev) => {
15391
16628
  const next = new Set(prev);
@@ -15394,7 +16631,7 @@ function PortfolioActivityPanel({
15394
16631
  return next;
15395
16632
  });
15396
16633
  };
15397
- const [expandedOpenOrders, setExpandedOpenOrders] = React5.useState(/* @__PURE__ */ new Set());
16634
+ const [expandedOpenOrders, setExpandedOpenOrders] = React9.useState(/* @__PURE__ */ new Set());
15398
16635
  const toggleOpenOrder = (id) => {
15399
16636
  setExpandedOpenOrders((prev) => {
15400
16637
  const next = new Set(prev);
@@ -15403,7 +16640,7 @@ function PortfolioActivityPanel({
15403
16640
  return next;
15404
16641
  });
15405
16642
  };
15406
- const [expandedRows, setExpandedRows] = React5.useState(/* @__PURE__ */ new Set());
16643
+ const [expandedRows, setExpandedRows] = React9.useState(/* @__PURE__ */ new Set());
15407
16644
  const toggleRow = (key) => {
15408
16645
  setExpandedRows((prev) => {
15409
16646
  const next = new Set(prev);
@@ -15431,7 +16668,7 @@ function PortfolioActivityPanel({
15431
16668
  }
15432
16669
  };
15433
16670
  const sortKeys = ["asset", "units", "value", "avgPrice", "currentPrice", "pnl"];
15434
- const sortedPositions = React5.useMemo(() => {
16671
+ const sortedPositions = React9.useMemo(() => {
15435
16672
  const arr = [...positions];
15436
16673
  const dir = posSortAsc ? 1 : -1;
15437
16674
  arr.sort((a, b) => {
@@ -15466,7 +16703,7 @@ function PortfolioActivityPanel({
15466
16703
  setAllExpanded(true);
15467
16704
  }
15468
16705
  };
15469
- const mergedOrderHistory = React5.useMemo(() => {
16706
+ const mergedOrderHistory = React9.useMemo(() => {
15470
16707
  if (showSubscriptionsTab || offeringOrders.length === 0) {
15471
16708
  return [...orderHistory].sort((a, b) => b.createdAt - a.createdAt);
15472
16709
  }
@@ -15491,32 +16728,32 @@ function PortfolioActivityPanel({
15491
16728
  }));
15492
16729
  return [...orderHistory, ...mapped].sort((a, b) => b.createdAt - a.createdAt);
15493
16730
  }, [showSubscriptionsTab, offeringOrders, orderHistory]);
15494
- const sortedOpenOrders = React5.useMemo(
16731
+ const sortedOpenOrders = React9.useMemo(
15495
16732
  () => [...openOrders].sort((a, b) => b.createdAt - a.createdAt),
15496
16733
  [openOrders]
15497
16734
  );
15498
- const sortedOfferingOrders = React5.useMemo(
16735
+ const sortedOfferingOrders = React9.useMemo(
15499
16736
  () => [...offeringOrders].sort((a, b) => b.createdAt - a.createdAt),
15500
16737
  [offeringOrders]
15501
16738
  );
15502
- const sortedTradeHistory = React5.useMemo(
16739
+ const sortedTradeHistory = React9.useMemo(
15503
16740
  () => [...tradeHistory].sort((a, b) => b.executedAt - a.executedAt),
15504
16741
  [tradeHistory]
15505
16742
  );
15506
- const sortedTransfers = React5.useMemo(
16743
+ const sortedTransfers = React9.useMemo(
15507
16744
  () => [...transfers].sort((a, b) => b.createdAt - a.createdAt),
15508
16745
  [transfers]
15509
16746
  );
15510
16747
  const positionsCount = positions.length;
15511
- const openOrdersCount = React5.useMemo(
16748
+ const openOrdersCount = React9.useMemo(
15512
16749
  () => openOrders.filter((o) => o.status === "OPEN" || o.status === "PARTIALLY_FILLED").length,
15513
16750
  [openOrders]
15514
16751
  );
15515
- const pendingHistoryCount = React5.useMemo(
16752
+ const pendingHistoryCount = React9.useMemo(
15516
16753
  () => mergedOrderHistory.filter((o) => o.status === "OPEN" || o.status === "PARTIALLY_FILLED").length,
15517
16754
  [mergedOrderHistory]
15518
16755
  );
15519
- const pendingOfferingsCount = React5.useMemo(
16756
+ const pendingOfferingsCount = React9.useMemo(
15520
16757
  () => offeringOrders.filter((o) => {
15521
16758
  const s = o.status.toUpperCase();
15522
16759
  return s !== "FILLED" && s !== "ALLOCATED" && s !== "CONFIRMED" && s !== "CANCELLED" && s !== "CANCELED" && s !== "REJECTED";
@@ -16363,7 +17600,7 @@ var ContentScroll = styled10__default.default.div`
16363
17600
  `;
16364
17601
  var TabContainer = styled10__default.default.div`
16365
17602
  display: flex;
16366
- gap: 0;
17603
+ gap: 1rem;
16367
17604
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
16368
17605
  margin-bottom: 0.75rem;
16369
17606
  overflow-x: auto;
@@ -16375,12 +17612,12 @@ var TabContainer = styled10__default.default.div`
16375
17612
  var Tab = styled10__default.default.button`
16376
17613
  background: transparent;
16377
17614
  border: none;
16378
- padding: 0.6rem 1rem;
16379
- font-size: 0.75rem;
17615
+ padding: 0 0 0.5rem;
17616
+ font-size: 0.8rem;
16380
17617
  font-weight: 500;
16381
17618
  white-space: nowrap;
16382
17619
  flex-shrink: 0;
16383
- color: ${({ $active }) => $active ? "#E6C87E" : "rgba(255, 255, 255, 0.4)"};
17620
+ color: ${({ $active }) => $active ? "#fff" : "rgba(255, 255, 255, 0.6)"};
16384
17621
  cursor: pointer;
16385
17622
  transition: color 0.15s;
16386
17623
  position: relative;
@@ -16393,12 +17630,11 @@ var Tab = styled10__default.default.button`
16393
17630
  right: 0;
16394
17631
  height: 2px;
16395
17632
  background: ${({ $active }) => $active ? "#E6C87E" : "transparent"};
16396
- border-radius: 1px;
16397
17633
  transition: background 0.15s;
16398
17634
  }
16399
17635
 
16400
17636
  &:hover {
16401
- color: ${({ $active }) => $active ? "#fff" : "rgba(255, 255, 255, 0.65)"};
17637
+ color: ${({ $active }) => $active ? "#fff" : "rgba(255, 255, 255, 0.85)"};
16402
17638
  }
16403
17639
  `;
16404
17640
  var TabCount = styled10__default.default.span`
@@ -17117,17 +18353,17 @@ function PropertyBuy({
17117
18353
  videoUrl,
17118
18354
  toast
17119
18355
  }) {
17120
- const [sliderValue, setSliderValue] = React5.useState(0);
17121
- const [availableBalance, setAvailableBalance] = React5.useState(walletUsdcBalance ?? 0);
17122
- const [manualOrderAmount, setManualOrderAmount] = React5.useState(null);
17123
- const [ownedTokens, setOwnedTokens] = React5.useState(0);
17124
- const [displayedOwnedTokens, setDisplayedOwnedTokens] = React5.useState(0);
17125
- const [ownedTokensJustUpdated, setOwnedTokensJustUpdated] = React5.useState(false);
17126
- const [lastOrderQuantity, setLastOrderQuantity] = React5.useState(0);
17127
- const [orderPendingAllocation, setOrderPendingAllocation] = React5.useState(false);
17128
- const [orderPlacedSuccess, setOrderPlacedSuccess] = React5.useState(false);
17129
- const [lastOrderDetails, setLastOrderDetails] = React5.useState(null);
17130
- const [showOrderConfirmModal, setShowOrderConfirmModal] = React5.useState(false);
18356
+ const [sliderValue, setSliderValue] = React9.useState(0);
18357
+ const [availableBalance, setAvailableBalance] = React9.useState(walletUsdcBalance ?? 0);
18358
+ const [manualOrderAmount, setManualOrderAmount] = React9.useState(null);
18359
+ const [ownedTokens, setOwnedTokens] = React9.useState(0);
18360
+ const [displayedOwnedTokens, setDisplayedOwnedTokens] = React9.useState(0);
18361
+ const [ownedTokensJustUpdated, setOwnedTokensJustUpdated] = React9.useState(false);
18362
+ const [lastOrderQuantity, setLastOrderQuantity] = React9.useState(0);
18363
+ const [orderPendingAllocation, setOrderPendingAllocation] = React9.useState(false);
18364
+ const [orderPlacedSuccess, setOrderPlacedSuccess] = React9.useState(false);
18365
+ const [lastOrderDetails, setLastOrderDetails] = React9.useState(null);
18366
+ const [showOrderConfirmModal, setShowOrderConfirmModal] = React9.useState(false);
17131
18367
  const tokenPrice = saleData?.tokenPrice ?? 250;
17132
18368
  const feeRate = (saleData?.feePercent ?? 0.5) / 100;
17133
18369
  const totalSold = saleData?.totalSold ?? 0;
@@ -17147,12 +18383,12 @@ function PropertyBuy({
17147
18383
  const orderTotal = tokenQuantity * tokenPrice + feeInUsd;
17148
18384
  const estExposure = (tokenQuantity / supplyToSell * 100).toFixed(4);
17149
18385
  const hasInsufficientFunds = orderTotal > availableBalance;
17150
- React5.useEffect(() => {
18386
+ React9.useEffect(() => {
17151
18387
  if (walletUsdcBalance != null) {
17152
18388
  setAvailableBalance(walletUsdcBalance);
17153
18389
  }
17154
18390
  }, [walletUsdcBalance]);
17155
- React5.useEffect(() => {
18391
+ React9.useEffect(() => {
17156
18392
  if (walletPropertyTokenBalance != null) {
17157
18393
  setOwnedTokens(walletPropertyTokenBalance);
17158
18394
  setDisplayedOwnedTokens(walletPropertyTokenBalance);
@@ -17168,17 +18404,17 @@ function PropertyBuy({
17168
18404
  }
17169
18405
  setShowOrderConfirmModal(true);
17170
18406
  };
17171
- React5.useEffect(() => {
18407
+ React9.useEffect(() => {
17172
18408
  if (purchaseStatus === "success" && orderPendingAllocation) {
17173
18409
  setOrderPendingAllocation(false);
17174
18410
  setOrderPlacedSuccess(true);
17175
18411
  }
17176
18412
  }, [purchaseStatus, orderPendingAllocation]);
17177
- const handlePlaceAnotherOrder = React5.useCallback(() => {
18413
+ const handlePlaceAnotherOrder = React9.useCallback(() => {
17178
18414
  setOrderPlacedSuccess(false);
17179
18415
  setOrderPendingAllocation(false);
17180
18416
  }, []);
17181
- const sortedOrders = React5.useMemo(
18417
+ const sortedOrders = React9.useMemo(
17182
18418
  () => [...recentOrders].sort((a, b) => b.timestamp - a.timestamp).slice(0, 7),
17183
18419
  [recentOrders]
17184
18420
  );
@@ -17748,7 +18984,7 @@ function PaymentPopup({
17748
18984
  userTokenHoldings,
17749
18985
  propertyName
17750
18986
  }) {
17751
- const [selectedPaymentMethod, setSelectedPaymentMethod] = React5.useState(
18987
+ const [selectedPaymentMethod, setSelectedPaymentMethod] = React9.useState(
17752
18988
  "tokens"
17753
18989
  );
17754
18990
  if (!isOpen) return null;
@@ -17927,20 +19163,20 @@ function PaymentPopup({
17927
19163
  var payment_popup_default = PaymentPopup;
17928
19164
  var SUMMER_MONTHS = /* @__PURE__ */ new Set([11, 0, 1]);
17929
19165
  var OwnerBooking = ({ propertyName, token }) => {
17930
- const today = React5.useMemo(() => {
19166
+ const today = React9.useMemo(() => {
17931
19167
  const base = /* @__PURE__ */ new Date();
17932
19168
  base.setHours(0, 0, 0, 0);
17933
19169
  return base;
17934
19170
  }, []);
17935
- const [currentYear, setCurrentYear] = React5.useState(today.getFullYear());
17936
- const [currentMonth, setCurrentMonth] = React5.useState(today.getMonth());
17937
- const [dateRanges, setDateRanges] = React5.useState([]);
17938
- const [selectedDates, setSelectedDates] = React5.useState([]);
17939
- const [hoverDate, setHoverDate] = React5.useState(null);
17940
- const [isDateSelectorOpen, setIsDateSelectorOpen] = React5.useState(false);
17941
- const [selectorYear, setSelectorYear] = React5.useState(today.getFullYear());
17942
- const [isPaymentPopupOpen, setIsPaymentPopupOpen] = React5.useState(false);
17943
- const dateSelectorRef = React5.useRef(null);
19171
+ const [currentYear, setCurrentYear] = React9.useState(today.getFullYear());
19172
+ const [currentMonth, setCurrentMonth] = React9.useState(today.getMonth());
19173
+ const [dateRanges, setDateRanges] = React9.useState([]);
19174
+ const [selectedDates, setSelectedDates] = React9.useState([]);
19175
+ const [hoverDate, setHoverDate] = React9.useState(null);
19176
+ const [isDateSelectorOpen, setIsDateSelectorOpen] = React9.useState(false);
19177
+ const [selectorYear, setSelectorYear] = React9.useState(today.getFullYear());
19178
+ const [isPaymentPopupOpen, setIsPaymentPopupOpen] = React9.useState(false);
19179
+ const dateSelectorRef = React9.useRef(null);
17944
19180
  const tokenPrice = token.price ?? 700;
17945
19181
  const userTokenHoldings = 2.2;
17946
19182
  const MIN_NIGHTS = 3;
@@ -17950,25 +19186,25 @@ var OwnerBooking = ({ propertyName, token }) => {
17950
19186
  { bg: "rgba(230, 126, 34, 0.25)", border: "rgba(230, 126, 34, 0.6)" },
17951
19187
  { bg: "rgba(46, 204, 113, 0.25)", border: "rgba(46, 204, 113, 0.6)" }
17952
19188
  ];
17953
- const calculateTokensPerDayOwner = React5.useCallback(() => {
19189
+ const calculateTokensPerDayOwner = React9.useCallback(() => {
17954
19190
  if (tokenPrice <= 0) return 0;
17955
19191
  const target = 650;
17956
19192
  const raw = target / tokenPrice;
17957
19193
  return Math.ceil(raw * 1e4) / 1e4;
17958
19194
  }, [tokenPrice]);
17959
- const calculateTokensPerDayMarket = React5.useCallback(() => {
19195
+ const calculateTokensPerDayMarket = React9.useCallback(() => {
17960
19196
  if (tokenPrice <= 0) return 0;
17961
19197
  const target = 900;
17962
19198
  const raw = target / tokenPrice;
17963
19199
  return Math.ceil(raw * 1e4) / 1e4;
17964
19200
  }, [tokenPrice]);
17965
19201
  const qualifiesForOwnerRate = userTokenHoldings >= 0.1;
17966
- const getTokensPerDay = React5.useCallback(
19202
+ const getTokensPerDay = React9.useCallback(
17967
19203
  () => calculateTokensPerDayOwner() ,
17968
19204
  [qualifiesForOwnerRate, calculateTokensPerDayOwner, calculateTokensPerDayMarket]
17969
19205
  );
17970
19206
  const getRateLabel = () => "Owner's Rate" ;
17971
- const selectionStats = React5.useMemo(() => {
19207
+ const selectionStats = React9.useMemo(() => {
17972
19208
  if (selectedDates.length !== 2) return null;
17973
19209
  const [a, b] = selectedDates;
17974
19210
  const start = a < b ? a : b;
@@ -17979,10 +19215,10 @@ var OwnerBooking = ({ propertyName, token }) => {
17979
19215
  const usd = tokens * tokenPrice;
17980
19216
  return { nights, tokensPerDay, tokens, usd, start, end };
17981
19217
  }, [selectedDates, getTokensPerDay, tokenPrice]);
17982
- const totalNights = React5.useMemo(() => dateRanges.reduce((sum, r) => sum + r.nights, 0), [dateRanges]);
17983
- const totalTokens = React5.useMemo(() => dateRanges.reduce((sum, r) => sum + r.tokens, 0), [dateRanges]);
19218
+ const totalNights = React9.useMemo(() => dateRanges.reduce((sum, r) => sum + r.nights, 0), [dateRanges]);
19219
+ const totalTokens = React9.useMemo(() => dateRanges.reduce((sum, r) => sum + r.tokens, 0), [dateRanges]);
17984
19220
  const totalUSD = totalTokens * tokenPrice;
17985
- const generateCalendarDays = React5.useCallback(
19221
+ const generateCalendarDays = React9.useCallback(
17986
19222
  (year, month) => {
17987
19223
  const first = new Date(year, month, 1);
17988
19224
  const last = new Date(year, month + 1, 0);
@@ -18011,11 +19247,11 @@ var OwnerBooking = ({ propertyName, token }) => {
18011
19247
  },
18012
19248
  [today]
18013
19249
  );
18014
- const calendarDays = React5.useMemo(
19250
+ const calendarDays = React9.useMemo(
18015
19251
  () => generateCalendarDays(currentYear, currentMonth),
18016
19252
  [currentYear, currentMonth, generateCalendarDays]
18017
19253
  );
18018
- const isDateReserved = React5.useCallback(
19254
+ const isDateReserved = React9.useCallback(
18019
19255
  (date) => dateRanges.some((r) => date >= r.arrival && date <= r.departure),
18020
19256
  [dateRanges]
18021
19257
  );
@@ -18109,14 +19345,14 @@ var OwnerBooking = ({ propertyName, token }) => {
18109
19345
  "December"
18110
19346
  ];
18111
19347
  const dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
18112
- const setMonthYear = React5.useCallback(
19348
+ const setMonthYear = React9.useCallback(
18113
19349
  (month, year) => {
18114
19350
  setCurrentMonth(month);
18115
19351
  setCurrentYear(year);
18116
19352
  },
18117
19353
  [setCurrentMonth, setCurrentYear]
18118
19354
  );
18119
- const shiftMonth = React5.useCallback(
19355
+ const shiftMonth = React9.useCallback(
18120
19356
  (direction) => {
18121
19357
  const delta = direction === "prev" ? -1 : 1;
18122
19358
  let newMonth = currentMonth + delta;
@@ -18133,7 +19369,7 @@ var OwnerBooking = ({ propertyName, token }) => {
18133
19369
  },
18134
19370
  [currentMonth, currentYear, setMonthYear]
18135
19371
  );
18136
- const jumpToNextAvailable = React5.useCallback(() => {
19372
+ const jumpToNextAvailable = React9.useCallback(() => {
18137
19373
  for (let offset = 1; offset <= 12; offset++) {
18138
19374
  const candidateMonth = (currentMonth + offset) % 12;
18139
19375
  if (!SUMMER_MONTHS.has(candidateMonth)) continue;
@@ -18150,7 +19386,7 @@ var OwnerBooking = ({ propertyName, token }) => {
18150
19386
  setMonthYear(month, selectorYear);
18151
19387
  setIsDateSelectorOpen(false);
18152
19388
  };
18153
- React5.useEffect(() => {
19389
+ React9.useEffect(() => {
18154
19390
  const handleClickOutside = (event) => {
18155
19391
  if (dateSelectorRef.current && !dateSelectorRef.current.contains(event.target)) {
18156
19392
  setIsDateSelectorOpen(false);
@@ -18583,8 +19819,8 @@ function PropertyValuation({
18583
19819
  offeringValuationValue,
18584
19820
  totalUnits
18585
19821
  }) {
18586
- const [hoveredSaleYear, setHoveredSaleYear] = React5.useState(null);
18587
- const gaugePosition = React5.useMemo(() => {
19822
+ const [hoveredSaleYear, setHoveredSaleYear] = React9.useState(null);
19823
+ const gaugePosition = React9.useMemo(() => {
18588
19824
  const under = valSummary?.undervaluedThreshold;
18589
19825
  const over = valSummary?.overvaluedThreshold;
18590
19826
  const price = tokenPriceValue;
@@ -18594,7 +19830,7 @@ function PropertyValuation({
18594
19830
  }
18595
19831
  return 50;
18596
19832
  }, [valSummary?.undervaluedThreshold, valSummary?.overvaluedThreshold, tokenPriceValue]);
18597
- const chartOptions = React5.useMemo(() => ({
19833
+ const chartOptions = React9.useMemo(() => ({
18598
19834
  responsive: true,
18599
19835
  maintainAspectRatio: false,
18600
19836
  onHover: (_event, elements) => {
@@ -18637,7 +19873,7 @@ function PropertyValuation({
18637
19873
  }
18638
19874
  }
18639
19875
  }), []);
18640
- const chartData = React5.useMemo(() => ({
19876
+ const chartData = React9.useMemo(() => ({
18641
19877
  labels: musgraveComparisonData.labels,
18642
19878
  datasets: musgraveComparisonData.datasets.map((dataset, index) => ({
18643
19879
  label: dataset.name,
@@ -18678,8 +19914,8 @@ function PropertyValuation({
18678
19914
  fill: false
18679
19915
  }))
18680
19916
  }), [hoveredSaleYear]);
18681
- const handleMarkerEnter = React5.useCallback((year) => setHoveredSaleYear(year), []);
18682
- const handleMarkerLeave = React5.useCallback(() => setHoveredSaleYear(null), []);
19917
+ const handleMarkerEnter = React9.useCallback((year) => setHoveredSaleYear(year), []);
19918
+ const handleMarkerLeave = React9.useCallback(() => setHoveredSaleYear(null), []);
18683
19919
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
18684
19920
  /* @__PURE__ */ jsxRuntime.jsx(ValuationHeading, { children: "Valuation Model" }),
18685
19921
  /* @__PURE__ */ jsxRuntime.jsxs(LoafPricingSection, { children: [
@@ -19604,20 +20840,20 @@ function PropertyPhotoGallery({
19604
20840
  );
19605
20841
  }
19606
20842
  function GalleryContent({ galleryImages, startIndex, startWithVideo, title, subtitle, onClose, videoUrl }) {
19607
- const [currentIndex, setCurrentIndex] = React5.useState(() => clampIndex(startIndex, galleryImages.length));
19608
- const [activeHotspotId, setActiveHotspotId] = React5.useState(null);
19609
- const [showVideo, setShowVideo] = React5.useState(startWithVideo ?? false);
19610
- const sliderRef = React5.useRef(null);
19611
- const [isDragging, setIsDragging] = React5.useState(false);
19612
- const wasDraggingRef = React5.useRef(false);
19613
- const touchStartX = React5.useRef(null);
19614
- const touchStartY = React5.useRef(null);
19615
- const isSwiping = React5.useRef(false);
19616
- const progress = React5.useMemo(() => {
20843
+ const [currentIndex, setCurrentIndex] = React9.useState(() => clampIndex(startIndex, galleryImages.length));
20844
+ const [activeHotspotId, setActiveHotspotId] = React9.useState(null);
20845
+ const [showVideo, setShowVideo] = React9.useState(startWithVideo ?? false);
20846
+ const sliderRef = React9.useRef(null);
20847
+ const [isDragging, setIsDragging] = React9.useState(false);
20848
+ const wasDraggingRef = React9.useRef(false);
20849
+ const touchStartX = React9.useRef(null);
20850
+ const touchStartY = React9.useRef(null);
20851
+ const isSwiping = React9.useRef(false);
20852
+ const progress = React9.useMemo(() => {
19617
20853
  if (galleryImages.length <= 1) return 0;
19618
20854
  return currentIndex / (galleryImages.length - 1) * 100;
19619
20855
  }, [currentIndex, galleryImages.length]);
19620
- React5.useEffect(() => {
20856
+ React9.useEffect(() => {
19621
20857
  document.body.style.overflow = "hidden";
19622
20858
  document.body.style.touchAction = "none";
19623
20859
  return () => {
@@ -19625,7 +20861,7 @@ function GalleryContent({ galleryImages, startIndex, startWithVideo, title, subt
19625
20861
  document.body.style.touchAction = "";
19626
20862
  };
19627
20863
  }, []);
19628
- React5.useEffect(() => {
20864
+ React9.useEffect(() => {
19629
20865
  const handleKey = (event) => {
19630
20866
  if (event.key === "Escape") onClose();
19631
20867
  if (event.key === "ArrowLeft") setCurrentIndex((i) => i === 0 ? galleryImages.length - 1 : i - 1);
@@ -20029,23 +21265,23 @@ function truncateHash(hash) {
20029
21265
  }
20030
21266
  var SWIPE_DISMISS_PX = 100;
20031
21267
  function ToastItem({ toast, onDismiss }) {
20032
- const [exiting, setExiting] = React5.useState(false);
20033
- const [dragX, setDragX] = React5.useState(0);
20034
- const [dragging, setDragging] = React5.useState(false);
20035
- const [touched, setTouched] = React5.useState(false);
20036
- const timerRef = React5.useRef(null);
20037
- const dragStartRef = React5.useRef(null);
20038
- const dragXRef = React5.useRef(0);
20039
- const dismiss = React5.useCallback(() => {
21268
+ const [exiting, setExiting] = React9.useState(false);
21269
+ const [dragX, setDragX] = React9.useState(0);
21270
+ const [dragging, setDragging] = React9.useState(false);
21271
+ const [touched, setTouched] = React9.useState(false);
21272
+ const timerRef = React9.useRef(null);
21273
+ const dragStartRef = React9.useRef(null);
21274
+ const dragXRef = React9.useRef(0);
21275
+ const dismiss = React9.useCallback(() => {
20040
21276
  setExiting(true);
20041
21277
  setTimeout(() => onDismiss(toast.id), 280);
20042
21278
  }, [onDismiss, toast.id]);
20043
- const armAutoDismiss = React5.useCallback(() => {
21279
+ const armAutoDismiss = React9.useCallback(() => {
20044
21280
  if (timerRef.current) clearTimeout(timerRef.current);
20045
21281
  const duration2 = toast.duration ?? 6e3;
20046
21282
  if (duration2 > 0) timerRef.current = setTimeout(dismiss, duration2);
20047
21283
  }, [dismiss, toast.duration]);
20048
- React5.useEffect(() => {
21284
+ React9.useEffect(() => {
20049
21285
  armAutoDismiss();
20050
21286
  return () => {
20051
21287
  if (timerRef.current) clearTimeout(timerRef.current);
@@ -20125,10 +21361,10 @@ function ToastItem({ toast, onDismiss }) {
20125
21361
  }
20126
21362
  );
20127
21363
  }
20128
- var ToastContext = React5.createContext(null);
21364
+ var ToastContext = React9.createContext(null);
20129
21365
  function ToastProvider({ children, maxVisible }) {
20130
- const [toasts, setToasts] = React5.useState([]);
20131
- const addToast = React5.useCallback((data) => {
21366
+ const [toasts, setToasts] = React9.useState([]);
21367
+ const addToast = React9.useCallback((data) => {
20132
21368
  const id = `toast-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
20133
21369
  setToasts((prev) => {
20134
21370
  const next = [...prev, { ...data, id }];
@@ -20136,10 +21372,10 @@ function ToastProvider({ children, maxVisible }) {
20136
21372
  });
20137
21373
  return id;
20138
21374
  }, [maxVisible]);
20139
- const update = React5.useCallback((id, patch) => {
21375
+ const update = React9.useCallback((id, patch) => {
20140
21376
  setToasts((prev) => prev.map((t) => t.id === id ? { ...t, ...patch } : t));
20141
21377
  }, []);
20142
- const dismiss = React5.useCallback((id) => {
21378
+ const dismiss = React9.useCallback((id) => {
20143
21379
  setToasts((prev) => prev.filter((t) => t.id !== id));
20144
21380
  }, []);
20145
21381
  return /* @__PURE__ */ jsxRuntime.jsxs(ToastContext.Provider, { value: { toast: addToast, update, dismiss }, children: [
@@ -20148,15 +21384,15 @@ function ToastProvider({ children, maxVisible }) {
20148
21384
  ] });
20149
21385
  }
20150
21386
  function useToast() {
20151
- const ctx = React5.useContext(ToastContext);
21387
+ const ctx = React9.useContext(ToastContext);
20152
21388
  if (!ctx) throw new Error("useToast must be used within a ToastProvider");
20153
21389
  return ctx;
20154
21390
  }
20155
21391
  var INTERVALS = [1e3, 2e3, 4e3, 5e3, 7e3, 1e4, 12e3];
20156
21392
  function useAdaptivePolling({ enabled, onPoll }) {
20157
- const onPollRef = React5.useRef(onPoll);
21393
+ const onPollRef = React9.useRef(onPoll);
20158
21394
  onPollRef.current = onPoll;
20159
- React5.useEffect(() => {
21395
+ React9.useEffect(() => {
20160
21396
  if (!enabled) return;
20161
21397
  let cancelled = false;
20162
21398
  let timeoutId = null;
@@ -20329,7 +21565,7 @@ var OpenMailButton = styled10__default.default.a`
20329
21565
  }
20330
21566
  `;
20331
21567
  var ContactPopup = ({ onClose }) => {
20332
- const [copied, setCopied] = React5.useState(false);
21568
+ const [copied, setCopied] = React9.useState(false);
20333
21569
  const email = "admin@loafmarkets.com";
20334
21570
  const handleCopy = async () => {
20335
21571
  try {
@@ -20538,7 +21774,7 @@ var DisclaimerText = styled10__default.default.p`
20538
21774
  max-width: 100%;
20539
21775
  `;
20540
21776
  var SiteFooter = () => {
20541
- const [showContactPopup, setShowContactPopup] = React5.useState(false);
21777
+ const [showContactPopup, setShowContactPopup] = React9.useState(false);
20542
21778
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
20543
21779
  /* @__PURE__ */ jsxRuntime.jsx(FooterContainer, { children: /* @__PURE__ */ jsxRuntime.jsxs(FooterContent, { children: [
20544
21780
  /* @__PURE__ */ jsxRuntime.jsxs(FooterTop, { children: [
@@ -20627,10 +21863,10 @@ function extractSparkline(candles, maxPoints = 20) {
20627
21863
  }
20628
21864
  return points;
20629
21865
  }
20630
- var SparklineChart = React5__namespace.default.memo(function SparklineChart2({ data, width, height, positive }) {
20631
- const [hoverX, setHoverX] = React5.useState(null);
20632
- const svgRef = React5.useRef(null);
20633
- const { linePath, fillPath, points } = React5.useMemo(() => {
21866
+ var SparklineChart = React9__namespace.default.memo(function SparklineChart2({ data, width, height, positive }) {
21867
+ const [hoverX, setHoverX] = React9.useState(null);
21868
+ const svgRef = React9.useRef(null);
21869
+ const { linePath, fillPath, points } = React9.useMemo(() => {
20634
21870
  const mn = Math.min(...data);
20635
21871
  const mx = Math.max(...data);
20636
21872
  const range = mx - mn || 1;
@@ -20643,7 +21879,7 @@ var SparklineChart = React5__namespace.default.memo(function SparklineChart2({ d
20643
21879
  const fill = `${line} L${width},${height} L0,${height} Z`;
20644
21880
  return { linePath: line, fillPath: fill, points: pts };
20645
21881
  }, [data, width, height]);
20646
- const interpolated = React5.useMemo(() => {
21882
+ const interpolated = React9.useMemo(() => {
20647
21883
  if (hoverX == null || points.length < 2) return null;
20648
21884
  const stepX = width / (data.length - 1);
20649
21885
  const rawIdx = hoverX / stepX;
@@ -20656,14 +21892,14 @@ var SparklineChart = React5__namespace.default.memo(function SparklineChart2({ d
20656
21892
  price: lerp(data[i0], data[i1], t)
20657
21893
  };
20658
21894
  }, [hoverX, points, data, width]);
20659
- const handleMouseMove = React5.useCallback((e) => {
21895
+ const handleMouseMove = React9.useCallback((e) => {
20660
21896
  const svg = svgRef.current;
20661
21897
  if (!svg) return;
20662
21898
  const rect = svg.getBoundingClientRect();
20663
21899
  const x = Math.max(0, Math.min(width, e.clientX - rect.left));
20664
21900
  setHoverX(x);
20665
21901
  }, [width]);
20666
- const handleMouseLeave = React5.useCallback(() => setHoverX(null), []);
21902
+ const handleMouseLeave = React9.useCallback(() => setHoverX(null), []);
20667
21903
  if (data.length < 2) return null;
20668
21904
  const color = positive ? "#00C076" : "#FF5757";
20669
21905
  const gradId = `spark-grad-${positive ? "up" : "dn"}`;
@@ -20781,6 +22017,7 @@ exports.PropertyPhotoGallery = PropertyPhotoGallery;
20781
22017
  exports.PropertySubheader = PropertySubheader;
20782
22018
  exports.PropertyTour = PropertyTour;
20783
22019
  exports.PropertyValuation = PropertyValuation;
22020
+ exports.SentimentBar = SentimentBar;
20784
22021
  exports.SiteFooter = SiteFooter;
20785
22022
  exports.Skeleton = Skeleton;
20786
22023
  exports.SlideDigit = SlideDigit;
@@ -20789,10 +22026,13 @@ exports.ToastProvider = ToastProvider;
20789
22026
  exports.TradeConfirmationModal = TradeConfirmationModal;
20790
22027
  exports.TradingSlider = TradingSlider;
20791
22028
  exports.YourOrders = YourOrders;
22029
+ exports.__resetDismissedBreaking = __resetDismissedBreaking;
20792
22030
  exports.badgeVariants = badgeVariants;
20793
22031
  exports.buttonVariants = buttonVariants;
20794
22032
  exports.extractSparkline = extractSparkline;
22033
+ exports.formatAge = formatAge;
20795
22034
  exports.hasPendingActivity = hasPendingActivity;
22035
+ exports.rowAgeLabel = rowAgeLabel;
20796
22036
  exports.useAdaptivePolling = useAdaptivePolling;
20797
22037
  exports.useToast = useToast;
20798
22038
  //# sourceMappingURL=index.js.map