@latchprotocol/widgets 0.1.0

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.
Files changed (132) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +649 -0
  3. package/dist/adapters/binDistribution.d.ts +19 -0
  4. package/dist/adapters/binDistribution.d.ts.map +1 -0
  5. package/dist/adapters/protocol.d.ts +409 -0
  6. package/dist/adapters/protocol.d.ts.map +1 -0
  7. package/dist/adapters/sale.d.ts +142 -0
  8. package/dist/adapters/sale.d.ts.map +1 -0
  9. package/dist/adapters/viem.d.ts +89 -0
  10. package/dist/adapters/viem.d.ts.map +1 -0
  11. package/dist/callpath/constants.d.ts +218 -0
  12. package/dist/callpath/constants.d.ts.map +1 -0
  13. package/dist/callpath/launch.d.ts +1979 -0
  14. package/dist/callpath/launch.d.ts.map +1 -0
  15. package/dist/callpath/liquidity.d.ts +103 -0
  16. package/dist/callpath/liquidity.d.ts.map +1 -0
  17. package/dist/callpath/plan.d.ts +34 -0
  18. package/dist/callpath/plan.d.ts.map +1 -0
  19. package/dist/callpath/swap.d.ts +129 -0
  20. package/dist/callpath/swap.d.ts.map +1 -0
  21. package/dist/chunks/SwapWidget-DgyHAVc8.js +2404 -0
  22. package/dist/chunks/SwapWidget-DgyHAVc8.js.map +1 -0
  23. package/dist/chunks/scan-Dhu7f4Nl.js +2497 -0
  24. package/dist/chunks/scan-Dhu7f4Nl.js.map +1 -0
  25. package/dist/chunks/useLocker-D1c_NTk-.js +1613 -0
  26. package/dist/chunks/useLocker-D1c_NTk-.js.map +1 -0
  27. package/dist/components/LaunchWidget.d.ts +117 -0
  28. package/dist/components/LaunchWidget.d.ts.map +1 -0
  29. package/dist/components/LiquidityWidget.d.ts +33 -0
  30. package/dist/components/LiquidityWidget.d.ts.map +1 -0
  31. package/dist/components/LockerWidget.d.ts +51 -0
  32. package/dist/components/LockerWidget.d.ts.map +1 -0
  33. package/dist/components/LocksWidgets.d.ts +61 -0
  34. package/dist/components/LocksWidgets.d.ts.map +1 -0
  35. package/dist/components/SwapWidget.d.ts +165 -0
  36. package/dist/components/SwapWidget.d.ts.map +1 -0
  37. package/dist/components/TokenTrustPanel.d.ts +32 -0
  38. package/dist/components/TokenTrustPanel.d.ts.map +1 -0
  39. package/dist/components/lockCharts.d.ts +29 -0
  40. package/dist/components/lockCharts.d.ts.map +1 -0
  41. package/dist/components/portal.d.ts +10 -0
  42. package/dist/components/portal.d.ts.map +1 -0
  43. package/dist/components/primitives.d.ts +139 -0
  44. package/dist/components/primitives.d.ts.map +1 -0
  45. package/dist/components/swap/SwapSettings.d.ts +12 -0
  46. package/dist/components/swap/SwapSettings.d.ts.map +1 -0
  47. package/dist/components/swap/TokenSelect.d.ts +61 -0
  48. package/dist/components/swap/TokenSelect.d.ts.map +1 -0
  49. package/dist/components/swap/state.d.ts +208 -0
  50. package/dist/components/swap/state.d.ts.map +1 -0
  51. package/dist/config/chain.d.ts +163 -0
  52. package/dist/config/chain.d.ts.map +1 -0
  53. package/dist/config/integrator.d.ts +121 -0
  54. package/dist/config/integrator.d.ts.map +1 -0
  55. package/dist/context/WidgetProvider.d.ts +78 -0
  56. package/dist/context/WidgetProvider.d.ts.map +1 -0
  57. package/dist/core/clMath.d.ts +98 -0
  58. package/dist/core/clMath.d.ts.map +1 -0
  59. package/dist/core/errors.d.ts +18 -0
  60. package/dist/core/errors.d.ts.map +1 -0
  61. package/dist/core/format.d.ts +47 -0
  62. package/dist/core/format.d.ts.map +1 -0
  63. package/dist/core/liquidityGuards.d.ts +53 -0
  64. package/dist/core/liquidityGuards.d.ts.map +1 -0
  65. package/dist/core/liquidityRefresh.d.ts +35 -0
  66. package/dist/core/liquidityRefresh.d.ts.map +1 -0
  67. package/dist/core/math.d.ts +129 -0
  68. package/dist/core/math.d.ts.map +1 -0
  69. package/dist/core/pool.d.ts +33 -0
  70. package/dist/core/pool.d.ts.map +1 -0
  71. package/dist/core/tokenLists.d.ts +46 -0
  72. package/dist/core/tokenLists.d.ts.map +1 -0
  73. package/dist/embed/iframe.d.ts +185 -0
  74. package/dist/embed/iframe.d.ts.map +1 -0
  75. package/dist/embed/lockBadge.d.ts +55 -0
  76. package/dist/embed/lockBadge.d.ts.map +1 -0
  77. package/dist/embed/webComponent.d.ts +83 -0
  78. package/dist/embed/webComponent.d.ts.map +1 -0
  79. package/dist/embed.d.ts +25 -0
  80. package/dist/embed.d.ts.map +1 -0
  81. package/dist/embed.js +543 -0
  82. package/dist/embed.js.map +1 -0
  83. package/dist/headless.d.ts +60 -0
  84. package/dist/headless.d.ts.map +1 -0
  85. package/dist/headless.js +223 -0
  86. package/dist/headless.js.map +1 -0
  87. package/dist/hooks/useAsyncResource.d.ts +38 -0
  88. package/dist/hooks/useAsyncResource.d.ts.map +1 -0
  89. package/dist/hooks/useLaunch.d.ts +140 -0
  90. package/dist/hooks/useLaunch.d.ts.map +1 -0
  91. package/dist/hooks/useLiquidity.d.ts +91 -0
  92. package/dist/hooks/useLiquidity.d.ts.map +1 -0
  93. package/dist/hooks/useLocker.d.ts +77 -0
  94. package/dist/hooks/useLocker.d.ts.map +1 -0
  95. package/dist/hooks/useLocks.d.ts +33 -0
  96. package/dist/hooks/useLocks.d.ts.map +1 -0
  97. package/dist/hooks/useSwapExecute.d.ts +46 -0
  98. package/dist/hooks/useSwapExecute.d.ts.map +1 -0
  99. package/dist/hooks/useSwapQuote.d.ts +51 -0
  100. package/dist/hooks/useSwapQuote.d.ts.map +1 -0
  101. package/dist/hooks/useTokenLists.d.ts +26 -0
  102. package/dist/hooks/useTokenLists.d.ts.map +1 -0
  103. package/dist/hooks/useTokenTrust.d.ts +40 -0
  104. package/dist/hooks/useTokenTrust.d.ts.map +1 -0
  105. package/dist/hooks/useTokens.d.ts +15 -0
  106. package/dist/hooks/useTokens.d.ts.map +1 -0
  107. package/dist/index.d.ts +59 -0
  108. package/dist/index.d.ts.map +1 -0
  109. package/dist/index.js +1052 -0
  110. package/dist/index.js.map +1 -0
  111. package/dist/latch-embed.d.ts +19 -0
  112. package/dist/latch-embed.d.ts.map +1 -0
  113. package/dist/latch-embed.js +950 -0
  114. package/dist/latch-embed.js.map +1 -0
  115. package/dist/latch-embed.mjs +13162 -0
  116. package/dist/latch-embed.mjs.map +1 -0
  117. package/dist/locker/model.d.ts +73 -0
  118. package/dist/locker/model.d.ts.map +1 -0
  119. package/dist/locks/embedModel.d.ts +22 -0
  120. package/dist/locks/embedModel.d.ts.map +1 -0
  121. package/dist/locks/fetch.d.ts +60 -0
  122. package/dist/locks/fetch.d.ts.map +1 -0
  123. package/dist/locks/model.d.ts +143 -0
  124. package/dist/locks/model.d.ts.map +1 -0
  125. package/dist/locks/scan.d.ts +25 -0
  126. package/dist/locks/scan.d.ts.map +1 -0
  127. package/dist/styles/css.d.ts +32 -0
  128. package/dist/styles/css.d.ts.map +1 -0
  129. package/dist/styles.css +895 -0
  130. package/dist/trust/model.d.ts +48 -0
  131. package/dist/trust/model.d.ts.map +1 -0
  132. package/package.json +102 -0
