@loafmarkets/ui 0.1.414 → 0.1.416

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,13 +1,13 @@
1
- import * as React5 from 'react';
2
- import React5__default, { createContext, useState, useRef, useMemo, useCallback, useEffect, useContext } from 'react';
1
+ import * as React9 from 'react';
2
+ import React9__default, { createContext, useState, useRef, useMemo, useCallback, useEffect, useLayoutEffect, useContext } from 'react';
3
3
  import { Slot } from '@radix-ui/react-slot';
4
4
  import { cva } from 'class-variance-authority';
5
5
  import { clsx } from 'clsx';
6
6
  import { twMerge } from 'tailwind-merge';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
- import styled10, { keyframes, css } from 'styled-components';
9
8
  import * as ReactDOM from 'react-dom';
10
9
  import { createPortal } from 'react-dom';
10
+ import styled10, { keyframes, css } from 'styled-components';
11
11
  import { Newspaper } from 'lucide-react';
12
12
  import * as LightweightCharts from 'lightweight-charts';
13
13
  import { MdKingBed, MdBathtub, MdDirectionsCar, MdPool, MdYard, MdFitnessCenter } from 'react-icons/md';
@@ -54,7 +54,7 @@ var buttonVariants = cva(
54
54
  }
55
55
  }
56
56
  );
57
- var Button = React5.forwardRef(
57
+ var Button = React9.forwardRef(
58
58
  ({ className, variant, size, radius, asChild = false, ...props }, ref) => {
59
59
  const Comp = asChild ? Slot : "button";
60
60
  const coercedRadius = radius ?? (variant === "onboarding" || variant === "onboardingOutline" ? "square" : void 0);
@@ -93,11 +93,11 @@ var badgeVariants = cva(
93
93
  }
94
94
  }
95
95
  );
96
- var Badge = React5.forwardRef(
96
+ var Badge = React9.forwardRef(
97
97
  ({ className, variant, size, ...props }, ref) => /* @__PURE__ */ jsx("span", { ref, className: cn(badgeVariants({ variant, size }), className), ...props })
98
98
  );
99
99
  Badge.displayName = "Badge";
100
- var Card = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
100
+ var Card = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
101
101
  "div",
102
102
  {
103
103
  ref,
@@ -109,11 +109,11 @@ var Card = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ j
109
109
  }
110
110
  ));
111
111
  Card.displayName = "Card";
112
- var CardHeader = React5.forwardRef(
112
+ var CardHeader = React9.forwardRef(
113
113
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("flex flex-col space-y-1.5 p-6", className), ...props })
114
114
  );
115
115
  CardHeader.displayName = "CardHeader";