@@ -0,0 +1,73 @@
1
+ /**
2
+ * The locker widget: its pure half.
3
+ *
4
+ * Input parsing (exact decimals, UTC dates), the live fee in words, the
5
+ * transaction plan for each action (approval only when the chain says it is
6
+ * missing, and only for exactly what the lock takes), and which actions a
7
+ * connected wallet may take on each of its locks. The contracts enforce all
8
+ * of this; the widget checks first so a wallet is never asked to sign a call
9
+ * that would revert.
10
+ */
11
+ import { type Address } from "viem";
12
+ import type { FeeGateTerms, PositionLockRecord, TokenLockView, VestingSchedule } from "@latchprotocol/sdk/lock";
13
+ /** An exact decimal amount in base units, or `null` when it is not one (never rounded). */
14
+ export declare function parseLockAmount(text: string, decimals: number): bigint | null;
15
+ /** A `datetime-local` value ("2027-01-31T12:00"), read as UTC, to unix seconds; `null` when malformed. */
16
+ export declare function parseUtcInput(text: string): bigint | null;
17
+ /** Unix seconds to a `datetime-local` value in UTC. */
18
+ export declare function toUtcInput(seconds: bigint): string;
19
+ /** A whole number of days as seconds, or `null`. */
20
+ export declare function parseDays(text: string): bigint | null;
21
+ export type VestingMode = "timelock" | "vesting";
22
+ /**
23
+ * The schedule for a token lock: a time lock releases everything at `unlockAt`;
24
+ * vesting accrues linearly from `start` over `durationDays`, nothing claimable
25
+ * before `start + cliffDays`.
26
+ */
27
+ export declare function lockSchedule(mode: VestingMode, input: {
28
+ readonly unlockAt: bigint | null;
29
+ readonly start: bigint | null;
30
+ readonly cliffDays: bigint | null;
31
+ readonly durationDays: bigint | null;
32
+ }): VestingSchedule | null;
33
+ export interface LockerFeeView {
34
+ /** "0.075 ETH per lock". */
35
+ readonly fee: string;
36
+ /** What the lock transaction sends for the fee (the higher of the fee and an announced increase). */
37
+ readonly sends: string;
38
+ /** An announced increase, in words, or `null`. */
39
+ readonly pending: string | null;
40
+ readonly note: string;
41
+ }
42
+ /** The live fee in words. Native units only, never a dollar figure. */
43
+ export declare function lockerFeeView(terms: Pick<FeeGateTerms, "feeWei" | "pendingFee" | "recipient">, native: {
44
+ readonly symbol: string;
45
+ readonly decimals: number;
46
+ }): LockerFeeView;
47
+ export interface PlanStep {
48
+ readonly key: "approve" | "lock";
49
+ readonly label: string;
50
+ }
51
+ /** Lock a position: approve this ONE NFT to the lock (when not already approved), then lock. */
52
+ export declare function planPositionLock(args: {
53
+ readonly tokenId: bigint;
54
+ readonly approved: boolean;
55
+ readonly unlockAt: bigint;
56
+ }): PlanStep[];
57
+ /** Lock tokens: an exact `approve(lock, amount)` when the allowance is short (never for native), then lock. */
58
+ export declare function planTokenLock(args: {
59
+ readonly native: boolean;
60
+ readonly allowance: bigint;
61
+ readonly amount: bigint;
62
+ readonly amountText: string;
63
+ readonly symbol: string;
64
+ }): PlanStep[];
65
+ export type PositionLockAction = "collect" | "extend" | "withdraw" | "accept";
66
+ export type TokenLockAction = "claim" | "accept";
67
+ /** What `account` may do with one position lock, as the contract allows it. */
68
+ export declare function positionLockActions(r: Pick<PositionLockRecord, "owner" | "pendingOwner" | "withdrawn" | "unlockAt">, account: Address | null, now: bigint): PositionLockAction[];
69
+ /** What `account` may do with one token lock. */
70
+ export declare function tokenLockActions(l: Pick<TokenLockView, "beneficiary" | "pendingBeneficiary" | "claimable">, account: Address | null): TokenLockAction[];
71
+ export declare const POSITION_ACTION_LABEL: Record<PositionLockAction, string>;
72
+ export declare const TOKEN_ACTION_LABEL: Record<TokenLockAction, string>;
73
+ //# sourceMappingURL=model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/locker/model.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The "Locked by Latch" embed badge: its words, pure.
3
+ *
4
+ * The same reads as `LocksBadge`, cut to what a project site shows beside its
5
+ * token or pool. The badge says only what was read:
6
+ *
7
+ * pool "X% of active liquidity locked" / "Locked permanently (LP locker)"
8
+ * / "No locked liquidity" (delegates to `poolBadgeView`)
9
+ * token "Y% of supply locked" - the share of supply no beneficiary can
10
+ * take yet (`unvested / totalSupply`), not what merely sits in the
11
+ * locker; plus the permanent LP-locker liquidity when there is some.
12
+ *
13
+ * It never says "safe" or "rug-proof": a lock is a lock until its date, and
14
+ * that is all the badge claims.
15
+ */
16
+ import type { PoolLocksData, TokenLocksData } from "./fetch.js";
17
+ import { type LocksBadgeView } from "./model.js";
18
+ /** A pool badge from a pool read (CL or Bin). */
19
+ export declare function poolEmbedView(d: PoolLocksData): LocksBadgeView;
20
+ /** A token badge: the share of supply still locked, and any permanent LP-locker liquidity. */
21
+ export declare function tokenEmbedView(d: TokenLocksData): LocksBadgeView;
22
+ //# sourceMappingURL=embedModel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedModel.d.ts","sourceRoot":"","sources":["../../src/locks/embedModel.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,EAA+E,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAE9H,iDAAiD;AACjD,wBAAgB,aAAa,CAAC,CAAC,EAAE,aAAa,GAAG,cAAc,CAE9D;AAED,8FAA8F;AAC9F,wBAAgB,cAAc,CAAC,CAAC,EAAE,cAAc,GAAG,cAAc,CAsBhE"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The lock reads with no React in them: one pool's locks or one token's locks,
3
+ * read through the MIT SDK (`@latchprotocol/sdk/lock`). `usePoolLocks` /
4
+ * `useTokenLocks` wrap these as resources; the framework-free embed
5
+ * (`latch-embed.js`) calls them directly.
6
+ */
7
+ import { type Address, type Hex, type PublicClient } from "viem";
8
+ import { type BinPoolPermanentSummary, type CurrencyLockSummary, type PoolLockSummary, type TokenPermanentPool } from "@latchprotocol/sdk/lock";
9
+ import type { LocksTokenMeta, LpLockerSource } from "./model.js";
10
+ /** What a lock read needs beyond the addresses. */
11
+ export interface LocksReadOptions {
12
+ readonly native?: NativeCurrencyMeta;
13
+ /** The protocol's LP lockers: their permanent liquidity is read and counted. */
14
+ readonly lp?: LpLockerSource;
15
+ }
16
+ /** Native currency display facts (address zero). */
17
+ export interface NativeCurrencyMeta {
18
+ readonly symbol: string;
19
+ readonly decimals: number;
20
+ }
21
+ export declare const DEFAULT_NATIVE: NativeCurrencyMeta;
22
+ export interface PoolLocksData {
23
+ /** CL: time locks and the CL LP locker's permanent positions. Bin pool: an empty summary. */
24
+ readonly summary: PoolLockSummary;
25
+ /** Bin pool: what the Bin LP locker holds forever; `null` for a CL pool. */
26
+ readonly bin: BinPoolPermanentSummary | null;
27
+ readonly currency0: LocksTokenMeta;
28
+ readonly currency1: LocksTokenMeta;
29
+ /** The chain's `block.timestamp` when read. */
30
+ readonly chainNow: bigint;
31
+ /** `Date.now()` when read, to advance `chainNow` locally. */
32
+ readonly readAtMs: number;
33
+ }
34
+ export interface TokenLocksData {
35
+ /** `null` when no token lock is deployed (only LP-locker liquidity is read). */
36
+ readonly summary: CurrencyLockSummary | null;
37
+ /** How much of the token the LP lockers hold forever, per pool; `null` when no LP locker was given. */
38
+ readonly permanent: {
39
+ readonly pools: readonly TokenPermanentPool[];
40
+ readonly total: bigint;
41
+ readonly unpriced: number;
42
+ } | null;
43
+ readonly token: LocksTokenMeta;
44
+ readonly chainNow: bigint;
45
+ readonly readAtMs: number;
46
+ }
47
+ /**
48
+ * Every lock in one pool (the SDK's `readPoolLocks`), with both currencies'
49
+ * metadata. The loader behind {@link usePoolLocks}, usable without React (the
50
+ * framework-free embed reads through it).
51
+ */
52
+ export declare function fetchPoolLocks(client: PublicClient, positionLock: Address | null, poolId: Hex, options?: LocksReadOptions): Promise<PoolLocksData>;
53
+ /** Whether {@link fetchPoolLocks} has anything to read for this target. */
54
+ export declare function poolLocksEnabled(positionLock: Address | null, lp?: LpLockerSource): boolean;
55
+ /**
56
+ * Every lock of one token (the SDK's `readCurrencyLocks`), with its metadata
57
+ * and what the LP lockers hold of it. The loader behind {@link useTokenLocks}.
58
+ */
59
+ export declare function fetchTokenLocks(client: PublicClient, tokenLock: Address | null, token: Address, options?: LocksReadOptions): Promise<TokenLocksData>;
60
+ //# sourceMappingURL=fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/locks/fetch.ts"],"names":[],"mappings":"AACA;;;;;GAKG;AAEH,OAAO,EAAyB,KAAK,OAAO,EAAE,KAAK,GAAG,EAAE,KAAK,YAAY,EAAE,MAAM,MAAM,CAAC;AACxF,OAAO,EAWL,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACxB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjE,mDAAmD;AACnD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,kBAAkB,CAAC;IACrC,gFAAgF;IAChF,QAAQ,CAAC,EAAE,CAAC,EAAE,cAAc,CAAC;CAC9B;AAED,oDAAoD;AACpD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,cAAc,EAAE,kBAAoD,CAAC;AAelF,MAAM,WAAW,aAAa;IAC5B,6FAA6F;IAC7F,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,4EAA4E;IAC5E,QAAQ,CAAC,GAAG,EAAE,uBAAuB,GAAG,IAAI,CAAC;IAC7C,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC,+CAA+C;IAC/C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC7C,uGAAuG;IACvG,QAAQ,CAAC,SAAS,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAChI,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAWD;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,YAAY,EACpB,YAAY,EAAE,OAAO,GAAG,IAAI,EAC5B,MAAM,EAAE,GAAG,EACX,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,aAAa,CAAC,CA2BxB;AAED,2EAA2E;AAC3E,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,OAAO,GAAG,IAAI,EAAE,EAAE,GAAE,cAAmB,GAAG,OAAO,CAE/F;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,YAAY,EACpB,SAAS,EAAE,OAAO,GAAG,IAAI,EACzB,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,cAAc,CAAC,CAoBzB"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Locks: the pure half of `LocksBadge` / `LocksPanel`.
3
+ *
4
+ * Everything here turns what the SDK read from chain (`readPoolLocks`,
5
+ * `readCurrencyLocks` in `@latchprotocol/sdk/lock`) into words, with no
6
+ * estimate added. Two rules the copy never breaks:
7
+ *
8
+ * - A CL pool has no LP token. The pool figure is the locked share of the
9
+ * pool's ACTIVE liquidity at the current tick (`lockedActiveBps`), and it is
10
+ * called exactly that. When the pool has no active liquidity the share is
11
+ * "not measurable", never 0% and never 100%.
12
+ * - Time is the chain's `block.timestamp`, the clock the contracts compare
13
+ * `unlockAt` against. Callers pass `now` from the chain, never `Date.now()`.
14
+ *
15
+ * - Two kinds of lock, never blurred: "time-locked until <date>"
16
+ * (`LatchPositionLock`, `LatchTokenLock`) and "locked permanently (LP
17
+ * locker)" (`LatchLPLocker` / `LatchBinLPLocker`, which hold every Kit v2
18
+ * launch's seed liquidity and have NO withdraw path). Permanent in-range CL
19
+ * liquidity counts in the locked share and is also shown on its own. A Bin
20
+ * pool's permanent figure is the locked fraction of its ACTIVE bin.
21
+ *
22
+ * No USD anywhere: amounts are token units with a symbol.
23
+ */
24
+ import type { Address, Hex } from "viem";
25
+ import { type BinPoolPermanentSummary, type CurrencyLockSummary, type PoolLockSummary, type PositionLockStatus, type TokenLockView, type TokenPermanentPool } from "@latchprotocol/sdk/lock";
26
+ /** Which locks a widget shows. `null` lock address = the chain has no address for it; surfaces say "coming soon". */
27
+ export type LocksTarget = ({
28
+ readonly kind: "pool";
29
+ readonly poolId: Hex;
30
+ readonly positionLock: Address | null;
31
+ } & LpLockerSource) | ({
32
+ readonly kind: "token";
33
+ readonly token: Address;
34
+ readonly tokenLock: Address | null;
35
+ } & LpLockerSource);
36
+ /**
37
+ * The protocol's permanent LP lockers (the address book's `launchpadV2.clLPLocker`
38
+ * / `binLPLocker`). CL positions are found from the locker's `PositionLocked`
39
+ * logs: pass their token ids when you know them, or `lpLockerFromBlock` (the
40
+ * locker's deploy block) and one log scan to the latest block is made. Bin locks
41
+ * are enumerable and need neither. All optional: omitted = not counted.
42
+ */
43
+ export interface LpLockerSource {
44
+ readonly lpLocker?: Address | null;
45
+ readonly binLpLocker?: Address | null;
46
+ readonly lpLockerTokenIds?: readonly bigint[];
47
+ readonly lpLockerFromBlock?: bigint;
48
+ /** A pool target that is a Bin pool: its permanent share is read from the Bin locker. */
49
+ readonly poolType?: "cl" | "bin";
50
+ }
51
+ /** Token display facts. `decimals: null` means amounts are shown in base units. */
52
+ export interface LocksTokenMeta {
53
+ readonly address: Address;
54
+ readonly symbol: string;
55
+ readonly decimals: number | null;
56
+ }
57
+ /** The Latch lock page for a target: `<siteUrl>/app/locks/pool/<poolId>` or `/app/locks/token/<address>`. */
58
+ export declare function lockPageUrl(siteUrl: string, target: Pick<LocksTarget, "kind"> & {
59
+ readonly poolId?: Hex;
60
+ readonly token?: Address;
61
+ }, opts?: {
62
+ readonly pathBase?: string;
63
+ readonly query?: string;
64
+ }): string;
65
+ /** Basis points as a percentage: 8234 → "82.34%", 10000 → "100%", 1 → "0.01%". */
66
+ export declare function formatLockBps(bps: number): string;
67
+ /** `12d 04h 03m` above a day, `4h 03m 20s` below, `now` at zero. */
68
+ export declare function formatCountdown(seconds: bigint): string;
69
+ /** Unix seconds → "2026-10-01 14:05 UTC". */
70
+ export declare function formatUtc(unixSeconds: bigint): string;
71
+ /** Exact decimal, trimmed, never "0" for a non-zero amount. */
72
+ export declare function formatLockAmount(value: bigint, meta: Pick<LocksTokenMeta, "symbol" | "decimals">, maxFrac?: number): string;
73
+ /** The badge's words. `countdownTo` is the moment the countdown runs to (chain seconds). */
74
+ export interface LocksBadgeView {
75
+ /** `locked`: something is locked now. `open`: locks exist, none still locked. `none`: no lock at all. */
76
+ readonly tone: "locked" | "open" | "none";
77
+ readonly headline: string;
78
+ readonly detail: string | null;
79
+ readonly countdownTo: bigint | null;
80
+ /** What the countdown is to, e.g. "first unlock". */
81
+ readonly countdownLabel: string | null;
82
+ }
83
+ /** A pool: the locked share of active liquidity (time-locked + permanent), and the soonest unlock. */
84
+ export declare function poolBadgeView(s: Pick<PoolLockSummary, "locks" | "liveCount" | "lockedCount" | "lockedActiveBps" | "outOfRangeLocked" | "nextUnlockAt" | "lastUnlockAt" | "pool"> & Partial<Pick<PoolLockSummary, "permanent" | "permanentActiveBps" | "timeLockedActiveBps" | "permanentOutOfRange">>): LocksBadgeView;
85
+ /** A Bin pool: what the protocol's Bin LP locker holds forever. */
86
+ export declare function binPoolBadgeView(b: Pick<BinPoolPermanentSummary, "locks" | "activeBinLockedBps" | "amount0" | "amount1">, c0: Pick<LocksTokenMeta, "symbol" | "decimals">, c1: Pick<LocksTokenMeta, "symbol" | "decimals">): LocksBadgeView;
87
+ /** A token: the share of `totalSupply` held for locks and not yet vested. */
88
+ export declare function tokenBadgeView(s: Pick<CurrencyLockSummary, "locks" | "totalHeld" | "unvested" | "heldBps" | "unvestedBps" | "nextCliffOrEnd" | "lastEnd"> & {
89
+ readonly totalSupply?: bigint | null;
90
+ }, meta: Pick<LocksTokenMeta, "symbol" | "decimals">, lp?: {
91
+ readonly pools: readonly TokenPermanentPool[];
92
+ readonly total: bigint;
93
+ } | null): LocksBadgeView;
94
+ export declare const POSITION_STATUS_LABEL: Record<PositionLockStatus, string>;
95
+ export declare const TOKEN_STATUS_LABEL: Record<TokenLockView["status"], string>;
96
+ /** `vested / amount` in basis points (floor), for a vesting bar. */
97
+ export declare function vestedBps(l: Pick<TokenLockView, "vested" | "amount">): number;
98
+ /** The chain's `now` at a later wall-clock moment: `chainNow` read at `readAtMs`, advanced by elapsed seconds. */
99
+ export declare function advanceChainNow(chainNow: bigint, readAtMs: number, nowMs: number): bigint;
100
+ /** One part of a share bar. `bps` is floored; `nonZero` keeps a sub-0.01% part visible ("<0.01%"). */
101
+ export interface SharePart {
102
+ readonly key: "permanent" | "time" | "unvested" | "vested" | "lp";
103
+ readonly label: string;
104
+ readonly bps: number;
105
+ readonly nonZero: boolean;
106
+ }
107
+ /** A whole split into locked parts; the rest of the bar is "not locked". */
108
+ export interface ShareBarView {
109
+ readonly parts: readonly SharePart[];
110
+ /** What the share is OF: "of active liquidity", "of the active bin", "of supply". */
111
+ readonly of: string;
112
+ readonly restBps: number;
113
+ }
114
+ /** A CL pool's active liquidity: permanent (LP locker) and time-locked. `null` when no share is measurable. */
115
+ export declare function poolShareBar(s: Pick<PoolLockSummary, "pool" | "lockedActiveLiquidity"> & Partial<Pick<PoolLockSummary, "permanentActiveLiquidity" | "timeLockedActiveLiquidity">>): ShareBarView | null;
116
+ /** A Bin pool's active bin: the permanent share. `null` when no lock holds it. */
117
+ export declare function binShareBar(b: Pick<BinPoolPermanentSummary, "activeBinLockedBps">): ShareBarView | null;
118
+ /** A token's supply: unvested, vested-unclaimed, and (optionally) its side of permanent LP. `null` without a supply. */
119
+ export declare function tokenShareBar(s: Pick<CurrencyLockSummary, "totalHeld" | "unvested"> & {
120
+ readonly totalSupply?: bigint | null;
121
+ }, lpTotal?: bigint | null): ShareBarView | null;
122
+ /** A point of a release schedule: `t` unix seconds, `vested` summed over the locks (the contract's `_vested`). */
123
+ export interface SchedulePoint {
124
+ readonly t: bigint;
125
+ readonly vested: bigint;
126
+ }
127
+ /**
128
+ * The cumulative release schedule of one token's locks: every start, the
129
+ * second before and of each cliff, every end, `now`, and `samples` evenly
130
+ * spaced points between, each computed with the SDK's `vestedAt`. A SCHEDULE
131
+ * of fixed on-chain terms, not a history of claims. `null` when it has no length.
132
+ */
133
+ export declare function releaseSchedule(locks: readonly Pick<TokenLockView, "start" | "cliff" | "end" | "amount">[], now: bigint | null, samples?: number): {
134
+ readonly points: readonly SchedulePoint[];
135
+ readonly total: bigint;
136
+ } | null;
137
+ /** A share bar from figures already in basis points (the trust read's pool facts). */
138
+ export declare function shareBarFromBps(parts: readonly {
139
+ readonly key: SharePart["key"];
140
+ readonly label: string;
141
+ readonly bps: number | null;
142
+ }[], of: string): ShareBarView | null;
143
+ //# sourceMappingURL=model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/locks/model.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The CL LP locker's position ids for one pool (or every pool), from its
3
+ * `PositionLocked` logs, scanned in windows so an RPC that caps `eth_getLogs`
4
+ * ranges still returns every log rather than an error or a truncated list.
5
+ *
6
+ * A window that fails is split in half and retried, down to `minWindow`; a
7
+ * window that still fails throws - a partial scan is never passed off as a
8
+ * complete one.
9
+ */
10
+ import type { Address, Hex, PublicClient } from "viem";
11
+ export interface ScanOptions {
12
+ readonly fromBlock: bigint;
13
+ readonly toBlock?: bigint;
14
+ /** First window size; default 500,000 blocks. */
15
+ readonly window?: bigint;
16
+ /** Smallest window before giving up; default 2,000 blocks. */
17
+ readonly minWindow?: bigint;
18
+ }
19
+ /** Pure: `[from, to]` split into consecutive windows of at most `size` blocks. */
20
+ export declare function blockWindows(from: bigint, to: bigint, size: bigint): {
21
+ from: bigint;
22
+ to: bigint;
23
+ }[];
24
+ export declare function scanClLpLockerTokenIds(client: PublicClient, locker: Address, opts: ScanOptions, poolId?: Hex): Promise<bigint[]>;
25
+ //# sourceMappingURL=scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/locks/scan.ts"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAGvD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,iDAAiD;IACjD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EAAE,CAKnG;AAED,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAyBtI"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The stylesheet, as a string.
3
+ *
4
+ * It lives in TypeScript rather than a `.css` file for one reason: the web
5
+ * component renders into a shadow root, and a shadow root cannot see the host
6
+ * page's stylesheets. The same source therefore has to be injectable in three
7
+ * places - a `<style>` in the document head for React hosts, a
8
+ * `CSSStyleSheet` adopted by each shadow root, and `dist/styles.css` for hosts
9
+ * that prefer a `<link>`. One source, no drift.
10
+ *
11
+ * ## Theming
12
+ *
13
+ * Every colour, radius and font is a `--latch-*` custom property declared on
14
+ * `.latch-widget`. Overriding one from the host page is enough to restyle the
15
+ * widget; nothing is hardcoded inside a rule. Light is the base, dark is applied
16
+ * by `[data-latch-theme="dark"]` and by `prefers-color-scheme` when the theme is
17
+ * left on `system`.
18
+ *
19
+ * The layout is single-column and fluid from 280px up, because these widgets
20
+ * are expected to live in narrow sidebars.
21
+ */
22
+ /** The complete widget stylesheet. */
23
+ export declare const WIDGET_CSS = "\n.latch-widget {\n /* Palette */\n --latch-bg: #ffffff;\n --latch-bg-elevated: #f6f7f9;\n --latch-bg-sunken: #eceef2;\n --latch-border: #dfe3e8;\n --latch-border-strong: #c4cad3;\n --latch-text: #10131a;\n --latch-text-muted: #5c6472;\n --latch-text-inverted: #ffffff;\n --latch-accent: #3b5bdb;\n --latch-accent-hover: #2f49b2;\n --latch-accent-text: #ffffff;\n --latch-success: #157347;\n --latch-warning: #9a6700;\n --latch-warning-bg: #fff8e1;\n --latch-danger: #b42318;\n --latch-danger-bg: #fef3f2;\n --latch-focus: #3b5bdb;\n /* The compact swap's Buy side: the CVD-safe \"up\" colour, with a dark label (6:1). */\n --latch-buy: #1f9e89;\n --latch-buy-hover: #23b39b;\n --latch-buy-text: #04060c;\n\n /* Shape and type */\n --latch-radius: 14px;\n --latch-radius-sm: 9px;\n --latch-font: \"IBM Plex Sans\", system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n --latch-font-mono: \"IBM Plex Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n --latch-font-size: 14px;\n --latch-font-size-sm: 12px;\n --latch-font-size-lg: 22px;\n --latch-gap: 12px;\n --latch-padding: 16px;\n --latch-control-height: 44px;\n --latch-shadow: 0 1px 2px rgba(16, 19, 26, 0.06), 0 8px 24px rgba(16, 19, 26, 0.06);\n\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: var(--latch-gap);\n width: 100%;\n min-width: 0;\n max-width: 480px;\n padding: var(--latch-padding);\n background: var(--latch-bg);\n color: var(--latch-text);\n border: 1px solid var(--latch-border);\n border-radius: var(--latch-radius);\n box-shadow: var(--latch-shadow);\n font-family: var(--latch-font);\n font-size: var(--latch-font-size);\n line-height: 1.45;\n}\n\n.latch-widget *,\n.latch-widget *::before,\n.latch-widget *::after {\n box-sizing: inherit;\n}\n\n/* Text takes the WIDGET's ink, not the host page's. A host rule such as\n `h2 { color: var(--ink) }` otherwise beats inheritance: a dark-themed widget\n on a light page drew its title in the page's near-black on the widget's\n near-black (1.1:1, axe, the locker embed 2026-09-26). Specificity (0,1,0)\n beats any element selector; every widget class rule below comes later and\n still sets its own colour. A host's own content in a slot is left alone. */\n.latch-widget :where(h1, h2, h3, h4, h5, h6, p, label, legend, dt, dd, li, th, td, figcaption, caption):where(:not(.latch-slot *)) {\n color: inherit;\n}\n\n.latch-widget[data-latch-theme=\"dark\"] {\n --latch-bg: #14161b;\n --latch-bg-elevated: #1c1f26;\n --latch-bg-sunken: #23262f;\n --latch-border: #2b2f38;\n --latch-border-strong: #3d4350;\n --latch-text: #f2f4f8;\n --latch-text-muted: #98a1b1;\n --latch-text-inverted: #14161b;\n --latch-accent: #6b8afd;\n --latch-accent-hover: #849dff;\n --latch-accent-text: #0b0d12;\n --latch-success: #4ade80;\n --latch-warning: #fbbf5c;\n --latch-warning-bg: #33280f;\n --latch-danger: #ff8a80;\n --latch-danger-bg: #3a1a17;\n --latch-focus: #849dff;\n --latch-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 24px rgba(0, 0, 0, 0.35);\n}\n\n@media (prefers-color-scheme: dark) {\n .latch-widget[data-latch-theme=\"system\"] {\n --latch-bg: #14161b;\n --latch-bg-elevated: #1c1f26;\n --latch-bg-sunken: #23262f;\n --latch-border: #2b2f38;\n --latch-border-strong: #3d4350;\n --latch-text: #f2f4f8;\n --latch-text-muted: #98a1b1;\n --latch-text-inverted: #14161b;\n --latch-accent: #6b8afd;\n --latch-accent-hover: #849dff;\n --latch-accent-text: #0b0d12;\n --latch-success: #4ade80;\n --latch-warning: #fbbf5c;\n --latch-warning-bg: #33280f;\n --latch-danger: #ff8a80;\n --latch-danger-bg: #3a1a17;\n --latch-focus: #849dff;\n --latch-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 24px rgba(0, 0, 0, 0.35);\n }\n}\n\n.latch-header {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.latch-title {\n margin: 0;\n font-size: 16px;\n font-weight: 650;\n}\n\n.latch-subtitle {\n margin: 0;\n color: var(--latch-text-muted);\n font-size: var(--latch-font-size-sm);\n}\n\n.latch-mock-banner {\n display: flex;\n gap: 8px;\n align-items: flex-start;\n padding: 10px 12px;\n border: 1px solid var(--latch-warning);\n border-radius: var(--latch-radius-sm);\n background: var(--latch-warning-bg);\n color: var(--latch-warning);\n font-size: var(--latch-font-size-sm);\n font-weight: 600;\n}\n\n.latch-mock-banner span {\n font-weight: 500;\n}\n\n.latch-field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 12px;\n background: var(--latch-bg-elevated);\n border: 1px solid var(--latch-border);\n border-radius: var(--latch-radius-sm);\n}\n\n.latch-field-row {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.latch-label {\n display: block;\n color: var(--latch-text-muted);\n font-size: var(--latch-font-size-sm);\n font-weight: 600;\n letter-spacing: 0.01em;\n}\n\n.latch-amount-input {\n flex: 1 1 120px;\n min-width: 0;\n padding: 0;\n border: 0;\n background: transparent;\n color: var(--latch-text);\n font-family: var(--latch-font);\n font-size: var(--latch-font-size-lg);\n font-weight: 600;\n line-height: 1.2;\n}\n\n.latch-amount-input::placeholder {\n color: var(--latch-text-muted);\n font-weight: 500;\n}\n\n.latch-amount-input:focus-visible,\n.latch-select:focus-visible,\n.latch-button:focus-visible,\n.latch-icon-button:focus-visible,\n.latch-slider:focus-visible,\n.latch-chip:focus-visible {\n outline: 2px solid var(--latch-focus);\n outline-offset: 2px;\n border-radius: 4px;\n}\n\n.latch-select {\n flex: 0 1 auto;\n max-width: 100%;\n height: 34px;\n padding: 0 8px;\n background: var(--latch-bg);\n color: var(--latch-text);\n border: 1px solid var(--latch-border-strong);\n border-radius: 999px;\n font-family: inherit;\n font-size: var(--latch-font-size);\n font-weight: 600;\n cursor: pointer;\n}\n\n.latch-meta {\n display: flex;\n justify-content: space-between;\n gap: 8px;\n color: var(--latch-text-muted);\n font-size: var(--latch-font-size-sm);\n}\n\n.latch-meta-value {\n font-variant-numeric: tabular-nums;\n}\n\n.latch-switch-row {\n display: flex;\n justify-content: center;\n margin: -6px 0;\n}\n\n.latch-icon-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n padding: 0;\n background: var(--latch-bg);\n color: var(--latch-text);\n border: 1px solid var(--latch-border-strong);\n border-radius: 999px;\n cursor: pointer;\n font-size: 15px;\n line-height: 1;\n}\n\n.latch-icon-button:hover {\n background: var(--latch-bg-sunken);\n}\n\n.latch-summary {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 12px;\n background: var(--latch-bg-sunken);\n border-radius: var(--latch-radius-sm);\n font-size: var(--latch-font-size-sm);\n}\n\n.latch-summary dl {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin: 0;\n}\n\n.latch-summary-row {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 10px;\n}\n\n.latch-summary-row dt {\n color: var(--latch-text-muted);\n min-width: 0;\n}\n\n.latch-summary-row dd {\n margin: 0;\n text-align: right;\n font-variant-numeric: tabular-nums;\n overflow-wrap: anywhere;\n}\n\n.latch-summary-row[data-latch-emphasis=\"fee\"] dd {\n color: var(--latch-accent);\n font-weight: 650;\n}\n\n.latch-summary-row[data-latch-severity=\"high\"] dd,\n.latch-summary-row[data-latch-severity=\"severe\"] dd {\n color: var(--latch-danger);\n font-weight: 650;\n}\n\n.latch-route {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px;\n font-size: var(--latch-font-size-sm);\n color: var(--latch-text-muted);\n}\n\n.latch-route-token {\n padding: 2px 8px;\n background: var(--latch-bg);\n border: 1px solid var(--latch-border);\n border-radius: 999px;\n color: var(--latch-text);\n font-weight: 600;\n}\n\n.latch-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n width: 100%;\n min-height: var(--latch-control-height);\n padding: 0 16px;\n background: var(--latch-accent);\n color: var(--latch-accent-text);\n border: 1px solid transparent;\n border-radius: var(--latch-radius-sm);\n font-family: inherit;\n font-size: 15px;\n font-weight: 650;\n cursor: pointer;\n}\n\n.latch-button:hover:not(:disabled) {\n background: var(--latch-accent-hover);\n}\n\n.latch-button:disabled {\n opacity: 0.55;\n cursor: not-allowed;\n}\n\n.latch-button[data-latch-variant=\"secondary\"] {\n background: var(--latch-bg-elevated);\n color: var(--latch-text);\n border-color: var(--latch-border-strong);\n}\n\n.latch-button[data-latch-variant=\"danger\"] {\n background: var(--latch-danger);\n color: var(--latch-text-inverted);\n}\n\n.latch-status {\n min-height: 18px;\n margin: 0;\n font-size: var(--latch-font-size-sm);\n color: var(--latch-text-muted);\n}\n\n.latch-status[data-latch-tone=\"error\"] {\n color: var(--latch-danger);\n}\n\n.latch-status[data-latch-tone=\"success\"] {\n color: var(--latch-success);\n}\n\n.latch-error {\n padding: 10px 12px;\n background: var(--latch-danger-bg);\n border: 1px solid var(--latch-danger);\n border-radius: var(--latch-radius-sm);\n color: var(--latch-danger);\n font-size: var(--latch-font-size-sm);\n overflow-wrap: anywhere;\n}\n\n.latch-tabs {\n display: flex;\n gap: 4px;\n padding: 3px;\n background: var(--latch-bg-sunken);\n border-radius: 999px;\n}\n\n.latch-chip {\n flex: 1 1 0;\n min-width: 0;\n padding: 7px 10px;\n background: transparent;\n color: var(--latch-text-muted);\n border: 0;\n border-radius: 999px;\n font-family: inherit;\n font-size: var(--latch-font-size-sm);\n font-weight: 650;\n cursor: pointer;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.latch-chip[aria-selected=\"true\"],\n.latch-chip[aria-pressed=\"true\"] {\n background: var(--latch-bg);\n color: var(--latch-text);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);\n}\n\n.latch-progress {\n height: 8px;\n background: var(--latch-bg-sunken);\n border-radius: 999px;\n overflow: hidden;\n}\n\n.latch-progress-fill {\n height: 100%;\n background: var(--latch-accent);\n border-radius: 999px;\n transition: width 200ms ease;\n}\n\n.latch-slider {\n width: 100%;\n accent-color: var(--latch-accent);\n}\n\n.latch-range-grid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 8px;\n}\n\n.latch-range-grid input {\n width: 100%;\n min-width: 0;\n height: 36px;\n padding: 0 10px;\n background: var(--latch-bg);\n color: var(--latch-text);\n border: 1px solid var(--latch-border-strong);\n border-radius: var(--latch-radius-sm);\n font-family: var(--latch-font-mono);\n font-size: var(--latch-font-size);\n}\n\n.latch-curve {\n display: block;\n width: 100%;\n height: auto;\n border-radius: var(--latch-radius-sm);\n background: var(--latch-bg-sunken);\n}\n\n.latch-visually-hidden {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n border: 0;\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n overflow: hidden;\n white-space: nowrap;\n}\n\n.latch-footer {\n display: flex;\n justify-content: space-between;\n gap: 8px;\n color: var(--latch-text-muted);\n font-size: 11px;\n}\n\n.latch-footer a {\n color: inherit;\n}\n\n.latch-launch-legs {\n display: grid;\n gap: 16px;\n}\n\n.latch-empty {\n margin: 0;\n color: var(--latch-text-muted);\n font-size: 13px;\n line-height: 1.5;\n}\n\n@media (max-width: 380px) {\n .latch-widget {\n --latch-padding: 12px;\n --latch-font-size-lg: 20px;\n }\n\n .latch-range-grid {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .latch-progress-fill {\n transition: none;\n }\n}\n\n/* ============================================================================\n Swap surface (SwapWidget): panels, big inputs, token buttons, dialog,\n popover, CTA, details. Every colour is a --latch-* token above.\n ============================================================================ */\n\n.latch-swap { max-width: 480px; }\n.latch-swap__form { display: flex; flex-direction: column; gap: 4px; }\n\n.latch-panel {\n background: var(--latch-bg-elevated);\n border: 1px solid transparent;\n border-radius: var(--latch-radius);\n padding: 14px 16px 12px;\n transition: border-color 120ms ease, background 120ms ease;\n}\n.latch-panel:focus-within { border-color: var(--latch-border-strong); background: var(--latch-bg); }\n/* The amount field draws no outline of its own (it is the panel's big number), so the PANEL carries\n the keyboard focus ring; the border-strong change alone was invisible on a dark theme (WCAG 2.4.7). */\n.latch-panel:has(.latch-big-input:focus-visible) { border-color: var(--latch-focus); box-shadow: 0 0 0 1px var(--latch-focus); }\n.latch-panel__top { display: flex; justify-content: space-between; align-items: center; min-height: 18px; }\n.latch-panel__balance { font-size: var(--latch-font-size-sm); color: var(--latch-text-muted); display: inline-flex; gap: 8px; align-items: center; }\n/* Quick amounts under \"You pay\": real fractions of the balance just read, one row of equal chips. */\n.latch-portions { display: grid; grid-template-columns: repeat(auto-fit, minmax(52px, 1fr)); gap: 8px; margin-top: 12px; }\n.latch-portion {\n font: inherit; font-size: var(--latch-font-size-sm); font-weight: 600; min-height: 36px; padding: 0 8px; border-radius: var(--latch-radius-sm);\n border: 1px solid var(--latch-border); background: var(--latch-bg); color: var(--latch-text); cursor: pointer;\n transition: border-color 120ms, color 120ms;\n}\n.latch-portion:hover { border-color: var(--latch-accent); color: var(--latch-accent); }\n.latch-portion:focus-visible { outline: 2px solid var(--latch-focus); outline-offset: 2px; }\n\n/* The compact variant's Buy / Sell pill, in the header where the title was. */\n.latch-sides {\n flex: 1 1 auto; display: grid; grid-template-columns: 1fr 1fr; gap: 4px; padding: 4px; min-width: 0;\n border: 1px solid var(--latch-border); border-radius: 999px; background: var(--latch-bg-sunken);\n}\n.latch-sides__btn {\n min-height: 40px; border: 0; border-radius: 999px; background: transparent; color: var(--latch-text);\n font: inherit; font-weight: 600; cursor: pointer; transition: background 120ms, color 120ms;\n}\n.latch-sides__btn:hover { background: var(--latch-bg-elevated); }\n.latch-sides__btn:focus-visible { outline: 2px solid var(--latch-focus); outline-offset: 2px; }\n.latch-sides__btn[data-latch-side=\"buy\"][aria-pressed=\"true\"] { background: var(--latch-buy); color: var(--latch-buy-text); }\n.latch-sides__btn[data-latch-side=\"sell\"][aria-pressed=\"true\"] { background: var(--latch-accent); color: var(--latch-accent-text); }\n.latch-swap .latch-header:has(.latch-sides) { align-items: center; flex-wrap: nowrap; }\n\n/* The compact variant's lines, always visible above the button, one fee per line. */\n.latch-lines { margin: 8px 0 0; padding: 4px 4px 0; display: grid; gap: 8px; }\n.latch-lines .latch-details__row { font-size: 13px; }\n.latch-lines output { font: inherit; transition: opacity 150ms; }\n.latch-lines output.is-refreshing { opacity: 0.55; }\n.latch-swap__form[data-latch-variant=\"compact\"][data-latch-swap-side=\"buy\"] .latch-cta { background: var(--latch-buy); color: var(--latch-buy-text); }\n.latch-swap__form[data-latch-variant=\"compact\"][data-latch-swap-side=\"buy\"] .latch-cta:hover:not(:disabled) { background: var(--latch-buy-hover); }\n.latch-panel__main { display: flex; align-items: center; gap: 12px; margin-top: 6px; }\n.latch-big-input {\n flex: 1 1 auto; min-width: 0; width: 100%;\n font-family: var(--latch-font); font-size: 32px; font-weight: 500; letter-spacing: -0.01em;\n background: transparent; border: 0; color: var(--latch-text); padding: 4px 0; outline: none;\n}\n.latch-big-input::placeholder, .latch-big-input__placeholder { color: var(--latch-text-muted); opacity: 0.7; }\n.latch-big-input--out { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: opacity 150ms; }\n.latch-big-input--out.is-refreshing { opacity: 0.55; }\n.latch-panel__error { margin: 6px 0 0; font-size: var(--latch-font-size-sm); color: var(--latch-danger); }\n.latch-panel__note { margin: 6px 0 0; font-size: var(--latch-font-size-sm); color: var(--latch-text-muted); }\n\n.latch-token-button {\n flex: 0 0 auto; display: inline-flex; align-items: center; gap: 8px;\n padding: 6px 10px 6px 6px; border-radius: 999px; font: inherit; font-weight: 650; cursor: pointer;\n background: var(--latch-bg); color: var(--latch-text); border: 1px solid var(--latch-border);\n box-shadow: 0 1px 2px rgba(0,0,0,0.06); transition: border-color 120ms, transform 80ms;\n}\n.latch-token-button:hover { border-color: var(--latch-border-strong); }\n.latch-token-button:active { transform: scale(0.98); }\n.latch-token-button__chevron { color: var(--latch-text-muted); font-size: 12px; }\n.latch-token-button--locked { cursor: default; box-shadow: none; background: var(--latch-bg-sunken); }\n.latch-token-button--locked:hover { border-color: var(--latch-border); }\n.latch-slot { display: block; }\n.latch-slot--header { margin-top: -4px; }\n.latch-token-logo { border-radius: 50%; object-fit: cover; flex-shrink: 0; background: var(--latch-bg-sunken); }\n.latch-token-logo--initials {\n display: inline-flex; align-items: center; justify-content: center; font-weight: 700;\n color: #fff; letter-spacing: 0.01em; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25);\n background: linear-gradient(140deg, hsl(var(--latch-mono-hue, 215) 62% 52%), hsl(calc(var(--latch-mono-hue, 215) + 28) 58% 34%));\n box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12);\n}\n\n.latch-flip { display: flex; justify-content: center; height: 0; position: relative; z-index: 1; }\n.latch-flip__button {\n position: relative; top: -16px; width: 36px; height: 36px; border-radius: 12px; cursor: pointer;\n background: var(--latch-bg); border: 4px solid var(--latch-bg); color: var(--latch-text);\n box-shadow: 0 0 0 1px var(--latch-border); font-size: 16px; line-height: 1;\n display: inline-flex; align-items: center; justify-content: center; transition: transform 160ms ease, background 120ms;\n}\n.latch-flip__button:hover { background: var(--latch-bg-sunken); transform: rotate(180deg); }\n\n.latch-cta {\n margin-top: 8px; width: 100%; height: 52px; border: 0; border-radius: var(--latch-radius);\n background: var(--latch-accent); color: var(--latch-accent-text); font: inherit; font-size: 17px; font-weight: 700;\n cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap: 10px;\n transition: background 120ms, transform 80ms, opacity 120ms;\n}\n.latch-cta:hover:not(:disabled) { background: var(--latch-accent-hover); }\n.latch-cta:active:not(:disabled) { transform: scale(0.995); }\n.latch-cta:disabled { background: var(--latch-bg-sunken); color: var(--latch-text-muted); cursor: not-allowed; }\n.latch-cta[data-latch-busy=\"true\"] { opacity: 0.85; cursor: progress; }\n.latch-spinner {\n width: 16px; height: 16px; border-radius: 50%; border: 2px solid currentColor; border-right-color: transparent;\n animation: latch-spin 0.8s linear infinite;\n}\n@keyframes latch-spin { to { transform: rotate(360deg); } }\n\n.latch-ack {\n display: flex; gap: 10px; align-items: flex-start; margin-top: 6px; padding: 10px 12px;\n border-radius: var(--latch-radius-sm); background: var(--latch-warning-bg); color: var(--latch-text); font-size: var(--latch-font-size-sm);\n}\n.latch-ack input { margin-top: 2px; }\n\n.latch-details { margin-top: 8px; border: 1px solid var(--latch-border); border-radius: var(--latch-radius-sm); overflow: hidden; }\n.latch-details__toggle {\n width: 100%; display: flex; justify-content: space-between; align-items: center; gap: 8px;\n padding: 10px 12px; background: transparent; border: 0; font: inherit; color: var(--latch-text); cursor: pointer; text-align: left;\n}\n.latch-details__toggle:hover { background: var(--latch-bg-elevated); }\n.latch-details__rate { font-weight: 600; cursor: pointer; }\n.latch-details__hint { display: inline-flex; gap: 10px; align-items: center; color: var(--latch-text-muted); font-size: var(--latch-font-size-sm); }\n.latch-details__list { margin: 0; padding: 4px 12px 10px; display: grid; gap: 6px; border-top: 1px solid var(--latch-border); }\n.latch-details__row { display: flex; justify-content: space-between; gap: 12px; font-size: var(--latch-font-size-sm); }\n.latch-details__row dt { color: var(--latch-text-muted); }\n.latch-details__row dd { margin: 0; text-align: right; color: var(--latch-text); overflow-wrap: anywhere; }\n[data-latch-severity=\"high\"] { color: var(--latch-danger) !important; }\n[data-latch-severity=\"medium\"] { color: var(--latch-warning) !important; }\n\n.latch-settings-trigger { display: inline-flex; align-items: center; gap: 6px; width: auto; padding: 0 10px; border-radius: 999px; }\n.latch-settings-trigger__value { font-size: var(--latch-font-size-sm); font-weight: 600; }\n\n/* Overlays. Portalled into .latch-widget, so they inherit the tokens. */\n.latch-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.45); backdrop-filter: blur(2px); animation: latch-fade 120ms ease; z-index: 50; }\n.latch-dialog {\n position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 51;\n width: min(420px, calc(100vw - 32px)); max-height: min(640px, calc(100vh - 32px));\n display: flex; flex-direction: column; gap: 10px;\n background: var(--latch-bg); color: var(--latch-text); border: 1px solid var(--latch-border);\n border-radius: var(--latch-radius); box-shadow: var(--latch-shadow); padding: 14px 14px 8px;\n font-family: var(--latch-font); font-size: var(--latch-font-size); animation: latch-pop 140ms ease;\n}\n@keyframes latch-fade { from { opacity: 0; } }\n@keyframes latch-pop { from { opacity: 0; transform: translate(-50%, -48%) scale(0.98); } }\n.latch-dialog__head { display: flex; justify-content: space-between; align-items: center; }\n.latch-dialog__title { margin: 0; font-size: 17px; font-weight: 700; display: inline-flex; align-items: center; gap: 8px; }\n.latch-dialog__count {\n font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 999px; font-variant-numeric: tabular-nums;\n color: var(--latch-text-muted); background: var(--latch-bg-elevated); border: 1px solid var(--latch-border);\n}\n.latch-search-wrap { position: relative; display: flex; align-items: center; }\n.latch-search-wrap__icon { position: absolute; left: 14px; color: var(--latch-text-muted); pointer-events: none; }\n.latch-search-wrap__clear {\n position: absolute; right: 8px; width: 28px; height: 28px; border-radius: 999px; border: 0; cursor: pointer;\n background: var(--latch-bg-sunken); color: var(--latch-text-muted); font-size: 16px; line-height: 1;\n}\n.latch-search-wrap__clear:hover { color: var(--latch-text); }\n.latch-search {\n width: 100%; font: inherit; font-size: 16px; padding: 12px 40px; border-radius: 999px;\n border: 1px solid var(--latch-border); background: var(--latch-bg-elevated); color: var(--latch-text); outline: none;\n transition: border-color 120ms, box-shadow 120ms;\n}\n.latch-search:focus { border-color: var(--latch-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--latch-accent) 22%, transparent); }\n.latch-common { display: flex; flex-wrap: wrap; gap: 8px; padding-bottom: 4px; }\n.latch-chip--token {\n flex: 0 0 auto; display: inline-flex; align-items: center; gap: 8px; min-height: 36px; padding: 0 14px 0 6px;\n border-radius: 999px; border: 1px solid var(--latch-border); background: var(--latch-bg-elevated); color: var(--latch-text);\n font-weight: 600; transition: border-color 120ms, background 120ms;\n}\n.latch-chip--token:hover { border-color: color-mix(in srgb, var(--latch-accent) 55%, var(--latch-border)); }\n.latch-chip--token[aria-pressed=\"true\"] { border-color: var(--latch-accent); background: color-mix(in srgb, var(--latch-accent) 14%, var(--latch-bg-elevated)); }\n.latch-token-list { list-style: none; margin: 0 -14px; padding: 6px 8px 8px; overflow-y: auto; border-top: 1px solid var(--latch-border); }\n.latch-token-row {\n width: 100%; display: flex; align-items: center; gap: 12px; padding: 10px; min-height: 56px; background: transparent; border: 0;\n border-radius: 12px; font: inherit; color: var(--latch-text); cursor: pointer; text-align: left; transition: background 100ms;\n}\n.latch-token-row:hover:not(:disabled), .latch-token-row:focus-visible { background: var(--latch-bg-elevated); outline: none; }\n.latch-token-row:focus-visible { box-shadow: inset 0 0 0 2px var(--latch-accent); }\n.latch-token-row[aria-selected=\"true\"] { background: color-mix(in srgb, var(--latch-accent) 10%, transparent); }\n.latch-token-row:disabled { opacity: 0.4; cursor: not-allowed; }\n.latch-token-row--note { color: var(--latch-text-muted); font-size: var(--latch-font-size-sm); cursor: default; min-height: 0; }\n.latch-token-row--empty { justify-content: center; text-align: center; padding: 28px 16px; }\n.latch-token-row__main { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }\n.latch-token-row__symbol { font-weight: 650; font-size: 15px; display: inline-flex; align-items: center; flex-wrap: wrap; }\n.latch-token-row__name { font-size: var(--latch-font-size-sm); color: var(--latch-text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.latch-token-row__balance { display: flex; flex-direction: column; align-items: flex-end; font-variant-numeric: tabular-nums; color: var(--latch-text); font-weight: 600; }\n.latch-token-row__balance-sym { font-size: 11px; font-weight: 500; color: var(--latch-text-muted); }\n.latch-token-row__check { color: var(--latch-accent); font-weight: 700; font-size: 15px; margin-left: 4px; }\n@media (prefers-reduced-motion: reduce) {\n .latch-token-row, .latch-chip--token, .latch-search { transition: none; }\n}\n.latch-token-row--unlisted { background: color-mix(in srgb, var(--latch-warning) 8%, transparent); }\n.latch-token-row__badges { display: inline-flex; flex-wrap: wrap; gap: 4px; margin-left: 8px; vertical-align: middle; }\n.latch-badge {\n display: inline-block; padding: 1px 6px; border-radius: 999px; font-size: 11px; font-weight: 600; line-height: 16px; letter-spacing: 0.01em;\n border: 1px solid var(--latch-border); background: var(--latch-bg-elevated); color: var(--latch-text-muted); white-space: nowrap;\n}\n.latch-badge[data-latch-tone=\"info\"] { color: var(--latch-accent); border-color: color-mix(in srgb, var(--latch-accent) 35%, transparent); }\n.latch-badge[data-latch-tone=\"warn\"] { color: var(--latch-warning); border-color: color-mix(in srgb, var(--latch-warning) 45%, transparent); }\n.latch-token-group { list-style: none; }\n.latch-token-group__label {\n display: block; padding: 8px 14px 4px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;\n color: var(--latch-text-muted); border-top: 1px solid var(--latch-border);\n}\n.latch-token-group:first-child .latch-token-group__label { border-top: 0; }\n.latch-token-group__list { list-style: none; margin: 0; padding: 0; }\n\n.latch-popover {\n z-index: 51; width: min(320px, calc(100vw - 32px)); background: var(--latch-bg); color: var(--latch-text);\n border: 1px solid var(--latch-border); border-radius: var(--latch-radius); box-shadow: var(--latch-shadow); padding: 12px;\n display: flex; flex-direction: column; gap: 8px; font-family: var(--latch-font); font-size: var(--latch-font-size); animation: latch-fade 120ms ease;\n}\n.latch-popover__row { display: flex; justify-content: space-between; align-items: center; }\n.latch-popover__value { font-size: var(--latch-font-size-sm); color: var(--latch-text-muted); }\n.latch-custom-slippage { display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto; }\n.latch-custom-slippage input {\n width: 56px; font: inherit; font-size: var(--latch-font-size-sm); padding: 6px 8px; border-radius: 999px;\n border: 1px solid var(--latch-border); background: var(--latch-bg); color: var(--latch-text); text-align: right; outline: none;\n}\n.latch-custom-slippage input:focus { border-color: var(--latch-accent); }\n.latch-note { margin: 0; font-size: var(--latch-font-size-sm); color: var(--latch-text-muted); }\n.latch-note--warn { color: var(--latch-warning); }\n.latch-footer--brand { justify-content: center; color: var(--latch-text-muted); font-size: 11px; }\n.latch-footer a { color: var(--latch-accent); text-decoration: none; }\n\n/* ============================================================================\n Locks (LocksBadge, LocksPanel): the locked share and a countdown. Every\n colour is a --latch-* token above.\n ============================================================================ */\n.latch-locks { gap: 6px; }\n.latch-locks--badge { display: inline-flex; flex-direction: column; width: auto; max-width: 100%; padding: 10px 14px; box-shadow: none; border-radius: var(--latch-radius-sm); }\n.latch-locks--panel { max-width: none; }\n.latch-locks__headline { margin: 0; display: flex; align-items: baseline; gap: 8px; font-weight: 600; overflow-wrap: anywhere; }\n.latch-locks__headline--muted { color: var(--latch-text-muted); font-weight: 500; }\n.latch-locks__headline--error { color: var(--latch-danger); font-weight: 500; }\n.latch-locks__dot { flex: 0 0 auto; width: 9px; height: 9px; border-radius: 50%; background: var(--latch-text-muted); transform: translateY(-1px); }\n.latch-locks__dot--locked { background: var(--latch-success); }\n.latch-locks__dot--open { background: var(--latch-warning); }\n.latch-locks__dot--none { background: var(--latch-border-strong); }\n.latch-locks__countdown { margin: 0; font-size: var(--latch-font-size-sm); color: var(--latch-text-muted); overflow-wrap: anywhere; }\n.latch-locks__timer { font-family: var(--latch-font-mono); font-variant-numeric: tabular-nums; color: var(--latch-text); }\n.latch-locks__link { font-size: var(--latch-font-size-sm); color: var(--latch-accent); text-decoration: none; font-weight: 600; }\n.latch-locks__link:hover { text-decoration: underline; }\n.latch-locks__link:focus-visible { outline: 2px solid var(--latch-focus); outline-offset: 2px; border-radius: 4px; }\n.latch-locks__list { list-style: none; margin: 4px 0 0; padding: 0; display: grid; gap: 8px; }\n.latch-locks__item { display: grid; gap: 6px; padding: 10px 12px; border: 1px solid var(--latch-border); border-radius: var(--latch-radius-sm); background: var(--latch-bg-elevated); min-width: 0; }\n.latch-locks__item[data-status=\"withdrawn\"], .latch-locks__item[data-status=\"claimed\"] { opacity: 0.72; }\n.latch-locks__row { display: flex; justify-content: space-between; align-items: center; gap: 8px; }\n.latch-locks__status { flex: 0 0 auto; padding: 1px 8px; border: 1px solid currentColor; border-radius: 999px; font-size: 11px; font-weight: 650; white-space: nowrap; }\n.latch-locks__status--locked, .latch-locks__status--cliff, .latch-locks__status--vesting { color: var(--latch-success); }\n.latch-locks__status--unlockable, .latch-locks__status--vested { color: var(--latch-warning); }\n.latch-locks__status--withdrawn, .latch-locks__status--claimed { color: var(--latch-text-muted); }\n.latch-locks__status--permanent { color: var(--latch-accent, var(--latch-success)); }\n.latch-locks__facts { margin: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 6px 12px; font-size: var(--latch-font-size-sm); }\n.latch-locks__facts div { min-width: 0; }\n.latch-locks__facts dt { color: var(--latch-text-muted); }\n.latch-locks__facts dd { margin: 0; overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }\n\n/* Lock charts (lockCharts.tsx): a locked-share bar that grows in, and a release\n schedule that draws on. Motion off under prefers-reduced-motion. */\n.latch-sharebar { display: grid; gap: 6px; min-width: 0; }\n.latch-locks--badge .latch-sharebar { min-width: 180px; }\n.latch-sharebar__track { height: 8px; border-radius: 999px; overflow: hidden; background: var(--latch-bg-sunken); }\n.latch-sharebar__grow { display: flex; height: 100%; transform-origin: left center; animation: latch-grow 0.9s cubic-bezier(0.22, 1, 0.36, 1) both; }\n.latch-sharebar__part { flex: 0 0 auto; height: 100%; }\n.latch-sharebar__part + .latch-sharebar__part { box-shadow: inset 2px 0 0 var(--latch-bg); }\n.latch-sharebar__part--permanent, .latch-sharebar__part--lp { background: var(--latch-success); }\n.latch-sharebar__part--time, .latch-sharebar__part--unvested { background: var(--latch-accent); }\n.latch-sharebar__part--vested { background: var(--latch-accent); opacity: 0.45; }\n.latch-sharebar__key { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 2px 12px; font-size: 11px; color: var(--latch-text-muted); font-variant-numeric: tabular-nums; }\n.latch-sharebar__key li { display: inline-flex; align-items: center; gap: 5px; }\n.latch-sharebar__sw { display: inline-block; width: 8px; height: 8px; border-radius: 2px; }\n.latch-spark { margin: 2px 0 0; display: grid; gap: 4px; }\n.latch-spark__svg { display: block; width: 100%; height: 44px; overflow: visible; cursor: crosshair; border-radius: 4px; }\n.latch-spark__svg:focus-visible { outline: 2px solid var(--latch-focus); outline-offset: 2px; }\n.latch-spark__base { stroke: var(--latch-border); stroke-width: 1; }\n.latch-spark__line { stroke: var(--latch-accent); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; stroke-dasharray: 2000; animation: latch-draw 1.3s cubic-bezier(0.22, 1, 0.36, 1) both; }\n.latch-spark__now { stroke: var(--latch-text-muted); stroke-width: 1; stroke-dasharray: 3 3; }\n.latch-spark__cross { stroke: var(--latch-accent); stroke-width: 1; opacity: 0.6; }\n.latch-spark__cap { font-size: 11px; color: var(--latch-text-muted); overflow-wrap: anywhere; }\n.latch-locks .latch-progress-fill, .latch-locker .latch-progress-fill { transform-origin: left center; animation: latch-grow 0.9s cubic-bezier(0.22, 1, 0.36, 1) both; }\n@keyframes latch-grow { from { transform: scaleX(0.02); } to { transform: scaleX(1); } }\n@keyframes latch-draw { from { stroke-dashoffset: 2000; } to { stroke-dashoffset: 0; } }\n@media (prefers-reduced-motion: reduce) {\n .latch-sharebar__grow, .latch-spark__line, .latch-locks .latch-progress-fill, .latch-locker .latch-progress-fill { animation: none; transform: none; stroke-dashoffset: 0; }\n}\n\n/* ============================================================================\n Token trust (TokenTrustPanel): labelled rows, each with its source.\n ============================================================================ */\n.latch-trust { max-width: none; gap: 8px; }\n.latch-trust__list { margin: 0; display: grid; gap: 0; }\n.latch-trust__row { display: grid; grid-template-columns: minmax(120px, 180px) 1fr; gap: 4px 14px; padding: 9px 0; border-top: 1px solid var(--latch-border); min-width: 0; }\n.latch-trust__row:first-child { border-top: 0; }\n@media (max-width: 520px) { .latch-trust__row { grid-template-columns: 1fr; } }\n.latch-trust__label { color: var(--latch-text-muted); font-size: var(--latch-font-size-sm); font-weight: 600; }\n.latch-trust__value { margin: 0; min-width: 0; display: grid; gap: 3px; }\n.latch-trust__headline { margin: 0; display: flex; align-items: baseline; gap: 8px; font-weight: 600; overflow-wrap: anywhere; }\n.latch-trust__detail { margin: 0; font-size: var(--latch-font-size-sm); color: var(--latch-text-muted); overflow-wrap: anywhere; }\n.latch-trust__source { margin: 0; display: flex; flex-wrap: wrap; gap: 2px 10px; font-family: var(--latch-font-mono); font-size: 11px; color: var(--latch-text-muted); overflow-wrap: anywhere; }\n.latch-trust__source a { color: inherit; text-decoration: underline; text-decoration-color: var(--latch-border-strong); }\n.latch-trust__source a:hover { color: var(--latch-accent); }\n.latch-trust__dot { flex: 0 0 auto; width: 9px; height: 9px; border-radius: 50%; background: var(--latch-border-strong); transform: translateY(-1px); }\n.latch-trust__dot--locked { background: var(--latch-success); }\n.latch-trust__dot--neutral { background: var(--latch-text-muted); }\n.latch-trust__dot--caution { background: var(--latch-warning); }\n.latch-trust__dot--flagged, .latch-trust__dot--error { background: var(--latch-danger); }\n.latch-trust__row[data-tone=\"flagged\"] .latch-trust__headline, .latch-trust__row[data-tone=\"error\"] .latch-trust__headline { color: var(--latch-danger); }\n.latch-trust__subhead { margin: 6px 0 0; font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--latch-text-muted); }\n.latch-trust__pool { display: grid; gap: 4px; padding: 10px 12px; border: 1px solid var(--latch-border); border-radius: var(--latch-radius-sm); background: var(--latch-bg-elevated); min-width: 0; }\n.latch-trust__pool-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 12px; }\n.latch-trust__pool-head code { font-family: var(--latch-font-mono); font-size: 11px; color: var(--latch-text-muted); }\n.latch-trust__found { margin: 0; font-size: 11px; color: var(--latch-text-muted); }\n.latch-trust__link { font-size: var(--latch-font-size-sm); color: var(--latch-accent); text-decoration: none; font-weight: 600; margin-left: auto; }\n.latch-trust__link:hover { text-decoration: underline; }\n.latch-trust__state { margin: 0; color: var(--latch-text-muted); }\n.latch-trust__state--error { color: var(--latch-danger); }\n.latch-trust__retry { font: inherit; background: none; border: 0; padding: 0; color: var(--latch-accent); cursor: pointer; text-decoration: underline; }\n\n/* ============================================================================\n Locker (LockerWidget): lock a position, lock or vest tokens, manage locks.\n ============================================================================ */\n.latch-locker { gap: 10px; }\n.latch-locker__tabs { display: flex; gap: 4px; padding: 3px; border-radius: 999px; background: var(--latch-bg-sunken); }\n.latch-locker__tab { flex: 1; font: inherit; font-size: var(--latch-font-size-sm); font-weight: 600; padding: 7px 10px; border: 0; border-radius: 999px; background: transparent; color: var(--latch-text-muted); cursor: pointer; }\n.latch-locker__tab[aria-selected=\"true\"] { background: var(--latch-bg); color: var(--latch-text); box-shadow: var(--latch-shadow); }\n.latch-locker__field { display: grid; gap: 4px; font-size: var(--latch-font-size-sm); }\n.latch-locker__field > span { color: var(--latch-text-muted); font-weight: 600; }\n.latch-locker__input { width: 100%; font: inherit; padding: 10px 12px; border-radius: var(--latch-radius-sm); border: 1px solid var(--latch-border); background: var(--latch-bg-elevated); color: var(--latch-text); outline: none; min-width: 0; }\n.latch-locker__input:focus { border-color: var(--latch-accent); }\n.latch-locker__row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }\n@media (max-width: 420px) { .latch-locker__row2 { grid-template-columns: 1fr; } }\n.latch-locker__issues { margin: 0; padding-left: 18px; color: var(--latch-danger); font-size: var(--latch-font-size-sm); }\n.latch-locker__fee { margin: 0; padding: 8px 10px; border-radius: var(--latch-radius-sm); background: var(--latch-bg-elevated); font-size: var(--latch-font-size-sm); display: grid; gap: 2px; }\n.latch-locker__fee strong { font-variant-numeric: tabular-nums; }\n.latch-locker__steps { margin: 0; padding-left: 18px; display: grid; gap: 2px; font-size: var(--latch-font-size-sm); color: var(--latch-text-muted); }\n.latch-locker__steps li[data-state=\"done\"] { color: var(--latch-success); }\n.latch-locker__steps li[data-state=\"active\"] { color: var(--latch-text); font-weight: 600; }\n.latch-locker__status { margin: 0; font-size: var(--latch-font-size-sm); overflow-wrap: anywhere; }\n.latch-locker__status[data-kind=\"error\"] { color: var(--latch-danger); }\n.latch-locker__status[data-kind=\"success\"] { color: var(--latch-success); }\n.latch-locker__actions { display: flex; flex-wrap: wrap; gap: 6px; }\n.latch-locker__btn { font: inherit; font-size: var(--latch-font-size-sm); font-weight: 600; padding: 6px 12px; border-radius: 999px; border: 1px solid var(--latch-border-strong); background: var(--latch-bg); color: var(--latch-text); cursor: pointer; }\n.latch-locker__btn:hover:not(:disabled) { border-color: var(--latch-accent); color: var(--latch-accent); }\n.latch-locker__btn:disabled { opacity: 0.5; cursor: not-allowed; }\n.latch-locker__choice { display: flex; gap: 12px; flex-wrap: wrap; font-size: var(--latch-font-size-sm); }\n.latch-locker__choice label { display: inline-flex; gap: 6px; align-items: center; cursor: pointer; }\n\n/* ============================================================================\n The Latch mark (brand kit masters/latch-mark.svg, loaded as shipped, never\n redrawn). 24 px wide is the kit's minimum; the margin is its clear space\n (the height of the hook's eye at this size).\n ============================================================================ */\n.latch-brand { display: flex; align-items: center; gap: 6px; margin: 0; font-size: 11px; color: var(--latch-text-muted); }\n.latch-brand__mark { flex: 0 0 auto; width: 24px; height: auto; margin: 4px; display: block; }\n.latch-brand__spacer { flex: 1; }\n\n\n/* ============================================================================\n Touch (pointer: coarse): 44px targets, and inputs at 16px so iOS Safari does\n not zoom the host page when a field is focused.\n ============================================================================ */\n@media (pointer: coarse) {\n .latch-widget input:not([type=\"checkbox\"]):not([type=\"radio\"]):not([type=\"range\"]),\n .latch-widget select,\n .latch-widget textarea,\n .latch-locker input:not([type=\"checkbox\"]):not([type=\"radio\"]):not([type=\"range\"]),\n .latch-locker select { font-size: max(16px, 1em); }\n .latch-portion, .latch-chip, .latch-select, .latch-settings-trigger, .latch-icon-button,\n .latch-locker__tab, .latch-locker__input, .latch-locker__btn, .latch-button, .latch-token-button { min-height: 44px; }\n .latch-locker__choice label { min-height: 44px; }\n}\n";
24
+ /**
25
+ * Injects the stylesheet into the document once.
26
+ *
27
+ * React hosts call this automatically the first time a styled widget mounts.
28
+ * Hosts that bundle CSS themselves can import `@latchprotocol/widgets/styles.css`
29
+ * instead and never call it.
30
+ */
31
+ export declare function injectWidgetStyles(target?: Document | ShadowRoot | null): void;
32
+ //# sourceMappingURL=css.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"css.d.ts","sourceRoot":"","sources":["../../src/styles/css.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,sCAAsC;AACtC,eAAO,MAAM,UAAU,8tzCA+3BtB,CAAC;AAIF;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,GAAE,QAAQ,GAAG,UAAU,GAAG,IAAkC,GAAG,IAAI,CAY3G"}