116
- var CardTitle = React5.forwardRef(
116
+ var CardTitle = React9.forwardRef(
117
117
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
118
118
  "h3",
119
119
  {
@@ -124,15 +124,15 @@ var CardTitle = React5.forwardRef(
124
124
  )
125
125
  );
126
126
  CardTitle.displayName = "CardTitle";
127
- var CardDescription = React5.forwardRef(
127
+ var CardDescription = React9.forwardRef(
128
128
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx("p", { ref, className: cn("text-sm text-slate-500", className), ...props })
129
129
  );
130
130
  CardDescription.displayName = "CardDescription";
131
- var CardContent = React5.forwardRef(
131
+ var CardContent = React9.forwardRef(
132
132
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("px-6 pb-6 pt-2 text-sm text-slate-600", className), ...props })
133
133
  );
134
134
  CardContent.displayName = "CardContent";
135
- var CardFooter = React5.forwardRef(
135
+ var CardFooter = React9.forwardRef(
136
136
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("flex items-center border-t border-slate-100 px-6 py-4", className), ...props })
137
137
  );
138
138
  CardFooter.displayName = "CardFooter";
@@ -150,7 +150,7 @@ var defaultFormatSignedCurrency = (value) => {
150
150
  const sign = value >= 0 ? "+" : "-";
151
151
  return `${sign}${defaultFormatCurrency(Math.abs(value))}`;
152
152
  };
153
- var PortfolioSummary = React5.forwardRef(
153
+ var PortfolioSummary = React9.forwardRef(
154
154
  ({
155
155
  availableCash,
156
156
  portfolioValue,
@@ -300,23 +300,23 @@ function HousePositionSlider({
300
300
  className,
301
301
  ...props
302
302
  }) {
303
- const [orderMode, setOrderMode] = React5.useState("none");
304
- const [buyTrackingMode, setBuyTrackingMode] = React5.useState("dollars");
305
- const [deltaDollars, setDeltaDollars] = React5.useState(0);
306
- const [deltaTokensBuy, setDeltaTokensBuy] = React5.useState(0);
307
- const [deltaTokensSell, setDeltaTokensSell] = React5.useState(0);
308
- const [_isDragging, setIsDragging] = React5.useState(false);
309
- const [visualTargetPct, setVisualTargetPct] = React5.useState(null);
310
- const [orderType, setOrderType] = React5.useState(defaultOrderType);
311
- const [limitPrice, setLimitPrice] = React5.useState(currentPrice);
312
- const [limitPriceInput, setLimitPriceInput] = React5.useState(currentPrice.toFixed(2));
313
- const [limitPriceDirty, setLimitPriceDirty] = React5.useState(false);
314
- const [ownershipInput, setOwnershipInput] = React5.useState("");
315
- const [tokenAmountInput, setTokenAmountInput] = React5.useState("");
316
- const houseRef = React5.useRef(null);
303
+ const [orderMode, setOrderMode] = React9.useState("none");
304
+ const [buyTrackingMode, setBuyTrackingMode] = React9.useState("dollars");
305
+ const [deltaDollars, setDeltaDollars] = React9.useState(0);
306
+ const [deltaTokensBuy, setDeltaTokensBuy] = React9.useState(0);
307
+ const [deltaTokensSell, setDeltaTokensSell] = React9.useState(0);
308
+ const [_isDragging, setIsDragging] = React9.useState(false);
309
+ const [visualTargetPct, setVisualTargetPct] = React9.useState(null);
310
+ const [orderType, setOrderType] = React9.useState(defaultOrderType);
311
+ const [limitPrice, setLimitPrice] = React9.useState(currentPrice);
312
+ const [limitPriceInput, setLimitPriceInput] = React9.useState(currentPrice.toFixed(2));
313
+ const [limitPriceDirty, setLimitPriceDirty] = React9.useState(false);
314
+ const [ownershipInput, setOwnershipInput] = React9.useState("");
315
+ const [tokenAmountInput, setTokenAmountInput] = React9.useState("");
316
+ const houseRef = React9.useRef(null);
317
317
  const asks = orderbook?.asks ?? [];
318
318
  const bids = orderbook?.bids ?? [];
319
- React5.useEffect(() => {
319
+ React9.useEffect(() => {
320
320
  if (orderType !== "limit") return;
321
321
  if (limitPriceDirty) return;
322
322
  setLimitPrice(currentPrice);
@@ -391,7 +391,7 @@ function HousePositionSlider({
391
391
  const impliedDisplayTargetOwnership = clamp(targetOwnership + ownershipShift, 0, 100);
392
392
  const displayTargetOwnership = visualTargetPct ?? impliedDisplayTargetOwnership;
393
393
  const estFeeTokens = Math.abs(deltaValue) * 5e-3 / (effectivePrice || 1);
394
- const resetOrder = React5.useCallback(() => {
394
+ const resetOrder = React9.useCallback(() => {
395
395
  setOrderMode("none");
396
396
  setBuyTrackingMode("dollars");
397
397
  setDeltaDollars(0);
@@ -399,7 +399,7 @@ function HousePositionSlider({
399
399
  setDeltaTokensSell(0);
400
400
  setVisualTargetPct(null);
401
401
  }, []);
402
- const updateOrderFromTargetValue = React5.useCallback(
402
+ const updateOrderFromTargetValue = React9.useCallback(
403
403
  (newTargetValue) => {
404
404
  const newDeltaValue = newTargetValue - holdingsValue;
405
405
  if (newDeltaValue > 0) {
@@ -423,7 +423,7 @@ function HousePositionSlider({
423
423
  },
424
424
  [effectiveAvailableCash, effectivePrice, effectiveTokensHeld, holdingsValue, resetOrder, tokensHeld]
425
425
  );
426
- const updateOrderFromOwnership = React5.useCallback(
426
+ const updateOrderFromOwnership = React9.useCallback(
427
427
  (newOwnershipPercent) => {
428
428
  const nextOwnership = clamp(newOwnershipPercent, 0, 100);
429
429
  const newTargetTokens = nextOwnership / 100 * totalTokens;
@@ -432,7 +432,7 @@ function HousePositionSlider({
432
432
  },
433
433
  [effectivePrice, totalTokens, updateOrderFromTargetValue]
434
434
  );
435
- const updateOrderFromTokenAmount = React5.useCallback(
435
+ const updateOrderFromTokenAmount = React9.useCallback(
436
436
  (tokenAmountSigned) => {
437
437
  if (tokenAmountSigned > 0) {
438
438
  const maxTokens = effectiveAvailableCash / (effectivePrice || 1);
@@ -455,7 +455,7 @@ function HousePositionSlider({
455
455
  },
456
456
  [effectiveAvailableCash, effectivePrice, effectiveTokensHeld, resetOrder]
457
457
  );
458
- const updateOrderFromSlider = React5.useCallback(
458
+ const updateOrderFromSlider = React9.useCallback(
459
459
  (pct) => {
460
460
  const normalized = (pct - 50) / 50;
461
461
  const magnitude = Math.min(Math.abs(normalized), 1);
@@ -499,7 +499,7 @@ function HousePositionSlider({
499
499
  },
500
500
  [effectiveAvailableCash, effectiveTokensHeld, resetOrder]
501
501
  );
502
- const handleDragAtClientY = React5.useCallback(
502
+ const handleDragAtClientY = React9.useCallback(
503
503
  (clientY) => {
504
504
  if (!houseRef.current) return;
505
505
  const rect = houseRef.current.getBoundingClientRect();
@@ -1015,18 +1015,18 @@ function HousePositionSliderMobile({
1015
1015
  className,
1016
1016
  ...props
1017
1017
  }) {
1018
- const [orderMode, setOrderMode] = React5.useState("none");
1019
- const [deltaDollars, setDeltaDollars] = React5.useState(0);
1020
- const [deltaTokensSell, setDeltaTokensSell] = React5.useState(0);
1021
- const [deltaTokensBuy, setDeltaTokensBuy] = React5.useState(0);
1022
- const [buyTrackingMode, setBuyTrackingMode] = React5.useState("dollars");
1023
- const [orderType, setOrderType] = React5.useState(defaultOrderType);
1024
- const [limitPrice, setLimitPrice] = React5.useState(currentPrice);
1025
- const [limitPriceInput, setLimitPriceInput] = React5.useState(currentPrice.toFixed(2));
1026
- const [limitPriceDirty, setLimitPriceDirty] = React5.useState(false);
1027
- const [tokenAmountInput, setTokenAmountInput] = React5.useState("");
1028
- const houseRef = React5.useRef(null);
1029
- React5.useEffect(() => {
1018
+ const [orderMode, setOrderMode] = React9.useState("none");
1019
+ const [deltaDollars, setDeltaDollars] = React9.useState(0);
1020
+ const [deltaTokensSell, setDeltaTokensSell] = React9.useState(0);
1021
+ const [deltaTokensBuy, setDeltaTokensBuy] = React9.useState(0);
1022
+ const [buyTrackingMode, setBuyTrackingMode] = React9.useState("dollars");
1023
+ const [orderType, setOrderType] = React9.useState(defaultOrderType);
1024
+ const [limitPrice, setLimitPrice] = React9.useState(currentPrice);
1025
+ const [limitPriceInput, setLimitPriceInput] = React9.useState(currentPrice.toFixed(2));
1026
+ const [limitPriceDirty, setLimitPriceDirty] = React9.useState(false);
1027
+ const [tokenAmountInput, setTokenAmountInput] = React9.useState("");
1028
+ const houseRef = React9.useRef(null);
1029
+ React9.useEffect(() => {
1030
1030
  if (orderType !== "limit") return;
1031
1031
  if (limitPriceDirty) return;
1032
1032
  setLimitPrice(currentPrice);
@@ -1068,7 +1068,7 @@ function HousePositionSliderMobile({
1068
1068
  const hasChange = orderMode !== "none" && (Math.abs(deltaTokens) > 1e-3 || Math.abs(deltaValue) > 0.01);
1069
1069
  const targetOwnership = totalTokens > 0 ? targetTokens / totalTokens * 100 : 0;
1070
1070
  const estFeeTokens = effectivePrice > 0 ? Math.abs(deltaValue) * 5e-3 / effectivePrice : 0;
1071
- const updateOrderFromTargetValue = React5.useCallback(
1071
+ const updateOrderFromTargetValue = React9.useCallback(
1072
1072
  (newTargetValue) => {
1073
1073
  const newDeltaValue = newTargetValue - holdingsValue;
1074
1074
  if (newDeltaValue > 0.01) {
@@ -1096,7 +1096,7 @@ function HousePositionSliderMobile({
1096
1096
  },
1097
1097
  [holdingsValue, availableCash, effectivePrice, tokensHeld]
1098
1098
  );
1099
- const updateOrderFromTokenAmount = React5.useCallback(
1099
+ const updateOrderFromTokenAmount = React9.useCallback(
1100
1100
  (tokenAmount) => {
1101
1101
  if (tokenAmount > 0) {
1102
1102
  const maxTokens = effectivePrice > 0 ? availableCash / effectivePrice : 0;
@@ -1123,14 +1123,14 @@ function HousePositionSliderMobile({
1123
1123
  },
1124
1124
  [effectivePrice, availableCash, tokensHeld]
1125
1125
  );
1126
- const handleMarkerClick = React5.useCallback(
1126
+ const handleMarkerClick = React9.useCallback(
1127
1127
  (pct) => {
1128
1128
  const newTargetValue = pct / 100 * totalCapacity;
1129
1129
  updateOrderFromTargetValue(newTargetValue);
1130
1130
  },
1131
1131
  [totalCapacity, updateOrderFromTargetValue]
1132
1132
  );
1133
- const onMouseDown = React5.useCallback(
1133
+ const onMouseDown = React9.useCallback(
1134
1134
  (e) => {
1135
1135
  e.preventDefault();
1136
1136
  const move = (ev) => {
@@ -1150,7 +1150,7 @@ function HousePositionSliderMobile({
1150
1150
  },
1151
1151
  [totalCapacity, updateOrderFromTargetValue]
1152
1152
  );
1153
- const onTouchStart = React5.useCallback(
1153
+ const onTouchStart = React9.useCallback(
1154
1154
  (e) => {
1155
1155
  e.preventDefault();
1156
1156
  e.stopPropagation();
@@ -1630,7 +1630,7 @@ var StatusDot = styled10.span`
1630
1630
  background: rgba(255, 255, 255, 0.3);
1631
1631
  `}
1632
1632
  `;
1633
- var LoafLiquidityBadge = React5.forwardRef(
1633
+ var LoafLiquidityBadge = React9.forwardRef(
1634
1634
  ({ className, isGlowing, status = "unknown", onClick, children, ...props }, ref) => {
1635
1635
  return /* @__PURE__ */ jsxs(
1636
1636
  LogoContainer,
@@ -1654,7 +1654,7 @@ var LoafLiquidityBadge = React5.forwardRef(
1654
1654
  );
1655
1655
  LoafLiquidityBadge.displayName = "LoafLiquidityBadge";
1656
1656
  var toSize = (v, fallback) => v == null ? fallback : typeof v === "number" ? `${v}px` : v;
1657
- var Skeleton = React5.forwardRef(
1657
+ var Skeleton = React9.forwardRef(
1658
1658
  ({ width, height, radius, className, style, ...props }, ref) => {
1659
1659
  return /* @__PURE__ */ jsx(
1660
1660
  "div",
@@ -1678,9 +1678,17 @@ var Skeleton = React5.forwardRef(
1678
1678
  }
1679
1679
  );
1680
1680
  Skeleton.displayName = "Skeleton";
1681
+ var DEFAULT_PRICE_LABEL = "Price (USD)";
1682
+ var PRICE_UNIT_MIN_WIDTH_PX = 320;
1683
+ function priceHeaderLabel(label, panelWidth) {
1684
+ if (label === DEFAULT_PRICE_LABEL && panelWidth != null && panelWidth > 0 && panelWidth < PRICE_UNIT_MIN_WIDTH_PX) {
1685
+ return "Price";
1686
+ }
1687
+ return label;
1688
+ }
1681
1689
  function useViewportCompact(breakpoint) {
1682
- const [isCompact, setIsCompact] = React5.useState(false);
1683
- React5.useEffect(() => {
1690
+ const [isCompact, setIsCompact] = React9.useState(false);
1691
+ React9.useEffect(() => {
1684
1692
  if (typeof window === "undefined") return;
1685
1693
  const check = () => setIsCompact(window.innerWidth <= breakpoint);
1686
1694
  check();
@@ -1695,9 +1703,14 @@ var formatTradeTime = (time) => {
1695
1703
  if (Number.isNaN(d.getTime())) return time;
1696
1704
  return d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false });
1697
1705
  };
1698
- var FLASH_DURATION_MS = 450;
1699
- var FLASH_UP_COLOR = "rgba(14, 203, 129, 0.35)";
1700
- var FLASH_DOWN_COLOR = "rgba(246, 70, 93, 0.35)";
1706
+ var FLASH_DURATION_MS = 900;
1707
+ var FLASH_UP_COLOR = "rgba(14, 203, 129, 0.22)";
1708
+ var FLASH_DOWN_COLOR = "rgba(246, 70, 93, 0.22)";
1709
+ var FLASH_KEYFRAMES = (color) => [
1710
+ { backgroundColor: "rgba(0,0,0,0)" },
1711
+ { backgroundColor: color, offset: 0.3 },
1712
+ { backgroundColor: "rgba(0,0,0,0)" }
1713
+ ];
1701
1714
  function getTradeKey(trade, fallbackIndex) {
1702
1715
  if (trade.tradeId != null) return `id-${trade.tradeId}`;
1703
1716
  if (trade.time != null) return `t-${trade.time}-${trade.type}-${trade.price}-${trade.amount}`;
@@ -1706,14 +1719,11 @@ function getTradeKey(trade, fallbackIndex) {
1706
1719
  function flashElement(node, side) {
1707
1720
  if (typeof node.animate !== "function") return;
1708
1721
  const color = side === "bid" ? FLASH_UP_COLOR : FLASH_DOWN_COLOR;
1709
- node.animate(
1710
- [{ backgroundColor: color }, { backgroundColor: "transparent" }],
1711
- { duration: FLASH_DURATION_MS, easing: "ease-out" }
1712
- );
1722
+ node.animate(FLASH_KEYFRAMES(color), { duration: FLASH_DURATION_MS, easing: "ease-in-out" });
1713
1723
  }
1714
1724
  function useAmountChangeFlash(ref, amount, side, flashOnMount) {
1715
- const prevAmountRef = React5.useRef(flashOnMount ? null : amount);
1716
- React5.useEffect(() => {
1725
+ const prevAmountRef = React9.useRef(flashOnMount ? null : amount);
1726
+ React9.useEffect(() => {
1717
1727
  const prev = prevAmountRef.current;
1718
1728
  const node = ref.current;
1719
1729
  if (node && prev !== amount) {
@@ -1734,18 +1744,15 @@ function TradeRow({
1734
1744
  compact,
1735
1745
  explorerUrl
1736
1746
  }) {
1737
- const rowRef = React5.useRef(null);
1747
+ const rowRef = React9.useRef(null);
1738
1748
  const { type } = trade;
1739
- React5.useEffect(() => {
1749
+ React9.useEffect(() => {
1740
1750
  if (seenTradeKeysRef.current.has(tradeKey)) return;
1741
1751
  seenTradeKeysRef.current.add(tradeKey);
1742
1752
  const node = rowRef.current;
1743
1753
  if (!node || typeof node.animate !== "function") return;
1744
1754
  const color = type === "buy" ? FLASH_UP_COLOR : FLASH_DOWN_COLOR;
1745
- node.animate(
1746
- [{ backgroundColor: color }, { backgroundColor: "transparent" }],
1747
- { duration: FLASH_DURATION_MS, easing: "ease-out" }
1748
- );
1755
+ node.animate(FLASH_KEYFRAMES(color), { duration: FLASH_DURATION_MS, easing: "ease-in-out" });
1749
1756
  }, [tradeKey, seenTradeKeysRef, type]);
1750
1757
  if (compact) {
1751
1758
  return /* @__PURE__ */ jsxs(
@@ -1812,6 +1819,27 @@ function TradeRow({
1812
1819
  }
1813
1820
  );
1814
1821
  }
1822
+ function BreadSliceGlyph() {
1823
+ return /* @__PURE__ */ jsx(
1824
+ "svg",
1825
+ {
1826
+ width: "10",
1827
+ height: "10",
1828
+ viewBox: "0 0 10 10",
1829
+ "aria-hidden": "true",
1830
+ style: { display: "block", filter: "drop-shadow(0 0 4px rgba(201,162,39,0.6))" },
1831
+ children: /* @__PURE__ */ jsx(
1832
+ "path",
1833
+ {
1834
+ 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",
1835
+ fill: "none",
1836
+ stroke: "#C9A227",
1837
+ strokeWidth: "1.3"
1838
+ }
1839
+ )
1840
+ }
1841
+ );
1842
+ }
1815
1843
  function DepthRow({
1816
1844
  side,
1817
1845
  price,
@@ -1826,12 +1854,14 @@ function DepthRow({
1826
1854
  fillHeight
1827
1855
  }) {
1828
1856
  const isAsk = side === "ask";
1829
- const rowRef = React5.useRef(null);
1857
+ const rowRef = React9.useRef(null);
1830
1858
  useAmountChangeFlash(rowRef, amount, side, flashOnMount);
1831
1859
  return /* @__PURE__ */ jsxs(
1832
1860
  "div",
1833
1861
  {
1834
1862
  ref: rowRef,
1863
+ "data-depth-row": true,
1864
+ "data-price": `$${formatNumber(price, precision)}`,
1835
1865
  className: "relative grid grid-cols-3 items-center gap-3 px-3 text-[0.8rem]",
1836
1866
  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 },
1837
1867
  onClick: onPriceClick ? () => onPriceClick(price) : void 0,
@@ -1839,14 +1869,20 @@ function DepthRow({
1839
1869
  /* @__PURE__ */ jsx(
1840
1870
  "div",
1841
1871
  {
1872
+ "data-depth-bar": true,
1842
1873
  className: cn(isAsk ? "bg-rose-900/30" : "bg-emerald-900/30"),
1843
1874
  style: {
1844
1875
  position: "absolute",
1845
- top: 0,
1846
- bottom: 0,
1876
+ top: 1,
1877
+ bottom: 1,
1847
1878
  left: 0,
1848
1879
  width: `${clamp3(depthPct, 0, 100)}%`,
1849
- transition: "width 300ms ease-out"
1880
+ transition: "width 300ms ease-out",
1881
+ borderRadius: "0 4px 4px 0",
1882
+ // backgroundImage LONGHAND, not the `background` shorthand — jsdom's
1883
+ // cssstyle drops gradients from the shorthand (tests assert this).
1884
+ backgroundColor: "transparent",
1885
+ 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))"
1850
1886
  }
1851
1887
  }
1852
1888
  ),
@@ -1854,33 +1890,35 @@ function DepthRow({
1854
1890
  "span",
1855
1891
  {
1856
1892
  "aria-label": "Your order at this price",
1857
- 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)]"
1893
+ className: "absolute left-0 top-1/2 -translate-y-1/2 z-[2]",
1894
+ children: /* @__PURE__ */ jsx(BreadSliceGlyph, {})
1858
1895
  }
1859
1896
  ) : null,
1860
- /* @__PURE__ */ jsxs("div", { className: cn("relative z-[1] tabular-nums", isAsk ? "text-[#f6465d]" : "text-[#0ecb81]"), children: [
1897
+ /* @__PURE__ */ jsxs("div", { "data-cell": "price", className: cn("relative z-[1] tabular-nums", isAsk ? "text-[#f6465d]" : "text-[#0ecb81]"), children: [
1861
1898
  "$",
1862
1899
  formatNumber(price, precision)
1863
1900
  ] }),
1864
- /* @__PURE__ */ jsx("div", { className: "relative z-[1] text-right tabular-nums text-white/90", children: formatNumber(amount, amountPrecision) }),
1865
- /* @__PURE__ */ jsx("div", { className: "relative z-[1] text-right tabular-nums text-white/50", children: formatNumber(cumDepth, amountPrecision) })
1901
+ /* @__PURE__ */ jsx("div", { "data-cell": "amt", className: "relative z-[1] text-right tabular-nums text-white/90", children: formatNumber(amount, amountPrecision) }),
1902
+ /* @__PURE__ */ jsx("div", { "data-cell": "tot", className: "relative z-[1] text-right tabular-nums text-white/50", children: formatNumber(cumDepth, amountPrecision) })
1866
1903
  ]
1867
1904
  }
1868
1905
  );
1869
1906
  }
1870
1907
  var clamp3 = (value, min, max) => Math.min(max, Math.max(min, value));
1908
+ var fitLevelCount = (availableHeightPx, max) => clamp3(Math.floor(availableHeightPx / DEPTH_ROW_HEIGHT_PX), 1, max);
1871
1909
  var LEVEL_ROWS_VISIBLE = 8;
1872
1910
  var DEPTH_ROW_HEIGHT_PX = 28;
1873
1911
  var COMPACT_ROWS_VISIBLE = 5;
1874
1912
  var COMPACT_ROW_HEIGHT_PX = 30;
1875
1913
  var COMPACT_BREAKPOINT_PX = 1024;
1876
- var Orderbook = React5.forwardRef(
1914
+ var Orderbook = React9.forwardRef(
1877
1915
  ({
1878
1916
  asks,
1879
1917
  bids,
1880
1918
  midPrice,
1881
1919
  midChangePercent,
1882
1920
  trades = [],
1883
- priceLabel = "Price (USD)",
1921
+ priceLabel = DEFAULT_PRICE_LABEL,
1884
1922
  amountLabel = "Amount",
1885
1923
  precision = 2,
1886
1924
  amountPrecision = 2,
@@ -1908,12 +1946,12 @@ var Orderbook = React5.forwardRef(
1908
1946
  status: liquidityStatus
1909
1947
  }
1910
1948
  );
1911
- const [tab, setTabState] = React5.useState(defaultTab);
1949
+ const [tab, setTabState] = React9.useState(defaultTab);
1912
1950
  const setTab = setTabState;
1913
1951
  const effectiveTab = showTradesTab ? tab : "orderbook";
1914
- const [tradeFilter, setTradeFilter] = React5.useState("all");
1952
+ const [tradeFilter, setTradeFilter] = React9.useState("all");
1915
1953
  const viewportCompact = useViewportCompact(COMPACT_BREAKPOINT_PX);
1916
- const { userAskPrices, userBidPrices } = React5.useMemo(() => {
1954
+ const { userAskPrices, userBidPrices } = React9.useMemo(() => {
1917
1955
  const ask = /* @__PURE__ */ new Set();
1918
1956
  const bid = /* @__PURE__ */ new Set();
1919
1957
  if (!userOrderPrices) return { userAskPrices: ask, userBidPrices: bid };
@@ -1923,15 +1961,15 @@ var Orderbook = React5.forwardRef(
1923
1961
  }
1924
1962
  return { userAskPrices: ask, userBidPrices: bid };
1925
1963
  }, [userOrderPrices]);
1926
- const seenTradeKeysRef = React5.useRef(/* @__PURE__ */ new Set());
1927
- const initializedRef = React5.useRef(false);
1964
+ const seenTradeKeysRef = React9.useRef(/* @__PURE__ */ new Set());
1965
+ const initializedRef = React9.useRef(false);
1928
1966
  if (!initializedRef.current && trades.length > 0) {
1929
1967
  for (let i = 0; i < trades.length; i++) {
1930
1968
  seenTradeKeysRef.current.add(getTradeKey(trades[i], i));
1931
1969
  }
1932
1970
  initializedRef.current = true;
1933
1971
  }
1934
- React5.useEffect(() => {
1972
+ React9.useEffect(() => {
1935
1973
  if (!initializedRef.current) return;
1936
1974
  const live = /* @__PURE__ */ new Set();
1937
1975
  for (let i = 0; i < trades.length; i++) {
@@ -1942,7 +1980,7 @@ var Orderbook = React5.forwardRef(
1942
1980
  if (!live.has(key)) seen.delete(key);
1943
1981
  }
1944
1982
  }, [trades]);
1945
- React5.useEffect(() => {
1983
+ React9.useEffect(() => {
1946
1984
  setTab(defaultTab);
1947
1985
  }, [defaultTab]);
1948
1986
  const handleTab = (next) => {
@@ -1950,10 +1988,49 @@ var Orderbook = React5.forwardRef(
1950
1988
  onTabChange?.(next);
1951
1989
  };
1952
1990
  const isCompact = variant === "compact" || variant === "auto" && viewportCompact;
1991
+ const rootRef = React9.useRef(null);
1992
+ const setRootRef = React9.useCallback(
1993
+ (node) => {
1994
+ rootRef.current = node;
1995
+ if (typeof ref === "function") ref(node);
1996
+ else if (ref) ref.current = node;
1997
+ },
1998
+ [ref]
1999
+ );
2000
+ const [panelWidth, setPanelWidth] = React9.useState(null);
2001
+ React9.useEffect(() => {
2002
+ if (typeof ResizeObserver === "undefined") return;
2003
+ const el = rootRef.current;
2004
+ if (!el) return;
2005
+ const ro = new ResizeObserver((entries) => {
2006
+ const w = entries[0]?.contentRect?.width;
2007
+ if (typeof w === "number") setPanelWidth((prev) => prev === w ? prev : w);
2008
+ });
2009
+ ro.observe(el);
2010
+ return () => ro.disconnect();
2011
+ }, []);
2012
+ const effectivePriceLabel = priceHeaderLabel(priceLabel, panelWidth);
1953
2013
  const effectiveLevels = maxLevels ?? LEVEL_ROWS_VISIBLE;
1954
2014
  const effectiveCompactLevels = maxLevels ?? COMPACT_ROWS_VISIBLE;
1955
2015
  const sectionHeight = isCompact ? effectiveCompactLevels * COMPACT_ROW_HEIGHT_PX : effectiveLevels * DEPTH_ROW_HEIGHT_PX;
1956
- const rowCount = isCompact ? effectiveCompactLevels : effectiveLevels;
2016
+ const asksSectionRef = React9.useRef(null);
2017
+ const [depthSectionH, setDepthSectionH] = React9.useState(null);
2018
+ React9.useEffect(() => {
2019
+ if (!fillHeight || isCompact || effectiveTab !== "orderbook") return;
2020
+ if (typeof ResizeObserver === "undefined") return;
2021
+ const el = asksSectionRef.current;
2022
+ if (!el) return;
2023
+ const ro = new ResizeObserver((entries) => {
2024
+ const h = entries[0]?.contentRect?.height;
2025
+ if (typeof h === "number") {
2026
+ setDepthSectionH((prev) => prev === h ? prev : h);
2027
+ }
2028
+ });
2029
+ ro.observe(el);
2030
+ return () => ro.disconnect();
2031
+ }, [fillHeight, isCompact, effectiveTab]);
2032
+ const fitRows = fillHeight && !isCompact && depthSectionH != null ? fitLevelCount(depthSectionH, effectiveLevels) : null;
2033
+ const rowCount = isCompact ? effectiveCompactLevels : fitRows ?? effectiveLevels;
1957
2034
  const askVisibleLevels = asks.slice(-rowCount);
1958
2035
  const bidVisibleLevels = bids.slice(0, rowCount);
1959
2036
  const askCumDepths = new Array(askVisibleLevels.length);
@@ -1974,9 +2051,9 @@ var Orderbook = React5.forwardRef(
1974
2051
  }
1975
2052
  const combinedMaxCumDepth = Math.max(1, ...askCumDepths, ...bidCumDepths);
1976
2053
  const askMaxCumDepth = combinedMaxCumDepth;
1977
- const prevAskPricesRef = React5.useRef(null);
1978
- const prevBidPricesRef = React5.useRef(null);
1979
- const newAskPrices = React5.useMemo(() => {
2054
+ const prevAskPricesRef = React9.useRef(null);
2055
+ const prevBidPricesRef = React9.useRef(null);
2056
+ const newAskPrices = React9.useMemo(() => {
1980
2057
  const current = new Set(askVisibleLevels.map((l) => l.price));
1981
2058
  const prev = prevAskPricesRef.current;
1982
2059
  if (!prev) return /* @__PURE__ */ new Set();
@@ -1986,7 +2063,7 @@ var Orderbook = React5.forwardRef(
1986
2063
  }
1987
2064
  return added;
1988
2065
  }, [askVisibleLevels]);
1989
- const newBidPrices = React5.useMemo(() => {
2066
+ const newBidPrices = React9.useMemo(() => {
1990
2067
  const current = new Set(bidVisibleLevels.map((l) => l.price));
1991
2068
  const prev = prevBidPricesRef.current;
1992
2069
  if (!prev) return /* @__PURE__ */ new Set();
@@ -1996,12 +2073,16 @@ var Orderbook = React5.forwardRef(
1996
2073
  }
1997
2074
  return added;
1998
2075
  }, [bidVisibleLevels]);
1999
- React5.useEffect(() => {
2076
+ React9.useEffect(() => {
2000
2077
  prevAskPricesRef.current = new Set(askVisibleLevels.map((l) => l.price));
2001
2078
  prevBidPricesRef.current = new Set(bidVisibleLevels.map((l) => l.price));
2002
2079
  }, [askVisibleLevels, bidVisibleLevels]);
2003
2080
  const bidMaxCumDepth = combinedMaxCumDepth;
2004
2081
  const midClass = midChangePercent == null ? "text-white" : midChangePercent >= 0 ? "text-[#0ecb81]" : "text-[#f6465d]";
2082
+ const bestAskPrice = asks.length > 0 ? asks[asks.length - 1].price : null;
2083
+ const bestBidPrice = bids.length > 0 ? bids[0].price : null;
2084
+ const spread = bestAskPrice != null && bestBidPrice != null ? bestAskPrice - bestBidPrice : null;
2085
+ const spreadPct = spread != null && midPrice > 0 ? spread / midPrice * 100 : null;
2005
2086
  const tradeFiltered = trades.filter((t) => tradeFilter === "all" || t.type === tradeFilter);
2006
2087
  const layoutProps = {
2007
2088
  tab: effectiveTab,
@@ -2010,7 +2091,7 @@ var Orderbook = React5.forwardRef(
2010
2091
  tradeFilter,
2011
2092
  setTradeFilter,
2012
2093
  rightHeader: resolvedRightHeader,
2013
- priceLabel,
2094
+ priceLabel: effectivePriceLabel,
2014
2095
  amountLabel,
2015
2096
  tradeFiltered,
2016
2097
  precision,
@@ -2037,12 +2118,15 @@ var Orderbook = React5.forwardRef(
2037
2118
  compactLevelCount: effectiveCompactLevels,
2038
2119
  fillHeight,
2039
2120
  newAskPrices,
2040
- newBidPrices
2121
+ newBidPrices,
2122
+ spread,
2123
+ spreadPct,
2124
+ asksSectionRef
2041
2125
  };
2042
2126
  return /* @__PURE__ */ jsx(
2043
2127
  Card,
2044
2128
  {
2045
- ref,
2129
+ ref: setRootRef,
2046
2130
  className: cn(
2047
2131
  "w-full max-w-[520px] overflow-hidden rounded-[12px] border border-white/10 bg-[#111111] text-white shadow-md flex flex-col",
2048
2132
  isCompact && "max-w-none",
@@ -2056,6 +2140,170 @@ var Orderbook = React5.forwardRef(
2056
2140
  }
2057
2141
  );
2058
2142
  Orderbook.displayName = "Orderbook";
2143
+ var MID_ROW_GOLD_STYLE = {
2144
+ backgroundImage: "linear-gradient(90deg, rgba(230,200,126,0.07), rgba(230,200,126,0.015))",
2145
+ borderTop: "1px solid rgba(230,200,126,0.22)",
2146
+ borderBottom: "1px solid rgba(230,200,126,0.22)"
2147
+ };
2148
+ function SpreadReadout({
2149
+ spread,
2150
+ spreadPct,
2151
+ precision: _precision,
2152
+ compact
2153
+ }) {
2154
+ if (spread == null || spreadPct == null) return null;
2155
+ return /* @__PURE__ */ jsxs(
2156
+ "div",
2157
+ {
2158
+ style: {
2159
+ display: "flex",
2160
+ alignItems: "baseline",
2161
+ gap: 5,
2162
+ whiteSpace: "nowrap",
2163
+ lineHeight: 1
2164
+ },
2165
+ children: [
2166
+ /* @__PURE__ */ jsx(
2167
+ "span",
2168
+ {
2169
+ style: {
2170
+ fontSize: compact ? "0.48rem" : "0.52rem",
2171
+ letterSpacing: "0.14em",
2172
+ textTransform: "uppercase",
2173
+ color: "rgba(255,255,255,0.45)",
2174
+ fontWeight: 600
2175
+ },
2176
+ children: "Spread"
2177
+ }
2178
+ ),
2179
+ /* @__PURE__ */ jsxs(
2180
+ "span",
2181
+ {
2182
+ className: "tabular-nums",
2183
+ style: { fontSize: compact ? "0.56rem" : "0.62rem", color: "rgba(255,255,255,0.45)" },
2184
+ children: [
2185
+ spreadPct.toFixed(2),
2186
+ "%"
2187
+ ]
2188
+ }
2189
+ )
2190
+ ]
2191
+ }
2192
+ );
2193
+ }
2194
+ function useGlidePill() {
2195
+ const sectionRef = React9.useRef(null);
2196
+ const pillRef = React9.useRef(null);
2197
+ const priceSpanRef = React9.useRef(null);
2198
+ const boldedCellRef = React9.useRef(null);
2199
+ const [pillPrice, setPillPrice] = React9.useState(null);
2200
+ const motion = React9.useRef({ x: 0, targetX: 0, y: 0, targetY: 0, amp: 0, on: false, raf: 0 });
2201
+ const reducedMotion = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
2202
+ const unbold = () => {
2203
+ if (boldedCellRef.current) {
2204
+ boldedCellRef.current.style.fontWeight = "";
2205
+ boldedCellRef.current = null;
2206
+ }
2207
+ };
2208
+ const frame = React9.useCallback(() => {
2209
+ const m = motion.current;
2210
+ const section = sectionRef.current;
2211
+ const pill = pillRef.current;
2212
+ m.raf = 0;
2213
+ if (!section || !pill) return;
2214
+ const k = reducedMotion ? 1 : 0.12;
2215
+ m.x += (m.targetX - m.x) * k;
2216
+ m.y += (m.targetY - m.y) * k;
2217
+ pill.style.transform = `translate(${m.x.toFixed(1)}px, ${m.y.toFixed(1)}px)`;
2218
+ const ampTarget = m.on && !reducedMotion ? 1 : 0;
2219
+ m.amp += (ampTarget - m.amp) * 0.14;
2220
+ const crest = m.y + pill.offsetHeight / 2;
2221
+ const rows = section.querySelectorAll("[data-depth-row]");
2222
+ for (const row of rows) {
2223
+ const rect = row.getBoundingClientRect();
2224
+ const center = rect.top + rect.height / 2;
2225
+ const d = center - crest;
2226
+ const inf = Math.exp(-(d * d) / (2 * 42 * 42)) * m.amp;
2227
+ const bar = row.querySelector("[data-depth-bar]");
2228
+ const priceCell = row.querySelector('[data-cell="price"]');
2229
+ if (inf < 0.01) {
2230
+ if (row.dataset.waved) {
2231
+ delete row.dataset.waved;
2232
+ row.style.zIndex = "";
2233
+ row.style.background = "";
2234
+ row.style.boxShadow = "";
2235
+ if (bar) bar.style.filter = "";
2236
+ if (priceCell) priceCell.style.transform = "";
2237
+ }
2238
+ continue;
2239
+ }
2240
+ row.dataset.waved = "1";
2241
+ row.style.zIndex = String(3 + Math.round(inf * 10));
2242
+ row.style.background = `rgba(19,20,24,${(inf * 0.92).toFixed(2)})`;
2243
+ row.style.boxShadow = `0 ${(inf * 3).toFixed(1)}px ${(inf * 12).toFixed(1)}px rgba(0,0,0,${(inf * 0.45).toFixed(2)})`;
2244
+ if (bar) bar.style.filter = `brightness(${(1 + inf * 0.45).toFixed(2)})`;
2245
+ if (priceCell) {
2246
+ priceCell.style.transformOrigin = "left center";
2247
+ priceCell.style.transform = `scale(${(1 + inf * 0.09).toFixed(3)})`;
2248
+ }
2249
+ }
2250
+ if (m.on || Math.abs(m.targetX - m.x) + Math.abs(m.targetY - m.y) > 0.5 || m.amp > 5e-3) {
2251
+ m.raf = requestAnimationFrame(frame);
2252
+ }
2253
+ }, [reducedMotion]);
2254
+ const onMouseMove = React9.useCallback(
2255
+ (e) => {
2256
+ const target = e.target;
2257
+ const row = target?.closest?.("[data-depth-row]");
2258
+ const m = motion.current;
2259
+ if (!row || !row.dataset.price) {
2260
+ m.on = false;
2261
+ unbold();
2262
+ setPillPrice(null);
2263
+ return;
2264
+ }
2265
+ setPillPrice(row.dataset.price);
2266
+ const priceCell = row.querySelector('[data-cell="price"]');
2267
+ if (priceCell !== boldedCellRef.current) {
2268
+ unbold();
2269
+ if (priceCell) {
2270
+ priceCell.style.fontWeight = "600";
2271
+ boldedCellRef.current = priceCell;
2272
+ }
2273
+ }
2274
+ const pill = pillRef.current;
2275
+ if (pill && priceCell) {
2276
+ const rowRect = row.getBoundingClientRect();
2277
+ const cellRect = priceCell.getBoundingClientRect();
2278
+ const priceOffset = priceSpanRef.current?.offsetLeft || 48;
2279
+ const nextX = cellRect.left - priceOffset;
2280
+ const nextY = rowRect.top + (rowRect.height || 28) / 2 - pill.offsetHeight / 2;
2281
+ if (!m.on) {
2282
+ m.x = nextX;
2283
+ m.y = nextY;
2284
+ }
2285
+ m.targetX = nextX;
2286
+ m.targetY = nextY;
2287
+ }
2288
+ m.on = true;
2289
+ if (!m.raf && typeof requestAnimationFrame === "function") {
2290
+ m.raf = requestAnimationFrame(frame);
2291
+ }
2292
+ },
2293
+ [frame]
2294
+ );
2295
+ const onMouseLeave = React9.useCallback(() => {
2296
+ motion.current.on = false;
2297
+ unbold();
2298
+ setPillPrice(null);
2299
+ }, []);
2300
+ React9.useEffect(() => {
2301
+ return () => {
2302
+ if (motion.current.raf) cancelAnimationFrame(motion.current.raf);
2303
+ };
2304
+ }, []);
2305
+ return { sectionRef, pillRef, priceSpanRef, pillPrice, onMouseMove, onMouseLeave };
2306
+ }
2059
2307
  function SkeletonRow({ compact }) {
2060
2308
  if (compact) {
2061
2309
  return /* @__PURE__ */ jsxs(
@@ -2112,8 +2360,12 @@ function DesktopOrderbookLayout({
2112
2360
  compactLevelCount: _compactLevelCount,
2113
2361
  fillHeight,
2114
2362
  newAskPrices,
2115
- newBidPrices
2363
+ newBidPrices,
2364
+ spread,
2365
+ spreadPct,
2366
+ asksSectionRef
2116
2367
  }) {
2368
+ const glide = useGlidePill();
2117
2369
  return /* @__PURE__ */ jsxs(Fragment, { children: [
2118
2370
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 pt-4", children: [
2119
2371
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
@@ -2123,8 +2375,8 @@ function DesktopOrderbookLayout({
2123
2375
  type: "button",
2124
2376
  onClick: () => handleTab("orderbook"),
2125
2377
  className: cn(
2126
- "relative pb-2 text-sm font-semibold",
2127
- tab === "orderbook" ? "text-white" : "text-white/60"
2378
+ "relative pb-2 text-[0.8rem] font-medium transition-colors",
2379
+ tab === "orderbook" ? "text-white" : "text-white/60 hover:text-white/85"
2128
2380
  ),
2129
2381
  children: [
2130
2382
  "Orderbook",
@@ -2138,8 +2390,8 @@ function DesktopOrderbookLayout({
2138
2390
  type: "button",
2139
2391
  onClick: () => handleTab("trades"),
2140
2392
  className: cn(
2141
- "relative pb-2 text-sm font-medium",
2142
- tab === "trades" ? "text-white" : "text-white/60"
2393
+ "relative pb-2 text-[0.8rem] font-medium transition-colors",
2394
+ tab === "trades" ? "text-white" : "text-white/60 hover:text-white/85"
2143
2395
  ),
2144
2396
  children: [
2145
2397
  "Trades",
@@ -2187,100 +2439,170 @@ function DesktopOrderbookLayout({
2187
2439
  )
2188
2440
  ] }) : /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: rightHeader })
2189
2441
  ] }),
2190
- /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col min-h-0 px-4 pt-2", children: [
2191
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-3 px-3 pb-1.5 pt-1 text-xs text-white/60", children: [
2192
- /* @__PURE__ */ jsx("div", { children: priceLabel }),
2193
- /* @__PURE__ */ jsx("div", { className: "text-right", children: amountLabel }),
2194
- /* @__PURE__ */ jsx("div", { className: "text-right", children: tab === "orderbook" ? "Total" : "Time" })
2195
- ] }),
2196
- tab === "trades" ? /* @__PURE__ */ jsx(
2197
- "div",
2198
- {
2199
- className: "max-h-[380px] overflow-y-auto overflow-x-hidden",
2200
- style: { scrollbarGutter: "stable" },
2201
- children: isLoading && tradeFiltered.length === 0 ? /* @__PURE__ */ jsx("div", { children: Array.from({ length: effectiveLevels }).map((_, i) => /* @__PURE__ */ jsx(SkeletonRow, {}, `trade-skel-${i}`)) }) : tradeFiltered.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-3 py-10 text-center text-sm text-white/50", children: "No trades" }) : /* @__PURE__ */ jsx("div", { children: tradeFiltered.map((trade, i) => {
2202
- const tradeKey = getTradeKey(trade, i);
2203
- const explorerUrl = trade.txHash && tradeExplorerUrl ? tradeExplorerUrl(trade.txHash) : trade.txHash ? `https://sepolia.etherscan.io/tx/${trade.txHash}` : void 0;
2204
- return /* @__PURE__ */ jsx(
2205
- TradeRow,
2442
+ /* @__PURE__ */ jsxs(
2443
+ "div",
2444
+ {
2445
+ ref: glide.sectionRef,
2446
+ "data-glide-section": true,
2447
+ className: "relative flex flex-1 flex-col min-h-0 px-4 pt-2",
2448
+ onMouseMove: glide.onMouseMove,
2449
+ onMouseLeave: glide.onMouseLeave,
2450
+ children: [
2451
+ typeof document !== "undefined" ? createPortal(
2452
+ /* @__PURE__ */ jsxs(
2453
+ "div",
2206
2454
  {
2207
- trade,
2208
- tradeKey,
2209
- precision,
2210
- amountPrecision,
2211
- seenTradeKeysRef,
2212
- explorerUrl
2213
- },
2214
- tradeKey
2215
- );
2216
- }) })
2217
- }
2218
- ) : /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col min-h-0 overflow-hidden", fillHeight && "flex-1"), children: [
2219
- /* @__PURE__ */ jsx(
2220
- "div",
2221
- {
2222
- className: cn("flex flex-col justify-end overflow-hidden", !fillHeight && "flex-shrink-0"),
2223
- style: fillHeight ? { flex: 1, minHeight: 0 } : { height: `${sectionHeight}px` },
2224
- children: isLoading ? Array.from({ length: effectiveLevels }).map((_, i) => /* @__PURE__ */ jsx(SkeletonRow, {}, `ask-skel-${i}`)) : askVisibleLevels.length === 0 ? /* @__PURE__ */ 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__ */ jsx(
2225
- DepthRow,
2455
+ ref: glide.pillRef,
2456
+ "data-glide-pill": true,
2457
+ "data-visible": glide.pillPrice != null ? "true" : "false",
2458
+ style: {
2459
+ position: "fixed",
2460
+ left: 0,
2461
+ top: 0,
2462
+ zIndex: 60,
2463
+ pointerEvents: "none",
2464
+ display: "flex",
2465
+ alignItems: "center",
2466
+ gap: 8,
2467
+ whiteSpace: "nowrap",
2468
+ background: "#1b1d22",
2469
+ border: "1px solid rgba(255,255,255,0.14)",
2470
+ borderRadius: 999,
2471
+ padding: "4px 13px",
2472
+ boxShadow: "0 6px 20px rgba(0,0,0,0.55)",
2473
+ fontVariantNumeric: "tabular-nums",
2474
+ opacity: glide.pillPrice != null ? 1 : 0,
2475
+ transition: "opacity 160ms ease"
2476
+ },
2477
+ children: [
2478
+ /* @__PURE__ */ jsx(
2479
+ "span",
2480
+ {
2481
+ style: {
2482
+ fontSize: "0.6rem",
2483
+ fontWeight: 600,
2484
+ letterSpacing: "0.08em",
2485
+ textTransform: "uppercase",
2486
+ color: "#E6C87E",
2487
+ lineHeight: 1
2488
+ },
2489
+ children: "Limit"
2490
+ }
2491
+ ),
2492
+ /* @__PURE__ */ jsx(
2493
+ "span",
2494
+ {
2495
+ ref: glide.priceSpanRef,
2496
+ className: "tabular-nums",
2497
+ style: { fontSize: "0.8rem", fontWeight: 600, color: "#fff" },
2498
+ children: glide.pillPrice ?? ""
2499
+ }
2500
+ )
2501
+ ]
2502
+ }
2503
+ ),
2504
+ document.body
2505
+ ) : null,
2506
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-3 px-3 pb-1.5 pt-1 text-xs text-white/60", children: [
2507
+ /* @__PURE__ */ jsx("div", { children: priceLabel }),
2508
+ /* @__PURE__ */ jsx("div", { className: "text-right", children: amountLabel }),
2509
+ /* @__PURE__ */ jsx("div", { className: "text-right", children: tab === "orderbook" ? "Total" : "Time" })
2510
+ ] }),
2511
+ tab === "trades" ? /* @__PURE__ */ jsx(
2512
+ "div",
2513
+ {
2514
+ className: "max-h-[380px] overflow-y-auto overflow-x-hidden",
2515
+ style: { scrollbarGutter: "stable" },
2516
+ children: isLoading && tradeFiltered.length === 0 ? /* @__PURE__ */ jsx("div", { children: Array.from({ length: effectiveLevels }).map((_, i) => /* @__PURE__ */ jsx(SkeletonRow, {}, `trade-skel-${i}`)) }) : tradeFiltered.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-3 py-10 text-center text-sm text-white/50", children: "No trades" }) : /* @__PURE__ */ jsx("div", { children: tradeFiltered.map((trade, i) => {
2517
+ const tradeKey = getTradeKey(trade, i);
2518
+ const explorerUrl = trade.txHash && tradeExplorerUrl ? tradeExplorerUrl(trade.txHash) : trade.txHash ? `https://sepolia.etherscan.io/tx/${trade.txHash}` : void 0;
2519
+ return /* @__PURE__ */ jsx(
2520
+ TradeRow,
2521
+ {
2522
+ trade,
2523
+ tradeKey,
2524
+ precision,
2525
+ amountPrecision,
2526
+ seenTradeKeysRef,
2527
+ explorerUrl
2528
+ },
2529
+ tradeKey
2530
+ );
2531
+ }) })
2532
+ }
2533
+ ) : /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col min-h-0 overflow-hidden", fillHeight && "flex-1"), children: [
2534
+ /* @__PURE__ */ jsx(
2535
+ "div",
2226
2536
  {
2227
- side: "ask",
2228
- price: l.price,
2229
- amount: l.amount,
2230
- cumDepth: askCumDepths[i],
2231
- depthPct: askCumDepths[i] / askMaxCumDepth * 100,
2232
- precision,
2233
- amountPrecision,
2234
- hasUserOrder: userAskPrices.has(l.price),
2235
- onPriceClick,
2236
- flashOnMount: newAskPrices.has(l.price),
2237
- fillHeight
2238
- },
2239
- `ask-${l.price}`
2240
- ))
2241
- }
2242
- ),
2243
- /* @__PURE__ */ jsx(
2244
- "div",
2245
- {
2246
- className: "flex items-center gap-2 bg-[#0b1a24] px-3 py-2 flex-shrink-0 overflow-hidden orderbook-mid-row",
2247
- children: /* @__PURE__ */ jsx("div", { className: cn("text-lg font-semibold tabular-nums whitespace-nowrap orderbook-mid-price", midClass), children: isLoading ? /* @__PURE__ */ jsx(Skeleton, { width: 110, height: 20 }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2248
- "$",
2249
- formatNumber(midPrice, precision),
2250
- midChangePercent == null ? null : /* @__PURE__ */ jsxs("span", { className: cn("ml-2 text-sm font-semibold tabular-nums", midClass), children: [
2251
- midChangePercent >= 0 ? "+" : "",
2252
- midChangePercent.toFixed(2),
2253
- "%"
2254
- ] })
2255
- ] }) })
2256
- }
2257
- ),
2258
- /* @__PURE__ */ jsx(
2259
- "div",
2260
- {
2261
- className: cn("flex flex-col overflow-hidden", !fillHeight && "flex-shrink-0"),
2262
- style: fillHeight ? { flex: 1, minHeight: 0 } : { height: `${sectionHeight}px` },
2263
- children: isLoading ? Array.from({ length: effectiveLevels }).map((_, i) => /* @__PURE__ */ jsx(SkeletonRow, {}, `bid-skel-${i}`)) : bidVisibleLevels.length === 0 ? /* @__PURE__ */ 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__ */ jsx(
2264
- DepthRow,
2537
+ ref: asksSectionRef,
2538
+ className: cn("flex flex-col justify-end overflow-hidden", !fillHeight && "flex-shrink-0"),
2539
+ style: fillHeight ? { flex: 1, minHeight: 0 } : { height: `${sectionHeight}px` },
2540
+ children: isLoading ? Array.from({ length: effectiveLevels }).map((_, i) => /* @__PURE__ */ jsx(SkeletonRow, {}, `ask-skel-${i}`)) : askVisibleLevels.length === 0 ? /* @__PURE__ */ 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__ */ jsx(
2541
+ DepthRow,
2542
+ {
2543
+ side: "ask",
2544
+ price: l.price,
2545
+ amount: l.amount,
2546
+ cumDepth: askCumDepths[i],
2547
+ depthPct: askCumDepths[i] / askMaxCumDepth * 100,
2548
+ precision,
2549
+ amountPrecision,
2550
+ hasUserOrder: userAskPrices.has(l.price),
2551
+ onPriceClick,
2552
+ flashOnMount: newAskPrices.has(l.price),
2553
+ fillHeight
2554
+ },
2555
+ `ask-${l.price}`
2556
+ ))
2557
+ }
2558
+ ),
2559
+ /* @__PURE__ */ jsxs(
2560
+ "div",
2265
2561
  {
2266
- side: "bid",
2267
- price: l.price,
2268
- amount: l.amount,
2269
- cumDepth: bidCumDepths[i],
2270
- depthPct: bidCumDepths[i] / bidMaxCumDepth * 100,
2271
- precision,
2272
- amountPrecision,
2273
- hasUserOrder: userBidPrices.has(l.price),
2274
- onPriceClick,
2275
- flashOnMount: newBidPrices.has(l.price),
2276
- fillHeight
2277
- },
2278
- `bid-${l.price}`
2279
- ))
2280
- }
2281
- )
2282
- ] })
2283
- ] })
2562
+ className: "flex items-center justify-between gap-2 px-3 py-2 flex-shrink-0 overflow-hidden orderbook-mid-row",
2563
+ style: { ...MID_ROW_GOLD_STYLE, alignItems: "baseline" },
2564
+ children: [
2565
+ /* @__PURE__ */ jsx("div", { className: cn("text-lg font-semibold tabular-nums whitespace-nowrap orderbook-mid-price", midClass), children: isLoading ? /* @__PURE__ */ jsx(Skeleton, { width: 110, height: 20 }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2566
+ "$",
2567
+ formatNumber(midPrice, precision),
2568
+ midChangePercent == null ? null : /* @__PURE__ */ jsxs("span", { className: cn("ml-2 text-sm font-semibold tabular-nums", midClass), children: [
2569
+ midChangePercent >= 0 ? "+" : "",
2570
+ midChangePercent.toFixed(2),
2571
+ "%"
2572
+ ] })
2573
+ ] }) }),
2574
+ !isLoading && /* @__PURE__ */ jsx(SpreadReadout, { spread, spreadPct, precision })
2575
+ ]
2576
+ }
2577
+ ),
2578
+ /* @__PURE__ */ jsx(
2579
+ "div",
2580
+ {
2581
+ className: cn("flex flex-col overflow-hidden", !fillHeight && "flex-shrink-0"),
2582
+ style: fillHeight ? { flex: 1, minHeight: 0 } : { height: `${sectionHeight}px` },
2583
+ children: isLoading ? Array.from({ length: effectiveLevels }).map((_, i) => /* @__PURE__ */ jsx(SkeletonRow, {}, `bid-skel-${i}`)) : bidVisibleLevels.length === 0 ? /* @__PURE__ */ 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__ */ jsx(
2584
+ DepthRow,
2585
+ {
2586
+ side: "bid",
2587
+ price: l.price,
2588
+ amount: l.amount,
2589
+ cumDepth: bidCumDepths[i],
2590
+ depthPct: bidCumDepths[i] / bidMaxCumDepth * 100,
2591
+ precision,
2592
+ amountPrecision,
2593
+ hasUserOrder: userBidPrices.has(l.price),
2594
+ onPriceClick,
2595
+ flashOnMount: newBidPrices.has(l.price),
2596
+ fillHeight
2597
+ },
2598
+ `bid-${l.price}`
2599
+ ))
2600
+ }
2601
+ )
2602
+ ] })
2603
+ ]
2604
+ }
2605
+ )
2284
2606
  ] });
2285
2607
  }
2286
2608
  function MobileOrderbookLayout({
@@ -2407,8 +2729,7 @@ function MobileOrderbookLayout({
2407
2729
  gridTemplateColumns: "1.2fr 0.8fr",
2408
2730
  padding: "0.3rem 0",
2409
2731
  fontSize: "0.8rem",
2410
- borderTop: "1px solid rgba(255,255,255,0.1)",
2411
- borderBottom: "1px solid rgba(255,255,255,0.1)"
2732
+ ...MID_ROW_GOLD_STYLE
2412
2733
  },
2413
2734
  children: [
2414
2735
  /* @__PURE__ */ jsx(
@@ -2490,7 +2811,7 @@ function MobileDepthRow({
2490
2811
  flashOnMount
2491
2812
  }) {
2492
2813
  const isAsk = side === "ask";
2493
- const rowRef = React5.useRef(null);
2814
+ const rowRef = React9.useRef(null);
2494
2815
  useAmountChangeFlash(rowRef, amount, side, flashOnMount);
2495
2816
  return /* @__PURE__ */ jsxs(
2496
2817
  "div",
@@ -2516,16 +2837,12 @@ function MobileDepthRow({
2516
2837
  left: "2px",
2517
2838
  top: "50%",
2518
2839
  transform: "translateY(-50%)",
2519
- width: "6px",
2520
- height: "6px",
2521
- borderRadius: "50%",
2522
- backgroundColor: "#C9A227",
2523
- boxShadow: "0 0 6px rgba(201,162,39,0.7)",
2524
2840
  zIndex: 2
2525
- }
2841
+ },
2842
+ children: /* @__PURE__ */ jsx(BreadSliceGlyph, {})
2526
2843
  }
2527
2844
  ) : null,
2528
- /* @__PURE__ */ jsxs("div", { style: { position: "relative", zIndex: 1, color: isAsk ? "#f6465d" : "#0ecb81", paddingLeft: hasUserOrder ? "10px" : void 0 }, children: [
2845
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative", zIndex: 1, color: isAsk ? "#f6465d" : "#0ecb81", paddingLeft: hasUserOrder ? "14px" : void 0 }, children: [
2529
2846
  "$",
2530
2847
  formatNumber(price, precision)
2531
2848
  ] }),
@@ -2535,12 +2852,12 @@ function MobileDepthRow({
2535
2852
  {
2536
2853
  style: {
2537
2854
  position: "absolute",
2538
- top: 0,
2855
+ top: 1,
2539
2856
  left: 0,
2540
- bottom: 0,
2857
+ bottom: 1,
2541
2858
  width: `${clamp3(depthPct, 0, 100)}%`,
2542
- backgroundColor: isAsk ? "#f6465d" : "#0ecb81",
2543
- opacity: 0.1,
2859
+ 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))",
2860
+ borderRadius: "0 4px 4px 0",
2544
2861
  zIndex: 0,
2545
2862
  transition: "width 300ms ease-out"
2546
2863
  }
@@ -2553,20 +2870,20 @@ function MobileDepthRow({
2553
2870
  var isHlsManifest = (url) => /\.m3u8(\?|$)/i.test(url);
2554
2871
  var HLS_MIME = "application/vnd.apple.mpegurl";
2555
2872
  var MANIFEST_FAILURES = /* @__PURE__ */ new Set(["manifestLoadError", "manifestLoadTimeOut", "manifestParsingError"]);
2556
- var HlsVideo = React5.forwardRef(
2873
+ var HlsVideo = React9.forwardRef(
2557
2874
  ({ src, capLevelToPlayerSize = true, onError, ...videoProps }, ref) => {
2558
- const videoRef = React5.useRef(null);
2559
- React5.useImperativeHandle(ref, () => videoRef.current, []);
2875
+ const videoRef = React9.useRef(null);
2876
+ React9.useImperativeHandle(ref, () => videoRef.current, []);
2560
2877
  const primary = typeof src === "string" ? src : src.src;
2561
2878
  const fallbackSrc = typeof src === "string" ? void 0 : src.fallbackSrc;
2562
- const [failedSrc, setFailedSrc] = React5.useState(null);
2879
+ const [failedSrc, setFailedSrc] = React9.useState(null);
2563
2880
  const usingFallback = fallbackSrc != null && failedSrc === primary;
2564
2881
  const activeSrc = usingFallback ? fallbackSrc : primary;
2565
- const failPrimary = React5.useCallback(() => {
2882
+ const failPrimary = React9.useCallback(() => {
2566
2883
  if (fallbackSrc == null) return;
2567
2884
  setFailedSrc(primary);
2568
2885
  }, [fallbackSrc, primary]);
2569
- React5.useEffect(() => {
2886
+ React9.useEffect(() => {
2570
2887
  const video = videoRef.current;
2571
2888
  if (!video || !activeSrc) return;
2572
2889
  if (!isHlsManifest(activeSrc) || video.canPlayType(HLS_MIME)) {
@@ -2599,7 +2916,7 @@ var HlsVideo = React5.forwardRef(
2599
2916
  hls?.destroy();
2600
2917
  };
2601
2918
  }, [activeSrc, capLevelToPlayerSize, failPrimary]);
2602
- const handleError = React5.useCallback(
2919
+ const handleError = React9.useCallback(
2603
2920
  (e) => {
2604
2921
  if (!usingFallback) failPrimary();
2605
2922
  onError?.(e);
@@ -2610,7 +2927,7 @@ var HlsVideo = React5.forwardRef(
2610
2927
  }
2611
2928
  );
2612
2929
  HlsVideo.displayName = "HlsVideo";
2613
- var PropertyTour = React5.forwardRef(
2930
+ var PropertyTour = React9.forwardRef(
2614
2931
  ({
2615
2932
  className,
2616
2933
  title,
@@ -2623,8 +2940,8 @@ var PropertyTour = React5.forwardRef(
2623
2940
  playsInline = true,
2624
2941
  ...props
2625
2942
  }, ref) => {
2626
- const videoRef = React5.useRef(null);
2627
- React5.useEffect(() => {
2943
+ const videoRef = React9.useRef(null);
2944
+ React9.useEffect(() => {
2628
2945
  const video = videoRef.current;
2629
2946
  if (!video) return;
2630
2947
  const handleFullscreenChange = () => {
@@ -2732,8 +3049,50 @@ function isSafeUrl(url) {
2732
3049
  return false;
2733
3050
  }
2734
3051
  }
3052
+ var BREAKING_WINDOW_HOURS = 6;
3053
+ var MAX_PINNED_BREAKING = 2;
3054
+ var BREAKING_IMPACT_FLOOR = 8;
3055
+ var breakingImpact = (it) => typeof it.impactScore === "number" && Number.isFinite(it.impactScore) ? it.impactScore : BREAKING_IMPACT_FLOOR;
3056
+ var dismissedBreakingKeys = /* @__PURE__ */ new Set();
3057
+ var __resetDismissedBreaking = () => dismissedBreakingKeys.clear();
3058
+ var breakingKey = (it) => String(it.id ?? it.title);
3059
+ var BREAKING_SWIPE_MIN_PX = 80;
2735
3060
  var ITEMS_PER_PAGE = 5;
2736
3061
  var ITEMS_PER_PAGE_MOBILE = 5;
3062
+ function formatAge(publishedAt, now) {
3063
+ const elapsed = Math.max(0, now - new Date(publishedAt).getTime());
3064
+ const secs = Math.floor(elapsed / 1e3);
3065
+ if (secs < 60) return `${secs}s ago`;
3066
+ const mins = Math.floor(secs / 60);
3067
+ if (mins < 60) return `${mins}m ago`;
3068
+ return `${Math.floor(mins / 60)}h ago`;
3069
+ }
3070
+ function rowAgeLabel(publishedAt, now) {
3071
+ if (!publishedAt) return null;
3072
+ const t = new Date(publishedAt).getTime();
3073
+ if (!Number.isFinite(t)) return null;
3074
+ const ageMs = Math.max(0, now - t);
3075
+ if (ageMs >= 24 * 36e5) return null;
3076
+ return { label: formatAge(publishedAt, now), fresh: ageMs < 36e5 };
3077
+ }
3078
+ function useTickingNow(active, youngestPublishedAt) {
3079
+ const [now, setNow] = React9.useState(() => Date.now());
3080
+ React9.useEffect(() => {
3081
+ if (!active || !youngestPublishedAt) return;
3082
+ let timer;
3083
+ const schedule = () => {
3084
+ const ageSecs = (Date.now() - new Date(youngestPublishedAt).getTime()) / 1e3;
3085
+ const delay = ageSecs < 60 ? 1e3 : ageSecs < 3600 ? 15e3 : 6e4;
3086
+ timer = setTimeout(() => {
3087
+ setNow(Date.now());
3088
+ schedule();
3089
+ }, delay);
3090
+ };
3091
+ schedule();
3092
+ return () => clearTimeout(timer);
3093
+ }, [active, youngestPublishedAt]);
3094
+ return now;
3095
+ }
2737
3096
  var ensureAnimationsInjected = () => {
2738
3097
  if (typeof document === "undefined") return;
2739
3098
  if (document.getElementById("property-news-updates-animations")) return;
@@ -2756,6 +3115,110 @@ var ensureAnimationsInjected = () => {
2756
3115
  from { opacity: 0; transform: translateY(12px) scale(0.98); }
2757
3116
  to { opacity: 1; transform: translateY(0) scale(1); }
2758
3117
  }
3118
+ /* The list scrolls but never shows a scrollbar (user call 2026-08-22: the
3119
+ grey track crowded the rows). Same treatment as the rest of the app. */
3120
+ .news-list-scroll { scrollbar-width: none; -ms-overflow-style: none; }
3121
+ .news-list-scroll::-webkit-scrollbar { display: none; width: 0; height: 0; }
3122
+ .property-news-breaking-dismiss {
3123
+ opacity: 0;
3124
+ pointer-events: none;
3125
+ transform: translateY(3px);
3126
+ transition: opacity 0.16s ease, transform 0.2s cubic-bezier(0.22, 1, 0.36, 1), color 0.15s ease;
3127
+ }
3128
+ .property-news-breaking-item:hover .property-news-breaking-dismiss,
3129
+ .property-news-breaking-item:focus-within .property-news-breaking-dismiss {
3130
+ opacity: 1;
3131
+ pointer-events: auto;
3132
+ transform: translateY(0);
3133
+ }
3134
+ .property-news-breaking-dismiss:hover,
3135
+ .property-news-breaking-dismiss:focus-visible {
3136
+ color: #fff !important;
3137
+ text-decoration: underline;
3138
+ text-underline-offset: 2px;
3139
+ outline: none;
3140
+ }
3141
+ .property-news-breaking-item[data-dismissing="true"] {
3142
+ pointer-events: none;
3143
+ animation: propertyNewsBreakingDismiss 0.22s cubic-bezier(0.4, 0, 1, 1) forwards;
3144
+ }
3145
+ .property-news-breaking-item[data-new-breaking="true"] {
3146
+ position: relative;
3147
+ overflow: hidden;
3148
+ animation: propertyNewsBreakingArrive 1.18s cubic-bezier(0.16, 1, 0.3, 1) both;
3149
+ }
3150
+ .property-news-breaking-item[data-new-breaking="true"]::after {
3151
+ content: "";
3152
+ position: absolute;
3153
+ inset: 0;
3154
+ z-index: 0;
3155
+ pointer-events: none;
3156
+ background: linear-gradient(100deg, transparent 8%, rgba(var(--color-accent-rgb, 230, 200, 126), 0.42) 48%, transparent 82%);
3157
+ transform: translateX(-120%);
3158
+ animation: propertyNewsBreakingSheen 1.18s 0.2s cubic-bezier(0.22, 1, 0.36, 1) both;
3159
+ }
3160
+ .property-news-breaking-item[data-new-breaking="true"] > span {
3161
+ position: relative;
3162
+ z-index: 1;
3163
+ }
3164
+ .property-news-breaking-item[data-new-breaking="true"][data-dismissing="true"] {
3165
+ animation: propertyNewsBreakingDismiss 0.22s cubic-bezier(0.4, 0, 1, 1) forwards;
3166
+ }
3167
+ @keyframes propertyNewsBreakingArrive {
3168
+ 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); }
3169
+ 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); }
3170
+ 58% { box-shadow: 0 0 16px rgba(var(--color-accent-rgb, 230, 200, 126), 0.22); filter: brightness(1.06); }
3171
+ 100% { opacity: 1; transform: translateY(0) scale(1); box-shadow: 0 0 0 rgba(var(--color-accent-rgb, 230, 200, 126), 0); }
3172
+ }
3173
+ @keyframes propertyNewsBreakingSheen {
3174
+ from { transform: translateX(-120%); }
3175
+ to { transform: translateX(120%); }
3176
+ }
3177
+ @keyframes propertyNewsBreakingDismiss {
3178
+ from { opacity: 1; transform: translateX(0); max-height: 96px; }
3179
+ to { opacity: 0; transform: translateX(24px); max-height: 0; padding-top: 0; padding-bottom: 0; }
3180
+ }
3181
+ @media (prefers-reduced-motion: reduce) {
3182
+ .property-news-breaking-dismiss { transition: none; }
3183
+ .property-news-breaking-item[data-new-breaking="true"],
3184
+ .property-news-breaking-item[data-new-breaking="true"]::after { animation-duration: 1ms; animation-delay: 0ms; }
3185
+ .property-news-breaking-item[data-dismissing="true"] { animation-duration: 1ms; }
3186
+ }
3187
+ /* Asset badge: the wrapper hosts the INSTANT CSS tooltip (native title is
3188
+ too slow) and the hover affordance that says "the icon itself is the
3189
+ target" \u2014 the box scales up with a gold ring while the tooltip shows
3190
+ "Name (TICKER)" immediately. */
3191
+ .news-badge-wrap { position: relative; display: inline-flex; flex-shrink: 0; }
3192
+ .news-badge-wrap > .news-badge-box { transition: transform 0.15s ease, box-shadow 0.15s ease; }
3193
+ .news-badge-wrap:hover > .news-badge-box {
3194
+ transform: scale(1.08);
3195
+ box-shadow: 0 0 0 1px rgba(var(--color-accent-rgb, 230, 200, 126), 0.7);
3196
+ }
3197
+ /* Instant element tooltip (an ::after can't carry a coloured % change).
3198
+ PORTALED to document.body with position:fixed \u2014 as an absolute child it
3199
+ was clipped by the scrolling news list at the top/right edges. Anchored
3200
+ above the badge by default (translateY(-100%)), flipped below via
3201
+ data-tip-below when the badge sits too close to the viewport top. */
3202
+ .news-badge-tipbox {
3203
+ position: fixed;
3204
+ display: flex;
3205
+ transform: translate(-50%, -100%);
3206
+ flex-direction: column;
3207
+ align-items: center;
3208
+ gap: 2px;
3209
+ background: #1a1d24;
3210
+ color: #fff;
3211
+ font-size: 0.62rem;
3212
+ font-weight: 600;
3213
+ line-height: 1.2;
3214
+ padding: 0.32rem 0.55rem;
3215
+ border-radius: 5px;
3216
+ border: 1px solid rgba(255, 255, 255, 0.12);
3217
+ white-space: nowrap;
3218
+ z-index: 10060;
3219
+ pointer-events: none;
3220
+ }
3221
+ .news-badge-tipbox[data-tip-below="true"] { transform: translate(-50%, 0); }
2759
3222
  `;
2760
3223
  document.head.appendChild(style);
2761
3224
  };
@@ -2792,7 +3255,8 @@ var formatDateShort = (value) => {
2792
3255
  if (diffS < 3600) return `${Math.floor(diffS / 60)}m ago`;
2793
3256
  if (diffS < 86400) return `${Math.floor(diffS / 3600)}h ago`;
2794
3257
  const time = d.toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit", hour12: true });
2795
- return `${time} \xB7 ${d.toLocaleDateString(void 0, { month: "short", day: "numeric" })}`;
3258
+ const date = d.toLocaleDateString(void 0, { month: "short", day: "numeric" });
3259
+ return `${date} \xB7 ${time}`;
2796
3260
  };
2797
3261
  var formatTimeAgo = (timestamp) => {
2798
3262
  const diff = (Date.now() - new Date(timestamp).getTime()) / 1e3;
@@ -2806,10 +3270,66 @@ function getSentimentInfo(score) {
2806
3270
  if (score < -0.15) return { arrow: "\u25BC", label: "Bearish", color: "#f6465d" };
2807
3271
  return { arrow: "", label: "Neutral", color: "#848e9c" };
2808
3272
  }
3273
+ function SentimentBar({ score, color }) {
3274
+ const clamped = Math.max(-1, Math.min(1, score));
3275
+ const widthPct = Math.abs(clamped) * 50;
3276
+ const direction = clamped > 0 ? "positive" : clamped < 0 ? "negative" : "neutral";
3277
+ const word = clamped > 0.15 ? "bullish" : clamped < -0.15 ? "bearish" : "neutral";
3278
+ const signed = `${clamped >= 0 ? "+" : "\u2212"}${Math.abs(clamped).toFixed(2)}`;
3279
+ return /* @__PURE__ */ jsxs(
3280
+ "span",
3281
+ {
3282
+ "data-testid": "sentiment-bar",
3283
+ "data-score": String(score),
3284
+ role: "img",
3285
+ "aria-label": `Sentiment: ${word}, ${signed} of +1`,
3286
+ style: {
3287
+ position: "relative",
3288
+ display: "inline-block",
3289
+ width: "36px",
3290
+ height: "3px",
3291
+ borderRadius: "2px",
3292
+ background: "rgba(255,255,255,0.10)",
3293
+ flexShrink: 0
3294
+ },
3295
+ children: [
3296
+ /* @__PURE__ */ jsx(
3297
+ "span",
3298
+ {
3299
+ style: {
3300
+ position: "absolute",
3301
+ left: "50%",
3302
+ top: "-1px",
3303
+ width: "1px",
3304
+ height: "5px",
3305
+ background: "rgba(255,255,255,0.28)"
3306
+ }
3307
+ }
3308
+ ),
3309
+ /* @__PURE__ */ jsx(
3310
+ "span",
3311
+ {
3312
+ "data-testid": "sentiment-fill",
3313
+ "data-direction": direction,
3314
+ style: {
3315
+ position: "absolute",
3316
+ top: 0,
3317
+ height: "100%",
3318
+ borderRadius: "2px",
3319
+ background: color,
3320
+ width: `${widthPct}%`,
3321
+ left: `${clamped >= 0 ? 50 : 50 - widthPct}%`
3322
+ }
3323
+ }
3324
+ )
3325
+ ]
3326
+ }
3327
+ );
3328
+ }
2809
3329
  function NewsArticleModal({ item, onClose }) {
2810
3330
  const sentimentInfo = getSentimentInfo(item.sentimentScore);
2811
3331
  const catStyle = categoryStyles[item.type] ?? categoryStyles.market;
2812
- React5.useEffect(() => {
3332
+ React9.useEffect(() => {
2813
3333
  const handler = (e) => {
2814
3334
  if (e.key === "Escape") onClose();
2815
3335
  };
@@ -2870,7 +3390,7 @@ function NewsArticleModal({ item, onClose }) {
2870
3390
  children: /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "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" }) })
2871
3391
  }
2872
3392
  ) }),
2873
- sentimentInfo && /* @__PURE__ */ jsx("div", { style: { marginBottom: "0.6rem" }, children: /* @__PURE__ */ jsxs(
3393
+ sentimentInfo && /* @__PURE__ */ jsx("div", { style: { marginBottom: "0.6rem" }, children: /* @__PURE__ */ jsx(
2874
3394
  "span",
2875
3395
  {
2876
3396
  style: {
@@ -2885,10 +3405,11 @@ function NewsArticleModal({ item, onClose }) {
2885
3405
  background: `${sentimentInfo.color}1a`,
2886
3406
  border: `1px solid ${sentimentInfo.color}40`
2887
3407
  },
2888
- children: [
3408
+ children: /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.3rem" }, children: [
2889
3409
  sentimentInfo.arrow ? `${sentimentInfo.arrow} ` : "",
2890
- sentimentInfo.label
2891
- ]
3410
+ sentimentInfo.label,
3411
+ item.sentimentScore != null && /* @__PURE__ */ jsx(SentimentBar, { score: item.sentimentScore, color: sentimentInfo.color })
3412
+ ] })
2892
3413
  }
2893
3414
  ) }),
2894
3415
  /* @__PURE__ */ jsx("h2", { style: { fontSize: "1rem", fontWeight: 600, color: "#fff", lineHeight: 1.5, margin: "0 0 1rem" }, children: item.title }),
@@ -2903,7 +3424,13 @@ function NewsArticleModal({ item, onClose }) {
2903
3424
  fontSize: "0.65rem",
2904
3425
  fontWeight: 500,
2905
3426
  background: "rgba(255,255,255,0.07)",
2906
- color: "#848e9c"
3427
+ color: "#848e9c",
3428
+ // Truncation guard moved here with the source itself — a long
3429
+ // publication name would otherwise stretch the meta row.
3430
+ maxWidth: "120px",
3431
+ overflow: "hidden",
3432
+ textOverflow: "ellipsis",
3433
+ whiteSpace: "nowrap"
2907
3434
  },
2908
3435
  children: item.source
2909
3436
  }
@@ -2965,7 +3492,7 @@ function NewsArticleModal({ item, onClose }) {
2965
3492
  }
2966
3493
  return modal;
2967
3494
  }
2968
- var PropertyNewsUpdates = React5.forwardRef(
3495
+ var PropertyNewsUpdates = React9.forwardRef(
2969
3496
  ({
2970
3497
  className,
2971
3498
  heading,
@@ -2979,49 +3506,120 @@ var PropertyNewsUpdates = React5.forwardRef(
2979
3506
  viewAllLabel = "View All News",
2980
3507
  connectionStatus = "live",
2981
3508
  disablePagination = false,
3509
+ hideHeading = false,
3510
+ hideCategoryTabs = false,
3511
+ category,
3512
+ onCategoryChange,
3513
+ pinBreaking = false,
3514
+ onAssetOpen,
3515
+ showAssetBadges = true,
2982
3516
  ...props
2983
3517
  }, ref) => {
2984
3518
  const isPurchaseVariant = variant === "purchases";
2985
3519
  const isHomeVariant = variant === "home";
2986
3520
  const resolvedHeading = heading ?? (isPurchaseVariant ? "Live Purchases" : "Property News & Headlines");
2987
- const [homeTab, setHomeTab] = React5.useState("all");
2988
- const [homePage, setHomePage] = React5.useState(0);
3521
+ const headerTrailing = Boolean(isHomeVariant ? viewAllHref : !isPurchaseVariant);
3522
+ const [homeTabInternal, setHomeTabInternal] = React9.useState("all");
3523
+ const homeTab = category ?? homeTabInternal;
3524
+ const setHomeTab = React9.useCallback(
3525
+ (next) => {
3526
+ setHomeTabInternal(next);
3527
+ onCategoryChange?.(next);
3528
+ },
3529
+ [onCategoryChange]
3530
+ );
3531
+ const [homePage, setHomePage] = React9.useState(0);
2989
3532
  const purchaseItems = purchasesProp ?? [];
2990
- const [page, setPage] = React5.useState(0);
2991
- const [selectedItem, setSelectedItem] = React5.useState(null);
2992
- const [isMobile, setIsMobile] = React5.useState(
3533
+ const [page, setPage] = React9.useState(0);
3534
+ const [selectedItem, setSelectedItem] = React9.useState(null);
3535
+ const [isMobile, setIsMobile] = React9.useState(
2993
3536
  () => typeof window !== "undefined" && window.innerWidth <= 768
2994
3537
  );
2995
- React5.useEffect(() => {
3538
+ React9.useEffect(() => {
2996
3539
  const check = () => setIsMobile(window.innerWidth <= 768);
2997
3540
  window.addEventListener("resize", check);
2998
3541
  return () => window.removeEventListener("resize", check);
2999
3542
  }, []);
3000
3543
  const homePageSize = isMobile ? ITEMS_PER_PAGE_MOBILE : ITEMS_PER_PAGE;
3001
- React5.useEffect(() => {
3544
+ React9.useEffect(() => {
3002
3545
  ensureAnimationsInjected();
3003
3546
  }, []);
3004
- React5.useEffect(() => {
3547
+ React9.useEffect(() => {
3005
3548
  setHomePage(0);
3006
3549
  }, [homeTab]);
3007
- const hasItems = Array.isArray(items) && items.length > 0;
3008
- const totalPages = React5.useMemo(
3009
- () => hasItems ? Math.max(1, Math.ceil(items.length / ITEMS_PER_PAGE)) : 1,
3010
- [hasItems, items.length]
3550
+ const breakingCandidates = React9.useMemo(() => {
3551
+ if (!pinBreaking || !Array.isArray(items)) return [];
3552
+ 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());
3553
+ }, [pinBreaking, items]);
3554
+ const breakingNow = useTickingNow(
3555
+ breakingCandidates.length > 0,
3556
+ breakingCandidates[0]?.publishedAt ?? null
3557
+ );
3558
+ const breakingEligible = React9.useMemo(() => {
3559
+ const cutoff = breakingNow - BREAKING_WINDOW_HOURS * 36e5;
3560
+ return breakingCandidates.filter((it) => {
3561
+ if (new Date(it.publishedAt).getTime() < cutoff) return false;
3562
+ return homeTab === "all" || it.type === homeTab;
3563
+ });
3564
+ }, [breakingCandidates, breakingNow, homeTab]);
3565
+ const [dismissedVersion, setDismissedVersion] = React9.useState(0);
3566
+ const [dismissingBreakingKey, setDismissingBreakingKey] = React9.useState(null);
3567
+ const [breakingSwipe, setBreakingSwipe] = React9.useState(null);
3568
+ const breakingSwipeStartXRef = React9.useRef(0);
3569
+ const suppressBreakingClickRef = React9.useRef(false);
3570
+ const breakingSwipeActiveKeyRef = React9.useRef(null);
3571
+ const breakingSwipeMaxRef = React9.useRef(0);
3572
+ const [badgeTip, setBadgeTip] = React9.useState(null);
3573
+ const BADGE_TIP_GAP = 7;
3574
+ const BADGE_TIP_FLIP_PX = 56;
3575
+ const showBadgeTip = (el, asset) => {
3576
+ const rect = el.getBoundingClientRect();
3577
+ const below = rect.top < BADGE_TIP_FLIP_PX;
3578
+ setBadgeTip({
3579
+ asset,
3580
+ left: rect.left + rect.width / 2,
3581
+ top: below ? rect.bottom + BADGE_TIP_GAP : rect.top - BADGE_TIP_GAP,
3582
+ below
3583
+ });
3584
+ };
3585
+ const pinnedBreaking = React9.useMemo(() => {
3586
+ return breakingEligible.filter((it) => !dismissedBreakingKeys.has(breakingKey(it))).sort((a, b) => breakingImpact(b) - breakingImpact(a)).slice(0, MAX_PINNED_BREAKING);
3587
+ }, [breakingEligible, dismissedVersion]);
3588
+ const breakingRowKeys = React9.useMemo(
3589
+ () => new Set(breakingEligible.map(breakingKey)),
3590
+ [breakingEligible]
3591
+ );
3592
+ const listItems = React9.useMemo(() => {
3593
+ if (pinnedBreaking.length === 0) return items;
3594
+ const pinned = new Set(pinnedBreaking);
3595
+ return items.filter((it) => !pinned.has(it));
3596
+ }, [items, pinnedBreaking]);
3597
+ const hasItems = Array.isArray(listItems) && listItems.length > 0;
3598
+ const totalPages = React9.useMemo(
3599
+ () => hasItems ? Math.max(1, Math.ceil(listItems.length / ITEMS_PER_PAGE)) : 1,
3600
+ [hasItems, listItems.length]
3011
3601
  );
3012
- React5.useEffect(() => {
3602
+ React9.useEffect(() => {
3013
3603
  setPage((prev) => Math.min(prev, totalPages - 1));
3014
3604
  }, [totalPages]);
3015
- const paginatedItems = hasItems ? items.slice(page * ITEMS_PER_PAGE, page * ITEMS_PER_PAGE + ITEMS_PER_PAGE) : [];
3016
- const homeFilteredItems = React5.useMemo(
3017
- () => hasItems ? items.filter((it) => homeTab === "all" || it.type === homeTab) : [],
3018
- [hasItems, items, homeTab]
3605
+ const paginatedItems = hasItems ? listItems.slice(page * ITEMS_PER_PAGE, page * ITEMS_PER_PAGE + ITEMS_PER_PAGE) : [];
3606
+ const homeFilteredItems = React9.useMemo(
3607
+ () => hasItems ? listItems.filter((it) => homeTab === "all" || it.type === homeTab) : [],
3608
+ [hasItems, listItems, homeTab]
3019
3609
  );
3020
- const homeTotalPages = React5.useMemo(
3610
+ const homeTotalPages = React9.useMemo(
3021
3611
  () => Math.max(1, Math.ceil(homeFilteredItems.length / homePageSize)),
3022
3612
  [homeFilteredItems.length, homePageSize]
3023
3613
  );
3024
3614
  const homePaginatedItems = disablePagination ? homeFilteredItems : homeFilteredItems.slice(homePage * homePageSize, (homePage + 1) * homePageSize);
3615
+ const youngestRowPublishedAt = React9.useMemo(() => {
3616
+ let youngest = null;
3617
+ for (const it of homePaginatedItems) {
3618
+ if (it.publishedAt && (!youngest || it.publishedAt > youngest)) youngest = it.publishedAt;
3619
+ }
3620
+ return youngest;
3621
+ }, [homePaginatedItems]);
3622
+ const rowNow = useTickingNow(isHomeVariant && youngestRowPublishedAt != null, youngestRowPublishedAt);
3025
3623
  return /* @__PURE__ */ jsxs(
3026
3624
  "div",
3027
3625
  {
@@ -3032,8 +3630,8 @@ var PropertyNewsUpdates = React5.forwardRef(
3032
3630
  ),
3033
3631
  ...props,
3034
3632
  children: [
3035
- /* @__PURE__ */ 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: [
3036
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
3633
+ (!hideHeading || headerTrailing) && /* @__PURE__ */ 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: [
3634
+ !hideHeading && /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
3037
3635
  isHomeVariant && /* @__PURE__ */ 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__ */ 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" }) }),
3038
3636
  isPurchaseVariant && /* @__PURE__ */ 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" } }),
3039
3637
  /* @__PURE__ */ jsxs("div", { children: [
@@ -3052,7 +3650,7 @@ var PropertyNewsUpdates = React5.forwardRef(
3052
3650
  "LIVE"
3053
3651
  ] }) : null
3054
3652
  ] }),
3055
- isHomeVariant && /* @__PURE__ */ 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__ */ jsx(
3653
+ isHomeVariant && !hideCategoryTabs && /* @__PURE__ */ 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__ */ jsx(
3056
3654
  "button",
3057
3655
  {
3058
3656
  type: "button",
@@ -3073,10 +3671,229 @@ var PropertyNewsUpdates = React5.forwardRef(
3073
3671
  },
3074
3672
  tab
3075
3673
  )) }),
3674
+ pinnedBreaking.length > 0 && /* @__PURE__ */ jsx(
3675
+ "div",
3676
+ {
3677
+ "data-testid": "news-breaking",
3678
+ style: {
3679
+ display: "flex",
3680
+ flexDirection: "column",
3681
+ marginBottom: "0.5rem",
3682
+ // Clips the row while it's being swiped out to the right. The gold
3683
+ // band styling lives on each ITEM below, not here — the whole band
3684
+ // (rule, wash, hairlines) must travel with a swipe, not just the
3685
+ // text inside it (user bug report, 2026-08-20).
3686
+ overflow: "hidden"
3687
+ },
3688
+ children: pinnedBreaking.map((item, i) => {
3689
+ const sentiment = getSentimentInfo(item.sentimentScore);
3690
+ const k = breakingKey(item);
3691
+ const dx = breakingSwipe?.key === k ? breakingSwipe.dx : 0;
3692
+ const finishDismiss = () => {
3693
+ dismissedBreakingKeys.add(k);
3694
+ setDismissingBreakingKey(null);
3695
+ setDismissedVersion((v) => v + 1);
3696
+ };
3697
+ return /* @__PURE__ */ jsxs(
3698
+ "button",
3699
+ {
3700
+ type: "button",
3701
+ className: "property-news-breaking-item",
3702
+ "data-new-breaking": item.isNew ? "true" : void 0,
3703
+ "data-dismissing": dismissingBreakingKey === k ? "true" : void 0,
3704
+ "data-testid": "news-breaking-item",
3705
+ onAnimationEnd: (e) => {
3706
+ if (e.animationName === "propertyNewsBreakingDismiss" && dismissingBreakingKey === k) {
3707
+ finishDismiss();
3708
+ }
3709
+ },
3710
+ onClick: () => {
3711
+ if (suppressBreakingClickRef.current) {
3712
+ suppressBreakingClickRef.current = false;
3713
+ return;
3714
+ }
3715
+ setSelectedItem(item);
3716
+ },
3717
+ onPointerDown: (e) => {
3718
+ breakingSwipeStartXRef.current = e.clientX;
3719
+ breakingSwipeActiveKeyRef.current = k;
3720
+ breakingSwipeMaxRef.current = 0;
3721
+ setBreakingSwipe({ key: k, dx: 0 });
3722
+ try {
3723
+ e.currentTarget.setPointerCapture?.(e.pointerId);
3724
+ } catch {
3725
+ }
3726
+ },
3727
+ onPointerMove: (e) => {
3728
+ if (breakingSwipeActiveKeyRef.current !== k) return;
3729
+ const delta = e.clientX - breakingSwipeStartXRef.current;
3730
+ breakingSwipeMaxRef.current = Math.max(breakingSwipeMaxRef.current, Math.abs(delta));
3731
+ setBreakingSwipe({ key: k, dx: Math.max(0, delta) });
3732
+ },
3733
+ onPointerUp: (e) => {
3734
+ if (breakingSwipeActiveKeyRef.current !== k) return;
3735
+ breakingSwipeActiveKeyRef.current = null;
3736
+ if (breakingSwipeMaxRef.current > 6) suppressBreakingClickRef.current = true;
3737
+ setBreakingSwipe(null);
3738
+ const drag = Math.max(0, e.clientX - breakingSwipeStartXRef.current);
3739
+ const width = e.currentTarget.offsetWidth || 0;
3740
+ if (drag >= Math.max(BREAKING_SWIPE_MIN_PX, width * 0.3)) {
3741
+ dismissedBreakingKeys.add(k);
3742
+ setDismissedVersion((v) => v + 1);
3743
+ }
3744
+ },
3745
+ onPointerCancel: () => {
3746
+ breakingSwipeActiveKeyRef.current = null;
3747
+ setBreakingSwipe(null);
3748
+ },
3749
+ style: {
3750
+ display: "block",
3751
+ width: "100%",
3752
+ padding: "0.4rem 0 0.5rem 0.6rem",
3753
+ textAlign: "left",
3754
+ background: "none",
3755
+ border: "none",
3756
+ cursor: "pointer",
3757
+ minWidth: 0,
3758
+ // Highlighted on purpose (user call, 2026-08-20): BREAKING is
3759
+ // the one thing on this page allowed to shout. The gold rule,
3760
+ // hairlines and wash live HERE on the item — the whole band
3761
+ // travels when the item is swiped away.
3762
+ borderLeft: "3px solid var(--color-accent, #E6C87E)",
3763
+ borderTop: "1px solid rgba(var(--color-accent-rgb, 230, 200, 126), 0.28)",
3764
+ borderBottom: "1px solid rgba(var(--color-accent-rgb, 230, 200, 126), 0.28)",
3765
+ 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%)",
3766
+ // Horizontal drags are ours; vertical stays with the scroll.
3767
+ touchAction: "pan-y",
3768
+ transform: dx > 0 ? `translateX(${dx}px)` : void 0,
3769
+ opacity: dx > 0 ? Math.max(0.3, 1 - dx / 320) : 1,
3770
+ transition: dx > 0 ? "none" : "transform 0.15s ease, opacity 0.15s ease"
3771
+ },
3772
+ children: [
3773
+ /* @__PURE__ */ jsx(
3774
+ "span",
3775
+ {
3776
+ "data-testid": "news-breaking-title",
3777
+ style: {
3778
+ display: "-webkit-box",
3779
+ WebkitBoxOrient: "vertical",
3780
+ WebkitLineClamp: 2,
3781
+ fontSize: "0.8rem",
3782
+ fontWeight: 600,
3783
+ color: "#fff",
3784
+ lineHeight: 1.35,
3785
+ overflow: "hidden",
3786
+ overflowWrap: "anywhere"
3787
+ },
3788
+ children: item.title
3789
+ }
3790
+ ),
3791
+ /* @__PURE__ */ jsxs(
3792
+ "span",
3793
+ {
3794
+ style: {
3795
+ display: "flex",
3796
+ alignItems: "center",
3797
+ gap: "0.45rem",
3798
+ marginTop: "0.2rem"
3799
+ },
3800
+ children: [
3801
+ (() => {
3802
+ const age = rowAgeLabel(item.publishedAt, breakingNow);
3803
+ return age ? /* @__PURE__ */ jsx(
3804
+ "span",
3805
+ {
3806
+ "data-age-fresh": age.fresh ? "true" : void 0,
3807
+ style: {
3808
+ fontSize: "0.64rem",
3809
+ fontWeight: 600,
3810
+ fontVariantNumeric: "tabular-nums",
3811
+ color: age.fresh ? "var(--color-accent, #E6C87E)" : "rgba(255,255,255,0.65)"
3812
+ },
3813
+ children: age.label
3814
+ }
3815
+ ) : null;
3816
+ })(),
3817
+ /* @__PURE__ */ jsx(
3818
+ "span",
3819
+ {
3820
+ "data-testid": "breaking-tag",
3821
+ style: {
3822
+ fontSize: "0.55rem",
3823
+ fontWeight: 700,
3824
+ letterSpacing: "0.09em",
3825
+ color: "#0a0b0e",
3826
+ background: "var(--color-accent, #E6C87E)",
3827
+ padding: "0.16rem 0.34rem 0.13rem",
3828
+ borderRadius: "3px",
3829
+ lineHeight: 1
3830
+ },
3831
+ children: "BREAKING"
3832
+ }
3833
+ ),
3834
+ sentiment ? /* @__PURE__ */ jsxs(
3835
+ "span",
3836
+ {
3837
+ style: {
3838
+ display: "inline-flex",
3839
+ alignItems: "center",
3840
+ gap: "0.3rem",
3841
+ fontSize: "0.58rem",
3842
+ fontWeight: 600,
3843
+ color: sentiment.color
3844
+ },
3845
+ children: [
3846
+ sentiment.arrow ? `${sentiment.arrow} ` : "",
3847
+ sentiment.label,
3848
+ item.sentimentScore != null && /* @__PURE__ */ jsx(SentimentBar, { score: item.sentimentScore, color: sentiment.color })
3849
+ ]
3850
+ }
3851
+ ) : null,
3852
+ /* @__PURE__ */ jsx(
3853
+ "span",
3854
+ {
3855
+ className: "property-news-breaking-dismiss",
3856
+ "data-testid": "breaking-dismiss",
3857
+ role: "button",
3858
+ tabIndex: 0,
3859
+ "aria-label": `Dismiss breaking story: ${item.title}`,
3860
+ onPointerDown: (e) => e.stopPropagation(),
3861
+ onClick: (e) => {
3862
+ e.stopPropagation();
3863
+ setDismissingBreakingKey(k);
3864
+ },
3865
+ onKeyDown: (e) => {
3866
+ if (e.key !== "Enter" && e.key !== " ") return;
3867
+ e.preventDefault();
3868
+ e.stopPropagation();
3869
+ setDismissingBreakingKey(k);
3870
+ },
3871
+ style: {
3872
+ marginLeft: "auto",
3873
+ padding: "0.2rem 0.35rem 0.2rem 0.5rem",
3874
+ fontSize: "0.58rem",
3875
+ fontWeight: 600,
3876
+ color: "rgba(255,255,255,0.58)",
3877
+ cursor: "pointer",
3878
+ whiteSpace: "nowrap"
3879
+ },
3880
+ children: "Dismiss"
3881
+ }
3882
+ )
3883
+ ]
3884
+ }
3885
+ )
3886
+ ]
3887
+ },
3888
+ item.id ?? `breaking-${i}`
3889
+ );
3890
+ })
3891
+ }
3892
+ ),
3076
3893
  /* @__PURE__ */ jsx(
3077
3894
  "div",
3078
3895
  {
3079
- className: cn("flex flex-1 flex-col overflow-y-auto overflow-x-hidden", isHomeVariant ? "gap-0" : "mt-4 gap-3"),
3896
+ className: cn("news-list-scroll flex flex-1 flex-col overflow-y-auto overflow-x-hidden", isHomeVariant ? "gap-0" : "mt-4 gap-3"),
3080
3897
  style: !isPurchaseVariant && !isHomeVariant ? { minHeight: `${ITEMS_PER_PAGE * 86}px` } : void 0,
3081
3898
  children: isPurchaseVariant ? purchaseItems.length > 0 ? purchaseItems.slice(0, 7).map((purchase, index) => {
3082
3899
  const maxAmount = 6e4;
@@ -3107,10 +3924,12 @@ var PropertyNewsUpdates = React5.forwardRef(
3107
3924
  const key = item.displayId ?? item.id ?? `${item.title}-${index}`;
3108
3925
  const isProperty = item.type === "property";
3109
3926
  const sentimentInfo = getSentimentInfo(item.sentimentScore);
3927
+ const isBreakingRow = pinBreaking && Boolean(item.breaking) && breakingRowKeys.has(breakingKey(item));
3110
3928
  return /* @__PURE__ */ jsxs(
3111
3929
  "button",
3112
3930
  {
3113
3931
  type: "button",
3932
+ "data-breaking-row": isBreakingRow ? "true" : void 0,
3114
3933
  onClick: () => setSelectedItem(item),
3115
3934
  style: {
3116
3935
  width: "100%",
@@ -3126,6 +3945,10 @@ var PropertyNewsUpdates = React5.forwardRef(
3126
3945
  gap: isMobile ? "0.25rem" : "0.5rem",
3127
3946
  color: "inherit",
3128
3947
  transition: "background-color 0.15s"
3948
+ // No gold wash or left edge on a dismissed row: once swiped
3949
+ // out of the pin it is back in the ordinary feed, and the
3950
+ // BREAKING badge below is enough to find it again. Only the
3951
+ // PINNED strip keeps the gold treatment.
3129
3952
  },
3130
3953
  onMouseEnter: (e) => {
3131
3954
  e.currentTarget.style.backgroundColor = "rgba(255,255,255,0.04)";
@@ -3139,15 +3962,91 @@ var PropertyNewsUpdates = React5.forwardRef(
3139
3962
  },
3140
3963
  children: [
3141
3964
  /* @__PURE__ */ jsxs("div", { style: { flex: 1 }, children: [
3142
- /* @__PURE__ */ 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 }),
3965
+ /* @__PURE__ */ 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 }),
3143
3966
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: isMobile ? "0.35rem" : "0.5rem", alignItems: "center", flexWrap: "wrap" }, children: [
3144
- /* @__PURE__ */ jsx("span", { style: { color: "#848e9c", fontSize: isMobile ? "0.55rem" : "0.7rem" }, children: formatDateShort(item.date) }),
3145
- item.source && /* @__PURE__ */ 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 }),
3967
+ isBreakingRow && /* @__PURE__ */ jsx(
3968
+ "span",
3969
+ {
3970
+ "data-testid": "breaking-tag",
3971
+ style: {
3972
+ fontSize: "0.55rem",
3973
+ fontWeight: 700,
3974
+ letterSpacing: "0.09em",
3975
+ color: "#0a0b0e",
3976
+ background: "var(--color-accent, #E6C87E)",
3977
+ padding: "0.16rem 0.34rem 0.13rem",
3978
+ borderRadius: "3px",
3979
+ lineHeight: 1
3980
+ },
3981
+ children: "BREAKING"
3982
+ }
3983
+ ),
3984
+ (() => {
3985
+ const age = rowAgeLabel(item.publishedAt, rowNow);
3986
+ return age ? /* @__PURE__ */ jsx(
3987
+ "span",
3988
+ {
3989
+ "data-testid": "news-age",
3990
+ "data-age-fresh": age.fresh ? "true" : void 0,
3991
+ style: {
3992
+ // Sized against the 0.8rem row title (matches the
3993
+ // breaking strip's meta scale).
3994
+ fontSize: isMobile ? "0.58rem" : "0.64rem",
3995
+ fontWeight: 600,
3996
+ fontVariantNumeric: "tabular-nums",
3997
+ color: age.fresh ? "var(--color-accent, #E6C87E)" : "rgba(255,255,255,0.65)"
3998
+ },
3999
+ children: age.label
4000
+ }
4001
+ ) : /* @__PURE__ */ jsx("span", { style: { color: "#848e9c", fontSize: isMobile ? "0.55rem" : "0.7rem" }, children: formatDateShort(item.date) });
4002
+ })(),
4003
+ showAssetBadges && isProperty && (item.assets ?? (item.asset ? [item.asset] : [])).map((badgeAsset) => /* @__PURE__ */ jsx(
4004
+ "span",
4005
+ {
4006
+ className: "news-badge-wrap",
4007
+ onClick: onAssetOpen ? (e) => {
4008
+ e.stopPropagation();
4009
+ e.preventDefault();
4010
+ onAssetOpen(badgeAsset);
4011
+ } : void 0,
4012
+ onMouseEnter: (e) => showBadgeTip(e.currentTarget, badgeAsset),
4013
+ onMouseLeave: () => setBadgeTip(null),
4014
+ style: { cursor: onAssetOpen ? "pointer" : void 0 },
4015
+ children: /* @__PURE__ */ jsx(
4016
+ "span",
4017
+ {
4018
+ className: "news-badge-box",
4019
+ "data-testid": "news-asset-badge",
4020
+ style: {
4021
+ display: "inline-flex",
4022
+ alignItems: "center",
4023
+ justifyContent: "center",
4024
+ width: "20px",
4025
+ height: "20px",
4026
+ borderRadius: "5px",
4027
+ overflow: "hidden",
4028
+ background: "rgba(255,255,255,0.08)",
4029
+ flexShrink: 0
4030
+ },
4031
+ children: badgeAsset.imageUrl ? /* @__PURE__ */ jsx(
4032
+ "img",
4033
+ {
4034
+ src: badgeAsset.imageUrl,
4035
+ alt: badgeAsset.name ?? badgeAsset.ticker,
4036
+ style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
4037
+ }
4038
+ ) : /* @__PURE__ */ jsx("span", { style: { fontSize: "0.45rem", fontWeight: 700, letterSpacing: "0.02em", color: "rgba(255,255,255,0.75)" }, children: badgeAsset.ticker.slice(0, 4) })
4039
+ }
4040
+ )
4041
+ },
4042
+ badgeAsset.ticker
4043
+ )),
3146
4044
  /* @__PURE__ */ 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" }),
3147
- sentimentInfo && /* @__PURE__ */ jsxs("span", { style: { fontSize: isMobile ? "0.52rem" : "0.65rem", fontWeight: 600, color: sentimentInfo.color }, children: [
4045
+ sentimentInfo && /* @__PURE__ */ jsx("span", { style: { fontSize: isMobile ? "0.52rem" : "0.65rem", fontWeight: 600, color: sentimentInfo.color }, children: /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.3rem" }, children: [
3148
4046
  sentimentInfo.arrow ? `${sentimentInfo.arrow} ` : "",
3149
- sentimentInfo.label
3150
- ] })
4047
+ sentimentInfo.label,
4048
+ item.sentimentScore != null && /* @__PURE__ */ jsx(SentimentBar, { score: item.sentimentScore, color: sentimentInfo.color })
4049
+ ] }) })
3151
4050
  ] })
3152
4051
  ] }),
3153
4052
  /* @__PURE__ */ jsx("div", { style: { color: "#848e9c", paddingTop: "2px", flexShrink: 0 }, children: /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z" }) }) })
@@ -3196,15 +4095,13 @@ var PropertyNewsUpdates = React5.forwardRef(
3196
4095
  children: [
3197
4096
  /* @__PURE__ */ jsx("p", { style: { fontSize: "0.9375rem", fontWeight: 500, marginBottom: "0.4rem", color: "#fff", lineHeight: 1.4 }, children: item.title }),
3198
4097
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-end", gap: "0.5rem" }, children: [
3199
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem", minWidth: 0, flexWrap: "wrap" }, children: [
3200
- /* @__PURE__ */ jsx("span", { style: { fontSize: "0.75rem", color: isHighlighted ? "#0ecb81" : "#b5b8c5", whiteSpace: "nowrap" }, children: dateLabel }),
3201
- item.source && /* @__PURE__ */ 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 })
3202
- ] }),
4098
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem", minWidth: 0, flexWrap: "wrap" }, children: /* @__PURE__ */ jsx("span", { style: { fontSize: "0.75rem", color: isHighlighted ? "#0ecb81" : "#b5b8c5", whiteSpace: "nowrap" }, children: dateLabel }) }),
3203
4099
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "flex-end", gap: "0.3rem", flexShrink: 0 }, children: [
3204
- sentimentInfo && /* @__PURE__ */ jsxs("span", { style: { fontSize: "0.65rem", fontWeight: 700, color: sentimentInfo.color, whiteSpace: "nowrap" }, children: [
4100
+ sentimentInfo && /* @__PURE__ */ jsx("span", { style: { fontSize: "0.65rem", fontWeight: 700, color: sentimentInfo.color, whiteSpace: "nowrap" }, children: /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.3rem" }, children: [
3205
4101
  sentimentInfo.arrow ? `${sentimentInfo.arrow} ` : "",
3206
- sentimentInfo.label
3207
- ] }),
4102
+ sentimentInfo.label,
4103
+ item.sentimentScore != null && /* @__PURE__ */ jsx(SentimentBar, { score: item.sentimentScore, color: sentimentInfo.color })
4104
+ ] }) }),
3208
4105
  /* @__PURE__ */ 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" })
3209
4106
  ] })
3210
4107
  ] })
@@ -3277,7 +4174,21 @@ var PropertyNewsUpdates = React5.forwardRef(
3277
4174
  }
3278
4175
  )
3279
4176
  ] }),
3280
- selectedItem && !isPurchaseVariant && /* @__PURE__ */ jsx(NewsArticleModal, { item: selectedItem, onClose: () => setSelectedItem(null) })
4177
+ selectedItem && !isPurchaseVariant && /* @__PURE__ */ jsx(NewsArticleModal, { item: selectedItem, onClose: () => setSelectedItem(null) }),
4178
+ badgeTip && ReactDOM.createPortal(
4179
+ /* @__PURE__ */ jsx(
4180
+ "span",
4181
+ {
4182
+ className: "news-badge-tipbox",
4183
+ "data-testid": "news-asset-tip",
4184
+ "data-tip-below": badgeTip.below ? "true" : void 0,
4185
+ "aria-hidden": true,
4186
+ style: { position: "fixed", left: `${badgeTip.left}px`, top: `${badgeTip.top}px` },
4187
+ children: /* @__PURE__ */ jsx("span", { children: badgeTip.asset.name ? `${badgeTip.asset.name} (${badgeTip.asset.ticker})` : badgeTip.asset.ticker })
4188
+ }
4189
+ ),
4190
+ document.body
4191
+ )
3281
4192
  ]
3282
4193
  }
3283
4194
  );
@@ -3330,7 +4241,7 @@ var defaultFormat = (value) => new Intl.NumberFormat("en-US", {
3330
4241
  notation: value >= 1e5 ? "compact" : "standard",
3331
4242
  maximumFractionDigits: value >= 1e3 ? 0 : 2
3332
4243
  }).format(value);
3333
- var TradingSlider = React5.forwardRef(
4244
+ var TradingSlider = React9.forwardRef(
3334
4245
  ({
3335
4246
  label = "Trade size",
3336
4247
  helperText = "Drag to pick the desired notional.",
@@ -3348,7 +4259,7 @@ var TradingSlider = React5.forwardRef(
3348
4259
  ...rest
3349
4260
  }, ref) => {
3350
4261
  const isControlled = value !== void 0;
3351
- const [internalValue, setInternalValue] = React5.useState(
4262
+ const [internalValue, setInternalValue] = React9.useState(
3352
4263
  defaultValue ?? (typeof min === "number" ? min : 0)
3353
4264
  );
3354
4265
  const currentValue = isControlled ? Number(value) : internalValue;
@@ -3451,7 +4362,7 @@ var MobileToggleButton = styled10.button`
3451
4362
  padding: 0.6rem 0.5rem;
3452
4363
  }
3453
4364
  `;
3454
- var MobileTradeNav = React5.forwardRef(
4365
+ var MobileTradeNav = React9.forwardRef(
3455
4366
  ({ className, items, activeId, onChange, ...props }, ref) => {
3456
4367
  return /* @__PURE__ */ jsx(MobileToggleContainer, { ref, className: cn(className), ...props, children: items.map((item) => /* @__PURE__ */ jsxs(
3457
4368
  MobileToggleButton,
@@ -3977,7 +4888,7 @@ var formatPercent = (value, fractionDigits = 2) => {
3977
4888
  if (value == null || Number.isNaN(value)) return "\u2014";
3978
4889
  return `${value.toFixed(fractionDigits)}%`;
3979
4890
  };
3980
- var YourOrders = React5.forwardRef(
4891
+ var YourOrders = React9.forwardRef(
3981
4892
  ({
3982
4893
  className,
3983
4894
  title,
@@ -3990,10 +4901,10 @@ var YourOrders = React5.forwardRef(
3990
4901
  pageSize: pageSizeOverride,
3991
4902
  ...props
3992
4903
  }, ref) => {
3993
- const [internalActiveTab, setInternalActiveTab] = React5.useState(tabs?.[0]?.id ?? "portfolio");
3994
- const [page, setPage] = React5.useState(0);
4904
+ const [internalActiveTab, setInternalActiveTab] = React9.useState(tabs?.[0]?.id ?? "portfolio");
4905
+ const [page, setPage] = React9.useState(0);
3995
4906
  const effectiveActiveTabId = activeTabId ?? internalActiveTab;
3996
- React5.useEffect(() => {
4907
+ React9.useEffect(() => {
3997
4908
  setPage(0);
3998
4909
  }, [effectiveActiveTabId]);
3999
4910
  const handleTabChange = (tabId) => {
@@ -4375,7 +5286,7 @@ function createCandlestickSeries(chart, options) {
4375
5286
  }
4376
5287
  throw new Error("Candlestick series API is not available in the current lightweight-charts version.");
4377
5288
  }
4378
- var PriceChart = React5.forwardRef(
5289
+ var PriceChart = React9.forwardRef(
4379
5290
  ({
4380
5291
  className,
4381
5292
  title = "Price Chart",
@@ -4389,18 +5300,18 @@ var PriceChart = React5.forwardRef(
4389
5300
  height = 301.52,
4390
5301
  ...props
4391
5302
  }, ref) => {
4392
- const containerRef = React5.useRef(null);
4393
- const chartRef = React5.useRef(null);
4394
- const seriesRef = React5.useRef(null);
4395
- const priceLineRef = React5.useRef(null);
4396
- const [hoveredRange, setHoveredRange] = React5.useState(null);
4397
- const [dropdownOpen, setDropdownOpen] = React5.useState(false);
4398
- const dropdownRef = React5.useRef(null);
4399
- const isAutoScrollRef = React5.useRef(true);
5303
+ const containerRef = React9.useRef(null);
5304
+ const chartRef = React9.useRef(null);
5305
+ const seriesRef = React9.useRef(null);
5306
+ const priceLineRef = React9.useRef(null);
5307
+ const [hoveredRange, setHoveredRange] = React9.useState(null);
5308
+ const [dropdownOpen, setDropdownOpen] = React9.useState(false);
5309
+ const dropdownRef = React9.useRef(null);
5310
+ const isAutoScrollRef = React9.useRef(true);
4400
5311
  const visibleRanges = ranges.slice(0, VISIBLE_RANGE_COUNT);
4401
5312
  const dropdownRanges = ranges.slice(VISIBLE_RANGE_COUNT);
4402
5313
  const selectedInDropdown = dropdownRanges.includes(selectedRange);
4403
- React5.useEffect(() => {
5314
+ React9.useEffect(() => {
4404
5315
  const handleClickOutside = (e) => {
4405
5316
  if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
4406
5317
  setDropdownOpen(false);
@@ -4409,25 +5320,25 @@ var PriceChart = React5.forwardRef(
4409
5320
  document.addEventListener("mousedown", handleClickOutside);
4410
5321
  return () => document.removeEventListener("mousedown", handleClickOutside);
4411
5322
  }, []);
4412
- const resolvedPrice = React5.useMemo(() => {
5323
+ const resolvedPrice = React9.useMemo(() => {
4413
5324
  if (price != null) return price;
4414
5325
  const last = data.at(-1);
4415
5326
  return last?.close;
4416
5327
  }, [data, price]);
4417
- const inferredChangePercent = React5.useMemo(() => {
5328
+ const inferredChangePercent = React9.useMemo(() => {
4418
5329
  if (changePercent != null) return changePercent;
4419
5330
  const first = data[0]?.open;
4420
5331
  const last = data.at(-1)?.close;
4421
5332
  if (first == null || last == null || first === 0) return void 0;
4422
5333
  return (last - first) / first * 100;
4423
5334
  }, [changePercent, data]);
4424
- const dollarChange = React5.useMemo(() => {
5335
+ const dollarChange = React9.useMemo(() => {
4425
5336
  const first = data[0]?.open;
4426
5337
  const last = data.at(-1)?.close;
4427
5338
  if (first == null || last == null) return void 0;
4428
5339
  return last - first;
4429
5340
  }, [data]);
4430
- React5.useEffect(() => {
5341
+ React9.useEffect(() => {
4431
5342
  const el = containerRef.current;
4432
5343
  if (!el) return;
4433
5344
  const chart = LightweightCharts.createChart(el, {
@@ -4472,7 +5383,7 @@ var PriceChart = React5.forwardRef(
4472
5383
  chart.remove();
4473
5384
  };
4474
5385
  }, []);
4475
- React5.useEffect(() => {
5386
+ React9.useEffect(() => {
4476
5387
  const chart = chartRef.current;
4477
5388
  if (!chart) return;
4478
5389
  const effectiveRange = selectedRange ?? ranges?.[0] ?? "1D";
@@ -4480,7 +5391,7 @@ var PriceChart = React5.forwardRef(
4480
5391
  timeScale: getTimeScaleOptions(effectiveRange)
4481
5392
  });
4482
5393
  }, [selectedRange, ranges]);
4483
- React5.useEffect(() => {
5394
+ React9.useEffect(() => {
4484
5395
  const chart = chartRef.current;
4485
5396
  const series = seriesRef.current;
4486
5397
  if (!chart || !series) return;
@@ -4695,7 +5606,7 @@ var formatPrice3 = (value, currencySymbol, compact) => {
4695
5606
  maximumFractionDigits: 2
4696
5607
  })}`;
4697
5608
  };
4698
- var PropertyHeroHeader = React5.forwardRef(
5609
+ var PropertyHeroHeader = React9.forwardRef(
4699
5610
  ({
4700
5611
  className,
4701
5612
  imageUrl,
@@ -4729,12 +5640,12 @@ var PropertyHeroHeader = React5.forwardRef(
4729
5640
  const isPositive = changePercent == null ? void 0 : changePercent >= 0;
4730
5641
  const accentColor = "#e6c87e";
4731
5642
  const tradeHoverColor = "#f5dd9a";
4732
- const [isTradeInteracting, setIsTradeInteracting] = React5.useState(false);
4733
- const [isOfferInteracting, setIsOfferInteracting] = React5.useState(false);
4734
- const [isMobile, setIsMobile] = React5.useState(
5643
+ const [isTradeInteracting, setIsTradeInteracting] = React9.useState(false);
5644
+ const [isOfferInteracting, setIsOfferInteracting] = React9.useState(false);
5645
+ const [isMobile, setIsMobile] = React9.useState(
4735
5646
  () => typeof window !== "undefined" && window.innerWidth <= 768
4736
5647
  );
4737
- React5.useEffect(() => {
5648
+ React9.useEffect(() => {
4738
5649
  const onResize = () => setIsMobile(window.innerWidth <= 768);
4739
5650
  window.addEventListener("resize", onResize);
4740
5651
  return () => window.removeEventListener("resize", onResize);
@@ -5414,7 +6325,7 @@ var Header = ({
5414
6325
  setShowLoginPopup(true);
5415
6326
  }
5416
6327
  };
5417
- const handleLoginPopupClose = React5__default.useCallback(() => {
6328
+ const handleLoginPopupClose = React9__default.useCallback(() => {
5418
6329
  setShowLoginPopup(false);
5419
6330
  setLoginPopupInitialView(void 0);
5420
6331
  }, []);
@@ -5692,7 +6603,7 @@ var Header = ({
5692
6603
  }
5693
6604
  )
5694
6605
  ] }) : /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center" }, children: [
5695
- /* @__PURE__ */ jsx(Button2, { className: "signup", onClick: handleSignIn, children: "Join Competition" }),
6606
+ /* @__PURE__ */ jsx(Button2, { className: "signup", onClick: handleSignIn, children: "Access Beta" }),
5696
6607
  /* @__PURE__ */ jsx(
5697
6608
  MobileOnlyButton,
5698
6609
  {
@@ -6578,10 +7489,10 @@ var PBOperator = styled10.span`
6578
7489
  font-size: 0.55rem;
6579
7490
  }
6580
7491
  `;
6581
- var PropertySubheader = React5.forwardRef(
7492
+ var PropertySubheader = React9.forwardRef(
6582
7493
  ({ className, tabs, activeTabId, onTabChange, actions, ...props }, ref) => {
6583
- const tabsContainerRef = React5.useRef(null);
6584
- React5.useEffect(() => {
7494
+ const tabsContainerRef = React9.useRef(null);
7495
+ React9.useEffect(() => {
6585
7496
  const container = tabsContainerRef.current;
6586
7497
  if (!container) return;
6587
7498
  const isMobile = window.innerWidth <= 768;
@@ -6778,9 +7689,9 @@ var LoginPopup = ({
6778
7689
  const [fiatFundingLoading, setFiatFundingLoading] = useState(false);
6779
7690
  const [fundingError, setFundingError] = useState("");
6780
7691
  const [transakWidgetUrl, setTransakWidgetUrl] = useState(null);
6781
- const suppressAutoCloseRef = React5__default.useRef(false);
6782
- const validatedCodeRef = React5__default.useRef(null);
6783
- const finishGate = React5__default.useCallback(async () => {
7692
+ const suppressAutoCloseRef = React9__default.useRef(false);
7693
+ const validatedCodeRef = React9__default.useRef(null);
7694
+ const finishGate = React9__default.useCallback(async () => {
6784
7695
  if (!gate) return true;
6785
7696
  const code = validatedCodeRef.current;
6786
7697
  if (code) {
@@ -6806,7 +7717,7 @@ var LoginPopup = ({
6806
7717
  );
6807
7718
  return false;
6808
7719
  }, [gate, onSubmitReferralCode, onCheckAccess]);
6809
- const runCodeValidation = React5__default.useCallback(async (fullCode) => {
7720
+ const runCodeValidation = React9__default.useCallback(async (fullCode) => {
6810
7721
  setCodeStatus("checking");
6811
7722
  setError("");
6812
7723
  try {
@@ -6829,7 +7740,7 @@ var LoginPopup = ({
6829
7740
  setCodeStatus("invalid");
6830
7741
  }
6831
7742
  }, [onValidateCode, isAuthenticated, finishGate, onClose]);
6832
- const autoCodeTriedRef = React5__default.useRef(false);
7743
+ const autoCodeTriedRef = React9__default.useRef(false);
6833
7744
  useEffect(() => {
6834
7745
  if (autoCodeTriedRef.current || !gate || !initialCode) return;
6835
7746
  const full = normalizeAccessCode(initialCode);
@@ -7112,7 +8023,7 @@ var LoginPopup = ({
7112
8023
  setLoading(false);
7113
8024
  }
7114
8025
  };
7115
- const runGateOtpVerify = React5__default.useCallback(async (code) => {
8026
+ const runGateOtpVerify = React9__default.useCallback(async (code) => {
7116
8027
  if (!onVerifyEmailCode) return;
7117
8028
  setOtpStatus("checking");
7118
8029
  setError("");
@@ -7237,7 +8148,7 @@ var LoginPopup = ({
7237
8148
  /* @__PURE__ */ jsxs(Title, { children: [
7238
8149
  /* @__PURE__ */ jsxs(LogoContainer3, { children: [
7239
8150
  /* @__PURE__ */ jsx(LogoImage, { src: logoSrc, alt: logoAlt }),
7240
- /* @__PURE__ */ jsx(LogoBeta, { children: "Private Beta" })
8151
+ /* @__PURE__ */ jsx(LogoBeta, { children: "Beta" })
7241
8152
  ] }),
7242
8153
  /* @__PURE__ */ jsx(TitleText, { children: "Welcome to Loaf" })
7243
8154
  ] }),
@@ -7332,7 +8243,7 @@ var LoginPopup = ({
7332
8243
  /* @__PURE__ */ jsxs(GateBrand, { children: [
7333
8244
  /* @__PURE__ */ jsx(GateLogoLink, { href: "https://loafmarkets.com", "aria-label": "Go to loafmarkets.com", children: /* @__PURE__ */ jsx(GateLogo, { src: logoSrc, alt: logoAlt }) }),
7334
8245
  /* @__PURE__ */ jsx(GateBrandDivider, {}),
7335
- /* @__PURE__ */ jsx(GateBetaTag, { children: "Private Beta" })
8246
+ /* @__PURE__ */ jsx(GateBetaTag, { children: "Beta" })
7336
8247
  ] }),
7337
8248
  /* @__PURE__ */ jsxs(GateOtpText, { children: [
7338
8249
  "We sent a code to",
@@ -7671,6 +8582,16 @@ var LoginPopup = ({
7671
8582
  ),
7672
8583
  /* @__PURE__ */ jsx(GateSubmit, { type: "submit", "aria-label": "Continue", disabled: loading || !email, children: loading ? /* @__PURE__ */ jsx(GateSpinner, {}) : /* @__PURE__ */ jsx("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M5 12h14M13 6l6 6-6 6" }) }) })
7673
8584
  ] }),
8585
+ !signInMode && /* @__PURE__ */ jsx(GateHandleRow, { children: /* @__PURE__ */ jsx(
8586
+ GateEmailInput,
8587
+ {
8588
+ type: "text",
8589
+ autoComplete: "username",
8590
+ placeholder: "Claim your username (Optional)",
8591
+ value: handle,
8592
+ onChange: (event) => setHandle(event.target.value)
8593
+ }
8594
+ ) }),
7674
8595
  error && /* @__PURE__ */ jsx(StatusMessage, { $error: true, children: error })
7675
8596
  ] }),
7676
8597
  /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -7689,7 +8610,7 @@ var LoginPopup = ({
7689
8610
  /* @__PURE__ */ jsxs(GateBrand, { children: [
7690
8611
  /* @__PURE__ */ jsx(GateLogoLink, { href: "https://loafmarkets.com", "aria-label": "Go to loafmarkets.com", children: /* @__PURE__ */ jsx(GateLogo, { src: logoSrc, alt: logoAlt }) }),
7691
8612
  /* @__PURE__ */ jsx(GateBrandDivider, {}),
7692
- /* @__PURE__ */ jsx(GateBetaTag, { children: "Private Beta" })
8613
+ /* @__PURE__ */ jsx(GateBetaTag, { children: "Beta" })
7693
8614
  ] }),
7694
8615
  signInMode ? /* @__PURE__ */ jsxs(Fragment, { children: [
7695
8616
  signInStep,
@@ -7741,7 +8662,7 @@ var LoginPopup = ({
7741
8662
  /* @__PURE__ */ jsxs(Title, { children: [
7742
8663
  /* @__PURE__ */ jsxs(LogoContainer3, { children: [
7743
8664
  /* @__PURE__ */ jsx(LogoImage, { src: logoSrc, alt: logoAlt }),
7744
- /* @__PURE__ */ jsx(LogoBeta, { children: "Private Beta" })
8665
+ /* @__PURE__ */ jsx(LogoBeta, { children: "Beta" })
7745
8666
  ] }),
7746
8667
  /* @__PURE__ */ jsx(TitleText, { children: "Enter your access code" })
7747
8668
  ] }),
@@ -8405,6 +9326,9 @@ var GateSubmit = styled10.button`
8405
9326
  cursor: default;
8406
9327
  }
8407
9328
  `;
9329
+ var GateHandleRow = styled10(CodeInputWrapper)`
9330
+ margin-top: 0.6rem;
9331
+ `;
8408
9332
  var GateEmailInput = styled10.input`
8409
9333
  flex: 1;
8410
9334
  min-width: 0;
@@ -9323,7 +10247,7 @@ function OnboardingGuide({
9323
10247
  ] })
9324
10248
  ] }) });
9325
10249
  }
9326
- var PropertyCompareBar = React5.forwardRef(
10250
+ var PropertyCompareBar = React9.forwardRef(
9327
10251
  ({
9328
10252
  className,
9329
10253
  addresses,
@@ -9334,7 +10258,7 @@ var PropertyCompareBar = React5.forwardRef(
9334
10258
  price,
9335
10259
  ...props
9336
10260
  }, ref) => {
9337
- const normalizedAddresses = React5.useMemo(() => {
10261
+ const normalizedAddresses = React9.useMemo(() => {
9338
10262
  return addresses.map(
9339
10263
  (option) => typeof option === "string" ? { id: option, label: option } : option
9340
10264
  );
@@ -9342,11 +10266,11 @@ var PropertyCompareBar = React5.forwardRef(
9342
10266
  const hasAddresses = normalizedAddresses.length > 0;
9343
10267
  const firstAddressId = normalizedAddresses[0]?.id;
9344
10268
  const isControlled = selectedAddressId !== void 0;
9345
- const [internalSelectedId, setInternalSelectedId] = React5.useState(
10269
+ const [internalSelectedId, setInternalSelectedId] = React9.useState(
9346
10270
  () => isControlled ? void 0 : firstAddressId
9347
10271
  );
9348
10272
  const resolvedSelectedId = isControlled ? selectedAddressId : internalSelectedId;
9349
- React5.useEffect(() => {
10273
+ React9.useEffect(() => {
9350
10274
  if (!isControlled) {
9351
10275
  setInternalSelectedId((current) => {
9352
10276
  if (current != null && normalizedAddresses.some((option) => option.id === current)) {
@@ -9357,9 +10281,9 @@ var PropertyCompareBar = React5.forwardRef(
9357
10281
  }
9358
10282
  }, [firstAddressId, isControlled, normalizedAddresses]);
9359
10283
  const selectedOption = normalizedAddresses.find((option) => option.id === resolvedSelectedId) ?? normalizedAddresses[0];
9360
- const [isDropdownOpen, setIsDropdownOpen] = React5.useState(false);
9361
- const dropdownRef = React5.useRef(null);
9362
- React5.useEffect(() => {
10284
+ const [isDropdownOpen, setIsDropdownOpen] = React9.useState(false);
10285
+ const dropdownRef = React9.useRef(null);
10286
+ React9.useEffect(() => {
9363
10287
  if (!isDropdownOpen) return;
9364
10288
  const handleClick = (event) => {
9365
10289
  if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
@@ -12787,6 +13711,32 @@ var SignInButton = styled10.button`
12787
13711
  background: rgba(240, 185, 11, 0.1);
12788
13712
  }
12789
13713
  `;
13714
+ var SORT_COLUMNS = [
13715
+ { field: "price", label: "Price" },
13716
+ { field: "change", label: "24h Change" },
13717
+ { field: "volume", label: "24h Volume", hideOnPhone: true },
13718
+ { field: "value", label: "Asset Value", hideOnPhone: true }
13719
+ ];
13720
+ function sortValue(item, field) {
13721
+ switch (field) {
13722
+ case "volume":
13723
+ return item.volume24h ?? null;
13724
+ case "price":
13725
+ return item.price ?? null;
13726
+ case "change":
13727
+ return item.changePercent ?? null;
13728
+ case "value":
13729
+ return item.propertyValue ?? null;
13730
+ }
13731
+ }
13732
+ function fmtWholeUsd(value) {
13733
+ if (value == null) return null;
13734
+ return `$${value.toLocaleString(void 0, { maximumFractionDigits: 0 })}`;
13735
+ }
13736
+ function fmtPrice(value) {
13737
+ if (value == null) return null;
13738
+ return `$${value.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
13739
+ }
12790
13740
  function displayName(item) {
12791
13741
  if (item.name) return item.name;
12792
13742
  return item.tokenName.charAt(0).toUpperCase() + item.tokenName.slice(1);
@@ -12800,24 +13750,79 @@ function AssetSelectorBar({
12800
13750
  selectorItems,
12801
13751
  onSelect,
12802
13752
  trailing,
13753
+ afterName,
12803
13754
  imageUrl,
12804
13755
  badgeLabel,
12805
- tickerPrimary
13756
+ tickerPrimary,
13757
+ compactName
12806
13758
  }) {
12807
13759
  const [isDropdownOpen, setIsDropdownOpen] = useState(false);
13760
+ const [nameTip, setNameTip] = useState(null);
13761
+ const anchorRef = useRef(null);
13762
+ const [dropdownPos, setDropdownPos] = useState(null);
13763
+ useLayoutEffect(() => {
13764
+ if (!isDropdownOpen) return;
13765
+ const measure = () => {
13766
+ const rect = anchorRef.current?.getBoundingClientRect();
13767
+ if (rect) setDropdownPos({ top: rect.bottom + 6, left: rect.left });
13768
+ };
13769
+ measure();
13770
+ window.addEventListener("resize", measure);
13771
+ window.addEventListener("scroll", measure, true);
13772
+ return () => {
13773
+ window.removeEventListener("resize", measure);
13774
+ window.removeEventListener("scroll", measure, true);
13775
+ };
13776
+ }, [isDropdownOpen]);
13777
+ const [sortField, setSortField] = useState("volume");
13778
+ const [sortDir, setSortDir] = useState("desc");
12808
13779
  const hasItems = selectorItems && selectorItems.length > 0;
13780
+ const hasMarketData = !!selectorItems?.some(
13781
+ (i) => i.price != null || i.changePercent != null || i.volume24h != null || i.propertyValue != null
13782
+ );
13783
+ const sortedItems = useMemo(() => {
13784
+ if (!selectorItems) return [];
13785
+ if (!hasMarketData) return [...selectorItems];
13786
+ const dir = sortDir === "desc" ? -1 : 1;
13787
+ const missing = sortDir === "desc" ? -Infinity : Infinity;
13788
+ return [...selectorItems].sort((a, b) => {
13789
+ const av = sortValue(a, sortField) ?? missing;
13790
+ const bv = sortValue(b, sortField) ?? missing;
13791
+ return (av - bv) * dir || 0;
13792
+ });
13793
+ }, [selectorItems, hasMarketData, sortField, sortDir]);
13794
+ const handleSortClick = (field) => {
13795
+ if (field === sortField) {
13796
+ setSortDir((d) => d === "desc" ? "asc" : "desc");
13797
+ } else {
13798
+ setSortField(field);
13799
+ setSortDir("desc");
13800
+ }
13801
+ };
12809
13802
  const currentTicker = currentTokenName ? selectorItems?.find((i) => i.tokenName.toLowerCase() === currentTokenName.toLowerCase())?.ticker : void 0;
12810
13803
  const metrics = metricsProp ?? [
12811
13804
  ...tokenPrice != null ? [{ label: "Unit Price", value: `$${tokenPrice.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`, accent: true }] : [],
12812
13805
  ...offeringValuation != null ? [{ label: "Offering Valuation", value: `$${offeringValuation.toLocaleString()}` }] : []
12813
13806
  ];
12814
- return /* @__PURE__ */ jsx(AssetSelectorWrapper, { children: /* @__PURE__ */ jsxs(IPOAssetSelector, { children: [
13807
+ return /* @__PURE__ */ jsx(AssetSelectorWrapper, { children: /* @__PURE__ */ jsxs(IPOAssetSelector, { ref: anchorRef, children: [
12815
13808
  imageUrl && /* @__PURE__ */ jsx(AssetThumbnail, { src: imageUrl, alt: propertyName }),
12816
- /* @__PURE__ */ jsx(AssetSelectorDropdown, { $flush: tickerPrimary, onClick: () => hasItems && setIsDropdownOpen((prev) => !prev), children: /* @__PURE__ */ jsxs(AssetName, { children: [
13809
+ /* @__PURE__ */ jsx(AssetSelectorDropdown, { $flush: tickerPrimary, $noShrink: !!compactName && !!currentTicker && !tickerPrimary, onClick: () => hasItems && setIsDropdownOpen((prev) => !prev), children: /* @__PURE__ */ jsxs(AssetName, { children: [
12817
13810
  tickerPrimary && currentTicker ? /* @__PURE__ */ jsxs(AssetIdentity, { children: [
12818
13811
  /* @__PURE__ */ jsx(AssetTicker, { children: currentTicker.toUpperCase() }),
12819
13812
  /* @__PURE__ */ jsx(AssetSubName, { children: propertyName })
12820
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
13813
+ ] }) : compactName && currentTicker ? /* @__PURE__ */ jsx(
13814
+ CompactName,
13815
+ {
13816
+ className: "asset-bar-compact-name",
13817
+ "aria-label": propertyName,
13818
+ onMouseEnter: (e) => {
13819
+ const r = e.currentTarget.getBoundingClientRect();
13820
+ setNameTip({ left: r.left + r.width / 2, top: r.bottom + 10 });
13821
+ },
13822
+ onMouseLeave: () => setNameTip(null),
13823
+ children: currentTicker.toUpperCase()
13824
+ }
13825
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
12821
13826
  propertyName,
12822
13827
  currentTicker ? ` (${currentTicker.toUpperCase()})` : ""
12823
13828
  ] }),
@@ -12835,6 +13840,7 @@ function AssetSelectorBar({
12835
13840
  }
12836
13841
  )
12837
13842
  ] }) }),
13843
+ afterName != null && /* @__PURE__ */ jsx(AfterNameSlot, { className: "asset-bar-after-name", children: afterName }),
12838
13844
  metrics.length > 0 && /* @__PURE__ */ jsx(SelectorMetrics, { children: metrics.map((m, i) => /* @__PURE__ */ jsxs("div", { style: { display: "contents" }, children: [
12839
13845
  i > 0 && /* @__PURE__ */ jsx(Separator, { children: "|" }),
12840
13846
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
@@ -12850,40 +13856,128 @@ function AssetSelectorBar({
12850
13856
  ] })
12851
13857
  ] }, m.label)) }),
12852
13858
  trailing != null && /* @__PURE__ */ jsx(TrailingWrapper, { className: "asset-bar-trailing", children: /* @__PURE__ */ jsx(TrailingInner, { children: trailing }) }),
12853
- isDropdownOpen && hasItems && /* @__PURE__ */ jsx(IPODropdown, { children: selectorItems.map((item) => {
12854
- const isCurrent = item.tokenName === currentTokenName;
12855
- const status = item.status?.toUpperCase();
12856
- const statusLabel = status === "PENDING" ? "Preparing" : status === "CLOSED" ? "Closed" : status === "CANCELLED" ? "Cancelled" : null;
12857
- return /* @__PURE__ */ jsx(
12858
- IPOOption,
13859
+ nameTip != null && typeof document !== "undefined" && createPortal(
13860
+ /* @__PURE__ */ jsx(
13861
+ NameTip,
12859
13862
  {
12860
- onClick: () => {
12861
- if (!isCurrent && onSelect) {
12862
- onSelect(item.tokenName);
12863
- }
12864
- setIsDropdownOpen(false);
12865
- },
12866
- $selected: isCurrent,
12867
- style: { cursor: isCurrent ? "default" : "pointer" },
12868
- children: /* @__PURE__ */ jsxs(IPOOptionInner, { children: [
12869
- item.imageUrl && /* @__PURE__ */ jsx(IPOOptionThumb, { src: item.imageUrl, alt: displayName(item) }),
12870
- /* @__PURE__ */ jsxs("div", { children: [
12871
- /* @__PURE__ */ jsxs(IPOOptionName, { children: [
12872
- displayName(item),
12873
- item.ticker ? ` (${item.ticker})` : "",
12874
- item.badge && /* @__PURE__ */ jsx(CompetitionBadge, { children: item.badge })
12875
- ] }),
12876
- /* @__PURE__ */ jsxs(IPOOptionLocation, { children: [
12877
- item.streetAddress,
12878
- item.suburb ? `, ${item.suburb}` : "",
12879
- statusLabel && /* @__PURE__ */ jsx("span", { children: statusLabel })
12880
- ] })
12881
- ] })
12882
- ] })
12883
- },
12884
- item.tokenName
12885
- );
12886
- }) })
13863
+ role: "tooltip",
13864
+ "data-testid": "asset-name-tip",
13865
+ style: { position: "fixed", left: nameTip.left, top: nameTip.top, transform: "translateX(-50%)", zIndex: 10050 },
13866
+ children: propertyName
13867
+ }
13868
+ ),
13869
+ document.body
13870
+ ),
13871
+ isDropdownOpen && hasItems && dropdownPos != null && typeof document !== "undefined" && createPortal(
13872
+ /* @__PURE__ */ jsxs(
13873
+ IPODropdown,
13874
+ {
13875
+ $wide: hasMarketData,
13876
+ style: { position: "fixed", top: dropdownPos.top, left: dropdownPos.left, zIndex: 10050 },
13877
+ children: [
13878
+ hasMarketData && /* @__PURE__ */ jsxs(HeaderRow, { children: [
13879
+ /* @__PURE__ */ jsx(HeaderLabel, { children: "Asset" }),
13880
+ SORT_COLUMNS.map(({ field, label, hideOnPhone }) => /* @__PURE__ */ jsxs(
13881
+ SortButton,
13882
+ {
13883
+ type: "button",
13884
+ $active: sortField === field,
13885
+ $hideOnPhone: hideOnPhone,
13886
+ onClick: (e) => {
13887
+ e.stopPropagation();
13888
+ handleSortClick(field);
13889
+ },
13890
+ children: [
13891
+ label,
13892
+ sortField === field ? sortDir === "desc" ? " \u25BE" : " \u25B4" : ""
13893
+ ]
13894
+ },
13895
+ field
13896
+ ))
13897
+ ] }),
13898
+ sortedItems.map((item) => {
13899
+ const isCurrent = item.tokenName === currentTokenName;
13900
+ const status = item.status?.toUpperCase();
13901
+ const statusLabel = status === "PENDING" ? "Preparing" : status === "CLOSED" ? "Closed" : status === "CANCELLED" ? "Cancelled" : null;
13902
+ const price = fmtPrice(item.price);
13903
+ const change = item.changePercent;
13904
+ const vol = fmtWholeUsd(item.volume24h);
13905
+ const val = fmtWholeUsd(item.propertyValue);
13906
+ return /* @__PURE__ */ jsx(
13907
+ IPOOption,
13908
+ {
13909
+ "data-selector-row": item.tokenName,
13910
+ onClick: () => {
13911
+ if (!isCurrent && onSelect) {
13912
+ onSelect(item.tokenName);
13913
+ }
13914
+ setIsDropdownOpen(false);
13915
+ },
13916
+ $selected: isCurrent,
13917
+ style: {
13918
+ cursor: isCurrent ? "default" : "pointer",
13919
+ // Arena gold for competition assets — the same restrained cue as
13920
+ // the home Overview row (gold left crust + pooled spotlight, no
13921
+ // badge). backgroundImage LONGHAND: jsdom drops gradients from
13922
+ // the shorthand and UI6-AS-508 asserts it.
13923
+ ...item.isCompetition ? {
13924
+ // Straight crust bar — an inset box-shadow hugged the
13925
+ // row's rounded corners and read as a curved rim.
13926
+ // Left corners squared so the bar runs dead straight
13927
+ // like the home Overview arena row; right side keeps
13928
+ // the row rounding.
13929
+ borderLeft: "3px solid #E0B377",
13930
+ borderRadius: "0 7px 7px 0",
13931
+ backgroundImage: "radial-gradient(140% 90% at 0% 50%, rgba(224, 179, 119, 0.18) 0%, rgba(224, 179, 119, 0.06) 40%, transparent 68%)"
13932
+ } : {}
13933
+ },
13934
+ children: hasMarketData ? /* @__PURE__ */ jsxs(MarketRowInner, { children: [
13935
+ /* @__PURE__ */ jsxs(IdentityCell, { children: [
13936
+ item.imageUrl && /* @__PURE__ */ jsx(IPOOptionThumb, { src: item.imageUrl, alt: displayName(item) }),
13937
+ /* @__PURE__ */ jsxs(IPOOptionIdentity, { children: [
13938
+ /* @__PURE__ */ jsxs(IPOOptionName, { children: [
13939
+ /* @__PURE__ */ jsx(MarketTicker, { children: (item.ticker || displayName(item)).toUpperCase() }),
13940
+ item.typeBadge && /* @__PURE__ */ jsx(TypePill, { style: { color: item.typeBadge.color, background: item.typeBadge.background }, children: item.typeBadge.label }),
13941
+ item.badge && /* @__PURE__ */ jsx(CompetitionBadge, { children: item.badge })
13942
+ ] }),
13943
+ /* @__PURE__ */ jsxs(IPOOptionLocation, { children: [
13944
+ item.streetAddress,
13945
+ statusLabel && /* @__PURE__ */ jsx("span", { children: statusLabel })
13946
+ ] })
13947
+ ] })
13948
+ ] }),
13949
+ /* @__PURE__ */ jsx(PriceCell, { children: price ?? "\u2014" }),
13950
+ change != null ? /* @__PURE__ */ jsxs(ChangeCell, { $positive: change >= 0, children: [
13951
+ change >= 0 ? "+" : "",
13952
+ change.toFixed(2),
13953
+ "%"
13954
+ ] }) : /* @__PURE__ */ jsx(MutedCell, { children: "\u2014" }),
13955
+ /* @__PURE__ */ jsx(MutedCell, { $hideOnPhone: true, children: vol ?? "\u2014" }),
13956
+ /* @__PURE__ */ jsx(MutedCell, { $hideOnPhone: true, children: val ?? "\u2014" })
13957
+ ] }) : /* @__PURE__ */ jsxs(IPOOptionInner, { children: [
13958
+ item.imageUrl && /* @__PURE__ */ jsx(IPOOptionThumb, { src: item.imageUrl, alt: displayName(item) }),
13959
+ /* @__PURE__ */ jsxs(IPOOptionIdentity, { children: [
13960
+ /* @__PURE__ */ jsxs(IPOOptionName, { children: [
13961
+ displayName(item),
13962
+ item.ticker ? ` (${item.ticker})` : "",
13963
+ item.badge && /* @__PURE__ */ jsx(CompetitionBadge, { children: item.badge })
13964
+ ] }),
13965
+ /* @__PURE__ */ jsxs(IPOOptionLocation, { children: [
13966
+ item.streetAddress,
13967
+ item.suburb ? `, ${item.suburb}` : "",
13968
+ statusLabel && /* @__PURE__ */ jsx("span", { children: statusLabel })
13969
+ ] })
13970
+ ] })
13971
+ ] })
13972
+ },
13973
+ item.tokenName
13974
+ );
13975
+ })
13976
+ ]
13977
+ }
13978
+ ),
13979
+ document.body
13980
+ )
12887
13981
  ] }) });
12888
13982
  }
12889
13983
  var AssetSelectorWrapper = styled10.div`
@@ -12945,7 +14039,9 @@ var AssetSelectorDropdown = styled10.div`
12945
14039
  padding: ${(p) => p.$flush ? "0.25rem 0" : "0.5rem"};
12946
14040
  border-radius: 8px;
12947
14041
  transition: ${(p) => p.$flush ? "none" : "all 0.2s ease"};
12948
- flex: 0 1 auto;
14042
+ /* $noShrink (compactName): a four-letter ticker is already the floor — it must
14043
+ never clip to "Y" when the bar is starved; the trailing scrolls instead. */
14044
+ flex: ${(p) => p.$noShrink ? "0 0 auto" : "0 1 auto"};
12949
14045
  min-width: 0;
12950
14046
  overflow: hidden;
12951
14047
  &:hover { background-color: ${(p) => p.$flush ? "transparent" : "rgba(255,255,255,0.05)"}; }
@@ -13002,6 +14098,35 @@ var AssetName = styled10.div`
13002
14098
  svg { margin-left: 0.4rem; width: 16px; height: 16px; }
13003
14099
  }
13004
14100
  `;
14101
+ var CompactName = styled10.span`
14102
+ white-space: nowrap;
14103
+ letter-spacing: 0.01em;
14104
+ `;
14105
+ var NameTip = styled10.div`
14106
+ background: #1a1d24;
14107
+ border: 1px solid rgba(255, 255, 255, 0.1);
14108
+ border-radius: 8px;
14109
+ padding: 0.45rem 0.7rem;
14110
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
14111
+ pointer-events: none;
14112
+ white-space: nowrap;
14113
+ color: #fff;
14114
+ font-size: 0.78rem;
14115
+ font-weight: 600;
14116
+ letter-spacing: -0.01em;
14117
+ &::before {
14118
+ content: '';
14119
+ position: absolute;
14120
+ top: -5px;
14121
+ left: 50%;
14122
+ transform: translateX(-50%) rotate(45deg);
14123
+ width: 8px;
14124
+ height: 8px;
14125
+ background: #1a1d24;
14126
+ border-left: 1px solid rgba(255, 255, 255, 0.1);
14127
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
14128
+ }
14129
+ `;
13005
14130
  var SelectorMetrics = styled10.div`
13006
14131
  display: flex;
13007
14132
  align-items: center;
@@ -13022,6 +14147,12 @@ var SelectorMetrics = styled10.div`
13022
14147
  padding: 0.4rem 0.5rem;
13023
14148
  }
13024
14149
  `;
14150
+ var AfterNameSlot = styled10.div`
14151
+ display: flex;
14152
+ align-items: center;
14153
+ flex-shrink: 0;
14154
+ min-width: 0;
14155
+ `;
13025
14156
  var TrailingWrapper = styled10.div`
13026
14157
  display: flex;
13027
14158
  align-items: center;
@@ -13093,14 +14224,16 @@ var IPODropdown = styled10.div`
13093
14224
  position: absolute;
13094
14225
  top: calc(100% + 6px);
13095
14226
  left: 0;
13096
- width: 320px;
14227
+ width: ${(p) => p.$wide ? "min(660px, calc(100vw - 24px))" : "320px"};
13097
14228
  background: #0d0d0f;
13098
14229
  border-radius: 10px;
13099
14230
  box-shadow: 0 12px 32px rgba(0,0,0,0.7), 0 0 0 1px rgba(255,255,255,0.06);
13100
14231
  z-index: 1000;
13101
- max-height: 320px;
14232
+ max-height: 360px;
13102
14233
  overflow-y: auto;
13103
- padding: 4px;
14234
+ /* Market mode: NO top padding — the sticky header must sit flush against the
14235
+ container's top edge so scrolling rows can never peek above/behind it. */
14236
+ padding: ${(p) => p.$wide ? "0 0 4px" : "4px"};
13104
14237
 
13105
14238
  scrollbar-width: thin;
13106
14239
  scrollbar-color: rgba(255,255,255,0.12) transparent;
@@ -13108,8 +14241,8 @@ var IPODropdown = styled10.div`
13108
14241
  &::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.12); border-radius: 2px; }
13109
14242
 
13110
14243
  @media (max-width: 480px) {
13111
- width: 280px;
13112
- max-height: 260px;
14244
+ width: ${(p) => p.$wide ? "min(360px, calc(100vw - 16px))" : "280px"};
14245
+ max-height: 300px;
13113
14246
  border-radius: 8px;
13114
14247
  }
13115
14248
  animation: slideDown 0.2s cubic-bezier(0.16,1,0.3,1);
@@ -13118,6 +14251,99 @@ var IPODropdown = styled10.div`
13118
14251
  to { opacity: 1; transform: translateY(0); }
13119
14252
  }
13120
14253
  `;
14254
+ var MARKET_GRID = "minmax(0, 1fr) 76px 92px 108px 118px";
14255
+ var MARKET_GRID_PHONE = "minmax(0, 1fr) 76px 92px";
14256
+ var HeaderRow = styled10.div`
14257
+ position: sticky;
14258
+ top: 0;
14259
+ z-index: 2;
14260
+ display: grid;
14261
+ grid-template-columns: ${MARKET_GRID};
14262
+ gap: 10px;
14263
+ align-items: center;
14264
+ padding: 0.5rem 0.85rem 0.45rem;
14265
+ background: #0d0d0f;
14266
+ border-bottom: 1px solid rgba(255,255,255,0.06);
14267
+
14268
+ @media (max-width: 480px) { grid-template-columns: ${MARKET_GRID_PHONE}; }
14269
+ `;
14270
+ var HeaderLabel = styled10.span`
14271
+ font-size: 0.6rem;
14272
+ font-weight: 600;
14273
+ letter-spacing: 0.08em;
14274
+ text-transform: uppercase;
14275
+ color: rgba(255,255,255,0.45);
14276
+ white-space: nowrap;
14277
+ `;
14278
+ var SortButton = styled10.button`
14279
+ background: none;
14280
+ border: none;
14281
+ padding: 0;
14282
+ cursor: pointer;
14283
+ font-family: inherit;
14284
+ font-size: 0.6rem;
14285
+ font-weight: 600;
14286
+ letter-spacing: 0.08em;
14287
+ text-transform: uppercase;
14288
+ white-space: nowrap;
14289
+ /* Left-aligned like every other column (user call, 2026-08-20). */
14290
+ text-align: left;
14291
+ justify-self: start;
14292
+ color: ${(p) => p.$active ? "var(--color-accent, #E6C87E)" : "rgba(255,255,255,0.45)"};
14293
+ transition: color 0.15s ease;
14294
+ &:hover { color: ${(p) => p.$active ? "var(--color-accent, #E6C87E)" : "rgba(255,255,255,0.75)"}; }
14295
+
14296
+ @media (max-width: 480px) {
14297
+ display: ${(p) => p.$hideOnPhone ? "none" : "inline"};
14298
+ }
14299
+ `;
14300
+ var IPOOptionIdentity = styled10.div`
14301
+ flex: 1 1 0;
14302
+ min-width: 0;
14303
+ overflow: hidden;
14304
+ `;
14305
+ var MarketRowInner = styled10.div`
14306
+ display: grid;
14307
+ grid-template-columns: ${MARKET_GRID};
14308
+ gap: 10px;
14309
+ align-items: center;
14310
+
14311
+ @media (max-width: 480px) { grid-template-columns: ${MARKET_GRID_PHONE}; }
14312
+ `;
14313
+ var IdentityCell = styled10.div`
14314
+ display: flex;
14315
+ align-items: center;
14316
+ gap: 0.65rem;
14317
+ min-width: 0;
14318
+ `;
14319
+ var PriceCell = styled10.span`
14320
+ font-size: 0.8rem;
14321
+ font-weight: 600;
14322
+ color: #fff;
14323
+ font-variant-numeric: tabular-nums;
14324
+ white-space: nowrap;
14325
+ text-align: left;
14326
+ `;
14327
+ var ChangeCell = styled10.span`
14328
+ font-size: 0.72rem;
14329
+ font-weight: 600;
14330
+ color: ${(p) => p.$positive ? "#0ecb81" : "#f6465d"};
14331
+ font-variant-numeric: tabular-nums;
14332
+ white-space: nowrap;
14333
+ text-align: left;
14334
+ `;
14335
+ var MutedCell = styled10.span`
14336
+ font-size: 0.72rem;
14337
+ /* Same ink as the price column (user call, 2026-08-20). */
14338
+ color: #fff;
14339
+ font-variant-numeric: tabular-nums;
14340
+ white-space: nowrap;
14341
+ text-align: left;
14342
+
14343
+ @media (max-width: 480px) {
14344
+ display: ${(p) => p.$hideOnPhone ? "none" : "inline"};
14345
+ }
14346
+ `;
13121
14347
  var IPOOption = styled10.div`
13122
14348
  padding: 0.6rem 0.75rem;
13123
14349
  transition: background 0.15s ease;
@@ -13143,6 +14369,25 @@ var IPOOptionName = styled10.div`
13143
14369
  font-size: 0.82rem;
13144
14370
  color: ${"#ebebeb"};
13145
14371
  line-height: 1.3;
14372
+ /* One line like the location — wrapped names made market rows uneven. */
14373
+ white-space: nowrap;
14374
+ overflow: hidden;
14375
+ text-overflow: ellipsis;
14376
+ `;
14377
+ var MarketTicker = styled10.span`
14378
+ font-weight: 700;
14379
+ letter-spacing: 0.01em;
14380
+ `;
14381
+ var TypePill = styled10.span`
14382
+ margin-left: 0.45rem;
14383
+ font-size: 0.55rem;
14384
+ font-weight: 700;
14385
+ letter-spacing: 0.08em;
14386
+ text-transform: uppercase;
14387
+ padding: 1px 5px;
14388
+ border-radius: 3px;
14389
+ vertical-align: middle;
14390
+ white-space: nowrap;
13146
14391
  `;
13147
14392
  var CompetitionBadge = styled10.span`
13148
14393
  margin-left: 0.4rem;
@@ -13161,6 +14406,11 @@ var IPOOptionLocation = styled10.div`
13161
14406
  font-size: 0.72rem;
13162
14407
  color: rgba(255,255,255,0.38);
13163
14408
  margin-top: 1px;
14409
+ /* One line, ellipsized — a long address must never wrap the row taller
14410
+ (matches the deployed dropdown's compact rows). */
14411
+ white-space: nowrap;
14412
+ overflow: hidden;
14413
+ text-overflow: ellipsis;
13164
14414
  span {
13165
14415
  margin-left: 0.4rem;
13166
14416
  color: var(--color-accent, #E6C87E);
@@ -16325,7 +17575,7 @@ var ContentScroll = styled10.div`
16325
17575
  `;
16326
17576
  var TabContainer = styled10.div`
16327
17577
  display: flex;
16328
- gap: 0;
17578
+ gap: 1rem;
16329
17579
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
16330
17580
  margin-bottom: 0.75rem;
16331
17581
  overflow-x: auto;
@@ -16337,12 +17587,12 @@ var TabContainer = styled10.div`
16337
17587
  var Tab = styled10.button`
16338
17588
  background: transparent;
16339
17589
  border: none;
16340
- padding: 0.6rem 1rem;
16341
- font-size: 0.75rem;
17590
+ padding: 0 0 0.5rem;
17591
+ font-size: 0.8rem;
16342
17592
  font-weight: 500;
16343
17593
  white-space: nowrap;
16344
17594
  flex-shrink: 0;
16345
- color: ${({ $active }) => $active ? "#E6C87E" : "rgba(255, 255, 255, 0.4)"};
17595
+ color: ${({ $active }) => $active ? "#fff" : "rgba(255, 255, 255, 0.6)"};
16346
17596
  cursor: pointer;
16347
17597
  transition: color 0.15s;
16348
17598
  position: relative;
@@ -16355,12 +17605,11 @@ var Tab = styled10.button`
16355
17605
  right: 0;
16356
17606
  height: 2px;
16357
17607
  background: ${({ $active }) => $active ? "#E6C87E" : "transparent"};
16358
- border-radius: 1px;
16359
17608
  transition: background 0.15s;
16360
17609
  }
16361
17610
 
16362
17611
  &:hover {
16363
- color: ${({ $active }) => $active ? "#fff" : "rgba(255, 255, 255, 0.65)"};
17612
+ color: ${({ $active }) => $active ? "#fff" : "rgba(255, 255, 255, 0.85)"};
16364
17613
  }
16365
17614
  `;
16366
17615
  var TabCount = styled10.span`
@@ -20589,7 +21838,7 @@ function extractSparkline(candles, maxPoints = 20) {
20589
21838
  }
20590
21839
  return points;
20591
21840
  }
20592
- var SparklineChart = React5__default.memo(function SparklineChart2({ data, width, height, positive }) {
21841
+ var SparklineChart = React9__default.memo(function SparklineChart2({ data, width, height, positive }) {
20593
21842
  const [hoverX, setHoverX] = useState(null);
20594
21843
  const svgRef = useRef(null);
20595
21844
  const { linePath, fillPath, points } = useMemo(() => {
@@ -20703,6 +21952,6 @@ var SparklineChart = React5__default.memo(function SparklineChart2({ data, width
20703
21952
  );
20704
21953
  });
20705
21954
 
20706
- export { AssetSelectorBar, Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ContactPopup, Header, HlsVideo, HousePositionSlider, HousePositionSliderMobile, LoafLiquidityBadge, LoafLiquidityLogo, LoginPopup, MobileTradeNav, OfferingProgressCard, OnboardingGuide, Orderbook, owner_booking_default as OwnerBooking, PaymentPopup, PortfolioActivityPanel, PortfolioSummary, PriceChart, PropertyBuy, PropertyCompareBar, PropertyDocuments, PropertyHeroHeader, PropertyHistory, PropertyInspectionTimes, PropertyMediaRow, PropertyNewsUpdates, PropertyOffers, PropertyOverview, PropertyPhotoGallery, PropertySubheader, PropertyTour, PropertyValuation, SiteFooter, Skeleton, SlideDigit, SparklineChart, ToastProvider, TradeConfirmationModal, TradingSlider, YourOrders, badgeVariants, buttonVariants, extractSparkline, hasPendingActivity, useAdaptivePolling, useToast };
21955
+ export { AssetSelectorBar, Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ContactPopup, Header, HlsVideo, HousePositionSlider, HousePositionSliderMobile, LoafLiquidityBadge, LoafLiquidityLogo, LoginPopup, MobileTradeNav, OfferingProgressCard, OnboardingGuide, Orderbook, owner_booking_default as OwnerBooking, PaymentPopup, PortfolioActivityPanel, PortfolioSummary, PriceChart, PropertyBuy, PropertyCompareBar, PropertyDocuments, PropertyHeroHeader, PropertyHistory, PropertyInspectionTimes, PropertyMediaRow, PropertyNewsUpdates, PropertyOffers, PropertyOverview, PropertyPhotoGallery, PropertySubheader, PropertyTour, PropertyValuation, SentimentBar, SiteFooter, Skeleton, SlideDigit, SparklineChart, ToastProvider, TradeConfirmationModal, TradingSlider, YourOrders, __resetDismissedBreaking, badgeVariants, buttonVariants, extractSparkline, formatAge, hasPendingActivity, rowAgeLabel, useAdaptivePolling, useToast };
20707
21956
  //# sourceMappingURL=index.mjs.map
20708
21957
  //# sourceMappingURL=index.mjs.map