@gengage/assistant-fe 0.3.18 → 0.3.20

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 (309) hide show
  1. package/dist/chat/api.d.ts +3 -1
  2. package/dist/chat/assistant-mode.d.ts +19 -0
  3. package/dist/chat/attachment-utils.d.ts +0 -1
  4. package/dist/chat/catalog.d.ts +32 -1
  5. package/dist/chat/chat-presentation-state.d.ts +0 -1
  6. package/dist/chat/components/AIGroupingCards.d.ts +0 -1
  7. package/dist/chat/components/AISuggestedSearchCards.d.ts +0 -1
  8. package/dist/chat/components/AITopPicks.d.ts +0 -1
  9. package/dist/chat/components/BeautyPhotoStep.d.ts +16 -0
  10. package/dist/chat/components/CategoriesContainer.d.ts +0 -1
  11. package/dist/chat/components/ChatDrawer.d.ts +22 -2
  12. package/dist/chat/components/ChoicePrompter.d.ts +0 -1
  13. package/dist/chat/components/ComparisonTable.d.ts +0 -1
  14. package/dist/chat/components/ConsultingStylePicker.d.ts +25 -0
  15. package/dist/chat/components/FloatingComparisonButton.d.ts +0 -1
  16. package/dist/chat/components/GroundingReviewCard.d.ts +0 -1
  17. package/dist/chat/components/HandoffNotice.d.ts +0 -1
  18. package/dist/chat/components/KvkkBanner.d.ts +0 -1
  19. package/dist/chat/components/Launcher.d.ts +0 -1
  20. package/dist/chat/components/PanelTopBar.d.ts +0 -1
  21. package/dist/chat/components/PhotoAnalysisCard.d.ts +22 -0
  22. package/dist/chat/components/ProductSummaryCard.d.ts +0 -1
  23. package/dist/chat/components/ProsAndCons.d.ts +0 -1
  24. package/dist/chat/components/ReviewHighlights.d.ts +0 -1
  25. package/dist/chat/components/ThumbnailsColumn.d.ts +0 -1
  26. package/dist/chat/components/actionClassifier.d.ts +0 -1
  27. package/dist/chat/components/product-price-layout.d.ts +0 -1
  28. package/dist/chat/components/productMentionLinker.d.ts +0 -1
  29. package/dist/chat/components/renderUISpec.d.ts +2 -2
  30. package/dist/chat/components/typewriter.d.ts +0 -1
  31. package/dist/chat/extendedModeManager.d.ts +0 -1
  32. package/dist/chat/features/beauty-consulting/consulting-grid.d.ts +18 -0
  33. package/dist/chat/features/beauty-consulting/drawer-extensions.d.ts +20 -0
  34. package/dist/chat/features/beauty-consulting/mode-controller.d.ts +53 -0
  35. package/dist/chat/features/beauty-consulting/registry.d.ts +3 -0
  36. package/dist/chat/features/beauty-consulting/stream-handler.d.ts +38 -0
  37. package/dist/chat/index.d.ts +11 -1
  38. package/dist/chat/kvkk.d.ts +0 -1
  39. package/dist/chat/locales/en.d.ts +0 -1
  40. package/dist/chat/locales/index.d.ts +0 -1
  41. package/dist/chat/locales/tr.d.ts +0 -1
  42. package/dist/chat/panel-manager.d.ts +0 -1
  43. package/dist/chat/session-persistence.d.ts +0 -1
  44. package/dist/chat/stream-error-display.d.ts +0 -1
  45. package/dist/chat/types.d.ts +27 -2
  46. package/dist/chat/utils/chat-presentation-debug.d.ts +0 -1
  47. package/dist/chat/utils/get-chat-scroll-element.d.ts +0 -1
  48. package/dist/chat/utils/ui.d.ts +0 -1
  49. package/dist/chat-CuPO9t-Z.cjs +93 -0
  50. package/dist/chat-yAf9OADM.js +7111 -0
  51. package/dist/chat.cjs +1 -1
  52. package/dist/chat.iife.js +48 -49
  53. package/dist/chat.js +13 -2
  54. package/dist/common/action-router.d.ts +0 -1
  55. package/dist/common/activity-tracker.d.ts +0 -1
  56. package/dist/common/analytics-events.d.ts +0 -1
  57. package/dist/common/analytics.d.ts +0 -1
  58. package/dist/common/api-paths.d.ts +0 -1
  59. package/dist/common/client.d.ts +0 -1
  60. package/dist/common/communication-bridge.d.ts +0 -1
  61. package/dist/common/config-schema.d.ts +0 -1
  62. package/dist/common/connection-warning.d.ts +0 -1
  63. package/dist/common/consulting-sources.d.ts +9 -0
  64. package/dist/common/context.d.ts +0 -1
  65. package/dist/common/customization-factories.d.ts +0 -1
  66. package/dist/common/debug.d.ts +0 -1
  67. package/dist/common/events.d.ts +0 -1
  68. package/dist/common/ga-datalayer.d.ts +0 -1
  69. package/dist/common/global-error-toast.d.ts +0 -1
  70. package/dist/common/index.d.ts +0 -1
  71. package/dist/common/indexed-db.d.ts +0 -1
  72. package/dist/common/native-webview.d.ts +0 -1
  73. package/dist/common/overlay.d.ts +1 -1
  74. package/dist/common/page-detect.d.ts +0 -1
  75. package/dist/common/pill-launcher.d.ts +0 -1
  76. package/dist/common/preflight.d.ts +0 -1
  77. package/dist/common/price-formatter.d.ts +0 -1
  78. package/dist/common/product-utils.d.ts +0 -1
  79. package/dist/common/protocol-adapter.d.ts +0 -1
  80. package/dist/common/renderer/dom.d.ts +0 -1
  81. package/dist/common/renderer/index.d.ts +0 -1
  82. package/dist/common/renderer/overrides.d.ts +0 -1
  83. package/dist/common/renderer/registry.d.ts +0 -1
  84. package/dist/common/renderer/types.d.ts +0 -1
  85. package/dist/common/safe-html.d.ts +0 -1
  86. package/dist/common/skeleton.d.ts +0 -1
  87. package/dist/common/streaming.d.ts +0 -1
  88. package/dist/common/suggested-search-keywords.d.ts +0 -1
  89. package/dist/common/theme-utils.d.ts +0 -1
  90. package/dist/common/tts-player.d.ts +0 -1
  91. package/dist/common/types.d.ts +4 -1
  92. package/dist/common/ui-theme.d.ts +0 -1
  93. package/dist/common/uuidv7.d.ts +0 -1
  94. package/dist/common/voice-input.d.ts +0 -1
  95. package/dist/common/widget-base.d.ts +0 -1
  96. package/dist/common-BzegEdJs.cjs +1 -0
  97. package/dist/common-C8NRcP7O.js +367 -0
  98. package/dist/common.cjs +1 -1
  99. package/dist/common.js +86 -7
  100. package/dist/connection-warning-CL1CReZo.cjs +1 -0
  101. package/dist/connection-warning-CVZzlGJt.js +95 -0
  102. package/dist/ga-datalayer-CgI6Kb_i.js +1044 -0
  103. package/dist/ga-datalayer-DkfsAHf9.cjs +47 -0
  104. package/dist/index.cjs +1 -1
  105. package/dist/index.d.ts +0 -1
  106. package/dist/index.js +76 -10
  107. package/dist/native/index.d.ts +0 -1
  108. package/dist/native-webview-B8vOoxns.js +571 -0
  109. package/dist/native-webview-D2lDXTh3.cjs +1 -0
  110. package/dist/native.cjs +1 -1
  111. package/dist/native.iife.js +51 -52
  112. package/dist/native.js +8 -2
  113. package/dist/product-utils-Bm3aiAlJ.js +46 -0
  114. package/dist/product-utils-DLXQQJbh.cjs +1 -0
  115. package/dist/qna/api.d.ts +0 -1
  116. package/dist/qna/catalog.d.ts +0 -1
  117. package/dist/qna/components/ButtonRow.d.ts +0 -1
  118. package/dist/qna/components/TextInput.d.ts +0 -1
  119. package/dist/qna/components/renderUISpec.d.ts +0 -1
  120. package/dist/qna/index.d.ts +0 -1
  121. package/dist/qna/locales/en.d.ts +0 -1
  122. package/dist/qna/locales/index.d.ts +0 -1
  123. package/dist/qna/locales/tr.d.ts +0 -1
  124. package/dist/qna/normalize-ui-specs.d.ts +0 -1
  125. package/dist/qna/types.d.ts +0 -1
  126. package/dist/qna-KizG_W1J.cjs +1 -0
  127. package/dist/qna-YSaIDxjs.js +663 -0
  128. package/dist/qna.cjs +1 -1
  129. package/dist/qna.iife.js +35 -36
  130. package/dist/qna.js +9 -2
  131. package/dist/schemas-B6RjqKrg.cjs +40 -0
  132. package/dist/schemas-PkedCk_-.js +4047 -0
  133. package/dist/simbut/index.d.ts +0 -1
  134. package/dist/simbut/locales.d.ts +0 -1
  135. package/dist/simbut/types.d.ts +0 -1
  136. package/dist/simbut-B2jXtckF.cjs +1 -0
  137. package/dist/simbut-BI054QK0.js +71 -0
  138. package/dist/simbut.cjs +1 -1
  139. package/dist/simbut.iife.js +15 -16
  140. package/dist/simbut.js +5 -2
  141. package/dist/simrel/api.d.ts +0 -1
  142. package/dist/simrel/catalog.d.ts +0 -1
  143. package/dist/simrel/components/GroupTabs.d.ts +0 -1
  144. package/dist/simrel/components/ProductCard.d.ts +0 -1
  145. package/dist/simrel/components/ProductGrid.d.ts +0 -1
  146. package/dist/simrel/components/renderUISpec.d.ts +0 -1
  147. package/dist/simrel/index.d.ts +0 -1
  148. package/dist/simrel/locales/en.d.ts +0 -1
  149. package/dist/simrel/locales/index.d.ts +0 -1
  150. package/dist/simrel/locales/tr.d.ts +0 -1
  151. package/dist/simrel/types.d.ts +0 -1
  152. package/dist/simrel-C-DKpfVB.js +694 -0
  153. package/dist/simrel-vL0woDkE.cjs +1 -0
  154. package/dist/simrel.cjs +1 -1
  155. package/dist/simrel.iife.js +35 -36
  156. package/dist/simrel.js +9 -2
  157. package/package.json +1 -1
  158. package/dist/chat/api.d.ts.map +0 -1
  159. package/dist/chat/attachment-utils.d.ts.map +0 -1
  160. package/dist/chat/catalog.d.ts.map +0 -1
  161. package/dist/chat/chat-presentation-state.d.ts.map +0 -1
  162. package/dist/chat/components/AIGroupingCards.d.ts.map +0 -1
  163. package/dist/chat/components/AISuggestedSearchCards.d.ts.map +0 -1
  164. package/dist/chat/components/AITopPicks.d.ts.map +0 -1
  165. package/dist/chat/components/CategoriesContainer.d.ts.map +0 -1
  166. package/dist/chat/components/ChatDrawer.d.ts.map +0 -1
  167. package/dist/chat/components/ChoicePrompter.d.ts.map +0 -1
  168. package/dist/chat/components/ComparisonTable.d.ts.map +0 -1
  169. package/dist/chat/components/FloatingComparisonButton.d.ts.map +0 -1
  170. package/dist/chat/components/GroundingReviewCard.d.ts.map +0 -1
  171. package/dist/chat/components/HandoffNotice.d.ts.map +0 -1
  172. package/dist/chat/components/KvkkBanner.d.ts.map +0 -1
  173. package/dist/chat/components/Launcher.d.ts.map +0 -1
  174. package/dist/chat/components/PanelTopBar.d.ts.map +0 -1
  175. package/dist/chat/components/ProductSummaryCard.d.ts.map +0 -1
  176. package/dist/chat/components/ProsAndCons.d.ts.map +0 -1
  177. package/dist/chat/components/ReviewHighlights.d.ts.map +0 -1
  178. package/dist/chat/components/ThumbnailsColumn.d.ts.map +0 -1
  179. package/dist/chat/components/actionClassifier.d.ts.map +0 -1
  180. package/dist/chat/components/product-price-layout.d.ts.map +0 -1
  181. package/dist/chat/components/productMentionLinker.d.ts.map +0 -1
  182. package/dist/chat/components/renderUISpec.d.ts.map +0 -1
  183. package/dist/chat/components/typewriter.d.ts.map +0 -1
  184. package/dist/chat/extendedModeManager.d.ts.map +0 -1
  185. package/dist/chat/index.d.ts.map +0 -1
  186. package/dist/chat/kvkk.d.ts.map +0 -1
  187. package/dist/chat/locales/en.d.ts.map +0 -1
  188. package/dist/chat/locales/index.d.ts.map +0 -1
  189. package/dist/chat/locales/tr.d.ts.map +0 -1
  190. package/dist/chat/panel-manager.d.ts.map +0 -1
  191. package/dist/chat/session-persistence.d.ts.map +0 -1
  192. package/dist/chat/stream-error-display.d.ts.map +0 -1
  193. package/dist/chat/types.d.ts.map +0 -1
  194. package/dist/chat/utils/chat-presentation-debug.d.ts.map +0 -1
  195. package/dist/chat/utils/get-chat-scroll-element.d.ts.map +0 -1
  196. package/dist/chat/utils/ui.d.ts.map +0 -1
  197. package/dist/chat-Ck0cC4R_.cjs +0 -94
  198. package/dist/chat-Ck0cC4R_.cjs.map +0 -1
  199. package/dist/chat-CmKpcbQS.js +0 -6574
  200. package/dist/chat-CmKpcbQS.js.map +0 -1
  201. package/dist/chat.iife.js.map +0 -1
  202. package/dist/common/action-router.d.ts.map +0 -1
  203. package/dist/common/activity-tracker.d.ts.map +0 -1
  204. package/dist/common/analytics-events.d.ts.map +0 -1
  205. package/dist/common/analytics.d.ts.map +0 -1
  206. package/dist/common/api-paths.d.ts.map +0 -1
  207. package/dist/common/client.d.ts.map +0 -1
  208. package/dist/common/communication-bridge.d.ts.map +0 -1
  209. package/dist/common/config-schema.d.ts.map +0 -1
  210. package/dist/common/connection-warning.d.ts.map +0 -1
  211. package/dist/common/context.d.ts.map +0 -1
  212. package/dist/common/customization-factories.d.ts.map +0 -1
  213. package/dist/common/debug.d.ts.map +0 -1
  214. package/dist/common/events.d.ts.map +0 -1
  215. package/dist/common/ga-datalayer.d.ts.map +0 -1
  216. package/dist/common/global-error-toast.d.ts.map +0 -1
  217. package/dist/common/index.d.ts.map +0 -1
  218. package/dist/common/indexed-db.d.ts.map +0 -1
  219. package/dist/common/native-webview.d.ts.map +0 -1
  220. package/dist/common/overlay.d.ts.map +0 -1
  221. package/dist/common/page-detect.d.ts.map +0 -1
  222. package/dist/common/pill-launcher.d.ts.map +0 -1
  223. package/dist/common/preflight.d.ts.map +0 -1
  224. package/dist/common/price-formatter.d.ts.map +0 -1
  225. package/dist/common/product-utils.d.ts.map +0 -1
  226. package/dist/common/protocol-adapter.d.ts.map +0 -1
  227. package/dist/common/renderer/dom.d.ts.map +0 -1
  228. package/dist/common/renderer/index.d.ts.map +0 -1
  229. package/dist/common/renderer/overrides.d.ts.map +0 -1
  230. package/dist/common/renderer/registry.d.ts.map +0 -1
  231. package/dist/common/renderer/types.d.ts.map +0 -1
  232. package/dist/common/safe-html.d.ts.map +0 -1
  233. package/dist/common/skeleton.d.ts.map +0 -1
  234. package/dist/common/streaming.d.ts.map +0 -1
  235. package/dist/common/suggested-search-keywords.d.ts.map +0 -1
  236. package/dist/common/theme-utils.d.ts.map +0 -1
  237. package/dist/common/tts-player.d.ts.map +0 -1
  238. package/dist/common/types.d.ts.map +0 -1
  239. package/dist/common/ui-theme.d.ts.map +0 -1
  240. package/dist/common/uuidv7.d.ts.map +0 -1
  241. package/dist/common/voice-input.d.ts.map +0 -1
  242. package/dist/common/widget-base.d.ts.map +0 -1
  243. package/dist/common-C0CDlovf.cjs +0 -2
  244. package/dist/common-C0CDlovf.cjs.map +0 -1
  245. package/dist/common-D7J-qASM.js +0 -357
  246. package/dist/common-D7J-qASM.js.map +0 -1
  247. package/dist/connection-warning-BF8r3_5W.js +0 -96
  248. package/dist/connection-warning-BF8r3_5W.js.map +0 -1
  249. package/dist/connection-warning-Dlirb_Pi.cjs +0 -2
  250. package/dist/connection-warning-Dlirb_Pi.cjs.map +0 -1
  251. package/dist/ga-datalayer-DSVuycfp.cjs +0 -48
  252. package/dist/ga-datalayer-DSVuycfp.cjs.map +0 -1
  253. package/dist/ga-datalayer-DygUbUWr.js +0 -915
  254. package/dist/ga-datalayer-DygUbUWr.js.map +0 -1
  255. package/dist/index.d.ts.map +0 -1
  256. package/dist/native/index.d.ts.map +0 -1
  257. package/dist/native-webview-4YU7k1qK.js +0 -547
  258. package/dist/native-webview-4YU7k1qK.js.map +0 -1
  259. package/dist/native-webview-ifBEffwH.cjs +0 -2
  260. package/dist/native-webview-ifBEffwH.cjs.map +0 -1
  261. package/dist/native.iife.js.map +0 -1
  262. package/dist/product-utils-BEnINMdx.cjs +0 -2
  263. package/dist/product-utils-BEnINMdx.cjs.map +0 -1
  264. package/dist/product-utils-BUalkWz3.js +0 -45
  265. package/dist/product-utils-BUalkWz3.js.map +0 -1
  266. package/dist/qna/api.d.ts.map +0 -1
  267. package/dist/qna/catalog.d.ts.map +0 -1
  268. package/dist/qna/components/ButtonRow.d.ts.map +0 -1
  269. package/dist/qna/components/TextInput.d.ts.map +0 -1
  270. package/dist/qna/components/renderUISpec.d.ts.map +0 -1
  271. package/dist/qna/index.d.ts.map +0 -1
  272. package/dist/qna/locales/en.d.ts.map +0 -1
  273. package/dist/qna/locales/index.d.ts.map +0 -1
  274. package/dist/qna/locales/tr.d.ts.map +0 -1
  275. package/dist/qna/normalize-ui-specs.d.ts.map +0 -1
  276. package/dist/qna/types.d.ts.map +0 -1
  277. package/dist/qna-BUsDAFdn.cjs +0 -2
  278. package/dist/qna-BUsDAFdn.cjs.map +0 -1
  279. package/dist/qna-DrlUFVgh.js +0 -672
  280. package/dist/qna-DrlUFVgh.js.map +0 -1
  281. package/dist/qna.iife.js.map +0 -1
  282. package/dist/schemas-BneJIS3a.cjs +0 -41
  283. package/dist/schemas-BneJIS3a.cjs.map +0 -1
  284. package/dist/schemas-DwX3rpp6.js +0 -3871
  285. package/dist/schemas-DwX3rpp6.js.map +0 -1
  286. package/dist/simbut/index.d.ts.map +0 -1
  287. package/dist/simbut/locales.d.ts.map +0 -1
  288. package/dist/simbut/types.d.ts.map +0 -1
  289. package/dist/simbut-DG48EcoU.js +0 -68
  290. package/dist/simbut-DG48EcoU.js.map +0 -1
  291. package/dist/simbut-DHZFpGIA.cjs +0 -2
  292. package/dist/simbut-DHZFpGIA.cjs.map +0 -1
  293. package/dist/simbut.iife.js.map +0 -1
  294. package/dist/simrel/api.d.ts.map +0 -1
  295. package/dist/simrel/catalog.d.ts.map +0 -1
  296. package/dist/simrel/components/GroupTabs.d.ts.map +0 -1
  297. package/dist/simrel/components/ProductCard.d.ts.map +0 -1
  298. package/dist/simrel/components/ProductGrid.d.ts.map +0 -1
  299. package/dist/simrel/components/renderUISpec.d.ts.map +0 -1
  300. package/dist/simrel/index.d.ts.map +0 -1
  301. package/dist/simrel/locales/en.d.ts.map +0 -1
  302. package/dist/simrel/locales/index.d.ts.map +0 -1
  303. package/dist/simrel/locales/tr.d.ts.map +0 -1
  304. package/dist/simrel/types.d.ts.map +0 -1
  305. package/dist/simrel-B6PD6vfG.js +0 -699
  306. package/dist/simrel-B6PD6vfG.js.map +0 -1
  307. package/dist/simrel-BIp8IAYj.cjs +0 -2
  308. package/dist/simrel-BIp8IAYj.cjs.map +0 -1
  309. package/dist/simrel.iife.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"chat.iife.js","names":["initializer","util.jsonStringifyReplacer","core.$ZodAsyncError","util.finalizeIssue","core.config","parse","errors.$ZodRealError","parseAsync","errors.$ZodError","safeParse","safeParseAsync","encode","decode","encodeAsync","decodeAsync","safeEncode","safeDecode","safeEncodeAsync","safeDecodeAsync","duration","_emoji","date","time","datetime","string","number","boolean","util.floatSafeRemainder","util.NUMBER_FORMAT_RANGES","regexes.integer","util.nullish","util.getLengthableOrigin","regexes.lowercase","regexes.uppercase","util.escapeRegex","util.aborted","core.$ZodAsyncError","safeParse","safeParseAsync","regexes.string","regexes.guid","regexes.uuid","regexes.email","regexes.emoji","regexes.nanoid","regexes.cuid","regexes.cuid2","regexes.ulid","regexes.xid","regexes.ksuid","regexes.datetime","regexes.date","regexes.time","regexes.duration","regexes.ipv4","regexes.ipv6","regexes.cidrv4","regexes.cidrv6","regexes.base64","regexes.base64url","regexes.e164","regexes.number","regexes.boolean","util.prefixIssues","util.optionalKeys","util.cached","isObject","util.isObject","util.esc","allowsEval","util.allowsEval","util.finalizeIssue","core.config","util.cleanRegex","util.isPlainObject","util.getEnumValues","util.escapeRegex","core.$ZodEncodeError","util.issue","util.normalizeParams","checks.$ZodCheckLessThan","checks.$ZodCheckGreaterThan","checks.$ZodCheckMultipleOf","checks.$ZodCheckMaxLength","checks.$ZodCheckMinLength","checks.$ZodCheckLengthEquals","checks.$ZodCheckRegex","checks.$ZodCheckLowerCase","checks.$ZodCheckUpperCase","checks.$ZodCheckIncludes","checks.$ZodCheckStartsWith","checks.$ZodCheckEndsWith","checks.$ZodCheckOverwrite","util.slugify","issue","util.issue","checks.$ZodCheck","describe","meta","process","process","core._isoDateTime","core._isoDate","core._isoTime","core._isoDuration","core.formatError","core.flattenError","util.jsonStringifyReplacer","core.$constructor","util.mergeDefs","core.clone","parse.parse","parse.safeParse","parse.parseAsync","parse.safeParseAsync","parse.encode","parse.decode","parse.encodeAsync","parse.decodeAsync","parse.safeEncode","parse.safeDecode","parse.safeEncodeAsync","parse.safeDecodeAsync","checks.overwrite","processors.stringProcessor","checks.regex","checks.includes","checks.startsWith","checks.endsWith","checks.minLength","checks.maxLength","checks.length","checks.lowercase","checks.uppercase","checks.trim","checks.normalize","checks.toLowerCase","checks.toUpperCase","checks.slugify","core._email","core._url","core._jwt","core._emoji","core._guid","core._uuid","core._uuidv4","core._uuidv6","core._uuidv7","core._nanoid","core._cuid","core._cuid2","core._ulid","core._base64","core._base64url","core._xid","core._ksuid","core._ipv4","core._ipv6","core._cidrv4","core._cidrv6","core._e164","iso.datetime","iso.date","iso.time","iso.duration","core._string","processors.numberProcessor","checks.gt","checks.gte","checks.lt","checks.lte","checks.multipleOf","core._number","core._int","processors.booleanProcessor","core._boolean","processors.unknownProcessor","core._unknown","processors.neverProcessor","core._never","processors.arrayProcessor","core._array","processors.objectProcessor","util.extend","util.safeExtend","util.merge","util.pick","util.omit","util.partial","util.required","util.normalizeParams","processors.unionProcessor","processors.intersectionProcessor","processors.recordProcessor","processors.enumProcessor","processors.transformProcessor","core.$ZodEncodeError","issue","util.issue","processors.optionalProcessor","processors.nullableProcessor","processors.defaultProcessor","util.shallowClone","processors.prefaultProcessor","processors.nonoptionalProcessor","processors.catchProcessor","processors.pipeProcessor","processors.readonlyProcessor","processors.customProcessor","core._refine","core._superRefine","core.describe","core.meta"],"sources":["../src/common/renderer/dom.ts","../src/common/renderer/registry.ts","../src/common/analytics-events.ts","../src/common/safe-html.ts","../src/common/events.ts","../src/common/context.ts","../src/common/ui-theme.ts","../src/common/theme-utils.ts","../src/common/global-error-toast.ts","../src/common/debug.ts","../src/common/widget-base.ts","../src/common/uuidv7.ts","../src/common/communication-bridge.ts","../src/common/action-router.ts","../src/chat/attachment-utils.ts","../src/common/streaming.ts","../src/common/suggested-search-keywords.ts","../src/common/protocol-adapter.ts","../src/common/api-paths.ts","../src/chat/api.ts","../src/chat/utils/chat-presentation-debug.ts","../src/chat/utils/get-chat-scroll-element.ts","../src/chat/locales/tr.ts","../src/chat/locales/en.ts","../src/chat/locales/index.ts","../src/common/voice-input.ts","../src/chat/components/KvkkBanner.ts","../src/chat/components/PanelTopBar.ts","../src/chat/components/ThumbnailsColumn.ts","../src/chat/components/ChatDrawer.ts","../src/chat/components/Launcher.ts","../src/common/tts-player.ts","../src/common/price-formatter.ts","../src/chat/components/ComparisonTable.ts","../src/chat/utils/ui.ts","../src/chat/components/ReviewHighlights.ts","../src/common/product-utils.ts","../src/chat/components/product-price-layout.ts","../src/chat/components/AITopPicks.ts","../src/chat/components/GroundingReviewCard.ts","../src/chat/components/AIGroupingCards.ts","../src/chat/components/AISuggestedSearchCards.ts","../src/chat/components/FloatingComparisonButton.ts","../src/chat/components/ProsAndCons.ts","../src/chat/components/CategoriesContainer.ts","../src/chat/components/HandoffNotice.ts","../src/chat/components/ProductSummaryCard.ts","../src/chat/components/renderUISpec.ts","../src/chat/components/typewriter.ts","../src/chat/components/productMentionLinker.ts","../src/chat/components/actionClassifier.ts","../src/chat/components/ChoicePrompter.ts","../src/common/indexed-db.ts","../src/chat/extendedModeManager.ts","../src/chat/panel-manager.ts","../src/chat/session-persistence.ts","../src/chat/chat-presentation-state.ts","../src/common/pill-launcher.ts","../src/chat/stream-error-display.ts","../src/chat/kvkk.ts","../src/chat/components/chat.css?inline","../src/common/ga-datalayer.ts","../node_modules/zod/v4/core/core.js","../node_modules/zod/v4/core/util.js","../node_modules/zod/v4/core/errors.js","../node_modules/zod/v4/core/parse.js","../node_modules/zod/v4/core/regexes.js","../node_modules/zod/v4/core/checks.js","../node_modules/zod/v4/core/doc.js","../node_modules/zod/v4/core/versions.js","../node_modules/zod/v4/core/schemas.js","../node_modules/zod/v4/core/registries.js","../node_modules/zod/v4/core/api.js","../node_modules/zod/v4/core/to-json-schema.js","../node_modules/zod/v4/core/json-schema-processors.js","../node_modules/zod/v4/classic/iso.js","../node_modules/zod/v4/classic/errors.js","../node_modules/zod/v4/classic/parse.js","../node_modules/zod/v4/classic/schemas.js","../src/chat/catalog.ts","../src/chat/index.ts"],"sourcesContent":["import type { UISpecDomUnknownRenderer, RenderUISpecWithRegistryOptions } from './types.js';\n\nexport const defaultUnknownUISpecRenderer: UISpecDomUnknownRenderer<unknown> = ({ element, renderElement }) => {\n if (!element.children || element.children.length === 0) {\n return null;\n }\n\n const wrapper = document.createElement('div');\n for (const childId of element.children) {\n const rendered = renderElement(childId);\n if (rendered) wrapper.appendChild(rendered);\n }\n return wrapper;\n};\n\nexport function renderUISpecWithRegistry<TContext>(options: RenderUISpecWithRegistryOptions<TContext>): HTMLElement {\n const container = document.createElement('div');\n container.className = options.containerClassName;\n\n const rootEl = options.spec.elements[options.spec.root];\n if (!rootEl) return container;\n\n const unknownRenderer =\n options.unknownRenderer ?? (defaultUnknownUISpecRenderer as UISpecDomUnknownRenderer<TContext>);\n\n const renderElement = (elementId: string): HTMLElement | null => {\n const element = options.spec.elements[elementId];\n if (!element) return null;\n\n const renderer = options.registry[element.type];\n if (renderer) {\n return renderer({\n elementId,\n element,\n spec: options.spec,\n context: options.context,\n renderElement,\n });\n }\n\n return unknownRenderer({\n elementId,\n element,\n spec: options.spec,\n context: options.context,\n renderElement,\n });\n };\n\n const rendered = renderElement(options.spec.root);\n if (rendered) container.appendChild(rendered);\n return container;\n}\n","import type { UISpecDomRegistry } from './types.js';\n\nexport function mergeUISpecRegistry<TContext>(\n base: UISpecDomRegistry<TContext>,\n overrides?: Partial<UISpecDomRegistry<TContext>>,\n): UISpecDomRegistry<TContext> {\n if (!overrides) return base;\n\n const merged: UISpecDomRegistry<TContext> = { ...base };\n for (const [componentName, renderer] of Object.entries(overrides)) {\n if (!renderer) continue;\n merged[componentName] = renderer;\n }\n return merged;\n}\n","import type { AnalyticsInput } from './analytics.js';\n\n// Context shared across all events in a page session\nexport interface AnalyticsContext {\n account_id: string;\n session_id: string;\n correlation_id: string;\n view_id?: string;\n user_id?: string;\n page_type?: string;\n sku?: string;\n ab_test_variant?: string;\n ab_test_experiment_id?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Stream Lifecycle\n// ---------------------------------------------------------------------------\n\nexport function streamStartEvent(\n ctx: AnalyticsContext,\n payload: {\n endpoint: string;\n request_id: string;\n widget?: 'chat' | 'qna' | 'simrel';\n },\n): AnalyticsInput {\n const event: AnalyticsInput = {\n event_name: 'stream.start',\n account_id: ctx.account_id,\n session_id: ctx.session_id,\n correlation_id: ctx.correlation_id,\n payload: {\n endpoint: payload.endpoint,\n request_id: payload.request_id,\n },\n };\n if (ctx.view_id !== undefined) event.view_id = ctx.view_id;\n if (ctx.user_id !== undefined) event.user_id = ctx.user_id;\n if (payload.widget !== undefined) event.widget = payload.widget;\n if (ctx.page_type !== undefined) event.page_type = ctx.page_type;\n if (ctx.sku !== undefined) event.sku = ctx.sku;\n return event;\n}\n\nexport function streamChunkEvent(\n ctx: AnalyticsContext,\n payload: {\n request_id: string;\n chunk_index: number;\n latency_ms?: number;\n widget?: 'chat' | 'qna' | 'simrel';\n },\n): AnalyticsInput {\n const eventPayload: Record<string, unknown> = {\n request_id: payload.request_id,\n chunk_index: payload.chunk_index,\n };\n if (payload.latency_ms !== undefined) eventPayload.latency_ms = payload.latency_ms;\n\n const event: AnalyticsInput = {\n event_name: 'stream.chunk',\n account_id: ctx.account_id,\n session_id: ctx.session_id,\n correlation_id: ctx.correlation_id,\n payload: eventPayload,\n };\n if (ctx.view_id !== undefined) event.view_id = ctx.view_id;\n if (ctx.user_id !== undefined) event.user_id = ctx.user_id;\n if (payload.widget !== undefined) event.widget = payload.widget;\n if (ctx.page_type !== undefined) event.page_type = ctx.page_type;\n if (ctx.sku !== undefined) event.sku = ctx.sku;\n return event;\n}\n\nexport function streamUiSpecEvent(\n ctx: AnalyticsContext,\n payload: {\n request_id: string;\n chunk_index: number;\n component_type: string;\n widget?: 'chat' | 'qna' | 'simrel';\n },\n): AnalyticsInput {\n const event: AnalyticsInput = {\n event_name: 'stream.ui_spec',\n account_id: ctx.account_id,\n session_id: ctx.session_id,\n correlation_id: ctx.correlation_id,\n payload: {\n request_id: payload.request_id,\n chunk_index: payload.chunk_index,\n component_type: payload.component_type,\n },\n };\n if (ctx.view_id !== undefined) event.view_id = ctx.view_id;\n if (ctx.user_id !== undefined) event.user_id = ctx.user_id;\n if (payload.widget !== undefined) event.widget = payload.widget;\n if (ctx.page_type !== undefined) event.page_type = ctx.page_type;\n if (ctx.sku !== undefined) event.sku = ctx.sku;\n return event;\n}\n\nexport function streamDoneEvent(\n ctx: AnalyticsContext,\n payload: {\n request_id: string;\n latency_ms: number;\n chunk_count: number;\n widget?: 'chat' | 'qna' | 'simrel';\n },\n): AnalyticsInput {\n const event: AnalyticsInput = {\n event_name: 'stream.done',\n account_id: ctx.account_id,\n session_id: ctx.session_id,\n correlation_id: ctx.correlation_id,\n payload: {\n request_id: payload.request_id,\n latency_ms: payload.latency_ms,\n chunk_count: payload.chunk_count,\n },\n };\n if (ctx.view_id !== undefined) event.view_id = ctx.view_id;\n if (ctx.user_id !== undefined) event.user_id = ctx.user_id;\n if (payload.widget !== undefined) event.widget = payload.widget;\n if (ctx.page_type !== undefined) event.page_type = ctx.page_type;\n if (ctx.sku !== undefined) event.sku = ctx.sku;\n return event;\n}\n\nexport function streamErrorEvent(\n ctx: AnalyticsContext,\n payload: {\n request_id: string;\n error_code: string;\n error_message: string;\n widget?: 'chat' | 'qna' | 'simrel';\n },\n): AnalyticsInput {\n const event: AnalyticsInput = {\n event_name: 'stream.error',\n account_id: ctx.account_id,\n session_id: ctx.session_id,\n correlation_id: ctx.correlation_id,\n payload: {\n request_id: payload.request_id,\n error_code: payload.error_code,\n error_message: payload.error_message,\n },\n };\n if (ctx.view_id !== undefined) event.view_id = ctx.view_id;\n if (ctx.user_id !== undefined) event.user_id = ctx.user_id;\n if (payload.widget !== undefined) event.widget = payload.widget;\n if (ctx.page_type !== undefined) event.page_type = ctx.page_type;\n if (ctx.sku !== undefined) event.sku = ctx.sku;\n return event;\n}\n\n// ---------------------------------------------------------------------------\n// LLM Usage\n// ---------------------------------------------------------------------------\n\nexport function llmUsageEvent(\n ctx: AnalyticsContext,\n payload: {\n model: string;\n prompt_tokens: number;\n completion_tokens: number;\n total_tokens: number;\n provider?: string;\n },\n): AnalyticsInput {\n const eventPayload: Record<string, unknown> = {\n model: payload.model,\n prompt_tokens: payload.prompt_tokens,\n completion_tokens: payload.completion_tokens,\n total_tokens: payload.total_tokens,\n };\n if (payload.provider !== undefined) eventPayload.provider = payload.provider;\n\n const event: AnalyticsInput = {\n event_name: 'llm.usage',\n account_id: ctx.account_id,\n session_id: ctx.session_id,\n correlation_id: ctx.correlation_id,\n payload: eventPayload,\n };\n if (ctx.view_id !== undefined) event.view_id = ctx.view_id;\n if (ctx.user_id !== undefined) event.user_id = ctx.user_id;\n if (ctx.page_type !== undefined) event.page_type = ctx.page_type;\n if (ctx.sku !== undefined) event.sku = ctx.sku;\n return event;\n}\n\n// ---------------------------------------------------------------------------\n// Metering\n// ---------------------------------------------------------------------------\n\nexport function meteringIncrementEvent(\n ctx: AnalyticsContext,\n payload: {\n meter_key: string;\n quantity: number;\n unit: string;\n },\n): AnalyticsInput {\n const event: AnalyticsInput = {\n event_name: 'metering.increment',\n account_id: ctx.account_id,\n session_id: ctx.session_id,\n correlation_id: ctx.correlation_id,\n payload: {\n meter_key: payload.meter_key,\n quantity: payload.quantity,\n unit: payload.unit,\n },\n };\n if (ctx.view_id !== undefined) event.view_id = ctx.view_id;\n if (ctx.user_id !== undefined) event.user_id = ctx.user_id;\n if (ctx.page_type !== undefined) event.page_type = ctx.page_type;\n if (ctx.sku !== undefined) event.sku = ctx.sku;\n return event;\n}\n\nexport function meteringSummaryEvent(\n ctx: AnalyticsContext,\n payload: {\n meter_key: string;\n quantity: number;\n unit: string;\n },\n): AnalyticsInput {\n const event: AnalyticsInput = {\n event_name: 'metering.summary',\n account_id: ctx.account_id,\n session_id: ctx.session_id,\n correlation_id: ctx.correlation_id,\n payload: {\n meter_key: payload.meter_key,\n quantity: payload.quantity,\n unit: payload.unit,\n },\n };\n if (ctx.view_id !== undefined) event.view_id = ctx.view_id;\n if (ctx.user_id !== undefined) event.user_id = ctx.user_id;\n if (ctx.page_type !== undefined) event.page_type = ctx.page_type;\n if (ctx.sku !== undefined) event.sku = ctx.sku;\n return event;\n}\n\n// ---------------------------------------------------------------------------\n// Chat and Widget Histories\n// ---------------------------------------------------------------------------\n\nexport function chatHistorySnapshotEvent(\n ctx: AnalyticsContext,\n payload: {\n message_count: number;\n history_ref: string;\n redaction_level: string;\n },\n): AnalyticsInput {\n const event: AnalyticsInput = {\n event_name: 'chat.history.snapshot',\n account_id: ctx.account_id,\n session_id: ctx.session_id,\n correlation_id: ctx.correlation_id,\n payload: {\n message_count: payload.message_count,\n history_ref: payload.history_ref,\n redaction_level: payload.redaction_level,\n },\n };\n if (ctx.view_id !== undefined) event.view_id = ctx.view_id;\n if (ctx.user_id !== undefined) event.user_id = ctx.user_id;\n if (ctx.page_type !== undefined) event.page_type = ctx.page_type;\n if (ctx.sku !== undefined) event.sku = ctx.sku;\n return event;\n}\n\nexport function widgetHistorySnapshotEvent(\n ctx: AnalyticsContext,\n payload: {\n message_count: number;\n history_ref: string;\n redaction_level: string;\n widget: 'chat' | 'qna' | 'simrel';\n },\n): AnalyticsInput {\n const event: AnalyticsInput = {\n event_name: 'widget.history.snapshot',\n account_id: ctx.account_id,\n session_id: ctx.session_id,\n correlation_id: ctx.correlation_id,\n widget: payload.widget,\n payload: {\n message_count: payload.message_count,\n history_ref: payload.history_ref,\n redaction_level: payload.redaction_level,\n },\n };\n if (ctx.view_id !== undefined) event.view_id = ctx.view_id;\n if (ctx.user_id !== undefined) event.user_id = ctx.user_id;\n if (ctx.page_type !== undefined) event.page_type = ctx.page_type;\n if (ctx.sku !== undefined) event.sku = ctx.sku;\n return event;\n}\n\n// ---------------------------------------------------------------------------\n// Commerce Attribution\n// ---------------------------------------------------------------------------\n\nexport function basketAddEvent(\n ctx: AnalyticsContext,\n payload: {\n attribution_source: 'chat' | 'qna' | 'simrel';\n attribution_action_id: string;\n cart_value: number;\n currency: string;\n line_items: number;\n sku: string;\n },\n): AnalyticsInput {\n const event: AnalyticsInput = {\n event_name: 'basket.add',\n account_id: ctx.account_id,\n session_id: ctx.session_id,\n correlation_id: ctx.correlation_id,\n widget: payload.attribution_source,\n payload: {\n attribution_source: payload.attribution_source,\n attribution_action_id: payload.attribution_action_id,\n cart_value: payload.cart_value,\n currency: payload.currency,\n line_items: payload.line_items,\n sku: payload.sku,\n },\n };\n if (ctx.view_id !== undefined) event.view_id = ctx.view_id;\n if (ctx.user_id !== undefined) event.user_id = ctx.user_id;\n if (ctx.page_type !== undefined) event.page_type = ctx.page_type;\n if (ctx.sku !== undefined) event.sku = ctx.sku;\n return event;\n}\n\nexport function checkoutStartEvent(\n ctx: AnalyticsContext,\n payload: {\n attribution_source: 'chat' | 'qna' | 'simrel';\n attribution_action_id: string;\n cart_value: number;\n currency: string;\n line_items: number;\n },\n): AnalyticsInput {\n const event: AnalyticsInput = {\n event_name: 'checkout.start',\n account_id: ctx.account_id,\n session_id: ctx.session_id,\n correlation_id: ctx.correlation_id,\n widget: payload.attribution_source,\n payload: {\n attribution_source: payload.attribution_source,\n attribution_action_id: payload.attribution_action_id,\n cart_value: payload.cart_value,\n currency: payload.currency,\n line_items: payload.line_items,\n },\n };\n if (ctx.view_id !== undefined) event.view_id = ctx.view_id;\n if (ctx.user_id !== undefined) event.user_id = ctx.user_id;\n if (ctx.page_type !== undefined) event.page_type = ctx.page_type;\n if (ctx.sku !== undefined) event.sku = ctx.sku;\n return event;\n}\n\nexport function checkoutCompleteEvent(\n ctx: AnalyticsContext,\n payload: {\n attribution_source: 'chat' | 'qna' | 'simrel';\n attribution_action_id: string;\n cart_value: number;\n currency: string;\n line_items: number;\n },\n): AnalyticsInput {\n const event: AnalyticsInput = {\n event_name: 'checkout.complete',\n account_id: ctx.account_id,\n session_id: ctx.session_id,\n correlation_id: ctx.correlation_id,\n widget: payload.attribution_source,\n payload: {\n attribution_source: payload.attribution_source,\n attribution_action_id: payload.attribution_action_id,\n cart_value: payload.cart_value,\n currency: payload.currency,\n line_items: payload.line_items,\n },\n };\n if (ctx.view_id !== undefined) event.view_id = ctx.view_id;\n if (ctx.user_id !== undefined) event.user_id = ctx.user_id;\n if (ctx.page_type !== undefined) event.page_type = ctx.page_type;\n if (ctx.sku !== undefined) event.sku = ctx.sku;\n return event;\n}\n","/**\n * DOMParser-based HTML sanitizer.\n *\n * Backend sends HTML in assistant messages (e.g. KVKK notice).\n * This module strips dangerous elements/attributes while preserving\n * safe formatting tags.\n *\n * WARNING: Any new injection point that uses innerHTML must call this function first.\n */\n\nconst ALLOWED_TAGS = new Set([\n 'p',\n 'br',\n 'a',\n 'strong',\n 'b',\n 'em',\n 'i',\n 'u',\n 'ul',\n 'ol',\n 'li',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'span',\n 'div',\n 'table',\n 'thead',\n 'tbody',\n 'tr',\n 'th',\n 'td',\n 'hr',\n 'code',\n 'pre',\n 'blockquote',\n 'img',\n 'sup',\n 'sub',\n]);\n\n/** Elements removed entirely (children NOT promoted). */\nconst DISALLOWED_TAGS = new Set([\n 'script',\n 'iframe',\n 'object',\n 'embed',\n 'form',\n 'input',\n 'textarea',\n 'select',\n 'button',\n 'style',\n 'link',\n 'meta',\n 'template',\n 'noscript',\n]);\n\n/**\n * Attributes allowed per-tag. `'*'` means any allowed tag.\n * The `style` attribute is further sanitized by `sanitizeCssStyle()` to\n * strip dangerous CSS values (url(), expression(), -moz-binding, etc.).\n */\nconst ALLOWED_ATTRS: Record<string, Set<string>> = {\n '*': new Set(['class']),\n a: new Set(['href', 'target', 'rel']),\n img: new Set(['src', 'alt', 'width', 'height']),\n div: new Set(['style']),\n span: new Set(['style']),\n p: new Set(['style']),\n};\n\nfunction hasJavascriptProtocol(value: string): boolean {\n // Normalize whitespace + case then check\n return /^\\s*javascript\\s*:/i.test(value);\n}\n\n/** CSS properties considered safe in style attributes (layout + typography). */\nconst SAFE_CSS_PROPERTIES = new Set([\n 'color',\n 'background-color',\n 'font-size',\n 'font-weight',\n 'font-style',\n 'font-family',\n 'text-align',\n 'text-decoration',\n 'line-height',\n 'letter-spacing',\n 'margin',\n 'margin-top',\n 'margin-right',\n 'margin-bottom',\n 'margin-left',\n 'padding',\n 'padding-top',\n 'padding-right',\n 'padding-bottom',\n 'padding-left',\n 'border',\n 'border-top',\n 'border-right',\n 'border-bottom',\n 'border-left',\n 'border-radius',\n 'border-color',\n 'border-width',\n 'border-style',\n 'width',\n 'max-width',\n 'min-width',\n 'height',\n 'max-height',\n 'min-height',\n 'display',\n 'vertical-align',\n 'white-space',\n 'word-break',\n 'overflow',\n 'opacity',\n 'visibility',\n 'list-style',\n 'list-style-type',\n 'text-transform',\n 'text-indent',\n 'text-overflow',\n 'box-sizing',\n 'flex',\n 'flex-direction',\n 'flex-wrap',\n 'justify-content',\n 'align-items',\n 'gap',\n]);\n\n/** Patterns indicating dangerous CSS values that could execute code or load external resources. */\nconst DANGEROUS_CSS_VALUE = /url\\s*\\(|expression\\s*\\(|javascript\\s*:|\\bimport\\b|-moz-binding|behavior\\s*:/i;\n\n/**\n * Sanitize a CSS style attribute value.\n * Allowlists safe properties and rejects values containing dangerous patterns.\n * Returns empty string if nothing is safe.\n */\nfunction sanitizeCssStyle(raw: string): string {\n const safe: string[] = [];\n for (const decl of raw.split(';')) {\n const trimmed = decl.trim();\n if (!trimmed) continue;\n const colonIdx = trimmed.indexOf(':');\n if (colonIdx === -1) continue;\n const property = trimmed.slice(0, colonIdx).trim().toLowerCase();\n const value = trimmed.slice(colonIdx + 1).trim();\n if (!SAFE_CSS_PROPERTIES.has(property)) continue;\n if (DANGEROUS_CSS_VALUE.test(value)) continue;\n safe.push(trimmed);\n }\n return safe.join('; ');\n}\n\nfunction sanitizeNode(node: Node, parent: Node): void {\n if (node.nodeType === Node.TEXT_NODE) return;\n\n if (node.nodeType !== Node.ELEMENT_NODE) {\n node.parentNode?.removeChild(node);\n return;\n }\n\n const el = node as Element;\n const tag = el.tagName.toLowerCase();\n\n // Disallowed: remove entirely (including children)\n if (DISALLOWED_TAGS.has(tag)) {\n el.parentNode?.removeChild(el);\n return;\n }\n\n // Unknown: unwrap (promote children to parent)\n if (!ALLOWED_TAGS.has(tag)) {\n const children = Array.from(el.childNodes);\n for (const child of children) {\n parent.insertBefore(child, el);\n }\n parent.removeChild(el);\n // Sanitize promoted children\n for (const child of children) {\n sanitizeNode(child, parent);\n }\n return;\n }\n\n // Sanitize attributes\n const globalAllowed = ALLOWED_ATTRS['*'] ?? new Set();\n const tagAllowed = ALLOWED_ATTRS[tag] ?? new Set();\n const attrs = Array.from(el.attributes);\n\n for (const attr of attrs) {\n const name = attr.name.toLowerCase();\n\n if (!globalAllowed.has(name) && !tagAllowed.has(name)) {\n el.removeAttribute(attr.name);\n continue;\n }\n\n // Strip javascript: protocol from any attribute value\n if (hasJavascriptProtocol(attr.value)) {\n el.removeAttribute(attr.name);\n continue;\n }\n\n // Sanitize CSS style values to prevent XSS via url(), expression(), etc.\n if (name === 'style') {\n const sanitized = sanitizeCssStyle(attr.value);\n if (sanitized) {\n el.setAttribute('style', sanitized);\n } else {\n el.removeAttribute('style');\n }\n continue;\n }\n }\n\n // Validate specific attribute values\n if (tag === 'a') {\n const href = el.getAttribute('href');\n if (href !== null) {\n const trimmed = href.trim().toLowerCase();\n if (!trimmed.startsWith('http://') && !trimmed.startsWith('https://') && !trimmed.startsWith('mailto:')) {\n el.removeAttribute('href');\n }\n }\n // Force safe link behavior\n el.setAttribute('target', '_blank');\n el.setAttribute('rel', 'noopener noreferrer');\n }\n\n if (tag === 'img') {\n const src = el.getAttribute('src');\n if (src !== null) {\n const trimmed = src.trim().toLowerCase();\n if (!trimmed.startsWith('https://')) {\n el.removeAttribute('src');\n }\n }\n }\n\n // Recurse into children (snapshot the list since we may mutate)\n const children = Array.from(el.childNodes);\n for (const child of children) {\n sanitizeNode(child, el);\n }\n}\n\n/**\n * Sanitize an HTML string for safe insertion via innerHTML.\n *\n * - Allowed tags are preserved; disallowed tags are removed entirely.\n * - Unknown tags are unwrapped (children promoted).\n * - `<a>` tags are forced to `target=\"_blank\" rel=\"noopener noreferrer\"`.\n * - Only `https://` is allowed for `<img src>`.\n */\nconst SAFE_URL_PROTOCOLS = ['http:', 'https:'];\n\n/** Check if a URL uses a safe protocol (http or https). */\nexport function isSafeImageUrl(url: string): boolean {\n try {\n return SAFE_URL_PROTOCOLS.includes(new URL(url).protocol);\n } catch {\n return false;\n }\n}\n\n/**\n * Check if a URL is safe for use in href/src attributes.\n * Allows http:, https:, and relative paths (starting with `/`).\n */\nexport function isSafeUrl(url: string): boolean {\n // Allow relative paths but reject protocol-relative URLs (//evil.com/...)\n if (url.startsWith('/') && !url.startsWith('//')) return true;\n try {\n const parsed = new URL(url);\n return SAFE_URL_PROTOCOLS.includes(parsed.protocol);\n } catch {\n return false;\n }\n}\n\n/**\n * Safely set an attribute on an element.\n * For `href` and `src` attributes, validates the URL against safe protocols.\n */\nexport function safeSetAttribute(el: HTMLElement, attr: string, value: string): void {\n if (attr === 'href' || attr === 'src') {\n if (!isSafeUrl(value)) return;\n }\n el.setAttribute(attr, value);\n}\n\nexport function sanitizeHtml(raw: string): string {\n if (!raw) return '';\n\n const doc = new DOMParser().parseFromString(raw, 'text/html');\n const body = doc.body;\n\n const children = Array.from(body.childNodes);\n for (const child of children) {\n sanitizeNode(child, body);\n }\n\n return body.innerHTML;\n}\n","/**\n * Cross-widget event bus utilities.\n *\n * All Gengage widgets communicate through window CustomEvents following\n * the naming convention: gengage:<widget>:<action>\n *\n * This creates a loosely-coupled event bus — widgets don't import each other,\n * host pages can intercept any event, and third-party analytics can hook in.\n */\n\nimport type { GengageEventDetailMap, GengageEventName } from './types.js';\nimport type { ActionPayload } from './types.js';\nimport { isSafeUrl } from './safe-html.js';\n\n/**\n * Dispatch a typed Gengage event on window.\n *\n * @example\n * dispatch('gengage:qna:action', { title: 'About shipping', type: 'query', payload: 'shipping' });\n */\nexport function dispatch<K extends GengageEventName>(name: K, detail: GengageEventDetailMap[K]): void {\n window.dispatchEvent(new CustomEvent(name, { detail, bubbles: false }));\n}\n\n/**\n * Listen for a typed Gengage event on window.\n * Returns an unsubscribe function.\n *\n * @example\n * const off = listen('gengage:qna:action', (detail) => {\n * window.gengage?.chat?.openWithAction(detail);\n * });\n * // Later:\n * off();\n */\nexport function listen<K extends GengageEventName>(\n name: K,\n handler: (detail: GengageEventDetailMap[K]) => void,\n): () => void {\n const listener = (e: Event) => {\n handler((e as CustomEvent<GengageEventDetailMap[K]>).detail);\n };\n window.addEventListener(name, listener);\n return () => window.removeEventListener(name, listener);\n}\n\nexport interface WireQNAToChatOptions {\n onChatUnavailable?: () => void;\n}\n\ninterface WireableChatAPI {\n open?: (options?: { state?: 'full' | 'half'; initialMessage?: string }) => void;\n openWithAction?: (action: ActionPayload) => void;\n sendMessage?: (text: string) => void;\n}\n\nfunction extractFreeTextActionMessage(action: ActionPayload): string | null {\n if (action.type !== 'user_message' && action.type !== 'inputText') return null;\n\n if (typeof action.payload === 'string' && action.payload.trim().length > 0) {\n return action.payload.trim();\n }\n\n if (typeof action.payload === 'object' && action.payload !== null) {\n const payloadObj = action.payload as Record<string, unknown>;\n if (typeof payloadObj.text === 'string') {\n const text = payloadObj.text.trim();\n if (text.length > 0) return text;\n }\n }\n\n if (typeof action.title === 'string' && action.title.trim().length > 0) {\n return action.title.trim();\n }\n\n return null;\n}\n\nfunction injectQnaLauncherSuggestedFlags(action: ActionPayload): ActionPayload {\n if (action.type !== 'inputText' && action.type !== 'user_message') {\n return action;\n }\n\n const base: Record<string, unknown> =\n typeof action.payload === 'string'\n ? { text: action.payload.trim() }\n : action.payload != null && typeof action.payload === 'object' && !Array.isArray(action.payload)\n ? { ...(action.payload as Record<string, unknown>) }\n : {};\n\n if (typeof base['text'] !== 'string' || !base['text'].trim()) {\n const fromTitle = action.title?.trim();\n if (fromTitle) base['text'] = fromTitle;\n }\n\n if (!('is_launcher' in base)) base['is_launcher'] = 1;\n if (!('is_suggested_text' in base)) base['is_suggested_text'] = 1;\n\n return { ...action, payload: base };\n}\n\n/**\n * Convenience: wire QNA → Chat automatically.\n * Call this once after both widgets are initialised.\n *\n * Listens for 'gengage:qna:action' and forwards to window.gengage.chat.openWithAction().\n * Listens for 'gengage:qna:open-chat' and forwards to window.gengage.chat.open().\n *\n * If chat is not available at dispatch time, emits a one-time console.warn and\n * calls options.onChatUnavailable (every time) if provided.\n *\n * @returns unsubscribe function that removes both listeners.\n */\nexport function wireQNAToChat(options?: WireQNAToChatOptions): () => void {\n let warnedOnce = false;\n const pendingActions: ActionPayload[] = [];\n let pendingOpenCount = 0;\n let pollTimer: number | null = null;\n let pollStartedAt = 0;\n const pollIntervalMs = 100;\n const pollTimeoutMs = 5000;\n\n function getChat(): WireableChatAPI | null {\n return (window.gengage?.chat as WireableChatAPI | undefined) ?? null;\n }\n\n function notifyUnavailable(): void {\n if (!warnedOnce) {\n console.warn(\n '[gengage] QNA tried to open chat, but chat widget is not initialized. ' +\n 'Ensure GengageChat is initialized before calling wireQNAToChat().',\n );\n warnedOnce = true;\n }\n options?.onChatUnavailable?.();\n }\n\n function routeActionToChat(chat: WireableChatAPI, action: ActionPayload): void {\n if (chat.openWithAction) {\n const routed =\n action.type === 'inputText' || action.type === 'user_message'\n ? injectQnaLauncherSuggestedFlags(action)\n : action;\n chat.openWithAction(routed);\n return;\n }\n const freeText = extractFreeTextActionMessage(action);\n if (freeText && chat.sendMessage) {\n chat.open?.();\n chat.sendMessage(freeText);\n return;\n }\n }\n\n function clearPollTimer(): void {\n if (pollTimer !== null) {\n window.clearInterval(pollTimer);\n pollTimer = null;\n }\n }\n\n function flushPendingToChat(): boolean {\n const chat = getChat();\n if (!chat) return false;\n\n if (pendingOpenCount > 0) {\n chat.open?.();\n pendingOpenCount = 0;\n }\n\n if (pendingActions.length > 0) {\n const queued = pendingActions.splice(0, pendingActions.length);\n for (const action of queued) {\n routeActionToChat(chat, action);\n }\n }\n\n clearPollTimer();\n return true;\n }\n\n function ensurePollTimer(): void {\n if (pollTimer !== null) return;\n pollStartedAt = Date.now();\n pollTimer = window.setInterval(() => {\n if (flushPendingToChat()) return;\n if (Date.now() - pollStartedAt >= pollTimeoutMs) {\n pendingActions.length = 0;\n pendingOpenCount = 0;\n clearPollTimer();\n }\n }, pollIntervalMs);\n }\n\n const offAction = listen('gengage:qna:action', (action) => {\n const chat = getChat();\n if (chat) {\n routeActionToChat(chat, action);\n return;\n }\n\n notifyUnavailable();\n if (pendingActions.length >= 20) pendingActions.shift();\n pendingActions.push(action);\n ensurePollTimer();\n });\n\n const offOpen = listen('gengage:qna:open-chat', () => {\n const chat = getChat();\n if (chat) {\n chat.open?.();\n return;\n }\n\n notifyUnavailable();\n pendingOpenCount += 1;\n ensurePollTimer();\n });\n\n return () => {\n offAction();\n offOpen();\n pendingActions.length = 0;\n pendingOpenCount = 0;\n clearPollTimer();\n };\n}\n\n/**\n * Convenience: wire Similar Products → Chat for cross-page session continuity.\n * Call this once after both widgets are initialised.\n *\n * When the user navigates to a product page, the chat widget can restore\n * the conversation where it left off using saveSession().\n *\n * @returns unsubscribe function.\n */\nexport function wireSimilarToChat(): () => void {\n return listen('gengage:similar:product-click', ({ sku, url, sessionId }) => {\n if (sessionId) {\n window.gengage?.chat?.saveSession(sessionId, sku);\n }\n if (isSafeUrl(url)) {\n window.location.href = url;\n }\n });\n}\n","/**\n * Page context management.\n *\n * Best practice for passing page context to widgets:\n *\n * ## SSR / Static pages\n * Set window.gengage.pageContext before widget scripts load:\n *\n * <script>\n * window.gengage = {\n * pageContext: {\n * pageType: 'pdp',\n * sku: '{{ product.sku }}',\n * price: '{{ product.price }}',\n * categoryTree: {{ product.categories | json }},\n * }\n * };\n * </script>\n * <script src=\"https://cdn.gengage.ai/widgets/latest/chat.iife.js\"></script>\n *\n * ## CSR / SPA (React, Vue, Next.js, etc.)\n * Call widget.update() after each navigation:\n *\n * router.afterEach((to) => {\n * chatWidget.update({\n * pageType: to.meta.pageType,\n * sku: to.params.sku,\n * });\n * });\n *\n * ## Event-based (loosest coupling)\n * Dispatch 'gengage:context:update' from anywhere:\n *\n * window.dispatchEvent(new CustomEvent('gengage:context:update', {\n * detail: { pageType: 'pdp', sku: '12345' }\n * }));\n */\n\nimport type { PageContext, SessionContext } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Session bootstrap\n// ---------------------------------------------------------------------------\n\n/**\n * Returns the shared session ID for this browser tab session.\n * Creates and persists it on first call.\n *\n * Call this once at page load and share the result with all widget configs:\n *\n * const sessionId = bootstrapSession();\n * chatWidget.init({ ..., session: { sessionId } });\n * qnaWidget.init({ ..., session: { sessionId } });\n */\nexport function bootstrapSession(): string {\n const existing = window.__gengageSessionId ?? sessionStorage.getItem('gengage_session_id') ?? null;\n\n const sessionId = existing ?? crypto.randomUUID();\n\n window.__gengageSessionId = sessionId;\n sessionStorage.setItem('gengage_session_id', sessionId);\n\n if (!window.gengage) window.gengage = {};\n window.gengage.sessionId = sessionId;\n\n return sessionId;\n}\n\n// ---------------------------------------------------------------------------\n// Page context resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Reads the current page context from window.gengage.pageContext.\n * Returns null if not set.\n */\nexport function getWindowPageContext(): PageContext | null {\n return window.gengage?.pageContext ?? null;\n}\n\n/**\n * Merges a partial context update into the current window.gengage.pageContext.\n * Dispatches 'gengage:context:update' so all listening widgets update.\n */\nexport function updatePageContext(patch: Partial<PageContext>): void {\n if (!window.gengage) window.gengage = {};\n window.gengage.pageContext = {\n pageType: 'other',\n ...window.gengage.pageContext,\n ...patch,\n };\n\n window.dispatchEvent(new CustomEvent('gengage:context:update', { detail: patch }));\n}\n\n/**\n * Resolves the session context, bootstrapping if necessary.\n * Merges provided overrides with auto-generated session ID.\n */\nexport function resolveSession(overrides?: Partial<SessionContext>): SessionContext {\n const sessionId = overrides?.sessionId ?? bootstrapSession();\n return {\n sessionId,\n ...overrides,\n };\n}\n","import type { WidgetTheme } from './types.js';\n\n/**\n * Shared SDK-wide visual tokens.\n *\n * Applied to every widget root by default so account customizations only\n * override what they need.\n */\nexport const DEFAULT_WIDGET_THEME_TOKENS: WidgetTheme = {\n '--gengage-chat-offset': '20px',\n '--gengage-chat-launcher-size': '56px',\n '--gengage-chat-width': '400px',\n '--gengage-chat-shell-radius': '1rem' /* lg roundedness */,\n '--gengage-chat-header-height': '72px',\n '--gengage-chat-conversation-width': '396px',\n '--gengage-chat-panel-min-width': '320px',\n '--gengage-chat-panel-max-width': '1200px',\n '--gengage-chat-input-height': '48px',\n\n '--gengage-qna-pill-radius': '999px' /* roundedness-full */,\n /** Rounded rect (not full pill) — matches `src/qna/components/qna.css` */\n '--gengage-qna-input-radius': '12px',\n\n '--gengage-simrel-card-radius': '0.75rem' /* md roundedness */,\n\n '--radius-control': '12px',\n '--radius-card': '16px',\n '--radius-panel': '24px',\n '--radius-pill': '999px',\n '--surface-card-muted': '#f8fafc',\n '--text-secondary': '#4b5563',\n '--text-muted': '#6b7280',\n '--border-default': 'rgba(17, 24, 39, 0.10)',\n};\n\nexport function withDefaultWidgetTheme(theme?: WidgetTheme): WidgetTheme {\n if (!theme) {\n return { ...DEFAULT_WIDGET_THEME_TOKENS };\n }\n return { ...DEFAULT_WIDGET_THEME_TOKENS, ...theme };\n}\n","import type { WidgetTheme } from './types.js';\n\n/**\n * Shared baseline tokens used by account customizations.\n *\n * These values come from historical Gengage host defaults and are\n * intentionally conservative so account themes can override only what differs.\n */\nexport const BASE_WIDGET_THEME: WidgetTheme = {\n /* ── Editorial Commerce Framework ────────────────────────────────────── */\n /* Primary: signature red; never use pure black for text — use on_surface */\n primaryColor: '#b7102a',\n primaryForeground: '#ffffff',\n backgroundColor: '#ffffff' /* surface-card */,\n foregroundColor: '#111827' /* text-primary */,\n borderRadius: '0.75rem' /* md roundedness */,\n fontFamily: '\"Plus Jakarta Sans\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n fontSize: '14px',\n zIndex: '1000',\n\n '--glov-chatbot-width': '420px',\n '--glov-left-spacing': '260px',\n '--chatbot-padding': '16px',\n '--root-wrapper-background-color': '#f8f9fa' /* background token */,\n '--root-wrapper-border-color': '#edeeef' /* surface-container */,\n\n '--gengage-chat-width': '400px',\n '--gengage-chat-shell-radius': '1rem' /* lg roundedness */,\n '--gengage-chat-header-height': '72px',\n '--gengage-chat-conversation-width': '396px',\n '--gengage-chat-panel-min-width': '320px',\n '--gengage-chat-panel-max-width': '1200px',\n '--gengage-chat-input-height': '48px',\n '--gengage-qna-pill-radius': '999px' /* roundedness-full */,\n '--gengage-qna-input-radius': '0.75rem' /* md roundedness */,\n '--gengage-simrel-card-radius': '0.75rem' /* md roundedness */,\n\n '--client-primary': '#b7102a',\n '--client-primary-hover': 'color-mix(in srgb, #b7102a 88%, black 12%)',\n '--client-primary-active': 'color-mix(in srgb, #b7102a 78%, black 22%)',\n '--client-primary-subtle': 'color-mix(in srgb, #b7102a 12%, white)',\n '--client-primary-soft': 'color-mix(in srgb, #b7102a 20%, white)',\n '--client-on-primary': '#ffffff',\n '--client-focus-ring': 'color-mix(in srgb, #b7102a 32%, transparent)',\n\n '--surface-page': '#f6f7fb',\n '--surface-shell': '#10131a',\n '--surface-card': '#ffffff',\n '--surface-card-muted': '#f8fafc',\n '--surface-card-soft': '#f8fafc',\n '--surface-elevated': '#ffffff',\n '--surface-input': '#ffffff',\n '--surface-overlay': 'rgba(16, 19, 26, 0.52)',\n\n '--text-primary': '#111827',\n '--text-secondary': '#4b5563',\n '--text-muted': '#6b7280',\n '--text-inverse': '#f9fafb',\n\n '--border-subtle': 'rgba(17, 24, 39, 0.06)',\n '--border-default': 'rgba(17, 24, 39, 0.10)',\n '--border-strong': 'rgba(17, 24, 39, 0.18)',\n\n '--shadow-1': '0 1px 2px rgba(16, 24, 40, 0.04), 0 1px 3px rgba(16, 24, 40, 0.06)',\n '--shadow-2': '0 4px 12px rgba(16, 24, 40, 0.08)',\n '--shadow-3': '0 10px 24px rgba(16, 24, 40, 0.12)',\n\n '--radius-control': '12px',\n '--radius-card': '16px',\n '--radius-panel': '24px',\n '--radius-pill': '999px',\n\n '--success': '#16a34a',\n '--warning': '#d97706',\n '--error': '#dc2626',\n '--info': '#2563eb',\n '--rating': '#f5b301',\n\n '--ai-accent-start': '#0b24d6',\n '--ai-accent-end': '#f768f2',\n '--ai-accent-soft': 'linear-gradient(135deg, rgba(11, 36, 214, 0.08), rgba(247, 104, 242, 0.08))',\n};\n\n/**\n * Merge account overrides on top of the shared base theme.\n *\n * Account customization files should call this helper so shared defaults stay\n * centralized under src/common.\n */\nexport function withBaseTheme(overrides: WidgetTheme): WidgetTheme {\n return { ...BASE_WIDGET_THEME, ...overrides };\n}\n","import type { GengageEventDetailMap } from './types.js';\nimport { listen } from './events.js';\nimport { BASE_WIDGET_THEME } from './theme-utils.js';\n\nconst ROOT_ID = 'gengage-global-toast-root';\nconst STYLE_ID = 'gengage-global-toast-style';\nconst ROOT_VISIBLE_CLASS = 'gengage-global-toast-root--visible';\nconst DEFAULT_DURATION_MS = 4200;\nconst MIN_DURATION_MS = 1500;\nconst MAX_DURATION_MS = 15000;\nconst THEME_SYNC_VARS = [\n '--gengage-font-family',\n '--surface-card',\n '--text-primary',\n '--text-muted',\n '--border-default',\n '--radius-card',\n '--shadow-3',\n '--error',\n '--ds-toast-error-bg',\n '--ds-toast-error-border',\n '--ds-toast-error-accent',\n '--ds-toast-error-fg',\n '--ds-toast-error-shadow',\n] as const;\n\nlet listenerRegistered = false;\nlet dismissTimer: ReturnType<typeof setTimeout> | null = null;\n\nconst CONNECTIVITY_ERROR_PATTERNS = [\n /failed to fetch/i,\n /networkerror/i,\n /network request failed/i,\n /load failed/i,\n /err_network/i,\n /fetch failed/i,\n /network error/i,\n] as const;\n\nfunction isTurkishLocale(locale?: string): boolean {\n return typeof locale === 'string' && locale.toLowerCase().startsWith('tr');\n}\n\nexport function isLikelyConnectivityIssue(error?: unknown): boolean {\n if (typeof navigator !== 'undefined' && navigator.onLine === false) {\n return true;\n }\n\n const message = error instanceof Error ? error.message.trim() : '';\n if (!message) return false;\n\n return CONNECTIVITY_ERROR_PATTERNS.some((pattern) => pattern.test(message));\n}\n\nexport function getGlobalErrorMessage(locale?: string, error?: unknown): string {\n if (isLikelyConnectivityIssue(error)) {\n if (isTurkishLocale(locale)) {\n return 'Bağlantı sorunu oluştu. Lütfen tekrar deneyin.';\n }\n return 'Connection issue. Please try again.';\n }\n\n if (isTurkishLocale(locale)) {\n return 'Bir hata oluştu. Lütfen tekrar deneyin.';\n }\n return 'Something went wrong. Please try again.';\n}\n\nexport function registerGlobalErrorToastListener(): void {\n if (listenerRegistered || typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n listenerRegistered = true;\n listen('gengage:global:error', (detail) => {\n showGlobalErrorToast(detail);\n });\n}\n\nexport function showGlobalErrorToast(detail: GengageEventDetailMap['gengage:global:error']): void {\n if (typeof document === 'undefined') return;\n const message = detail.message.trim();\n if (!message) return;\n\n ensureStyles();\n const root = ensureRoot();\n syncRootThemeVars(root);\n root.innerHTML = '';\n\n const toast = document.createElement('section');\n toast.className = 'gengage-global-toast gengage-global-toast--error';\n toast.setAttribute('role', 'status');\n toast.setAttribute('aria-live', 'polite');\n\n const title = document.createElement('div');\n title.className = 'gengage-global-toast-title';\n title.textContent = sourceTitle(detail.source);\n\n const body = document.createElement('div');\n body.className = 'gengage-global-toast-message';\n body.textContent = message;\n\n toast.appendChild(title);\n toast.appendChild(body);\n root.appendChild(toast);\n root.classList.add(ROOT_VISIBLE_CLASS);\n\n if (dismissTimer) {\n clearTimeout(dismissTimer);\n dismissTimer = null;\n }\n\n if (detail.sticky === true) {\n return;\n }\n\n dismissTimer = setTimeout(() => {\n dismissGlobalErrorToast();\n }, clampDuration(detail.durationMs));\n}\n\nexport function dismissGlobalErrorToast(): void {\n if (typeof document === 'undefined') return;\n const root = document.getElementById(ROOT_ID);\n if (!root) return;\n\n root.classList.remove(ROOT_VISIBLE_CLASS);\n root.innerHTML = '';\n\n if (dismissTimer) {\n clearTimeout(dismissTimer);\n dismissTimer = null;\n }\n}\n\nfunction sourceTitle(source: GengageEventDetailMap['gengage:global:error']['source']): string {\n switch (source) {\n case 'chat':\n return 'Chat warning';\n case 'qna':\n return 'QnA warning';\n case 'simrel':\n return 'Widget warning';\n default:\n return 'Connection warning';\n }\n}\n\nfunction ensureRoot(): HTMLElement {\n const existing = document.getElementById(ROOT_ID);\n if (existing instanceof HTMLElement) return existing;\n\n const root = document.createElement('div');\n root.id = ROOT_ID;\n root.className = 'gengage-global-toast-root';\n document.body.appendChild(root);\n return root;\n}\n\nfunction syncRootThemeVars(root: HTMLElement): void {\n if (typeof document === 'undefined' || typeof window === 'undefined') return;\n const source = document.querySelector<HTMLElement>(\n '.gengage-chat-root, .gengage-qna-container, .gengage-simrel-container, .gengage-simbut-root',\n );\n if (!source) return;\n const computed = window.getComputedStyle(source);\n for (const name of THEME_SYNC_VARS) {\n const value = computed.getPropertyValue(name).trim();\n if (value) {\n root.style.setProperty(name, value);\n } else {\n root.style.removeProperty(name);\n }\n }\n}\n\nfunction clampDuration(durationMs?: number): number {\n if (typeof durationMs !== 'number' || !Number.isFinite(durationMs)) {\n return DEFAULT_DURATION_MS;\n }\n return Math.min(MAX_DURATION_MS, Math.max(MIN_DURATION_MS, Math.round(durationMs)));\n}\n\nfunction ensureStyles(): void {\n if (document.getElementById(STYLE_ID)) return;\n\n const style = document.createElement('style');\n style.id = STYLE_ID;\n const surfaceCard = BASE_WIDGET_THEME['--surface-card'] ?? BASE_WIDGET_THEME.backgroundColor ?? '#ffffff';\n const textPrimary = BASE_WIDGET_THEME['--text-primary'] ?? BASE_WIDGET_THEME.foregroundColor ?? '#111827';\n const borderDefault = BASE_WIDGET_THEME['--border-default'] ?? 'rgba(17, 24, 39, 0.10)';\n const error = BASE_WIDGET_THEME['--error'] ?? '#dc2626';\n const shadow3 = BASE_WIDGET_THEME['--shadow-3'] ?? '0 10px 24px rgba(16, 24, 40, 0.12)';\n const radiusCard = BASE_WIDGET_THEME['--radius-card'] ?? '16px';\n const textMuted = BASE_WIDGET_THEME['--text-muted'] ?? '#6b7280';\n style.textContent = `\n#${ROOT_ID} {\n position: fixed;\n top: 16px;\n right: 16px;\n z-index: 2147483646;\n pointer-events: none;\n}\n#${ROOT_ID}.${ROOT_VISIBLE_CLASS} {\n pointer-events: auto;\n}\n#${ROOT_ID} .gengage-global-toast {\n min-width: 260px;\n max-width: min(92vw, 420px);\n border-radius: var(--radius-card, ${radiusCard});\n border: 1px solid var(--ds-toast-error-border, color-mix(in srgb, var(--error, ${error}) 18%, var(--border-default, ${borderDefault})));\n border-left: 4px solid var(--ds-toast-error-accent, var(--error, ${error}));\n background: var(--ds-toast-error-bg, color-mix(in srgb, var(--error, ${error}) 5%, var(--surface-card, ${surfaceCard})));\n color: var(--ds-toast-error-fg, color-mix(in srgb, var(--error, ${error}) 22%, var(--text-primary, ${textPrimary})));\n box-shadow: var(--ds-toast-error-shadow, var(--shadow-3, ${shadow3}));\n padding: 10px 12px;\n font-family: var(--gengage-font-family, ${JSON.stringify(BASE_WIDGET_THEME.fontFamily ?? '-apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif')});\n font-size: 13px;\n line-height: 1.4;\n animation: gengage-global-toast-in 180ms ease-out forwards;\n}\n#${ROOT_ID} .gengage-global-toast-title {\n margin: 0 0 4px;\n font-size: 12px;\n font-weight: 700;\n color: var(--text-muted, ${textMuted});\n}\n#${ROOT_ID} .gengage-global-toast-message {\n margin: 0;\n font-weight: 500;\n}\n@keyframes gengage-global-toast-in {\n from {\n opacity: 0;\n transform: translateY(-8px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n`;\n document.head.appendChild(style);\n}\n","/**\n * Debug logging for integrators.\n *\n * Enable with: `localStorage.setItem('gengage:debug', '1')`\n * Disable with: `localStorage.removeItem('gengage:debug')`\n *\n * When disabled, all debug calls are no-ops with zero overhead\n * (the check is a single localStorage read cached per page load).\n */\n\nlet _enabled: boolean | null = null;\n\nfunction isEnabled(): boolean {\n if (_enabled !== null) return _enabled;\n try {\n _enabled = localStorage.getItem('gengage:debug') === '1';\n } catch {\n _enabled = false;\n }\n return _enabled;\n}\n\n/** Log a debug message (only when gengage:debug is enabled). */\nexport function debugLog(category: string, message: string, data?: unknown): void {\n if (!isEnabled()) return;\n const args: unknown[] = [`[gengage:${category}]`, message];\n if (data !== undefined) args.push(data);\n // eslint-disable-next-line no-console -- debug utility, gated by localStorage flag\n console.debug(...args);\n}\n\n/** Reset the cached enabled state (for testing). */\nexport function _resetDebugCache(): void {\n _enabled = null;\n}\n","/**\n * Abstract base class for all Gengage widgets.\n *\n * Provides:\n * - Lifecycle management (init → update → show/hide → destroy)\n * - Typed event emitter\n * - Theme application via CSS custom properties\n * - SPA context-update listener (gengage:context:update)\n * - Mount target resolution (selector string or HTMLElement)\n *\n * Subclasses must implement:\n * - protected onInit(config): Promise<void>\n * - protected onUpdate(context): void\n * - protected onShow(): void\n * - protected onHide(): void\n * - protected onDestroy(): void\n */\n\nimport type { BaseWidgetConfig, GengageWidget, PageContext, WidgetTheme } from './types.js';\nimport type { AnalyticsInput } from './analytics.js';\nimport type { AnalyticsContext } from './analytics-events.js';\nimport { checkoutStartEvent, checkoutCompleteEvent, meteringSummaryEvent } from './analytics-events.js';\nimport { listen } from './events.js';\nimport { resolveSession } from './context.js';\nimport { withDefaultWidgetTheme } from './ui-theme.js';\nimport { registerGlobalErrorToastListener } from './global-error-toast.js';\nimport { debugLog } from './debug.js';\n\ntype AnyHandler = (...args: unknown[]) => void;\n\nexport abstract class BaseWidget<\n TConfig extends BaseWidgetConfig = BaseWidgetConfig,\n> implements GengageWidget<TConfig> {\n protected config!: TConfig;\n protected root!: HTMLElement;\n protected isVisible = false;\n protected isInitialised = false;\n\n private readonly _handlers = new Map<string, Set<AnyHandler>>();\n private readonly _cleanups: Array<() => void> = [];\n private _ownsRoot = false;\n private _destroying = false;\n\n // ---------------------------------------------------------------------------\n // Public API\n // ---------------------------------------------------------------------------\n\n async init(config: TConfig): Promise<void> {\n if (this.isInitialised) {\n console.warn('[gengage] Widget already initialised. Call update() instead.');\n return;\n }\n\n const mergedTheme = withDefaultWidgetTheme(config.theme);\n\n this.config = {\n ...config,\n theme: mergedTheme,\n session: resolveSession(config.session),\n };\n\n this.root = this._resolveMount(config.mountTarget);\n this._applyTheme(mergedTheme);\n registerGlobalErrorToastListener();\n\n // Listen for context updates dispatched by the host page\n const off = listen('gengage:context:update', (patch) => this.update(patch));\n this._cleanups.push(off);\n\n debugLog('lifecycle', `${this.constructor.name}.init`, {\n accountId: config.accountId,\n sku: config.pageContext?.sku,\n });\n\n try {\n await this.onInit(this.config);\n } catch (err) {\n this.destroy();\n throw err;\n }\n if (this._destroying) return;\n this.isInitialised = true;\n debugLog('lifecycle', `${this.constructor.name} ready`);\n this.emit('ready');\n }\n\n update(context: Partial<PageContext>): void {\n if (!this.isInitialised) return;\n if (this.config.pageContext) {\n this.config = {\n ...this.config,\n pageContext: { ...this.config.pageContext, ...context },\n };\n } else if (context.pageType !== undefined) {\n // Only create a new pageContext when pageType is present (required field)\n this.config = { ...this.config, pageContext: context as PageContext };\n }\n this.onUpdate(context);\n this.emit('context-update', this.config.pageContext);\n }\n\n show(): void {\n if (this.isVisible) return;\n this.isVisible = true;\n this.root.style.display = '';\n this.onShow();\n this.emit('show');\n }\n\n hide(): void {\n if (!this.isVisible) return;\n this.isVisible = false;\n this.root.style.display = 'none';\n this.onHide();\n this.emit('hide');\n }\n\n destroy(): void {\n if (this._destroying) return;\n this._destroying = true;\n this.emit('destroy');\n this._cleanups.forEach((fn) => fn());\n this._cleanups.length = 0;\n this._handlers.clear();\n this.onDestroy();\n this.config.analyticsClient?.destroy();\n if (this._ownsRoot) {\n this.root.remove();\n } else {\n this.root.innerHTML = '';\n }\n this.isInitialised = false;\n }\n\n on(event: string, handler: AnyHandler): () => void {\n if (!this._handlers.has(event)) this._handlers.set(event, new Set());\n this._handlers.get(event)!.add(handler);\n return () => this._handlers.get(event)?.delete(handler);\n }\n\n /** Track a checkout start event. Called by host page to attribute checkout to widget interaction. */\n trackCheckout(\n type: 'start' | 'complete',\n data: {\n attribution_source: 'chat' | 'qna' | 'simrel';\n attribution_action_id: string;\n cart_value: number;\n currency: string;\n line_items: number;\n },\n ): void {\n const builder = type === 'start' ? checkoutStartEvent : checkoutCompleteEvent;\n this.track(builder(this.analyticsContext(), data));\n }\n\n /** Track a metering summary event. Called by host page for session-level aggregation. */\n flushMeteringSummary(data: { meter_key: string; quantity: number; unit: string }): void {\n this.track(meteringSummaryEvent(this.analyticsContext(), data));\n }\n\n // ---------------------------------------------------------------------------\n // Protected — subclasses implement these\n // ---------------------------------------------------------------------------\n\n protected abstract onInit(config: TConfig): Promise<void>;\n protected abstract onUpdate(context: Partial<PageContext>): void;\n protected abstract onShow(): void;\n protected abstract onHide(): void;\n protected abstract onDestroy(): void;\n\n // ---------------------------------------------------------------------------\n // Protected helpers\n // ---------------------------------------------------------------------------\n\n /** Emit a widget event to all registered handlers. */\n protected emit(event: string, ...args: unknown[]): void {\n this._handlers.get(event)?.forEach((h) => h(...args));\n }\n\n /** Register a cleanup function to run on destroy(). */\n protected addCleanup(fn: () => void): void {\n this._cleanups.push(fn);\n }\n\n /** Track an analytics event (no-op if analyticsClient is not configured). */\n protected track(input: AnalyticsInput): void {\n this.config.analyticsClient?.track(input);\n }\n\n /** Build the shared analytics context from widget config. */\n protected analyticsContext(): AnalyticsContext {\n const ctx: AnalyticsContext = {\n account_id: this.config.accountId,\n session_id: this.config.session?.sessionId ?? '',\n correlation_id: this.config.session?.sessionId ?? '',\n };\n if (this.config.session?.viewId !== undefined) ctx.view_id = this.config.session.viewId;\n if (this.config.session?.userId !== undefined) ctx.user_id = this.config.session.userId;\n if (this.config.pageContext?.pageType !== undefined) ctx.page_type = this.config.pageContext.pageType;\n if (this.config.pageContext?.sku !== undefined) ctx.sku = this.config.pageContext.sku;\n if (this.config.session?.abTestVariant !== undefined) ctx.ab_test_variant = this.config.session.abTestVariant;\n if (this.config.session?.abTestExperimentId !== undefined)\n ctx.ab_test_experiment_id = this.config.session.abTestExperimentId;\n return ctx;\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private _resolveMount(target?: HTMLElement | string): HTMLElement {\n if (target instanceof HTMLElement) return target;\n if (typeof target === 'string') {\n const el = document.querySelector<HTMLElement>(target);\n if (!el) throw new Error(`[gengage] Mount target not found: \"${target}\"`);\n return el;\n }\n // Default: create a div prepended to body so the widget's launcher appears\n // near the start of the tab order rather than being buried at the very end\n // (which would force keyboard-only users to tab through the entire page).\n const div = document.createElement('div');\n div.dataset['gengageWidget'] = this.constructor.name.toLowerCase();\n document.body.insertBefore(div, document.body.firstChild);\n this._ownsRoot = true;\n return div;\n }\n\n private _applyTheme(theme?: WidgetTheme): void {\n if (!theme) return;\n for (const [key, value] of Object.entries(theme)) {\n if (value !== undefined) {\n const prop = key.startsWith('--') ? key : `--gengage-${toKebab(key)}`;\n this.root.style.setProperty(prop, value);\n }\n }\n }\n}\n\nfunction toKebab(str: string): string {\n return str.replace(/([A-Z])/g, '-$1').toLowerCase();\n}\n\n// ---------------------------------------------------------------------------\n// Chat widget public API (exposed on window.gengage.chat)\n// ---------------------------------------------------------------------------\n\nexport interface ChatPublicAPI {\n open(options?: { state?: 'full' | 'half'; initialMessage?: string }): void;\n openWithAction(action: import('./types.js').ActionPayload, options?: { sku?: string; state?: 'full' | 'half' }): void;\n /** Send a user message programmatically (same as typing + submit). */\n sendMessage(text: string): void;\n /** Send a backend action programmatically. */\n sendAction(action: import('./types.js').ActionPayload, options?: { silent?: boolean }): void;\n close(): void;\n saveSession(sessionId: string, sku: string): void;\n readonly isOpen: boolean;\n on(\n event: 'open' | 'close' | 'ready' | 'message' | 'error' | 'context-update' | 'destroy',\n handler: (...args: unknown[]) => void,\n ): () => void;\n trackCheckout(\n type: 'start' | 'complete',\n data: {\n attribution_source: 'chat' | 'qna' | 'simrel';\n attribution_action_id: string;\n cart_value: number;\n currency: string;\n line_items: number;\n },\n ): void;\n flushMeteringSummary(data: { meter_key: string; quantity: number; unit: string }): void;\n /**\n * Register a callback for a GA4 event name.\n * When the widget fires that event, the callback is invoked with the event detail.\n * Callbacks that return `false` or throw signal failure — the widget reacts accordingly\n * (e.g. showing an error message for add-to-cart failures).\n *\n * @returns unsubscribe function\n */\n addCallback(eventName: string, callback: (detail: Record<string, unknown>) => boolean | Promise<boolean>): () => void;\n}\n","/**\n * Zero-dependency UUIDv7 generator.\n *\n * 48-bit Unix-ms timestamp + random bits, version nibble 0x7, variant 0b10.\n * Lexicographically sortable — critical for thread ordering (threadA < threadB).\n */\n\nexport function uuidv7(): string {\n const now = Date.now();\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n\n // 48-bit timestamp (big-endian) in bytes 0–5\n bytes[0] = (now / 2 ** 40) & 0xff;\n bytes[1] = (now / 2 ** 32) & 0xff;\n bytes[2] = (now / 2 ** 24) & 0xff;\n bytes[3] = (now / 2 ** 16) & 0xff;\n bytes[4] = (now / 2 ** 8) & 0xff;\n bytes[5] = now & 0xff;\n\n // Version 7: set high nibble of byte 6\n bytes[6] = (bytes[6]! & 0x0f) | 0x70;\n\n // Variant 10xx: set high bits of byte 8\n bytes[8] = (bytes[8]! & 0x3f) | 0x80;\n\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n","/**\n * Two-way communication bridge between the host page and embedded widgets.\n *\n * Host -> Widget: The host sends `window.postMessage({ gengage, type, payload })`.\n * The bridge validates the origin, checks the namespace, and routes to handlers.\n *\n * Widget -> Host: The bridge dispatches a `CustomEvent('gengage:bridge:message')`\n * on window so the host can listen without tight coupling.\n *\n * Built-in message types:\n * - 'addToCart' -- host confirms cart addition\n * - 'productFavorite' -- widget → host: product card heart toggled (payload: sku, product, favorited, sessionId)\n * - 'navigate' -- host navigates to URL\n * - 'openChat' -- programmatic open\n * - 'closeChat' -- programmatic close\n * - 'getContext' -- host requests current context\n */\n\nexport interface BridgeMessage {\n type: string;\n payload?: unknown;\n}\n\nexport interface CommunicationBridgeOptions {\n /** Widget namespace for message identification (e.g. 'chat', 'qna'). */\n namespace: string;\n /** Allowed origins for postMessage security (default: ['*']). */\n allowedOrigins?: string[];\n /** Callback when a message is received from the host. */\n onMessage?: (msg: BridgeMessage) => void;\n}\n\ntype BridgeHandler = (payload: unknown) => void;\n\nexport class CommunicationBridge {\n private readonly _namespace: string;\n private readonly _allowedOrigins: readonly string[];\n private readonly _onMessage: ((msg: BridgeMessage) => void) | undefined;\n private readonly _handlers = new Map<string, Set<BridgeHandler>>();\n private readonly _messageListener: (event: MessageEvent) => void;\n private _destroyed = false;\n\n constructor(options: CommunicationBridgeOptions) {\n this._namespace = options.namespace;\n // Default to same-origin for postMessage security. Customers who need\n // cross-origin iframe communication can pass allowedOrigins: ['*'] or a\n // specific list of origins via ChatWidgetConfig.allowedOrigins.\n this._allowedOrigins = options.allowedOrigins ?? [location.origin];\n this._onMessage = options.onMessage;\n\n if (this._allowedOrigins.includes('*') && _isDevMode()) {\n console.warn('[gengage] postMessage bridge using wildcard origin. Set allowedOrigins for production security.');\n }\n\n this._messageListener = (event: MessageEvent) => this._handlePostMessage(event);\n window.addEventListener('message', this._messageListener);\n }\n\n /** Send a message to the host page via CustomEvent on window. */\n send(type: string, payload?: unknown): void {\n if (this._destroyed) return;\n\n const detail: { namespace: string; type: string; payload?: unknown } = {\n namespace: this._namespace,\n type,\n };\n if (payload !== undefined) {\n detail.payload = payload;\n }\n\n window.dispatchEvent(\n new CustomEvent('gengage:bridge:message', {\n detail,\n bubbles: false,\n }),\n );\n }\n\n /**\n * Register a handler for a specific message type.\n * Returns an unsubscribe function.\n */\n on(type: string, handler: BridgeHandler): () => void {\n if (!this._handlers.has(type)) {\n this._handlers.set(type, new Set());\n }\n // The Map.get is guaranteed non-null after the set above\n const handlers = this._handlers.get(type)!;\n handlers.add(handler);\n\n return () => {\n handlers.delete(handler);\n if (handlers.size === 0) {\n this._handlers.delete(type);\n }\n };\n }\n\n /** Clean up all event listeners and handlers. */\n destroy(): void {\n if (this._destroyed) return;\n this._destroyed = true;\n window.removeEventListener('message', this._messageListener);\n this._handlers.clear();\n }\n\n // ---------------------------------------------------------------------------\n // Private\n // ---------------------------------------------------------------------------\n\n private _handlePostMessage(event: MessageEvent): void {\n if (this._destroyed) return;\n\n // Validate origin\n if (!this._isOriginAllowed(event.origin)) return;\n\n // Validate message shape: must be an object with { gengage, type }\n const data: unknown = event.data;\n if (!isValidBridgeData(data)) return;\n\n // Only process messages targeting this namespace\n if (data.gengage !== this._namespace) return;\n\n const msg: BridgeMessage = { type: data.type };\n if (data.payload !== undefined) {\n msg.payload = data.payload;\n }\n\n // Invoke the general onMessage callback\n this._onMessage?.(msg);\n\n // Route to type-specific handlers\n const handlers = this._handlers.get(msg.type);\n if (handlers) {\n for (const handler of handlers) {\n handler(msg.payload);\n }\n }\n }\n\n private _isOriginAllowed(origin: string): boolean {\n if (this._allowedOrigins.includes('*')) return true;\n return this._allowedOrigins.includes(origin);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Shape guard for incoming postMessage data. */\nfunction isValidBridgeData(data: unknown): data is { gengage: string; type: string; payload?: unknown } {\n if (typeof data !== 'object' || data === null) return false;\n const obj = data as Record<string, unknown>;\n return typeof obj['gengage'] === 'string' && typeof obj['type'] === 'string';\n}\n\n/**\n * Returns true when running in a development build.\n * Uses process.env.NODE_ENV (injected by Vite/bundlers for all output formats)\n * instead of import.meta.env which is ESM-only and triggers CJS build warnings.\n */\nfunction _isDevMode(): boolean {\n try {\n return typeof process !== 'undefined' && process.env?.['NODE_ENV'] !== 'production';\n } catch {\n return false;\n }\n}\n","import type { UnknownActionPolicy } from './config-schema.js';\nimport type { ActionPayload, AddToCartParams, StreamEventAction } from './types.js';\nimport { isSafeUrl } from './safe-html.js';\nimport { debugLog } from './debug.js';\n\nexport interface HostActionHandlers {\n openChat?: (payload?: ActionPayload | unknown) => void;\n navigate?: (params: { url: string; newTab?: boolean }) => void;\n saveSession?: (params: { sessionId: string; sku: string }) => void;\n addToCart?: (params: AddToCartParams) => void;\n scriptCall?: (params: { name: string; payload?: Record<string, unknown> }) => void;\n unknown?: (action: StreamEventAction['action']) => void;\n}\n\nexport interface ActionRouterOptions {\n allowScriptCall?: boolean;\n unknownActionPolicy?: UnknownActionPolicy;\n logger?: Pick<Console, 'warn' | 'error' | 'debug'>;\n}\n\nconst defaultLogger: Pick<Console, 'warn' | 'error' | 'debug'> = console;\n\nexport function routeStreamAction(\n event: StreamEventAction,\n handlers: HostActionHandlers,\n options: ActionRouterOptions = {},\n): void {\n const action = event.action;\n const logger = options.logger ?? defaultLogger;\n debugLog('action', `routing action: ${action.kind}`, action);\n\n switch (action.kind) {\n case 'open_chat': {\n handlers.openChat?.(action.payload);\n return;\n }\n case 'navigate': {\n if (typeof action.url !== 'string') {\n handleUnknownAction(action, handlers, options, logger);\n return;\n }\n if (!isSafeUrl(action.url)) {\n logger.warn('[gengage] Blocked navigation to unsafe URL:', action.url);\n return;\n }\n const newTab = typeof action.newTab === 'boolean' ? action.newTab : undefined;\n if (handlers.navigate) {\n handlers.navigate({ url: action.url, ...(newTab !== undefined && { newTab }) });\n return;\n }\n defaultNavigate(action.url, newTab);\n return;\n }\n case 'save_session': {\n if (typeof action.sessionId !== 'string' || typeof action.sku !== 'string') {\n handleUnknownAction(action, handlers, options, logger);\n return;\n }\n handlers.saveSession?.({ sessionId: action.sessionId, sku: action.sku });\n return;\n }\n case 'add_to_cart': {\n if (\n typeof action.sku !== 'string' ||\n typeof action.quantity !== 'number' ||\n typeof action.cartCode !== 'string'\n ) {\n handleUnknownAction(action, handlers, options, logger);\n return;\n }\n handlers.addToCart?.({\n sku: action.sku,\n quantity: action.quantity,\n cartCode: action.cartCode,\n });\n return;\n }\n case 'script_call': {\n if (options.allowScriptCall === false) {\n handleUnknownAction(action, handlers, options, logger);\n return;\n }\n if (typeof action.name !== 'string') {\n handleUnknownAction(action, handlers, options, logger);\n return;\n }\n const payload = isRecord(action.payload) ? action.payload : undefined;\n handlers.scriptCall?.({ name: action.name, ...(payload !== undefined && { payload }) });\n return;\n }\n default: {\n handleUnknownAction(action, handlers, options, logger);\n }\n }\n}\n\nfunction handleUnknownAction(\n action: StreamEventAction['action'],\n handlers: HostActionHandlers,\n options: ActionRouterOptions,\n logger: Pick<Console, 'warn' | 'error' | 'debug'>,\n): void {\n const policy = options.unknownActionPolicy ?? 'log-and-ignore';\n if (policy === 'delegate') {\n handlers.unknown?.(action);\n if (!handlers.unknown) {\n logger.warn('[gengage] Unknown action received without delegate handler', action);\n }\n return;\n }\n\n if (policy === 'throw') {\n throw new Error(`[gengage] Unknown action kind: ${(action as { kind?: unknown }).kind}`);\n }\n\n logger.warn('[gengage] Unknown action ignored', action);\n}\n\nfunction defaultNavigate(url: string, newTab?: boolean): void {\n if (typeof window === 'undefined') return;\n if (!isSafeUrl(url)) {\n console.warn('[gengage] Blocked navigation to unsafe URL:', url);\n return;\n }\n if (newTab) {\n window.open(url, '_blank', 'noopener,noreferrer');\n return;\n }\n window.location.href = url;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","export const ALLOWED_MIME_TYPES = ['image/jpeg', 'image/png', 'image/webp'] as const;\nexport const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5 MB\n\nexport type ValidationResult = { ok: true } | { ok: false; reason: 'invalid_type' | 'too_large' };\n\nexport function validateImageFile(file: File): ValidationResult {\n if (!(ALLOWED_MIME_TYPES as readonly string[]).includes(file.type)) {\n return { ok: false, reason: 'invalid_type' };\n }\n if (file.size > MAX_FILE_SIZE) {\n return { ok: false, reason: 'too_large' };\n }\n return { ok: true };\n}\n","/**\n * Wire protocol streaming utilities.\n *\n * The Gengage backend streams responses as Newline-Delimited JSON (NDJSON)\n * over a single HTTP response. This module provides utilities to consume\n * that stream and parse events in real time.\n *\n * Protocol shape:\n * {\"type\":\"metadata\",\"sessionId\":\"...\",\"model\":\"...\"}\n * {\"type\":\"text_chunk\",\"content\":\"Hello \"}\n * {\"type\":\"text_chunk\",\"content\":\"there!\",\"final\":true}\n * {\"type\":\"ui_spec\",\"widget\":\"chat\",\"spec\":{...}}\n * {\"type\":\"action\",\"action\":{\"kind\":\"navigate\",\"url\":\"...\"}}\n * {\"type\":\"done\"}\n */\n\nimport type { StreamEvent } from './types.js';\nimport { debugLog } from './debug.js';\n\nexport type StreamEventHandler = (event: StreamEvent) => void;\n\n/** Lightweight runtime check: parsed object must have a string `type` field. */\nfunction isMinimalStreamEvent(value: unknown): value is StreamEvent {\n return typeof value === 'object' && value !== null && typeof (value as Record<string, unknown>)['type'] === 'string';\n}\n\n/**\n * Split a string of concatenated JSON objects into individual JSON strings.\n * Handles nested braces and strings correctly.\n *\n * Example: '{\"a\":1}{\"b\":2}' → ['{\"a\":1}', '{\"b\":2}']\n */\nfunction splitConcatenatedJson(text: string): string[] {\n const parts: string[] = [];\n let depth = 0;\n let inString = false;\n let escape = false;\n let start = 0;\n\n for (let i = 0; i < text.length; i++) {\n const ch = text[i]!;\n if (escape) {\n escape = false;\n continue;\n }\n if (ch === '\\\\' && inString) {\n escape = true;\n continue;\n }\n if (ch === '\"') {\n inString = !inString;\n continue;\n }\n if (inString) continue;\n if (ch === '{') depth++;\n if (ch === '}') {\n depth--;\n if (depth === 0) {\n parts.push(text.slice(start, i + 1));\n start = i + 1;\n }\n }\n }\n return parts.length > 0 ? parts : [text];\n}\n\nexport interface StreamOptions {\n /** Called for each parsed StreamEvent. */\n onEvent: StreamEventHandler;\n /** Called once when the stream closes normally. */\n onDone?: () => void;\n /** Called if the stream errors or the response is non-2xx. */\n onError?: (err: Error) => void;\n /** AbortController signal to cancel mid-stream. */\n signal?: AbortSignal;\n /**\n * Max milliseconds to wait between chunks before treating the stream as dead.\n * Prevents the UI from hanging indefinitely when the backend stops sending\n * data without closing the connection. Default: 60_000 (60 s).\n */\n idleTimeoutMs?: number;\n}\n\n/**\n * Process a single line from the stream buffer. Returns true if 'done' event was received.\n * Handles SSE prefix, concatenated JSON, and malformed lines gracefully.\n */\nfunction processLine(line: string, options: StreamOptions): boolean {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(':')) return false;\n\n const jsonStr = trimmed.startsWith('data: ') ? trimmed.slice(6) : trimmed;\n if (jsonStr === '[DONE]') {\n options.onDone?.();\n return true;\n }\n\n // Try direct parse first (fast path for well-formed NDJSON)\n try {\n const event = JSON.parse(jsonStr) as StreamEvent;\n if (!isMinimalStreamEvent(event)) {\n if (import.meta.env?.DEV) {\n console.warn('[gengage] Skipping event without valid type:', jsonStr.slice(0, 100));\n }\n return false;\n }\n debugLog('stream', `event: ${event.type}`, event);\n options.onEvent(event);\n if (event.type === 'done') {\n options.onDone?.();\n return true;\n }\n return false;\n } catch {\n // Direct parse failed — try splitting concatenated JSON objects\n // (backend may send multiple objects without newline separators)\n const parts = splitConcatenatedJson(jsonStr);\n if (parts.length > 1) {\n for (const part of parts) {\n try {\n const event = JSON.parse(part) as StreamEvent;\n if (!isMinimalStreamEvent(event)) continue;\n options.onEvent(event);\n if (event.type === 'done') {\n options.onDone?.();\n return true;\n }\n } catch {\n if (import.meta.env?.DEV) {\n console.warn('[gengage] Skipping malformed stream fragment:', part.slice(0, 100));\n }\n }\n }\n return false;\n }\n\n if (import.meta.env?.DEV) {\n console.warn('[gengage] Skipping malformed stream line:', jsonStr.slice(0, 100));\n }\n return false;\n }\n}\n\n/**\n * Consume an NDJSON streaming response and call onEvent for each line.\n *\n * Usage:\n * const controller = new AbortController();\n * await consumeStream(response, {\n * onEvent: (event) => { ... },\n * signal: controller.signal,\n * });\n */\nexport async function consumeStream(response: Response, options: StreamOptions): Promise<void> {\n if (!response.ok) {\n options.onError?.(new Error(`HTTP ${response.status}: ${response.statusText}`));\n return;\n }\n\n if (!response.body) {\n options.onError?.(new Error('Response body is null — streaming not supported'));\n return;\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder('utf-8');\n let buffer = '';\n const idleTimeout = options.idleTimeoutMs ?? 60_000;\n\n const IDLE_TIMEOUT_SENTINEL = Symbol('idle-timeout');\n let readerCancelled = false;\n\n try {\n while (true) {\n // Race reader against an idle timeout so the UI never hangs indefinitely\n // when the backend stops sending data without closing the connection.\n const readPromise = reader.read();\n let timer: ReturnType<typeof setTimeout> | undefined;\n const result = await (idleTimeout > 0\n ? Promise.race([\n readPromise.then((r) => {\n clearTimeout(timer);\n return r;\n }),\n new Promise<typeof IDLE_TIMEOUT_SENTINEL>((resolve) => {\n timer = setTimeout(() => resolve(IDLE_TIMEOUT_SENTINEL), idleTimeout);\n }),\n ])\n : readPromise);\n\n if (result === IDLE_TIMEOUT_SENTINEL) {\n // Timeout won the race — reader.read() is still pending.\n // Cancel the reader to release the underlying stream body.\n readerCancelled = true;\n await reader.cancel();\n break;\n }\n\n const { done, value } = result;\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process all complete lines in the buffer\n const lines = buffer.split('\\n');\n // Last element may be an incomplete line — keep it in the buffer\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (processLine(line, options)) return;\n }\n }\n\n // Process remaining buffer (final line without trailing newline).\n // Check return value — if a 'done' event was in the trailing buffer,\n // processLine already called onDone; returning here prevents a second fire.\n if (buffer.trim()) {\n if (processLine(buffer, options)) return;\n }\n } catch (err) {\n if (err instanceof DOMException && err.name === 'AbortError') return;\n options.onError?.(err instanceof Error ? err : new Error(String(err)));\n } finally {\n // reader.releaseLock() throws if a read is pending; cancel() above handles\n // the timeout case (cancel releases the lock implicitly).\n if (!readerCancelled) {\n reader.releaseLock();\n }\n }\n\n // Guard: only fire onDone if processLine didn't already fire it via a 'done' event.\n // processLine calls onDone for type:'done' events; if that happened, it returned true\n // and we exited via `return` above. Reaching here means no 'done' event was received,\n // so it's safe to fire onDone as a stream-completion fallback.\n options.onDone?.();\n}\n\n/**\n * Convenience: POST to a streaming endpoint and consume the response.\n *\n * @returns an AbortController that cancels the stream when aborted.\n */\nexport function streamPost(\n url: string,\n body: unknown,\n options: StreamOptions & { headers?: Record<string, string> },\n): AbortController {\n const controller = new AbortController();\n const signal = options.signal ? anySignal([options.signal, controller.signal]) : controller.signal;\n\n fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n body: JSON.stringify(body),\n signal,\n })\n .then((response) => consumeStream(response, { ...options, signal }))\n .catch((err) => {\n if (err instanceof DOMException && err.name === 'AbortError') return;\n options.onError?.(err instanceof Error ? err : new Error(String(err)));\n });\n\n return controller;\n}\n\n/**\n * Merge multiple AbortSignals — fires when any one aborts.\n * (Native AbortSignal.any() not universally available yet.)\n */\nfunction anySignal(signals: AbortSignal[]): AbortSignal {\n const controller = new AbortController();\n const cleanups: Array<() => void> = [];\n\n for (const signal of signals) {\n if (signal.aborted) {\n controller.abort(signal.reason);\n return controller.signal;\n }\n const handler = () => controller.abort(signal.reason);\n signal.addEventListener('abort', handler, { once: true });\n cleanups.push(() => signal.removeEventListener('abort', handler));\n }\n\n // When the merged controller aborts, clean up listeners on all source signals\n controller.signal.addEventListener(\n 'abort',\n () => {\n for (const cleanup of cleanups) cleanup();\n },\n { once: true },\n );\n\n return controller.signal;\n}\n","/**\n * Compact browse-card keyword line for `aiSuggestedSearches`.\n *\n * Prefer backend `display_keywords`; otherwise derive short fragments from\n * `chosen_attribute` / `short_name`. Does not use `why_different` (avoids long\n * explanatory sentences in the tertiary line).\n */\n\nexport type SuggestedSearchKeywordSource = {\n display_keywords?: string[];\n chosen_attribute?: string;\n short_name?: string;\n};\n\nconst MAX_KEYWORDS = 3;\n\nconst cleanKeyword = (value: string, options?: { stripLeadingStopWords?: boolean }): string => {\n const normalized = value\n .replace(/\\s+/g, ' ')\n .trim()\n .replace(/^[,.;:!?•-]+|[,.;:!?•-]+$/g, '');\n\n if (!normalized) {\n return '';\n }\n\n const withoutStopWords = options?.stripLeadingStopWords\n ? normalized.replace(/^(?:daha|için)\\s+/i, '').trim()\n : normalized;\n\n if (!withoutStopWords) {\n return '';\n }\n\n return withoutStopWords.split(/\\s+/).slice(0, 3).join(' ').trim();\n};\n\nconst splitKeywordSource = (value?: string, options?: { stripLeadingStopWords?: boolean }): string[] => {\n if (!value) {\n return [];\n }\n\n return value\n .split(/[•,;:/()]|(?:\\sve\\s)|(?:\\sand\\s)|(?:\\sile\\s)|(?:\\sfor\\s)|(?:\\swith\\s)/i)\n .map((part) => cleanKeyword(part, options))\n .filter(Boolean);\n};\n\n/**\n * Ordered unique keywords (max 3) for suggested-search browse cards.\n */\nexport function getSuggestedSearchKeywords(search: SuggestedSearchKeywordSource): string[] {\n const explicitKeywords = (search.display_keywords ?? []).flatMap((keyword) =>\n splitKeywordSource(keyword, { stripLeadingStopWords: true }),\n );\n const uniqueExplicit = explicitKeywords.filter((keyword, index) => explicitKeywords.indexOf(keyword) === index);\n if (uniqueExplicit.length > 0) {\n return uniqueExplicit.slice(0, MAX_KEYWORDS);\n }\n\n const fallbackKeywords = [\n ...splitKeywordSource(search.chosen_attribute, { stripLeadingStopWords: true }),\n ...splitKeywordSource(search.short_name),\n ];\n return fallbackKeywords\n .filter((keyword, index) => fallbackKeywords.indexOf(keyword) === index)\n .slice(0, MAX_KEYWORDS);\n}\n\n/** Join keywords for the tertiary browse line (e.g. \"A • B • C\"). */\nexport function getSuggestedSearchKeywordsText(search: SuggestedSearchKeywordSource): string {\n return getSuggestedSearchKeywords(search).join(' • ');\n}\n","/**\n * Wire protocol adapter.\n *\n * Backend emits NDJSON events with `type` values like\n * `outputText`, `suggestedActions`, `productList`, etc.\n *\n * This module translates those backend events into the SDK's normalized\n * `StreamEvent` model.\n *\n * Also handles JSON-mode responses from `similar_products` and\n * `product_groupings` endpoints.\n */\n\nimport type {\n StreamEvent,\n StreamEventMetadata,\n StreamEventTextChunk,\n StreamEventUISpec,\n StreamEventAction,\n StreamEventDone,\n StreamEventError,\n UIElement,\n} from './types.js';\nimport { getSuggestedSearchKeywordsText } from './suggested-search-keywords.js';\n\ntype WidgetName = 'chat' | 'qna' | 'simrel';\n\ninterface V1RequestDetails {\n type?: string;\n payload?: unknown;\n [key: string]: unknown;\n}\n\ninterface V1ProductSuggestionsLabel {\n label?: string;\n sentiment?: string;\n [key: string]: unknown;\n}\n\ninterface V1ReviewHighlightItem {\n review_class?: string;\n review_text?: string;\n review_rating?: string | number;\n review_tag?: string;\n [key: string]: unknown;\n}\n\ninterface V1OutputText {\n type: 'outputText';\n payload: {\n text?: string;\n plain_text?: string;\n is_error?: boolean;\n [key: string]: unknown;\n };\n}\n\ninterface V1SuggestedActionItem {\n title?: string;\n icon?: string;\n image?: string | null;\n requestDetails?: V1RequestDetails;\n [key: string]: unknown;\n}\n\ninterface V1SuggestedActions {\n type: 'suggestedActions';\n payload: {\n actions?: V1SuggestedActionItem[];\n [key: string]: unknown;\n };\n}\n\nexport interface V1Product {\n sku: string;\n name: string;\n brand?: string;\n images?: string[];\n price?: number;\n price_discounted?: number;\n price_discount_rate?: number;\n price_currency?: string;\n discount_reason?: string;\n url?: string;\n rating?: number;\n review_count?: number;\n cart_code?: string;\n in_stock?: boolean;\n description?: string;\n description_html?: string;\n features?: Array<{ name?: string; key?: string; value?: string | number | boolean; [key: string]: unknown }>;\n specifications?: Record<string, string> | Array<{ key: string; value: string }>;\n facet_tags?: string[];\n short_name?: string;\n category_ids?: string[];\n category_names?: string[];\n variants?: Array<Record<string, unknown>>;\n facet_hits?: Record<string, unknown> | null;\n promotions?: string[];\n}\n\ninterface V1ProductList {\n type: 'productList';\n payload: {\n product_list?: V1Product[];\n source?: string;\n title?: string;\n offset?: number;\n page_size?: number;\n end_of_list?: boolean;\n [key: string]: unknown;\n };\n}\n\ninterface V1ProductDetails {\n type: 'productDetails';\n payload: { productDetails?: V1Product; [key: string]: unknown };\n}\n\ninterface V1ProductDetailsSimilars {\n type: 'productDetailsSimilars';\n payload: { similarProducts?: V1Product[]; [key: string]: unknown };\n}\n\ninterface V1ComparisonTable {\n type: 'comparisonTable';\n payload: {\n multiple_product_details?: V1Product[];\n table?: Record<string, string[] | Record<string, unknown>>;\n features_list?: string[];\n product_comparison_framework?: {\n key_differences?: string[];\n recommended_choice?: string;\n recommended_choice_sku?: string;\n special_considerations?: string[];\n criteria_view?: Record<string, string>;\n criteria_view_short?: Record<string, string>;\n compared_field_names?: string[];\n winner_product?: Array<{ sku?: string; name?: string; product_detail?: { sku?: string } }>;\n winner_hits?: Record<string, { positive?: string[]; negative?: string[] }>;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n };\n}\n\ninterface V1Context {\n type: 'context';\n payload: {\n panel?: Record<string, unknown>;\n messages?: Array<{ role?: string; content?: string }>;\n message_id?: string;\n [key: string]: unknown;\n };\n}\n\ninterface V1ChatStreamEnd {\n type: 'chatStreamEnd';\n payload?: Record<string, unknown>;\n}\n\ninterface V1Loading {\n type: 'loading';\n payload: {\n text?: string;\n is_dynamic?: boolean;\n thinking_messages?: string[];\n [key: string]: unknown;\n };\n}\n\ninterface V1PanelLoading {\n type: 'panelLoading';\n payload?: { text?: string; pending_type?: string; [key: string]: unknown };\n}\n\ninterface V1SimilarLoading {\n type: 'similarLoading';\n payload?: { text?: string; pending_type?: string; [key: string]: unknown };\n}\n\ninterface V1Redirect {\n type: 'redirect';\n payload: { url?: string; new_tab?: boolean; to?: string; [key: string]: unknown };\n}\n\ninterface V1Error {\n type: 'error';\n payload?: {\n text?: string;\n message?: string;\n error?: string;\n [key: string]: unknown;\n };\n}\n\ninterface V1Noop {\n type: 'dummy';\n payload?: Record<string, unknown>;\n}\n\ninterface V1LauncherAction {\n type: 'launcherAction';\n payload?: Record<string, unknown>;\n}\n\ninterface V1LauncherText {\n type: 'text';\n payload: {\n type?: string;\n text?: string;\n payload?: Record<string, unknown>;\n theme?: string;\n [key: string]: unknown;\n };\n}\n\ninterface V1ProductItem {\n type: 'productItem';\n payload: V1Product & { group_id?: string; [key: string]: unknown };\n}\n\ninterface V1LauncherTextImage {\n type: 'text_image';\n payload: {\n type?: string;\n text?: string;\n image_url?: string;\n theme?: string;\n action?: V1RequestDetails;\n [key: string]: unknown;\n };\n}\n\ninterface V1LauncherQuickQna {\n type: 'quick_qna';\n payload: {\n type?: string;\n theme?: string;\n action_list?: V1SuggestedActionItem[];\n [key: string]: unknown;\n };\n}\n\ninterface V1ReviewHighlights {\n type: 'reviewHighlights';\n payload: {\n sku?: string;\n reviews?: V1ReviewHighlightItem[];\n [key: string]: unknown;\n };\n}\n\nexport interface V1ProsAndCons {\n type: 'prosAndCons';\n payload: { pros?: string[]; cons?: string[]; product_name?: string; [key: string]: unknown };\n}\n\ninterface V1VisitorDataResponse {\n type: 'visitorDataResponse';\n payload: Record<string, unknown>;\n}\n\ninterface V1AiProductSuggestion {\n sku?: string;\n short_name?: string;\n role?: string;\n labels?: V1ProductSuggestionsLabel[];\n reason?: string;\n expert_quality_score?: number;\n review_highlight?: string;\n /** Campaign / merchant discount label; merged into normalized product for price UI. */\n discount_reason?: string;\n product_item?: V1Product;\n requestDetails?: V1RequestDetails;\n [key: string]: unknown;\n}\n\ninterface V1AiProductSuggestions {\n type: 'aiProductSuggestions';\n payload: {\n product_suggestions?: V1AiProductSuggestion[];\n [key: string]: unknown;\n };\n}\n\ninterface V1AiProductGrouping {\n name?: string;\n image?: string;\n labels?: string[];\n sku?: string;\n requestDetails?: V1RequestDetails;\n [key: string]: unknown;\n}\n\ninterface V1AiProductGroupings {\n type: 'aiProductGroupings';\n payload: {\n product_groupings?: V1AiProductGrouping[];\n [key: string]: unknown;\n };\n}\n\ninterface V1AiSuggestedSearch {\n short_name?: string;\n detailed_user_message?: string;\n /** Long explanatory copy — not used for the compact browse keyword line */\n why_different?: string;\n /** Preferred compact keyword chips for the browse card (see suggested-search-keywords) */\n display_keywords?: string[];\n chosen_attribute?: string;\n representative_product_sku?: string;\n group_skus?: string[];\n sku?: string;\n image?: string;\n requestDetails?: V1RequestDetails;\n [key: string]: unknown;\n}\n\ninterface V1AiSuggestedSearches {\n type: 'aiSuggestedSearches';\n payload: {\n suggested_searches?: V1AiSuggestedSearch[];\n [key: string]: unknown;\n };\n}\n\ninterface V1GetGroundingReview {\n type: 'getGroundingReview';\n payload: {\n title?: string;\n text?: string;\n review_count?: string;\n requestDetails?: V1RequestDetails;\n [key: string]: unknown;\n };\n}\n\ninterface V1Voice {\n type: 'voice';\n payload: {\n text?: string;\n audio_base64?: string;\n content_type?: string;\n [key: string]: unknown;\n };\n}\n\ninterface V1GroupList {\n type: 'groupList';\n payload: {\n group_list?: Array<{ group_name?: string; product_list?: V1Product[] }>;\n filter_tags?: Array<{ title?: string; requestDetails?: V1RequestDetails }>;\n [key: string]: unknown;\n };\n}\n\ninterface V1FormEvent {\n type: 'formGetInfo' | 'formTestDrive' | 'formServiceRequest' | 'launchFormPage';\n payload?: Record<string, unknown>;\n}\n\ninterface V1LauncherContent {\n type: 'launcherContent';\n payload?: Record<string, unknown>;\n}\n\ninterface V1Handoff {\n type: 'handoff';\n payload?: {\n summary?: string;\n products_discussed?: string[];\n user_sentiment?: string;\n [key: string]: unknown;\n };\n}\n\ntype V1StreamEvent =\n | V1OutputText\n | V1SuggestedActions\n | V1ProductList\n | V1ProductDetails\n | V1ProductDetailsSimilars\n | V1ComparisonTable\n | V1Context\n | V1ChatStreamEnd\n | V1Loading\n | V1PanelLoading\n | V1SimilarLoading\n | V1Redirect\n | V1Error\n | V1Noop\n | V1LauncherAction\n | V1LauncherText\n | V1ProductItem\n | V1LauncherTextImage\n | V1LauncherQuickQna\n | V1ReviewHighlights\n | V1AiProductSuggestions\n | V1AiProductGroupings\n | V1AiSuggestedSearches\n | V1GetGroundingReview\n | V1Voice\n | V1GroupList\n | V1FormEvent\n | V1LauncherContent\n | V1Handoff\n | { type: string; payload?: unknown; [key: string]: unknown };\n\nexport function adaptBackendEvent(raw: Record<string, unknown>): StreamEvent | null {\n const type = raw['type'];\n if (typeof type !== 'string') return null;\n\n if (isNormalizedStreamEvent(raw)) {\n return raw as unknown as StreamEvent;\n }\n\n const event = raw as V1StreamEvent;\n\n switch (event.type) {\n case 'outputText':\n return adaptOutputText(event as V1OutputText);\n case 'suggestedActions':\n return adaptSuggestedActions(event as V1SuggestedActions);\n case 'productList':\n return adaptProductList(event as V1ProductList);\n case 'productDetails':\n return adaptProductDetails(event as V1ProductDetails);\n case 'productDetailsSimilars':\n return adaptProductDetailsSimilars(event as V1ProductDetailsSimilars);\n case 'comparisonTable':\n return adaptComparisonTable(event as V1ComparisonTable);\n case 'context':\n return adaptContext(event as V1Context);\n case 'chatStreamEnd':\n return adaptChatStreamEnd();\n case 'loading':\n return adaptLoading(event as V1Loading);\n case 'panelLoading':\n return adaptPanelLoading(event as V1PanelLoading);\n case 'similarLoading':\n return adaptSimilarLoading(event as V1SimilarLoading);\n case 'redirect':\n return adaptRedirect(event as V1Redirect);\n case 'error':\n return adaptV1Error(event as V1Error);\n case 'dummy':\n return adaptNoop(event as V1Noop);\n case 'launcherAction':\n return adaptLauncherAction(event as V1LauncherAction);\n case 'text':\n return adaptLauncherText(event as V1LauncherText);\n case 'productItem':\n return adaptProductItem(event as V1ProductItem);\n case 'text_image':\n return adaptLauncherTextImage(event as V1LauncherTextImage);\n case 'quick_qna':\n return adaptLauncherQuickQna(event as V1LauncherQuickQna);\n case 'reviewHighlights':\n return adaptReviewHighlights(event as V1ReviewHighlights);\n case 'aiProductSuggestions':\n return adaptAiProductSuggestions(event as V1AiProductSuggestions);\n case 'aiProductGroupings':\n return adaptAiProductGroupings(event as V1AiProductGroupings);\n case 'aiSuggestedSearches':\n return adaptAiSuggestedSearches(event as V1AiSuggestedSearches);\n case 'prosAndCons':\n return adaptProsAndCons(event as V1ProsAndCons);\n case 'getGroundingReview':\n return adaptGetGroundingReview(event as V1GetGroundingReview);\n case 'voice':\n return adaptVoice(event as V1Voice);\n case 'visitorDataResponse':\n return adaptVisitorDataResponse(event as V1VisitorDataResponse);\n case 'productListPreview':\n return adaptProductListPreview();\n case 'groupList':\n return adaptGroupList(event as V1GroupList);\n case 'formGetInfo':\n case 'formTestDrive':\n case 'formServiceRequest':\n case 'launchFormPage':\n return adaptFormEvent(event as V1FormEvent);\n case 'launcherContent':\n return adaptLauncherContent(event as V1LauncherContent);\n case 'handoff':\n return adaptHandoff(event as V1Handoff);\n default:\n if (import.meta.env?.DEV) {\n console.warn('[gengage:protocol] Unknown backend event type:', event.type);\n }\n return null;\n }\n}\n\nfunction isNormalizedStreamEvent(raw: Record<string, unknown>): boolean {\n const type = raw['type'];\n if (typeof type !== 'string') return false;\n\n switch (type) {\n case 'metadata':\n return typeof raw['sessionId'] === 'string' && typeof raw['model'] === 'string';\n case 'text_chunk':\n return typeof raw['content'] === 'string';\n case 'ui_spec': {\n const widget = raw['widget'];\n if (widget !== 'chat' && widget !== 'qna' && widget !== 'simrel') return false;\n const spec = asRecord(raw['spec']);\n if (!spec) return false;\n return typeof spec['root'] === 'string' && asRecord(spec['elements']) !== null;\n }\n case 'action': {\n const action = asRecord(raw['action']);\n return action !== null && typeof action['kind'] === 'string';\n }\n case 'error':\n return typeof raw['code'] === 'string' && typeof raw['message'] === 'string';\n case 'done':\n return true;\n default:\n return false;\n }\n}\n\nfunction adaptOutputText(event: V1OutputText): StreamEventTextChunk | StreamEventError {\n const renderText = firstNonEmptyString(event.payload.text, event.payload.plain_text) ?? '';\n const plainText = firstNonEmptyString(event.payload.plain_text, event.payload.text) ?? renderText;\n if (event.payload.is_error) {\n return {\n type: 'error',\n code: 'BACKEND_ERROR',\n message: plainText || 'Backend returned an error',\n };\n }\n const result: StreamEventTextChunk = {\n type: 'text_chunk',\n content: renderText,\n final: true,\n };\n\n // Pass through product mentions for in-text linking\n const mentions = event.payload['product_mentions'];\n if (Array.isArray(mentions) && mentions.length > 0) {\n result.productMentions = mentions.filter(\n (m): m is { sku: string; short_name: string } =>\n typeof m === 'object' && m !== null && typeof m['sku'] === 'string' && typeof m['short_name'] === 'string',\n );\n }\n\n const skuMap = event.payload['sku_to_product_item'];\n if (skuMap && typeof skuMap === 'object' && !Array.isArray(skuMap)) {\n result.skuToProductItem = skuMap as Record<string, Record<string, unknown>>;\n }\n\n const convMode = event.payload['conversation_mode'];\n if (typeof convMode === 'string' && convMode) {\n result.conversationMode = convMode;\n }\n\n return result;\n}\n\nfunction adaptSuggestedActions(event: V1SuggestedActions): StreamEventUISpec {\n const entries = (event.payload.actions ?? []).map((action) => {\n const label = firstNonEmptyString(action.title) ?? '';\n const actionPayload = requestDetailsToAction(action.requestDetails, label);\n const result: ButtonEntry | null = actionPayload\n ? {\n label,\n action: actionPayload,\n }\n : null;\n if (!result) return null;\n if (typeof action.icon === 'string') result.icon = action.icon;\n if (typeof action.image === 'string') result.image = action.image;\n return result;\n });\n return buildActionButtonsUISpec(entries.filter(isNonNullable), 'chat');\n}\n\nfunction adaptProductList(event: V1ProductList): StreamEventUISpec {\n const spec = buildProductGridUISpec(event.payload.product_list ?? [], 'chat');\n spec.panelHint = 'panel';\n // Pass pagination fields and backend-provided title\n const root = spec.spec.elements[spec.spec.root];\n if (root) {\n if (typeof event.payload.offset === 'number') root.props = { ...root.props, offset: event.payload.offset };\n if (typeof event.payload.end_of_list === 'boolean')\n root.props = { ...root.props, endOfList: event.payload.end_of_list };\n if (typeof event.payload.title === 'string') root.props = { ...root.props, panelTitle: event.payload.title };\n }\n return spec;\n}\n\nfunction adaptProductDetails(event: V1ProductDetails): StreamEventUISpec {\n const product = event.payload.productDetails;\n if (!product) {\n return buildEmptyUISpec('chat');\n }\n const normalized = productToNormalized(product) as unknown as Record<string, unknown>;\n const detailProduct = {\n ...(product as unknown as Record<string, unknown>),\n ...normalized,\n };\n return {\n type: 'ui_spec',\n widget: 'chat',\n spec: {\n root: 'root',\n elements: {\n root: {\n type: 'ProductDetailsPanel',\n props: { product: detailProduct },\n },\n },\n },\n panelHint: 'panel',\n };\n}\n\nfunction adaptProductDetailsSimilars(event: V1ProductDetailsSimilars): StreamEventUISpec {\n const base = buildProductGridUISpec(event.payload.similarProducts ?? [], 'chat');\n // Mark for panel append rather than replace\n const root = base.spec.elements[base.spec.root];\n if (root) root.props = { ...root.props, similarsAppend: true };\n return { ...base, panelHint: 'panel' };\n}\n\nfunction adaptComparisonTable(event: V1ComparisonTable): StreamEventUISpec {\n const products = event.payload.multiple_product_details ?? [];\n const payloadRecord = event.payload as Record<string, unknown>;\n const framework = {\n key_differences: payloadRecord['key_differences'],\n recommended_choice: payloadRecord['recommended_choice'],\n recommended_choice_sku: payloadRecord['recommended_choice_sku'],\n special_considerations: payloadRecord['special_considerations'],\n criteria_view: payloadRecord['criteria_view'],\n criteria_view_short: payloadRecord['criteria_view_short'],\n compared_field_names: payloadRecord['compared_field_names'],\n winner_product: payloadRecord['winner_product'],\n winner_hits: payloadRecord['winner_hits'],\n ...(event.payload.product_comparison_framework ?? {}),\n } as NonNullable<V1ComparisonTable['payload']['product_comparison_framework']>;\n const table = event.payload.table;\n const featuresList = event.payload.features_list;\n\n // Normalize products\n const normalizedProducts: Array<Record<string, unknown>> = [];\n for (const p of products) {\n const norm = productToNormalized(p);\n normalizedProducts.push(norm as unknown as Record<string, unknown>);\n }\n\n const attributes = buildComparisonAttributes(table, normalizedProducts, framework, featuresList);\n\n // Find recommended product\n let recommendedSku: string | undefined;\n if (framework?.recommended_choice_sku) {\n recommendedSku = framework.recommended_choice_sku;\n } else if (framework?.winner_product && framework.winner_product.length > 0) {\n // Legacy structure wraps sku in product_detail; fall back to flat sku\n recommendedSku = framework.winner_product[0]?.product_detail?.sku ?? framework.winner_product[0]?.sku;\n }\n\n // Find recommended product object\n const recommended = recommendedSku\n ? (normalizedProducts.find((p) => p['sku'] === recommendedSku) ?? normalizedProducts[0])\n : normalizedProducts[0];\n\n // Extract highlights (key differences)\n const highlights: string[] = [];\n if (Array.isArray(framework?.key_differences)) {\n for (const diff of framework.key_differences) {\n if (typeof diff === 'string') highlights.push(diff);\n }\n }\n\n // Extract special cases\n const specialCases = normalizeStringList(framework?.special_considerations);\n\n // Build recommended choice explanation\n const recommendedText = framework?.recommended_choice;\n\n // Build winner hits per product\n const winnerHits = framework?.winner_hits;\n\n // Build product actions for \"View Product\" buttons\n const productActions: Record<string, Record<string, unknown>> = {};\n for (const p of normalizedProducts) {\n const sku = p['sku'] as string;\n if (sku) {\n productActions[sku] = {\n title: (p['name'] as string) ?? sku,\n type: 'launchSingleProduct',\n payload: { sku },\n };\n }\n }\n\n const props: Record<string, unknown> = {\n products: normalizedProducts,\n attributes,\n highlights,\n productActions,\n };\n\n if (recommended) props['recommended'] = recommended;\n if (specialCases.length > 0) props['specialCases'] = specialCases;\n if (recommendedText) props['recommendedText'] = recommendedText;\n if (winnerHits) props['winnerHits'] = winnerHits;\n\n // key_differences may be a single HTML string — pass raw for formatted rendering\n if (typeof framework?.key_differences === 'string') {\n props['keyDifferencesHtml'] = framework.key_differences;\n }\n\n // Pass special_considerations as structured data\n if (framework?.special_considerations) {\n props['specialConsiderations'] = framework.special_considerations;\n }\n\n return {\n type: 'ui_spec',\n widget: 'chat',\n spec: {\n root: 'root',\n elements: {\n root: {\n type: 'ComparisonTable',\n props,\n },\n },\n },\n panelHint: 'panel',\n };\n}\n\nfunction buildComparisonAttributes(\n table: V1ComparisonTable['payload']['table'],\n products: Array<Record<string, unknown>>,\n framework: NonNullable<V1ComparisonTable['payload']['product_comparison_framework']>,\n featuresList?: string[],\n): Array<{ label: string; values: string[] }> {\n if (!table) return [];\n\n const entries = Object.entries(table);\n if (entries.length === 0) return [];\n\n const firstValue = entries[0]?.[1];\n if (Array.isArray(firstValue)) {\n const displayNames = framework.criteria_view ?? framework.criteria_view_short ?? {};\n const fieldOrder = framework.compared_field_names ?? Object.keys(table);\n const attributes: Array<{ label: string; values: string[] }> = [];\n for (const fieldName of fieldOrder) {\n const values = table[fieldName];\n if (!values || !Array.isArray(values)) continue;\n const label = displayNames[fieldName] ?? fieldName;\n attributes.push({ label, values: values.map((v) => (typeof v === 'string' ? v : String(v ?? ''))) });\n }\n return attributes;\n }\n\n const rowMap = table as Record<string, Record<string, unknown>>;\n const orderedSkus = products.map((product) => String(product['sku'] ?? '')).filter((sku) => sku.length > 0);\n const displayNames = framework.criteria_view ?? framework.criteria_view_short ?? {};\n const rawFieldOrder =\n featuresList && featuresList.length > 0\n ? featuresList\n : framework.compared_field_names && framework.compared_field_names.length > 0\n ? framework.compared_field_names\n : collectComparisonFields(rowMap);\n\n const fieldOrder = rawFieldOrder.filter(\n (field) => field !== 'name' && field !== 'name_short' && !field.endsWith('_short'),\n );\n const attributes: Array<{ label: string; values: string[] }> = [];\n\n for (const fieldName of fieldOrder) {\n const values = orderedSkus.map((sku) => {\n const row = rowMap[sku];\n if (!row || typeof row !== 'object') return '';\n const shortValue = row[`${fieldName}_short`];\n const longValue = row[fieldName];\n return stringifyComparisonValue(shortValue ?? longValue);\n });\n if (values.every((value) => value.length === 0)) continue;\n const label = displayNames[fieldName] ?? fieldName;\n attributes.push({ label, values });\n }\n\n return attributes;\n}\n\nfunction collectComparisonFields(rowMap: Record<string, Record<string, unknown>>): string[] {\n const fields: string[] = [];\n const seen = new Set<string>();\n for (const row of Object.values(rowMap)) {\n if (!row || typeof row !== 'object') continue;\n for (const key of Object.keys(row)) {\n if (seen.has(key)) continue;\n seen.add(key);\n fields.push(key);\n }\n }\n return fields;\n}\n\nfunction stringifyComparisonValue(value: unknown): string {\n if (typeof value === 'string') return value;\n if (typeof value === 'number' || typeof value === 'boolean') return String(value);\n return '';\n}\n\nfunction adaptContext(event: V1Context): StreamEventMetadata {\n return {\n type: 'metadata',\n sessionId: '',\n model: '',\n meta: {\n panel: event.payload.panel,\n messages: event.payload.messages,\n message_id: event.payload.message_id,\n },\n };\n}\n\nfunction adaptChatStreamEnd(): StreamEventDone {\n return { type: 'done' };\n}\n\nfunction adaptLoading(event: V1Loading): StreamEventMetadata {\n return {\n type: 'metadata',\n sessionId: '',\n model: '',\n meta: {\n loading: true,\n loadingText: event.payload.text,\n thinkingMessages: event.payload.thinking_messages,\n dynamicLoading: event.payload.is_dynamic === true,\n },\n };\n}\n\nfunction adaptPanelLoading(event: V1PanelLoading): StreamEventMetadata {\n return {\n type: 'metadata',\n sessionId: '',\n model: '',\n meta: {\n loading: true,\n panelLoading: true,\n panelPendingType: event.payload?.pending_type,\n loadingText: event.payload?.text,\n },\n };\n}\n\nfunction adaptSimilarLoading(event: V1SimilarLoading): StreamEventMetadata {\n return {\n type: 'metadata',\n sessionId: '',\n model: '',\n meta: {\n loading: true,\n similarPanelLoading: true,\n panelPendingType: event.payload?.pending_type,\n loadingText: event.payload?.text,\n },\n };\n}\n\nfunction adaptRedirect(event: V1Redirect): StreamEventAction | StreamEventMetadata {\n const url = firstNonEmptyString(event.payload.url);\n if (url) {\n return {\n type: 'action',\n action: {\n kind: 'navigate',\n url,\n newTab: event.payload.new_tab === true,\n },\n };\n }\n\n return {\n type: 'metadata',\n sessionId: '',\n model: '',\n meta: {\n redirect: event.payload,\n redirectTarget: firstNonEmptyString(event.payload.to),\n },\n };\n}\n\nfunction adaptV1Error(event: V1Error): StreamEventError {\n const message =\n firstNonEmptyString(event.payload?.text, event.payload?.message, event.payload?.error) ??\n 'Backend returned an error';\n return {\n type: 'error',\n code: 'BACKEND_ERROR',\n message,\n };\n}\n\nfunction adaptNoop(_event: V1Noop): StreamEventMetadata {\n return {\n type: 'metadata',\n sessionId: '',\n model: '',\n meta: {\n noop: true,\n },\n };\n}\n\nfunction adaptLauncherAction(event: V1LauncherAction): StreamEvent {\n const payload = event.payload ?? {};\n\n const hasActionList = Array.isArray(payload['action_list']);\n if (hasActionList) {\n const quickQnaPayload: V1LauncherQuickQna['payload'] = {\n action_list: payload['action_list'] as V1SuggestedActionItem[],\n };\n const launcherType = typeof payload['type'] === 'string' ? payload['type'] : undefined;\n const theme = typeof payload['theme'] === 'string' ? payload['theme'] : undefined;\n if (launcherType !== undefined) quickQnaPayload.type = launcherType;\n if (theme !== undefined) quickQnaPayload.theme = theme;\n\n return adaptLauncherQuickQna({\n type: 'quick_qna',\n payload: quickQnaPayload,\n });\n }\n\n const action = asRecord(payload['action']);\n if (action) {\n const textImagePayload: V1LauncherTextImage['payload'] = {\n action,\n };\n const text = typeof payload['text'] === 'string' ? payload['text'] : undefined;\n const imageUrl = typeof payload['image_url'] === 'string' ? payload['image_url'] : undefined;\n const theme = typeof payload['theme'] === 'string' ? payload['theme'] : undefined;\n if (text !== undefined) textImagePayload.text = text;\n if (imageUrl !== undefined) textImagePayload.image_url = imageUrl;\n if (theme !== undefined) textImagePayload.theme = theme;\n\n return adaptLauncherTextImage({\n type: 'text_image',\n payload: textImagePayload,\n });\n }\n\n const text = typeof payload['text'] === 'string' ? payload['text'] : '';\n if (text) {\n const launcherTextPayload: V1LauncherText['payload'] = { text };\n const launcherType = typeof payload['type'] === 'string' ? payload['type'] : undefined;\n const theme = typeof payload['theme'] === 'string' ? payload['theme'] : undefined;\n const payloadObj = asRecord(payload['payload']) ?? undefined;\n if (launcherType !== undefined) launcherTextPayload.type = launcherType;\n if (payloadObj !== undefined) launcherTextPayload.payload = payloadObj;\n if (theme !== undefined) launcherTextPayload.theme = theme;\n\n return adaptLauncherText({\n type: 'text',\n payload: launcherTextPayload,\n });\n }\n\n return {\n type: 'metadata',\n sessionId: '',\n model: '',\n meta: {\n launcherAction: payload,\n },\n };\n}\n\nfunction adaptLauncherText(event: V1LauncherText): StreamEventUISpec {\n const props: Record<string, unknown> = {\n text: firstNonEmptyString(event.payload.text) ?? '',\n };\n if (typeof event.payload.theme === 'string') props['theme'] = event.payload.theme;\n if (event.payload.payload !== undefined) props['payload'] = event.payload.payload;\n\n return {\n type: 'ui_spec',\n widget: 'qna',\n spec: {\n root: 'root',\n elements: {\n root: {\n type: 'QuestionHeading',\n props,\n },\n },\n },\n };\n}\n\nfunction adaptProductItem(event: V1ProductItem): StreamEventUISpec {\n return buildSingleProductUISpec(event.payload, 'qna');\n}\n\nfunction adaptLauncherTextImage(event: V1LauncherTextImage): StreamEventUISpec {\n const label = firstNonEmptyString(event.payload.text) ?? '';\n const action = requestDetailsToAction(event.payload.action, label);\n if (action) {\n const props: Record<string, unknown> = {\n label: label || action.title,\n action,\n };\n if (typeof event.payload.image_url === 'string') props['image'] = event.payload.image_url;\n if (typeof event.payload.theme === 'string') props['theme'] = event.payload.theme;\n\n return {\n type: 'ui_spec',\n widget: 'qna',\n spec: {\n root: 'root',\n elements: {\n root: {\n type: 'ActionButton',\n props,\n },\n },\n },\n };\n }\n\n const fallbackPayload: V1LauncherText['payload'] = {\n text: label,\n };\n if (typeof event.payload.theme === 'string') fallbackPayload.theme = event.payload.theme;\n\n return adaptLauncherText({\n type: 'text',\n payload: fallbackPayload,\n });\n}\n\nfunction adaptLauncherQuickQna(event: V1LauncherQuickQna): StreamEventUISpec {\n const entries = (event.payload.action_list ?? []).map((action) => {\n const label = firstNonEmptyString(action.title) ?? '';\n const actionPayload = requestDetailsToAction(action.requestDetails, label);\n const result: ButtonEntry | null = actionPayload\n ? {\n label,\n action: actionPayload,\n }\n : null;\n if (!result) return null;\n if (typeof action.icon === 'string') result.icon = action.icon;\n if (typeof action.image === 'string') result.image = action.image;\n return result;\n });\n\n return buildActionButtonsUISpec(entries.filter(isNonNullable), 'qna');\n}\n\nfunction adaptReviewHighlights(event: V1ReviewHighlights): StreamEventUISpec {\n const reviews = (event.payload.reviews ?? []).map((item) => {\n const review: Record<string, unknown> = {};\n if (typeof item.review_class === 'string') review['review_class'] = item.review_class;\n if (typeof item.review_text === 'string') review['review_text'] = item.review_text;\n if (typeof item.review_rating === 'string' || typeof item.review_rating === 'number') {\n review['review_rating'] = item.review_rating;\n }\n if (typeof item.review_tag === 'string') review['review_tag'] = item.review_tag;\n return review;\n });\n\n const props: Record<string, unknown> = { reviews };\n if (typeof event.payload.sku === 'string') props['sku'] = event.payload.sku;\n\n return {\n type: 'ui_spec',\n widget: 'chat',\n spec: {\n root: 'root',\n elements: {\n root: {\n type: 'ReviewHighlights',\n props,\n },\n },\n },\n };\n}\n\nfunction adaptProsAndCons(event: V1ProsAndCons): StreamEventUISpec {\n const props: Record<string, unknown> = {};\n if (Array.isArray(event.payload.pros)) props['pros'] = event.payload.pros;\n if (Array.isArray(event.payload.cons)) props['cons'] = event.payload.cons;\n if (typeof event.payload.product_name === 'string') props['productName'] = event.payload.product_name;\n\n return {\n type: 'ui_spec',\n widget: 'chat',\n spec: {\n root: 'root',\n elements: {\n root: {\n type: 'ProsAndCons',\n props,\n },\n },\n },\n };\n}\n\nfunction adaptVisitorDataResponse(event: V1VisitorDataResponse): StreamEventMetadata {\n return {\n type: 'metadata',\n sessionId: '',\n model: '',\n meta: {\n visitorDataResponse: event.payload,\n },\n };\n}\n\nfunction adaptAiProductSuggestions(event: V1AiProductSuggestions): StreamEventUISpec | StreamEventMetadata {\n const suggestions = event.payload.product_suggestions ?? [];\n const items: Record<string, unknown>[] = [];\n\n for (let i = 0; i < suggestions.length; i++) {\n const suggestion = suggestions[i];\n if (!suggestion) continue;\n\n const product = suggestionToNormalizedProduct(suggestion);\n if (!product) continue;\n\n const item: Record<string, unknown> = { product };\n\n const action = requestDetailsToAction(suggestion.requestDetails, product.name);\n if (action) item['action'] = action;\n if (typeof suggestion.role === 'string') item['role'] = suggestion.role;\n if (typeof suggestion.reason === 'string') item['reason'] = suggestion.reason;\n if (typeof suggestion.review_highlight === 'string') item['reviewHighlight'] = suggestion.review_highlight;\n if (Array.isArray(suggestion.labels)) item['labels'] = suggestion.labels;\n if (typeof suggestion.expert_quality_score === 'number')\n item['expertQualityScore'] = suggestion.expert_quality_score;\n\n items.push(item);\n }\n\n if (items.length === 0) {\n return {\n type: 'metadata',\n sessionId: '',\n model: '',\n meta: {\n aiProductSuggestions: event.payload.product_suggestions ?? [],\n },\n };\n }\n\n return {\n type: 'ui_spec',\n widget: 'chat',\n spec: {\n root: 'root',\n elements: {\n root: {\n type: 'AITopPicks',\n props: { suggestions: items },\n },\n },\n },\n };\n}\n\nfunction adaptAiProductGroupings(event: V1AiProductGroupings): StreamEventUISpec | StreamEventMetadata {\n const payloadGroupings = event.payload.product_groupings ?? [];\n const entries: Array<Record<string, unknown>> = [];\n\n for (let i = 0; i < payloadGroupings.length; i++) {\n const grouping = payloadGroupings[i];\n if (!grouping) continue;\n const label = firstNonEmptyString(grouping.name) ?? '';\n const fallbackRequest: V1RequestDetails | undefined =\n grouping.sku && grouping.sku.length > 0 ? { type: 'findSimilar', payload: { sku: grouping.sku } } : undefined;\n const action = requestDetailsToAction(grouping.requestDetails ?? fallbackRequest, label);\n if (!action) continue;\n\n const entry: Record<string, unknown> = { name: label, action };\n if (Array.isArray(grouping.labels)) {\n const filteredLabels = grouping.labels.filter((x) => typeof x === 'string');\n if (filteredLabels.length > 0) entry['labels'] = filteredLabels;\n }\n if (typeof grouping.image === 'string') entry['image'] = grouping.image;\n entries.push(entry);\n }\n\n if (entries.length === 0) {\n return {\n type: 'metadata',\n sessionId: '',\n model: '',\n meta: {\n aiProductGroupings: event.payload.product_groupings ?? [],\n },\n };\n }\n\n return {\n type: 'ui_spec',\n widget: 'chat',\n spec: {\n root: 'root',\n elements: {\n root: {\n type: 'AIGroupingCards',\n props: { entries },\n },\n },\n },\n };\n}\n\nfunction adaptAiSuggestedSearches(event: V1AiSuggestedSearches): StreamEventUISpec | StreamEventMetadata {\n const searches = event.payload.suggested_searches ?? [];\n const entries: Array<Record<string, unknown>> = [];\n\n for (let i = 0; i < searches.length; i++) {\n const search = searches[i];\n if (!search) continue;\n\n const shortName =\n firstNonEmptyString(search.short_name, search.chosen_attribute, search.detailed_user_message) ??\n `Search ${i + 1}`;\n const fallbackPayload: Record<string, unknown> = {};\n const text = firstNonEmptyString(search.detailed_user_message);\n if (text) fallbackPayload['text'] = text;\n const requestDetailsRecord = asRecord(search.requestDetails);\n const requestPayload = asRecord(requestDetailsRecord?.['payload']);\n const requestGroupSkus = requestPayload?.['group_skus'];\n if (search.group_skus && Array.isArray(search.group_skus)) {\n fallbackPayload['group_skus'] = search.group_skus;\n } else if (Array.isArray(requestGroupSkus)) {\n fallbackPayload['group_skus'] = requestGroupSkus.filter((sku): sku is string => typeof sku === 'string');\n }\n const sku = firstNonEmptyString(search.sku, search.representative_product_sku, requestPayload?.['sku']);\n if (sku) fallbackPayload['sku'] = sku;\n fallbackPayload['is_suggested_text'] = 1;\n\n const fallbackRequest: V1RequestDetails = { type: 'inputText', payload: fallbackPayload };\n const requestedAction = requestDetailsToAction(search.requestDetails, shortName);\n const action =\n requestedAction?.type === 'findSimilar' && typeof fallbackPayload['text'] === 'string'\n ? requestDetailsToAction(fallbackRequest, shortName)\n : (requestedAction ?? requestDetailsToAction(fallbackRequest, shortName));\n if (!action) continue;\n\n const entry: Record<string, unknown> = { shortName, action };\n const detailedMessage = firstNonEmptyString(search.detailed_user_message);\n if (detailedMessage && detailedMessage !== shortName) entry['detailedMessage'] = detailedMessage;\n const keywordLine = getSuggestedSearchKeywordsText(search);\n if (keywordLine) {\n const dm = detailedMessage ?? '';\n if (keywordLine !== shortName && keywordLine !== dm) {\n entry['whyDifferent'] = keywordLine;\n }\n }\n if (typeof search.image === 'string') entry['image'] = search.image;\n entries.push(entry);\n }\n\n if (entries.length === 0) {\n return {\n type: 'metadata',\n sessionId: '',\n model: '',\n meta: {\n aiSuggestedSearches: event.payload.suggested_searches ?? [],\n },\n };\n }\n\n return {\n type: 'ui_spec',\n widget: 'chat',\n spec: {\n root: 'root',\n elements: {\n root: {\n type: 'AISuggestedSearchCards',\n props: { entries },\n },\n },\n },\n };\n}\n\nfunction adaptGetGroundingReview(event: V1GetGroundingReview): StreamEventUISpec | StreamEventMetadata {\n const p = event.payload;\n const requestDetails = p.requestDetails ?? p['request_details'];\n const action = requestDetailsToAction(\n requestDetails,\n firstNonEmptyString(p.review_count, p['reviewCount'], p.text, p.title) ?? 'Show product reviews',\n );\n if (!action) {\n return {\n type: 'metadata',\n sessionId: '',\n model: '',\n meta: {\n groundingReview: p,\n },\n };\n }\n\n const props: Record<string, unknown> = { action };\n if (p.title) props['title'] = p.title;\n if (p.text) props['text'] = p.text;\n if (p.review_count) props['reviewCount'] = p.review_count;\n else if (typeof p['reviewCount'] === 'string' && p['reviewCount'].trim()) props['reviewCount'] = p['reviewCount'];\n\n return {\n type: 'ui_spec',\n widget: 'chat',\n spec: {\n root: 'root',\n elements: {\n root: {\n type: 'GroundingReviewCard',\n props,\n },\n },\n },\n };\n}\n\nfunction adaptProductListPreview(): StreamEventMetadata {\n return {\n type: 'metadata',\n sessionId: '',\n model: '',\n meta: {\n analyzeAnimation: true,\n },\n };\n}\n\nfunction adaptVoice(event: V1Voice): StreamEventMetadata {\n return {\n type: 'metadata',\n sessionId: '',\n model: '',\n meta: {\n voice: event.payload,\n },\n };\n}\n\nfunction adaptGroupList(event: V1GroupList): StreamEventUISpec {\n const groupList = event.payload.group_list ?? [];\n const groups: Array<{ groupName: string; products: NormalizedProduct[] }> = [];\n\n for (const group of groupList) {\n const groupName = group.group_name ?? '';\n const products = (group.product_list ?? []).map(productToNormalized);\n groups.push({ groupName, products });\n }\n\n const filterTags: Array<{ title: string; action?: { title: string; type: string; payload?: unknown } }> = [];\n for (const tag of event.payload.filter_tags ?? []) {\n const title = tag.title ?? '';\n if (!title) continue;\n const action = requestDetailsToAction(tag.requestDetails, title);\n const entry: (typeof filterTags)[number] = { title };\n if (action) entry.action = action;\n filterTags.push(entry);\n }\n\n return {\n type: 'ui_spec',\n widget: 'chat',\n spec: {\n root: 'root',\n elements: {\n root: {\n type: 'CategoriesContainer',\n props: { groups, filterTags },\n },\n },\n },\n panelHint: 'panel',\n };\n}\n\nfunction adaptFormEvent(event: V1FormEvent): StreamEventMetadata {\n return {\n type: 'metadata',\n sessionId: '',\n model: '',\n meta: {\n formType: event.type,\n formPayload: event.payload ?? {},\n },\n };\n}\n\nfunction adaptHandoff(event: V1Handoff): StreamEventUISpec {\n const props: Record<string, unknown> = {};\n if (typeof event.payload?.summary === 'string') props['summary'] = event.payload.summary;\n if (Array.isArray(event.payload?.products_discussed)) {\n props['products_discussed'] = event.payload.products_discussed;\n }\n if (typeof event.payload?.user_sentiment === 'string') {\n props['user_sentiment'] = event.payload.user_sentiment;\n }\n\n return {\n type: 'ui_spec',\n widget: 'chat',\n spec: {\n root: 'root',\n elements: {\n root: {\n type: 'HandoffNotice',\n props,\n },\n },\n },\n };\n}\n\nfunction adaptLauncherContent(event: V1LauncherContent): StreamEventMetadata {\n return {\n type: 'metadata',\n sessionId: '',\n model: '',\n meta: {\n launcherContent: event.payload ?? {},\n },\n };\n}\n\ninterface ButtonEntry {\n label: string;\n action: { title: string; type: string; payload?: unknown };\n icon?: string;\n image?: string;\n description?: string;\n}\n\nfunction buildActionButtonsUISpec(entries: ButtonEntry[], widget: WidgetName): StreamEventUISpec {\n const elements: Record<string, UIElement> = {};\n const childIds: string[] = [];\n\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i];\n if (!entry) continue;\n const id = `action-${i}`;\n childIds.push(id);\n const props: Record<string, unknown> = {\n label: entry.label,\n action: entry.action,\n };\n if (entry.icon !== undefined) props['icon'] = entry.icon;\n if (entry.image !== undefined) props['image'] = entry.image;\n if (entry.description !== undefined) props['description'] = entry.description;\n\n elements[id] = {\n type: 'ActionButton',\n props,\n };\n }\n\n elements['root'] = {\n type: 'ActionButtons',\n props: {\n buttons: entries.map((entry) => {\n const btn: Record<string, unknown> = {\n label: entry.label,\n action: entry.action,\n };\n if (entry.image !== undefined) btn['image'] = entry.image;\n if (entry.description !== undefined) btn['description'] = entry.description;\n if (entry.icon !== undefined) btn['icon'] = entry.icon;\n return btn;\n }),\n },\n children: childIds,\n };\n\n return {\n type: 'ui_spec',\n widget,\n spec: { root: 'root', elements },\n };\n}\n\nfunction buildProductGridUISpec(products: V1Product[], widget: WidgetName): StreamEventUISpec {\n const elements: Record<string, UIElement> = {};\n const childIds: string[] = [];\n\n for (let i = 0; i < products.length; i++) {\n const product = products[i];\n if (!product) continue;\n const normalized = productToNormalized(product);\n const id = `product-${i}`;\n childIds.push(id);\n const props: Record<string, unknown> = { product: normalized, index: i };\n if (normalized.sku) {\n props['action'] = {\n title: normalized.name,\n type: 'launchSingleProduct',\n payload: { sku: normalized.sku },\n };\n }\n elements[id] = {\n type: 'ProductCard',\n props,\n };\n }\n\n elements['root'] = {\n type: 'ProductGrid',\n props: { layout: 'grid' },\n children: childIds,\n };\n\n return {\n type: 'ui_spec',\n widget,\n spec: { root: 'root', elements },\n };\n}\n\nfunction buildSingleProductUISpec(product: V1Product, widget: WidgetName): StreamEventUISpec {\n return {\n type: 'ui_spec',\n widget,\n spec: {\n root: 'root',\n elements: {\n root: {\n type: 'ProductCard',\n props: { product: productToNormalized(product), index: 0 },\n },\n },\n },\n };\n}\n\nfunction buildEmptyUISpec(widget: WidgetName): StreamEventUISpec {\n return {\n type: 'ui_spec',\n widget,\n spec: {\n root: 'root',\n elements: {\n root: {\n type: 'ActionButtons',\n props: { buttons: [] },\n children: [],\n },\n },\n },\n };\n}\n\nfunction suggestionToNormalizedProduct(suggestion: V1AiProductSuggestion): NormalizedProduct | null {\n const fallbackSku = firstNonEmptyString(suggestion.sku);\n const fallbackName = firstNonEmptyString(suggestion.short_name);\n const inner = asRecord(suggestion.product_item);\n const base = inner ?? (suggestion as Record<string, unknown>);\n const merged: Record<string, unknown> = { ...base };\n const fromSuggestionRoot = firstNonEmptyString(suggestion.discount_reason, suggestion['discountReason']);\n if (\n fromSuggestionRoot &&\n !firstNonEmptyString(merged['discount_reason'] as unknown, merged['discountReason'] as unknown)\n ) {\n merged['discount_reason'] = fromSuggestionRoot;\n }\n return productRecordToNormalized(merged, fallbackSku, fallbackName);\n}\n\nfunction productRecordToNormalized(\n raw: Record<string, unknown>,\n fallbackSku?: string,\n fallbackName?: string,\n): NormalizedProduct | null {\n const sku = firstNonEmptyString(raw['sku'], fallbackSku);\n const name = firstNonEmptyString(raw['name'], fallbackName);\n if (!sku || !name) return null;\n\n const product: V1Product = {\n sku,\n name,\n };\n\n const brand = firstNonEmptyString(raw['brand']);\n if (brand) product.brand = brand;\n\n const url = firstNonEmptyString(raw['url']);\n if (url) product.url = url;\n\n const images = stringArray(raw['images']);\n if (images.length > 0) {\n product.images = images;\n } else {\n const image = firstNonEmptyString(raw['image'], raw['image_url'], raw['imageUrl']);\n if (image) product.images = [image];\n }\n\n const priceDiscounted = toNumber(raw['price_discounted']);\n if (priceDiscounted !== undefined) product.price_discounted = priceDiscounted;\n const price = toNumber(raw['price']);\n if (price !== undefined) product.price = price;\n const rating = toNumber(raw['rating']);\n if (rating !== undefined) product.rating = rating;\n const reviewCount = toNumber(raw['review_count']) ?? toNumber(raw['reviewCount']);\n if (reviewCount !== undefined) product.review_count = reviewCount;\n const cartCode = firstNonEmptyString(raw['cart_code'], raw['cartCode']);\n if (cartCode) product.cart_code = cartCode;\n if (typeof raw['in_stock'] === 'boolean') product.in_stock = raw['in_stock'];\n if (typeof raw['inStock'] === 'boolean') product.in_stock = raw['inStock'];\n\n const discountReason = firstNonEmptyString(raw['discount_reason'], raw['discountReason']);\n if (discountReason) product.discount_reason = discountReason;\n\n return productToNormalized(product);\n}\n\nfunction requestDetailsToAction(\n requestDetails: unknown,\n label: string,\n): { title: string; type: string; payload?: unknown } | null {\n const details = asRecord(requestDetails);\n if (!details) return null;\n\n const type = details['type'];\n if (typeof type !== 'string' || type.length === 0) return null;\n\n const action: { title: string; type: string; payload?: unknown } = {\n title: label || type,\n type,\n };\n if (details['payload'] !== undefined) action.payload = details['payload'];\n return action;\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null;\n return value as Record<string, unknown>;\n}\n\nfunction firstNonEmptyString(...values: unknown[]): string | undefined {\n for (const value of values) {\n if (typeof value === 'string') {\n const trimmed = value.trim();\n if (trimmed.length > 0) return trimmed;\n }\n }\n return undefined;\n}\n\nfunction stringArray(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return value.filter((item): item is string => typeof item === 'string' && item.length > 0);\n}\n\nfunction normalizeStringList(value: unknown): string[] {\n if (typeof value === 'string') {\n const trimmed = value.trim();\n return trimmed ? [trimmed] : [];\n }\n if (!Array.isArray(value)) return [];\n return value\n .filter((item): item is string => typeof item === 'string')\n .map((item) => item.trim())\n .filter((item) => item.length > 0);\n}\n\nfunction toNumber(value: unknown): number | undefined {\n if (typeof value === 'number' && Number.isFinite(value)) return value;\n if (typeof value === 'string') {\n // Handle Turkish number format: \"1.299,99\" → \"1299.99\"\n // If the string contains both '.' and ',', treat '.' as thousands separator and ',' as decimal\n let normalized: string;\n if (value.includes('.') && value.includes(',')) {\n normalized = value.replace(/\\./g, '').replace(',', '.');\n } else {\n normalized = value.replace(',', '.');\n }\n const parsed = Number(normalized);\n if (Number.isFinite(parsed)) return parsed;\n }\n return undefined;\n}\n\nfunction isNonNullable<T>(value: T | null | undefined): value is T {\n return value !== null && value !== undefined;\n}\n\nexport interface NormalizedProduct {\n sku: string;\n name: string;\n imageUrl?: string;\n images?: string[];\n price?: string;\n originalPrice?: string;\n discountPercent?: number;\n url: string;\n brand?: string;\n rating?: number;\n reviewCount?: number;\n cartCode?: string;\n inStock?: boolean;\n variants?: Array<Record<string, unknown>>;\n discountReason?: string;\n promotions?: string[];\n description?: string;\n descriptionHtml?: string;\n features?: Array<{ name?: string; key?: string; value?: string | number | boolean; [key: string]: unknown }>;\n specifications?: Record<string, string> | Array<{ key: string; value: string }>;\n facetHits?: Record<string, unknown>;\n shortName?: string;\n /** Pass-through bag for backend fields not consumed by the SDK. */\n extras?: Record<string, unknown>;\n}\n\n/** V1 product keys consumed by productToNormalized — hoisted to avoid per-call Set allocation. */\nconst KNOWN_V1_PRODUCT_KEYS: ReadonlySet<string> = new Set([\n 'sku',\n 'name',\n 'brand',\n 'images',\n 'price',\n 'price_discounted',\n 'price_discount_rate',\n 'price_currency',\n 'discount_reason',\n 'url',\n 'rating',\n 'review_count',\n 'cart_code',\n 'in_stock',\n 'description',\n 'description_html',\n 'features',\n 'specifications',\n 'facet_tags',\n 'short_name',\n 'category_ids',\n 'category_names',\n 'variants',\n 'facet_hits',\n 'promotions',\n]);\n\nexport function productToNormalized(p: V1Product): NormalizedProduct {\n const hasDiscount = p.price_discounted != null && p.price_discounted > 0;\n const price = hasDiscount ? p.price_discounted : p.price;\n const originalPrice = hasDiscount && p.price != null ? p.price : undefined;\n\n let discountPercent: number | undefined;\n if (originalPrice != null && price != null && originalPrice > 0) {\n discountPercent = Math.round(((originalPrice - price) / originalPrice) * 100);\n } else if (p.price_discount_rate != null && p.price_discount_rate > 0) {\n discountPercent = p.price_discount_rate;\n }\n\n const brand = firstNonEmptyString(p.brand);\n const name = firstNonEmptyString(p.name) ?? p.sku;\n const normalizedName = brand && !name.toLowerCase().startsWith(brand.toLowerCase()) ? `${brand} ${name}` : name;\n\n const result: NormalizedProduct = {\n sku: p.sku,\n name: normalizedName,\n url: firstNonEmptyString(p.url) ?? '',\n };\n\n const image = p.images?.[0];\n if (image) result.imageUrl = image;\n if (p.images && p.images.length > 1) result.images = p.images;\n if (price != null) result.price = String(price);\n if (originalPrice != null) result.originalPrice = String(originalPrice);\n if (discountPercent !== undefined) result.discountPercent = discountPercent;\n if (brand !== undefined) result.brand = brand;\n if (p.rating !== undefined) result.rating = p.rating;\n if (p.review_count !== undefined) result.reviewCount = p.review_count;\n if (p.cart_code !== undefined) result.cartCode = p.cart_code;\n if (p.in_stock !== undefined) result.inStock = p.in_stock;\n if (p.variants && p.variants.length > 0) result.variants = p.variants;\n if (p.discount_reason !== undefined) result.discountReason = p.discount_reason;\n if (p.promotions && p.promotions.length > 0) result.promotions = p.promotions;\n if (p.description !== undefined) result.description = p.description;\n if (p.description_html !== undefined) result.descriptionHtml = p.description_html;\n if (p.features && p.features.length > 0) result.features = p.features;\n if (p.specifications !== undefined) result.specifications = p.specifications;\n if (p.facet_hits) result.facetHits = p.facet_hits;\n if (p.short_name !== undefined) result.shortName = p.short_name;\n\n // Collect any extra backend fields not consumed above.\n const raw = p as unknown as Record<string, unknown>;\n const extras: Record<string, unknown> = {};\n let hasExtras = false;\n for (const key of Object.keys(raw)) {\n if (!KNOWN_V1_PRODUCT_KEYS.has(key)) {\n extras[key] = raw[key];\n hasExtras = true;\n }\n }\n if (hasExtras) result.extras = extras;\n\n return result;\n}\n\nexport interface SimilarProductsJsonResponse {\n results: V1Product[];\n count: number;\n source_sku?: string;\n}\n\nexport interface ProductGroupingsJsonResponse {\n intro_message?: string;\n product_groupings: Array<{\n name: string;\n description?: string;\n highlight?: string;\n repr_sku?: string;\n repr_image?: string;\n group_skus?: string[];\n group_products?: V1Product[];\n }>;\n count: number;\n}\n\nexport function normalizeSimilarProductsResponse(json: SimilarProductsJsonResponse): NormalizedProduct[] {\n return json.results.map(productToNormalized);\n}\n\nexport function normalizeProductGroupingsResponse(json: ProductGroupingsJsonResponse): Array<{\n name: string;\n highlight?: string;\n products: NormalizedProduct[];\n}> {\n return json.product_groupings.map((group) => {\n const result: { name: string; highlight?: string; products: NormalizedProduct[] } = {\n name: group.name,\n products: (group.group_products ?? []).map(productToNormalized),\n };\n if (group.highlight !== undefined) result.highlight = group.highlight;\n return result;\n });\n}\n","/**\n * Endpoint path helpers.\n *\n * All endpoints use `/chat/*` prefix. API versioning is handled via\n * request-level compatibility params or feature-toggle params.\n */\n\nexport type ChatEndpointName = 'process_action' | 'launcher_action' | 'similar_products' | 'product_groupings';\n\nexport interface ChatTransportConfig {\n middlewareUrl: string;\n attachment?: File;\n /** Account ID (used in URL path when needed). */\n accountId?: string;\n}\n\nconst CHAT_ENDPOINT_PATHS: Record<ChatEndpointName, `/${string}`> = {\n process_action: '/process_action',\n launcher_action: '/launcher_action',\n similar_products: '/similar_products',\n product_groupings: '/product_groupings',\n};\n\nexport function normalizeMiddlewareUrl(input?: string): string {\n if (input === undefined) {\n throw new Error('[gengage] middlewareUrl is required. Pass your Gengage backend URL in widget config.');\n }\n const raw = input.trim();\n if (raw === '') return '';\n return raw.replace(/\\/+$/, '');\n}\n\nexport function buildChatEndpointUrl(endpoint: ChatEndpointName, config: ChatTransportConfig): string {\n const baseUrl = normalizeMiddlewareUrl(config?.middlewareUrl);\n return `${baseUrl}/chat${CHAT_ENDPOINT_PATHS[endpoint]}`;\n}\n","import { consumeStream } from '../common/streaming.js';\nimport { adaptBackendEvent } from '../common/protocol-adapter.js';\nimport { buildChatEndpointUrl } from '../common/api-paths.js';\nimport type { StreamEvent, UISpec, PageContext } from '../common/types.js';\nimport type { ChatTransportConfig } from '../common/api-paths.js';\n\nexport interface BackendRequestMeta {\n outputLanguage: string;\n parentUrl: string;\n windowWidth: string;\n windowHeight: string;\n selfUrl: string;\n id: string;\n userId: string;\n appId: string;\n threads: unknown[];\n createdAt: string;\n kvkkApproved: boolean;\n voiceEnabled: boolean;\n threadId: string;\n isControlGroup: boolean;\n isMobile: boolean;\n viewId?: string;\n}\n\nexport interface ProcessActionRequest {\n account_id: string;\n session_id: string;\n correlation_id: string;\n user_id?: string;\n view_id?: string;\n\n /**\n * @deprecated Use top-level `type` and `payload` instead.\n * Kept for one release cycle of backward compatibility.\n */\n action?: {\n title: string;\n type: string;\n payload?: unknown;\n };\n\n /** Backend action type identifier (preferred over `action.type`). */\n type?: string;\n /** Arbitrary action data passed to the backend (preferred over `action.payload`). */\n payload?: unknown;\n\n sku?: string;\n page_type?: string;\n locale?: string;\n meta?: BackendRequestMeta;\n context?: {\n messages?: Array<{ role: string; content: string }>;\n [key: string]: unknown;\n };\n}\n\nexport interface ActionEnrichmentContext {\n pageContext?: PageContext | undefined;\n backendContext?: import('../common/types.js').BackendContext | null | undefined;\n isMobile?: boolean | undefined;\n}\n\n/**\n * Enriches action payloads with fields the backend expects.\n * Only adds fields that are not already present in the payload.\n */\nexport function enrichActionPayload(\n action: { title: string; type: string; payload?: unknown },\n ctx: ActionEnrichmentContext,\n): { title: string; type: string; payload?: unknown } {\n const type = action.type;\n const existing =\n action.payload != null && typeof action.payload === 'object' && !Array.isArray(action.payload)\n ? (action.payload as Record<string, unknown>)\n : {};\n\n // Helper: only set if not already present\n const merge = (additions: Record<string, unknown>): Record<string, unknown> => {\n const result = { ...existing };\n for (const [key, value] of Object.entries(additions)) {\n if (!(key in result)) {\n result[key] = value;\n }\n }\n return result;\n };\n\n switch (type) {\n case 'inputText': {\n const additions: Record<string, unknown> = {\n is_launcher: 0,\n };\n if (ctx.pageContext?.extra) additions['page_details'] = ctx.pageContext.extra;\n // is_suggested_text may already be set by adapter — don't overwrite\n if (!('is_suggested_text' in existing)) additions['is_suggested_text'] = 0;\n return { ...action, payload: merge(additions) };\n }\n\n case 'findSimilar': {\n const additions: Record<string, unknown> = {\n is_launcher: 0,\n };\n if (action.title) {\n additions['text'] = action.title;\n additions['input'] = action.title;\n }\n return { ...action, payload: merge(additions) };\n }\n\n case 'getComparisonTable': {\n // sku_list should already be set; no-op if present\n return action;\n }\n\n case 'addToCart': {\n const additions: Record<string, unknown> = {};\n if (!('error_message' in existing)) additions['error_message'] = '';\n return { ...action, payload: merge(additions) };\n }\n\n case 'reviewSummary': {\n const additions: Record<string, unknown> = {};\n if (ctx.pageContext?.sku && !('sku' in existing)) {\n additions['sku'] = ctx.pageContext.sku;\n }\n if (Object.keys(additions).length === 0) return action;\n return { ...action, payload: merge(additions) };\n }\n\n default:\n return action;\n }\n}\n\nexport interface StreamCallbacks {\n onTextChunk: (\n content: string,\n isFinal: boolean,\n extra?: {\n productMentions?: Array<{ sku: string; short_name: string }> | undefined;\n skuToProductItem?: Record<string, Record<string, unknown>> | undefined;\n conversationMode?: string | undefined;\n },\n ) => void;\n onUISpec: (spec: UISpec, widget: string, panelHint?: 'panel', clearPanel?: boolean) => void;\n onAction: (event: StreamEvent) => void;\n onMetadata: (event: StreamEvent) => void;\n onError: (err: Error) => void;\n onDone: () => void;\n}\n\n/**\n * Action type mapping.\n * The backend's ActionType enum uses `inputText` for user messages,\n * while the frontend uses `user_message`. Map at the boundary.\n */\nconst ACTION_TYPE_MAP: Record<string, string> = {\n user_message: 'inputText',\n};\n\n/**\n * Builds the request body for `/chat/process_action`.\n *\n * Backend expects `type` and `payload` at the top level —\n * matching chat_api.py's current schema.\n */\nfunction buildRequestBody(request: ProcessActionRequest): string {\n const { action, type: flatType, payload: flatPayload, ...rest } = request;\n // Prefer top-level type/payload; fall back to deprecated action wrapper.\n const rawType = flatType ?? action?.type ?? 'inputText';\n const rawPayload = flatPayload ?? action?.payload;\n const mappedType = ACTION_TYPE_MAP[rawType] ?? rawType;\n const body: Record<string, unknown> = {\n ...rest,\n type: mappedType,\n };\n if (rawPayload !== undefined) {\n // Backend expects payload as an object. Wrap string payloads for compatibility.\n body.payload = typeof rawPayload === 'string' ? { text: rawPayload } : rawPayload;\n }\n return JSON.stringify(body);\n}\n\nexport function sendChatMessage(\n request: ProcessActionRequest,\n callbacks: StreamCallbacks,\n transport: ChatTransportConfig,\n): AbortController {\n const url = buildChatEndpointUrl('process_action', transport);\n const controller = new AbortController();\n\n const run = async (): Promise<void> => {\n try {\n const requestBody = buildRequestBody(request);\n\n // Use FormData only when an attachment is present; otherwise send JSON.\n const useFormData = transport.attachment !== undefined;\n\n let fetchInit: RequestInit;\n if (useFormData) {\n const formData = new FormData();\n formData.append('request', requestBody);\n if (transport.attachment !== undefined) {\n formData.append('attachment', transport.attachment);\n }\n fetchInit = {\n method: 'POST',\n body: formData,\n signal: controller.signal,\n };\n } else {\n fetchInit = {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: requestBody,\n signal: controller.signal,\n };\n }\n\n const response = await fetch(url, fetchInit);\n\n if (!response.ok) {\n let detail = response.statusText;\n try {\n const body = await response.json();\n const msg =\n (body as Record<string, unknown>).detail ??\n (body as Record<string, unknown>).message ??\n (body as Record<string, unknown>).error;\n if (typeof msg === 'string') detail = msg;\n } catch {\n /* body not JSON — keep statusText */\n }\n callbacks.onError(new Error(`HTTP ${response.status}: ${detail}`));\n return;\n }\n\n // Guard against double-fire: onDone must fire exactly once\n let doneFired = false;\n const fireDone = () => {\n if (doneFired) return;\n doneFired = true;\n callbacks.onDone();\n };\n\n await consumeStream(response, {\n onEvent: (event: StreamEvent) => {\n const normalized = adaptBackendEvent(event as unknown as Record<string, unknown>);\n\n if (!normalized) return;\n\n switch (normalized.type) {\n case 'text_chunk':\n callbacks.onTextChunk(normalized.content, normalized.final === true, {\n productMentions: normalized.productMentions,\n skuToProductItem: normalized.skuToProductItem,\n conversationMode: normalized.conversationMode,\n });\n break;\n case 'ui_spec':\n callbacks.onUISpec(\n normalized.spec,\n normalized.widget,\n normalized.panelHint,\n normalized.clearPanel === true,\n );\n break;\n case 'action':\n callbacks.onAction(normalized);\n break;\n case 'metadata':\n callbacks.onMetadata(normalized);\n break;\n case 'error':\n callbacks.onError(new Error(normalized.message));\n break;\n case 'done':\n fireDone();\n break;\n }\n },\n onError: callbacks.onError,\n signal: controller.signal,\n });\n\n // Fallback: if stream completed without a chatStreamEnd event (backend\n // bug or truncated response), ensure onDone fires so the widget doesn't\n // get stuck with a spinning typing indicator.\n fireDone();\n } catch (err) {\n if (err instanceof DOMException && err.name === 'AbortError') return;\n callbacks.onError(err instanceof Error ? err : new Error(String(err)));\n }\n };\n\n void run();\n return controller;\n}\n","/**\n * Opt-in debug logging for chat presentation (query ?chat_debug=1 or localStorage gengage_chat_debug=1).\n */\n\nconst DEBUG_QUERY_KEYS = ['chat_debug', 'presentation_debug'];\nconst DEBUG_STORAGE_KEYS = ['gengage_chat_debug', 'gengage_presentation_debug'];\n\ntype Entry = { seq: number; time: string; scope: string; message: string; payload?: unknown };\n\ndeclare global {\n interface Window {\n __gengageChatPresentationDebugLog?: Entry[];\n __gengageChatPresentationDebugSeq?: number;\n }\n}\n\nexport function isChatPresentationDebugEnabled(): boolean {\n if (typeof window === 'undefined') return false;\n const params = new URLSearchParams(window.location.search);\n if (DEBUG_QUERY_KEYS.some((k) => params.get(k) === '1')) return true;\n try {\n return DEBUG_STORAGE_KEYS.some((k) => window.localStorage.getItem(k) === '1');\n } catch {\n return false;\n }\n}\n\nexport function logChatPresentation(scope: string, message: string, payload?: unknown): void {\n if (!isChatPresentationDebugEnabled()) return;\n const seq = (window.__gengageChatPresentationDebugSeq ?? 0) + 1;\n window.__gengageChatPresentationDebugSeq = seq;\n const entry: Entry = { seq, time: new Date().toISOString(), scope, message, payload };\n if (!window.__gengageChatPresentationDebugLog) window.__gengageChatPresentationDebugLog = [];\n window.__gengageChatPresentationDebugLog.push(entry);\n if (window.__gengageChatPresentationDebugLog.length > 400) window.__gengageChatPresentationDebugLog.shift();\n const prefix = `[gengage-chat-debug #${seq}][${scope}] ${message}`;\n // eslint-disable-next-line no-console\n if (payload === undefined) console.debug(prefix);\n // eslint-disable-next-line no-console\n else console.debug(prefix, payload);\n}\n","/**\n * Resolves the chat transcript scroll container (messages pane).\n * Prefers a widget-registered element (Shadow DOM–safe); falls back to #gengage-chat-scroll in light DOM.\n */\n\nimport { logChatPresentation } from './chat-presentation-debug.js';\n\n/** DOM id on the transcript scroller — stable for host tooling */\nexport const CHAT_SCROLL_ELEMENT_ID = 'gengage-chat-scroll';\n\nlet registered: HTMLElement | null = null;\n\n/** Called when the chat drawer mounts / updates its messages scroller */\nexport function registerChatScrollElement(el: HTMLElement | null): void {\n registered = el;\n}\n\nexport function getChatScrollElement(): HTMLElement | null {\n if (registered && registered.isConnected) {\n return registered;\n }\n registered = null;\n\n const light = document.getElementById(CHAT_SCROLL_ELEMENT_ID);\n if (light) {\n return light;\n }\n\n logChatPresentation('chat-scroll', 'failed to resolve scroll container (not registered)', {\n id: CHAT_SCROLL_ELEMENT_ID,\n });\n return null;\n}\n\nexport function invalidateChatScrollCache(): void {\n registered = null;\n}\n","import type { ChatI18n } from '../types.js';\n\nexport const CHAT_I18N_TR: ChatI18n = {\n headerTitle: 'Ürün Uzmanı',\n inputPlaceholder: 'Ürün ara, soru sor',\n sendButton: 'Gönder',\n closeButton: 'Kapat',\n openButton: 'Sohbeti aç',\n newChatButton: 'Yeni sohbet',\n poweredBy: 'Gengage ile',\n errorMessage: 'Bir hata oluştu. Lütfen tekrar deneyin.',\n retryButton: 'Tekrar Dene',\n loadingMessage: 'Düşünüyorum...',\n loadingSequenceGeneric: [\n 'Sizin için en önemli noktaları anlıyorum',\n 'Ürünleri, yorumları ve özellikleri inceliyorum',\n 'En güçlü seçenekleri hazırlıyorum',\n ],\n loadingSequencePanel: [\n 'En ilgili detayları inceliyorum',\n 'En güçlü sinyalleri kontrol ediyorum',\n 'Bu görünümü sizin için hazırlıyorum',\n ],\n loadingSequenceComparison: [\n 'Ürün farklarını inceliyorum',\n 'En güçlü farkları ve ödünleşimleri kontrol ediyorum',\n 'Karşılaştırmayı sizin için hazırlıyorum',\n ],\n productCtaLabel: 'Satın Al',\n viewOnSiteLabel: 'Sitede Gör',\n attachImageButton: 'Resim ekle',\n attachMenuSelectPhoto: 'Fotoğraf Seç',\n attachMenuPaste: 'Yapıştır',\n clipboardNoImageMessage: 'Panoda görsel bulunamadı. Önce bir görsel kopyalayın veya dosya seçin.',\n removeAttachmentButton: 'Resmi kaldır',\n invalidFileType: 'Sadece JPEG, PNG ve WebP dosyaları destekleniyor.',\n fileTooLarge: \"Dosya boyutu 5 MB'dan küçük olmalıdır.\",\n aiTopPicksTitle: 'Sizin İçin En İyiler',\n roleWinner: 'En Beğendiğim',\n roleBestValue: 'En Uygun Fiyatlı',\n roleBestAlternative: 'En İyi Alternatif',\n viewDetails: 'Detayları Gör',\n groundingReviewCta: 'Yorumları Oku',\n groundingReviewSubtitle: '{count} yorum mevcut',\n variantsLabel: 'Varyantlar',\n sortRelated: 'Önerilen',\n sortPriceAsc: 'Fiyat düşükten yükseğe',\n sortPriceDesc: 'Fiyat yüksekten düşüğe',\n sortToolbarAriaLabel: 'Ürünleri sırala',\n compareSelected: 'Karşılaştır',\n compareMinHint: 'En az 2 ürün seçin',\n compareMaxHint: 'En fazla 5 ürün seçebilirsiniz',\n comparisonSelectLabel: 'Karşılaştırmak için seç',\n comparisonSelectedLabel: 'Seçildi',\n comparisonSelectCardHint: 'Karşılaştırmaya eklemek veya çıkarmak için kartın herhangi bir yerine dokunun.',\n comparisonPreparingLabel: 'Karşılaştırma tablosu hazırlanıyor…',\n panelTitleProductDetails: 'Ürün Detayı',\n panelTitleSimilarProducts: 'Benzer Ürünler',\n panelTitleComparisonResults: 'Karşılaştırma Sonuçları',\n panelTitleCategories: 'Kategoriler',\n panelTitleSearchResults: 'Arama Sonuçları',\n inStockLabel: 'Stokta',\n outOfStockLabel: 'Tükendi',\n findSimilarLabel: 'Benzerlerini Bul',\n galleryPrevAriaLabel: 'Önceki görsel',\n galleryNextAriaLabel: 'Sonraki görsel',\n choicePrompterHeading: 'Kararsız mı kaldın?',\n choicePrompterSuggestion: 'Ürünleri seçip karşılaştırabilirsin',\n choicePrompterCta: 'Seç ve Karşılaştır',\n viewMoreLabel: 'Daha Fazla Göster',\n similarProductsLabel: 'Benzer Ürünler',\n addToCartButton: 'Sepete Ekle',\n addedToCartToast: 'Sepete eklendi',\n shareButton: 'Paylaş',\n productInfoTab: 'Ürün Bilgileri',\n specificationsTab: 'Teknik Özellikler',\n recommendedChoiceLabel: 'Önerilen Seçim',\n highlightsLabel: 'Öne Çıkan Özellikler',\n keyDifferencesLabel: 'Temel Farklar',\n specialCasesLabel: 'Özel Durumlar İçin',\n emptyReviewsMessage: 'Yorum özeti bulunamadı.',\n closeAriaLabel: 'Kapat',\n startChatLabel: 'Sohbete Başla',\n voiceButton: 'Sesli giriş',\n voiceListening: 'Dinleniyor...',\n voiceNotSupported: 'Sesli giriş bu tarayıcıda desteklenmiyor.',\n voicePermissionDenied: 'Mikrofon erişimi reddedildi.',\n voiceError: 'Sesli giriş hatası.',\n handoffHeading: 'Destek temsilcisine aktarılıyor',\n productNotFoundMessage: 'Bu ürün bilgisi şu an kullanılamıyor. Başka bir konuda yardımcı olabilirim.',\n stopGenerating: 'Yanıtı Durdur',\n offlineMessage: 'Çevrimdışısınız — bağlantı kurulduğunda mesajlar gönderilecek.',\n cartAriaLabel: 'Sepetim',\n favoritesAriaLabel: 'Favorilerim',\n showPanelAriaLabel: 'Sonuçları Göster',\n addToFavoritesLabel: 'Favorilere ekle',\n customerReviewsTitle: 'Müşteri Yorumları',\n togglePanelAriaLabel: 'Paneli aç/kapat',\n chatMessagesAriaLabel: 'Sohbet mesajları',\n suggestionsAriaLabel: 'Öneriler',\n moreSuggestionsAriaLabel: 'Daha fazla öneri',\n rollbackAriaLabel: 'Bu mesaja geri dön',\n backAriaLabel: 'Geri',\n forwardAriaLabel: 'İleri',\n closePanelAriaLabel: 'Paneli kapat',\n dismissAriaLabel: 'Kapat',\n cartAddErrorMessage: 'Üzgünüm sepete ekleyemedim, bir sorunla karşılaştım.',\n favoriteToggleErrorMessage: 'Favoriler güncellenemedi. Lütfen tekrar deneyin.',\n reviewFilterPositive: 'Olumlu',\n reviewFilterNegative: 'Olumsuz',\n decreaseLabel: 'Azalt',\n increaseLabel: 'Artır',\n reviewCustomersMentionSingular: '1 müşteri bahsediyor',\n reviewCustomersMentionPlural: 'müşteri bahsediyor',\n reviewSubjectsHeading: 'İncelemek için seçin',\n tryAgainButton: 'Tekrar dene',\n askSomethingElseButton: 'Başka bir şey sor',\n accountInactiveMessage: 'Bu hesap şu an aktif değil. Lütfen daha sonra tekrar deneyin.',\n favoritesPageTitle: 'Favorilerim',\n emptyFavoritesMessage: 'Henüz favori ürün yok. Bir ürünü kalp ikonuna tıklayarak favorilere ekleyebilirsin.',\n showFormerMessagesButton: '↑ Önceki mesajları göster',\n aiAnalysisAnalyzingLabel: 'Ürünler analiz ediliyor...',\n aiBrowseCategoriesTitle: 'Kategorilere Göz At',\n};\n","import type { ChatI18n } from '../types.js';\n\nexport const CHAT_I18N_EN: ChatI18n = {\n headerTitle: 'Product Expert',\n inputPlaceholder: 'Search products, ask questions',\n sendButton: 'Send',\n closeButton: 'Close',\n openButton: 'Open chat',\n newChatButton: 'New chat',\n poweredBy: 'Powered by Gengage',\n errorMessage: 'Something went wrong. Please try again.',\n retryButton: 'Retry',\n loadingMessage: 'Thinking...',\n loadingSequenceGeneric: [\n 'Understanding what matters most',\n 'Reviewing products, reviews, and specs',\n 'Preparing the best options to review',\n ],\n loadingSequencePanel: [\n 'Reviewing the most relevant details',\n 'Checking the strongest signals',\n 'Preparing this view for you',\n ],\n loadingSequenceComparison: [\n 'Reviewing the product differences',\n 'Checking the strongest tradeoffs',\n 'Preparing your comparison',\n ],\n productCtaLabel: 'Buy',\n viewOnSiteLabel: 'View on Site',\n attachImageButton: 'Attach image',\n attachMenuSelectPhoto: 'Choose photo',\n attachMenuPaste: 'Paste',\n clipboardNoImageMessage: 'No image found in clipboard. Copy an image first or choose a file.',\n removeAttachmentButton: 'Remove image',\n invalidFileType: 'Only JPEG, PNG and WebP files are supported.',\n fileTooLarge: 'File must be smaller than 5 MB.',\n aiTopPicksTitle: 'Top Picks for You',\n roleWinner: 'Top Pick',\n roleBestValue: 'Best Value',\n roleBestAlternative: 'Best Alternative',\n viewDetails: 'View Details',\n groundingReviewCta: 'Read Reviews',\n groundingReviewSubtitle: '{count} reviews available',\n variantsLabel: 'Variants',\n sortRelated: 'Recommended',\n sortPriceAsc: 'Price low to high',\n sortPriceDesc: 'Price high to low',\n sortToolbarAriaLabel: 'Sort products',\n compareSelected: 'Compare',\n compareMinHint: 'Select at least 2 products',\n compareMaxHint: 'You can select up to 5 products',\n comparisonSelectLabel: 'Select to compare',\n comparisonSelectedLabel: 'Selected',\n comparisonSelectCardHint: 'Tap anywhere on the card to add or remove it from comparison.',\n comparisonPreparingLabel: 'Preparing your comparison…',\n panelTitleProductDetails: 'Product Details',\n panelTitleSimilarProducts: 'Similar Products',\n panelTitleComparisonResults: 'Comparison Results',\n panelTitleCategories: 'Categories',\n panelTitleSearchResults: 'Search Results',\n inStockLabel: 'In Stock',\n outOfStockLabel: 'Out of Stock',\n findSimilarLabel: 'Find Similar',\n galleryPrevAriaLabel: 'Previous image',\n galleryNextAriaLabel: 'Next image',\n choicePrompterHeading: \"Can't decide?\",\n choicePrompterSuggestion: 'Select products to compare them',\n choicePrompterCta: 'Select & Compare',\n viewMoreLabel: 'Show More',\n similarProductsLabel: 'Similar Products',\n addToCartButton: 'Add to Cart',\n addedToCartToast: 'Added to cart',\n shareButton: 'Share',\n productInfoTab: 'Product Info',\n specificationsTab: 'Specifications',\n recommendedChoiceLabel: 'Recommended Choice',\n highlightsLabel: 'Key Highlights',\n keyDifferencesLabel: 'Key Differences',\n specialCasesLabel: 'For Special Cases',\n emptyReviewsMessage: 'No review summary found.',\n closeAriaLabel: 'Close',\n startChatLabel: 'Start Chat',\n voiceButton: 'Voice input',\n voiceListening: 'Listening...',\n voiceNotSupported: 'Voice input is not supported in this browser.',\n voicePermissionDenied: 'Microphone access denied.',\n voiceError: 'Voice input error.',\n handoffHeading: 'Transferring to a support agent',\n productNotFoundMessage: 'Product information is currently unavailable. I can help with something else.',\n stopGenerating: 'Stop generating',\n offlineMessage: \"You're offline — messages will send when you reconnect.\",\n cartAriaLabel: 'My cart',\n favoritesAriaLabel: 'My favorites',\n showPanelAriaLabel: 'Show Results',\n addToFavoritesLabel: 'Add to favorites',\n customerReviewsTitle: 'Customer Reviews',\n togglePanelAriaLabel: 'Toggle panel',\n chatMessagesAriaLabel: 'Chat messages',\n suggestionsAriaLabel: 'Suggestions',\n moreSuggestionsAriaLabel: 'More suggestions',\n rollbackAriaLabel: 'Rollback to this message',\n backAriaLabel: 'Back',\n forwardAriaLabel: 'Forward',\n closePanelAriaLabel: 'Close panel',\n dismissAriaLabel: 'Dismiss',\n cartAddErrorMessage: \"Sorry, I couldn't add that to your cart. Something went wrong.\",\n favoriteToggleErrorMessage: \"We couldn't update favorites. Please try again.\",\n reviewFilterPositive: 'Positive',\n reviewFilterNegative: 'Negative',\n decreaseLabel: 'Decrease',\n increaseLabel: 'Increase',\n reviewCustomersMentionSingular: '1 customer mentions',\n reviewCustomersMentionPlural: 'customers mention',\n reviewSubjectsHeading: 'Select to learn more',\n tryAgainButton: 'Try again',\n askSomethingElseButton: 'Ask something else',\n accountInactiveMessage: 'This account is currently inactive. Please try again later.',\n favoritesPageTitle: 'My Favorites',\n emptyFavoritesMessage: 'No favorites yet. Heart a product to save it here.',\n showFormerMessagesButton: '↑ Show earlier messages',\n aiAnalysisAnalyzingLabel: 'Analyzing products...',\n aiBrowseCategoriesTitle: 'Browse categories',\n};\n","import type { ChatI18n } from '../types.js';\nimport { CHAT_I18N_TR } from './tr.js';\nimport { CHAT_I18N_EN } from './en.js';\n\nfunction normalizeLocale(locale?: string): string {\n if (!locale) return 'tr';\n return locale.toLowerCase().split('-')[0] ?? 'tr';\n}\n\nexport function resolveChatLocale(locale?: string): ChatI18n {\n switch (normalizeLocale(locale)) {\n case 'en':\n return CHAT_I18N_EN;\n default:\n return CHAT_I18N_TR;\n }\n}\n\nexport { CHAT_I18N_TR, CHAT_I18N_EN };\n","/**\n * Browser-native Web Speech API voice input.\n *\n * Uses the SpeechRecognition API for real-time speech-to-text.\n * The frontend sends transcribed text directly — no audio blobs\n * are sent to the backend. This replaces server-side Groq Whisper.\n *\n * Supports:\n * - Real-time transcription with interim results\n * - Turkish (`tr-TR`) and English (`en-US`) language support\n * - Auto-submit on silence (configurable timeout)\n * - Microphone permission handling with descriptive errors\n *\n * Browser support:\n * - Chrome 33+, Edge 79+, Safari 14.1+ (via webkitSpeechRecognition)\n * - Firefox: NOT SUPPORTED (no SpeechRecognition API)\n */\n\n// ---------------------------------------------------------------------------\n// Web Speech API types (not in lib.dom.d.ts for all TS targets)\n// ---------------------------------------------------------------------------\n\ninterface SpeechRecognitionEvent extends Event {\n readonly resultIndex: number;\n readonly results: SpeechRecognitionResultList;\n}\n\ninterface SpeechRecognitionResultList {\n readonly length: number;\n item(index: number): SpeechRecognitionResult;\n [index: number]: SpeechRecognitionResult;\n}\n\ninterface SpeechRecognitionResult {\n readonly isFinal: boolean;\n readonly length: number;\n item(index: number): SpeechRecognitionAlternative;\n [index: number]: SpeechRecognitionAlternative;\n}\n\ninterface SpeechRecognitionAlternative {\n readonly transcript: string;\n readonly confidence: number;\n}\n\ninterface SpeechRecognitionErrorEvent extends Event {\n readonly error: string;\n readonly message: string;\n}\n\ninterface SpeechRecognitionConstructor {\n new (): SpeechRecognitionInstance;\n}\n\ninterface SpeechRecognitionInstance extends EventTarget {\n continuous: boolean;\n interimResults: boolean;\n lang: string;\n maxAlternatives: number;\n onresult: ((event: SpeechRecognitionEvent) => void) | null;\n onerror: ((event: SpeechRecognitionErrorEvent) => void) | null;\n onend: (() => void) | null;\n onstart: (() => void) | null;\n onspeechend: (() => void) | null;\n onaudiostart: (() => void) | null;\n start(): void;\n stop(): void;\n abort(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport type VoiceInputState = 'idle' | 'listening' | 'error';\n\nexport type VoiceInputErrorCode =\n | 'not-supported'\n | 'not-allowed'\n | 'no-microphone'\n | 'no-speech'\n | 'network'\n | 'aborted'\n | 'unknown';\n\nexport interface VoiceInputCallbacks {\n /** Called with interim transcript while the user speaks. */\n onInterim?: (text: string) => void;\n /** Called with final transcript when a phrase is recognized. */\n onFinal?: (text: string) => void;\n /** Called when auto-submit fires (silence timeout reached with final text). */\n onAutoSubmit?: (text: string) => void;\n /** Called when voice input state changes. */\n onStateChange?: (state: VoiceInputState) => void;\n /** Called on recognition error. */\n onError?: (code: VoiceInputErrorCode, message: string) => void;\n}\n\nexport interface VoiceInputOptions {\n /** BCP 47 language tag. Default: 'tr-TR'. */\n lang?: string;\n /** Silence duration in ms before auto-submit. Default: 1500. */\n silenceTimeoutMs?: number;\n /** Whether to auto-submit on silence. Default: true. */\n autoSubmit?: boolean;\n}\n\n/**\n * Check whether the browser supports the Web Speech API.\n */\nexport function isVoiceInputSupported(): boolean {\n return getSpeechRecognitionConstructor() !== null;\n}\n\nfunction getSpeechRecognitionConstructor(): SpeechRecognitionConstructor | null {\n const w = globalThis as unknown as Record<string, unknown>;\n return (w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null) as SpeechRecognitionConstructor | null;\n}\n\nexport class VoiceInput {\n private recognition: SpeechRecognitionInstance | null = null;\n private _state: VoiceInputState = 'idle';\n private silenceTimer: ReturnType<typeof setTimeout> | null = null;\n private accumulatedTranscript = '';\n private readonly callbacks: VoiceInputCallbacks;\n private readonly lang: string;\n private readonly silenceTimeoutMs: number;\n private readonly autoSubmit: boolean;\n private intentionalStop = false;\n private _lastRestartAt = 0;\n\n constructor(callbacks: VoiceInputCallbacks, options?: VoiceInputOptions) {\n this.callbacks = callbacks;\n this.lang = options?.lang ?? 'tr-TR';\n this.silenceTimeoutMs = options?.silenceTimeoutMs ?? 1500;\n this.autoSubmit = options?.autoSubmit ?? true;\n }\n\n get state(): VoiceInputState {\n return this._state;\n }\n\n /**\n * Start listening. Requests microphone permission on first call.\n */\n start(): void {\n if (this._state === 'listening') return;\n\n const Ctor = getSpeechRecognitionConstructor();\n if (!Ctor) {\n this.setState('error');\n this.callbacks.onError?.('not-supported', 'Web Speech API is not supported in this browser.');\n return;\n }\n\n // Require secure context (HTTPS)\n if (typeof globalThis.isSecureContext !== 'undefined' && !globalThis.isSecureContext) {\n this.setState('error');\n this.callbacks.onError?.('not-allowed', 'Voice input requires HTTPS.');\n return;\n }\n\n this.accumulatedTranscript = '';\n this.intentionalStop = false;\n\n const recognition = new Ctor();\n recognition.continuous = true;\n recognition.interimResults = true;\n recognition.lang = this.lang;\n recognition.maxAlternatives = 1;\n\n recognition.onstart = () => {\n this.setState('listening');\n };\n\n recognition.onresult = (event: SpeechRecognitionEvent) => {\n this.clearSilenceTimer();\n\n let interim = '';\n let latestFinal = '';\n\n for (let i = event.resultIndex; i < event.results.length; i++) {\n const result = event.results[i];\n if (!result) continue;\n const alt = result[0];\n if (!alt) continue;\n if (result.isFinal) {\n latestFinal += alt.transcript;\n } else {\n interim += alt.transcript;\n }\n }\n\n if (latestFinal) {\n this.accumulatedTranscript += latestFinal;\n this.callbacks.onFinal?.(this.accumulatedTranscript);\n }\n\n if (interim) {\n this.callbacks.onInterim?.(this.accumulatedTranscript + interim);\n }\n\n // Start silence timer for auto-submit\n if (this.autoSubmit && this.accumulatedTranscript) {\n this.startSilenceTimer();\n }\n };\n\n recognition.onerror = (event: SpeechRecognitionErrorEvent) => {\n const code = mapErrorCode(event.error);\n // 'no-speech' and 'aborted' during intentional stop are not real errors\n if (this.intentionalStop && (event.error === 'no-speech' || event.error === 'aborted')) {\n return;\n }\n this.setState('error');\n this.callbacks.onError?.(code, event.message || event.error);\n };\n\n recognition.onend = () => {\n this.clearSilenceTimer();\n // Auto-restart if still in listening state (browser may stop recognition arbitrarily)\n if (this._state === 'listening' && !this.intentionalStop) {\n const now = Date.now();\n // Prevent rapid restart loop on Chrome — if onend fires within 500ms\n // of the last restart, the browser is refusing to stay active.\n if (now - this._lastRestartAt < 500) {\n this.setState('idle');\n return;\n }\n this._lastRestartAt = now;\n try {\n recognition.start();\n } catch {\n this.setState('idle');\n }\n return;\n }\n this.setState('idle');\n };\n\n this.recognition = recognition;\n\n try {\n recognition.start();\n } catch {\n this.setState('error');\n this.callbacks.onError?.('unknown', 'Failed to start speech recognition.');\n }\n }\n\n /**\n * Stop listening. Returns the accumulated transcript.\n */\n stop(): string {\n this.intentionalStop = true;\n this.clearSilenceTimer();\n if (this.recognition) {\n try {\n this.recognition.stop();\n } catch {\n // Already stopped\n }\n this.recognition = null;\n }\n this.setState('idle');\n return this.accumulatedTranscript;\n }\n\n /**\n * Abort listening. Discards any accumulated transcript.\n */\n abort(): void {\n this.intentionalStop = true;\n this.clearSilenceTimer();\n this.accumulatedTranscript = '';\n if (this.recognition) {\n try {\n this.recognition.abort();\n } catch {\n // Already aborted\n }\n this.recognition = null;\n }\n this.setState('idle');\n }\n\n /** Destroy the instance and release resources. */\n destroy(): void {\n this.abort();\n }\n\n private setState(state: VoiceInputState): void {\n if (this._state !== state) {\n this._state = state;\n this.callbacks.onStateChange?.(state);\n }\n }\n\n private startSilenceTimer(): void {\n this.clearSilenceTimer();\n this.silenceTimer = setTimeout(() => {\n const text = this.stop();\n if (text.trim()) {\n this.callbacks.onAutoSubmit?.(text.trim());\n }\n }, this.silenceTimeoutMs);\n }\n\n private clearSilenceTimer(): void {\n if (this.silenceTimer !== null) {\n clearTimeout(this.silenceTimer);\n this.silenceTimer = null;\n }\n }\n}\n\nfunction mapErrorCode(error: string): VoiceInputErrorCode {\n switch (error) {\n case 'not-allowed':\n return 'not-allowed';\n case 'no-speech':\n return 'no-speech';\n case 'audio-capture':\n return 'no-microphone';\n case 'network':\n return 'network';\n case 'aborted':\n return 'aborted';\n default:\n return 'unknown';\n }\n}\n","import { sanitizeHtml } from '../../common/safe-html.js';\n\nexport interface KvkkBannerOptions {\n htmlContent: string;\n onDismiss: () => void;\n closeAriaLabel?: string;\n}\n\nexport function createKvkkBanner(options: KvkkBannerOptions): HTMLElement {\n const banner = document.createElement('div');\n banner.className = 'gengage-chat-kvkk-banner gds-evidence-card gds-evidence-card-warning';\n banner.dataset['gengagePart'] = 'kvkk-banner';\n banner.setAttribute('role', 'alert');\n\n const content = document.createElement('div');\n content.className = 'gengage-chat-kvkk-content';\n content.dataset['gengagePart'] = 'kvkk-content';\n content.innerHTML = sanitizeHtml(options.htmlContent);\n banner.appendChild(content);\n\n const dismiss = document.createElement('button');\n dismiss.className = 'gengage-chat-kvkk-dismiss';\n dismiss.dataset['gengagePart'] = 'kvkk-dismiss';\n dismiss.type = 'button';\n dismiss.setAttribute('aria-label', options.closeAriaLabel ?? 'Close privacy notice');\n dismiss.textContent = '\\u00D7';\n dismiss.addEventListener('click', options.onDismiss);\n banner.appendChild(dismiss);\n\n return banner;\n}\n","/**\n * PanelTopBar — navigation bar at the top of the panel pane.\n *\n * Shows back/forward arrow buttons and a title derived from the current\n * panel content type. On mobile a close (✕) button is also rendered so\n * users can dismiss all panel layers with a single tap and return to the\n * conversation, without having to navigate back through every history entry.\n */\n\nexport interface PanelTopBarOptions {\n onBack: () => void;\n onForward: () => void;\n /** Called when the mobile close (✕) button is tapped. Should clear all panel history. */\n onClose?: () => void;\n backAriaLabel?: string;\n forwardAriaLabel?: string;\n /** Aria label for the close button (mobile only). */\n closePanelAriaLabel?: string;\n}\n\nexport class PanelTopBar {\n private _el: HTMLElement;\n private _backBtn: HTMLButtonElement;\n private _forwardBtn: HTMLButtonElement;\n private _titleEl: HTMLElement;\n private _actionsEl: HTMLElement;\n private _closeBtn: HTMLButtonElement;\n\n constructor(options: PanelTopBarOptions) {\n this._el = document.createElement('div');\n this._el.className = 'gengage-chat-panel-topbar gds-toolbar';\n this._el.dataset['gengagePart'] = 'panel-topbar';\n\n this._backBtn = document.createElement('button');\n this._backBtn.className = 'gengage-chat-panel-topbar-back gds-btn gds-btn-ghost gds-icon-btn';\n this._backBtn.dataset['gengagePart'] = 'panel-topbar-back';\n this._backBtn.type = 'button';\n this._backBtn.disabled = true;\n this._backBtn.setAttribute('aria-label', options.backAriaLabel ?? 'Back');\n this._backBtn.title = options.backAriaLabel ?? 'Back';\n this._backBtn.innerHTML =\n '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"m15 18-6-6 6-6\"/><path d=\"M21 12H9\"/></svg>';\n this._backBtn.addEventListener('click', () => options.onBack());\n\n const titleWrap = document.createElement('div');\n titleWrap.className = 'gengage-chat-panel-topbar-title-wrap';\n\n this._titleEl = document.createElement('span');\n this._titleEl.className = 'gengage-chat-panel-topbar-title';\n this._titleEl.dataset['gengagePart'] = 'panel-topbar-title';\n titleWrap.appendChild(this._titleEl);\n\n this._forwardBtn = document.createElement('button');\n this._forwardBtn.className = 'gengage-chat-panel-topbar-forward gds-btn gds-btn-ghost gds-icon-btn';\n this._forwardBtn.dataset['gengagePart'] = 'panel-topbar-forward';\n this._forwardBtn.type = 'button';\n this._forwardBtn.disabled = true;\n this._forwardBtn.setAttribute('aria-label', options.forwardAriaLabel ?? 'Forward');\n this._forwardBtn.title = options.forwardAriaLabel ?? 'Forward';\n this._forwardBtn.innerHTML =\n '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"m9 18 6-6-6-6\"/><path d=\"M3 12h12\"/></svg>';\n this._forwardBtn.addEventListener('click', () => options.onForward());\n\n this._actionsEl = document.createElement('div');\n this._actionsEl.className = 'gengage-chat-panel-topbar-actions';\n this._actionsEl.dataset['gengagePart'] = 'panel-topbar-actions';\n\n // Close button — only visible on mobile via CSS.\n // Dismisses all panel layers and returns to the base conversation.\n this._closeBtn = document.createElement('button');\n this._closeBtn.className = 'gengage-chat-panel-topbar-close gds-btn gds-btn-ghost gds-icon-btn';\n this._closeBtn.dataset['gengagePart'] = 'panel-topbar-close';\n this._closeBtn.type = 'button';\n this._closeBtn.setAttribute('aria-label', options.closePanelAriaLabel ?? 'Close panel');\n this._closeBtn.title = options.closePanelAriaLabel ?? 'Close panel';\n this._closeBtn.innerHTML =\n '<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>';\n this._closeBtn.addEventListener('click', () => options.onClose?.());\n\n this._el.appendChild(this._backBtn);\n this._el.appendChild(titleWrap);\n this._el.appendChild(this._actionsEl);\n this._el.appendChild(this._forwardBtn);\n this._el.appendChild(this._closeBtn);\n }\n\n update(canBack: boolean, canForward: boolean, title: string): void {\n this._backBtn.disabled = !canBack;\n this._forwardBtn.disabled = !canForward;\n this._backBtn.hidden = !canBack;\n this._forwardBtn.hidden = !canForward;\n this._titleEl.textContent = title;\n this._titleEl.title = title;\n }\n\n getElement(): HTMLElement {\n return this._el;\n }\n\n setTitle(title: string): void {\n this._titleEl.textContent = title;\n }\n\n setActions(actionsEl: HTMLElement | null): void {\n this._actionsEl.replaceChildren();\n if (!actionsEl) return;\n this._actionsEl.appendChild(actionsEl);\n }\n\n getTitle(): string {\n return this._titleEl.textContent ?? '';\n }\n}\n","/**\n * Vertical strip of product thumbnails along the right edge of the panel (results) pane.\n *\n * Clicking a thumbnail dispatches a rollback to the thread where that product was shown.\n *\n * **Display:** The column is hidden via CSS (`chat.css`); wiring (`setThumbnails`) stays for a future toggle.\n */\n\nimport { isSafeImageUrl } from '../../common/safe-html.js';\n\nexport interface ThumbnailEntry {\n sku: string;\n imageUrl: string;\n threadId: string;\n}\n\nexport interface ThumbnailsColumnOptions {\n onThumbnailClick: (threadId: string) => void;\n}\n\nexport class ThumbnailsColumn {\n private readonly _el: HTMLElement;\n private readonly _onThumbnailClick: (threadId: string) => void;\n\n constructor(options: ThumbnailsColumnOptions) {\n this._onThumbnailClick = options.onThumbnailClick;\n\n this._el = document.createElement('div');\n this._el.className = 'gengage-chat-thumbnails-column';\n this._el.style.display = 'none';\n }\n\n getElement(): HTMLElement {\n return this._el;\n }\n\n setEntries(entries: ThumbnailEntry[]): void {\n // Deduplicate by SKU (keep first occurrence)\n const seen = new Set<string>();\n const unique: ThumbnailEntry[] = [];\n for (const entry of entries) {\n if (!seen.has(entry.sku)) {\n seen.add(entry.sku);\n unique.push(entry);\n }\n }\n\n this._el.innerHTML = '';\n\n for (const entry of unique) {\n const thumb = document.createElement('button');\n thumb.type = 'button';\n thumb.className = 'gengage-chat-thumbnail-btn';\n thumb.title = entry.sku;\n\n if (isSafeImageUrl(entry.imageUrl)) {\n const img = document.createElement('img');\n img.className = 'gengage-chat-thumbnail-img';\n img.src = entry.imageUrl;\n img.alt = entry.sku;\n img.width = 40;\n img.height = 40;\n thumb.appendChild(img);\n }\n\n thumb.addEventListener('click', () => {\n this._onThumbnailClick(entry.threadId);\n });\n\n this._el.appendChild(thumb);\n }\n }\n\n show(): void {\n this._el.style.display = '';\n }\n\n hide(): void {\n this._el.style.display = 'none';\n }\n}\n","import type { ChatI18n, ChatMessage } from '../types.js';\nimport { registerChatScrollElement, CHAT_SCROLL_ELEMENT_ID } from '../utils/get-chat-scroll-element.js';\nimport { sanitizeHtml, isSafeImageUrl } from '../../common/safe-html.js';\nimport { dispatch } from '../../common/events.js';\nimport { CHAT_I18N_TR } from '../locales/index.js';\nimport { VoiceInput, isVoiceInputSupported } from '../../common/voice-input.js';\nimport { createKvkkBanner } from './KvkkBanner.js';\nimport { PanelTopBar } from './PanelTopBar.js';\nimport { ThumbnailsColumn } from './ThumbnailsColumn.js';\nimport type { ThumbnailEntry } from './ThumbnailsColumn.js';\n\n/** Generic fallback icon (right-arrow) used when a pill specifies an icon name not in the map. */\nconst DEFAULT_ACTION_ICON =\n '<svg viewBox=\"0 0 16 16\" class=\"gengage-chat-icon\"><path d=\"M3 8h10M9 4l4 4-4 4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>';\n\n/** SVG icon map for suggested action chips/pills. Keys match backend icon names. */\nconst SUGGESTED_ACTION_ICONS: Record<string, string> = {\n search:\n '<svg viewBox=\"0 0 16 16\" class=\"gengage-chat-icon\"><circle cx=\"6.5\" cy=\"6.5\" r=\"5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><line x1=\"10\" y1=\"10\" x2=\"15\" y2=\"15\" stroke=\"currentColor\" stroke-width=\"1.5\"/></svg>',\n review:\n '<svg viewBox=\"0 0 16 16\" class=\"gengage-chat-icon\"><polygon points=\"8,1 10,6 15,6 11,9 12.5,14 8,11 3.5,14 5,9 1,6 6,6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/></svg>',\n info: '<svg viewBox=\"0 0 16 16\" class=\"gengage-chat-icon\"><circle cx=\"8\" cy=\"8\" r=\"7\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><line x1=\"8\" y1=\"7\" x2=\"8\" y2=\"12\" stroke=\"currentColor\" stroke-width=\"1.5\"/><circle cx=\"8\" cy=\"4.5\" r=\"0.8\" fill=\"currentColor\"/></svg>',\n similar:\n '<svg viewBox=\"0 0 16 16\" class=\"gengage-chat-icon\"><rect x=\"1\" y=\"3\" width=\"6\" height=\"6\" rx=\"1\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><rect x=\"9\" y=\"3\" width=\"6\" height=\"6\" rx=\"1\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/></svg>',\n};\n\nexport { SUGGESTED_ACTION_ICONS, DEFAULT_ACTION_ICON };\n\nexport interface ChatDrawerOptions {\n i18n: ChatI18n;\n onSend: (text: string, attachment?: File) => void;\n /** Callback fired when the cart icon button in the header is clicked. */\n onCartClick?: (() => void) | undefined;\n onClose: () => void;\n onAttachment?: (file: File) => void;\n onPanelToggle?: () => void;\n /**\n * Fired when side panel visibility/content changes so the parent can refresh host scroll lock\n * and backdrop classes. Not related to the Google Chrome browser — “host shell” means the\n * surrounding page/document integration.\n */\n onHostShellSync?: () => void;\n onRollback?: (messageId: string) => void;\n headerTitle?: string | undefined;\n headerAvatarUrl?: string | undefined;\n /** Launcher image URL — used as avatar fallback when headerAvatarUrl is not set. */\n launcherImageUrl?: string | undefined;\n headerBadge?: string | undefined;\n /** URL for the cart icon link in the header (e.g. \"/sepetim\"). */\n /** @deprecated Use onCartClick instead. If set, the cart button will navigate to this URL. */\n headerCartUrl?: string | undefined;\n /** When true, render the header favorites (heart) button. */\n showHeaderFavorites?: boolean | undefined;\n onFavoritesClick?: (() => void) | undefined;\n /** Callback fired when the panel back button is clicked. */\n onPanelBack?: (() => void) | undefined;\n /** Callback fired when the panel forward button is clicked. */\n onPanelForward?: (() => void) | undefined;\n /** Callback fired when the mobile panel close (✕) button is tapped.\n * Should clear panel history and comparison state in the caller. */\n onPanelClose?: (() => void) | undefined;\n /**\n * Fired when the user drags the mobile handle and releases.\n * 'half' | 'full' → switch to that snap position.\n * 'close' → close the drawer.\n */\n onMobileSnap?: ((state: 'half' | 'full' | 'close') => void) | undefined;\n /** Returns the current mobile sheet state so the drag handler knows which snap to target. */\n getMobileState?: (() => 'half' | 'full') | undefined;\n /** Returns true when the chat is displayed as a mobile bottom-sheet. Used to keep the side-panel back button always enabled. */\n getMobileViewport?: (() => boolean) | undefined;\n /** Callback fired when a product thumbnail is clicked (for thread rollback). */\n onThumbnailClick?: ((threadId: string) => void) | undefined;\n /** Callback fired when a link in bot HTML is clicked. */\n onLinkClick?: ((url: string) => void) | undefined;\n /** Enable voice input (Web Speech API STT). Default: false. */\n voiceEnabled?: boolean | undefined;\n /** BCP 47 language for speech recognition. Default: 'tr-TR'. */\n voiceLang?: string | undefined;\n /** Callback fired when the \"New Chat\" button is clicked. */\n onNewChat?: (() => void) | undefined;\n /**\n * Transcript presentation hooks (focus thread, pin-to-bottom heuristics).\n * Optional — when omitted, legacy scroll behaviour is unchanged.\n */\n presentation?: {\n onPinnedToBottomChange?: (pinned: boolean) => void;\n onUserInteractingChange?: (interacting: boolean) => void;\n /** User scrolled up while a thread focus is active — parent may show \"former messages\" */\n onFormerMessagesHint?: () => void;\n /** When true, stream-driven soft scroll-to-bottom is suppressed */\n shouldBlockSoftAutoScroll?: () => boolean;\n /** User tapped \"show former messages\" */\n onReleasePresentationFocus?: () => void;\n };\n}\n\nconst DEFAULT_I18N: ChatI18n = CHAT_I18N_TR;\nconst LOADING_STEP_INTERVAL_MS = 1400;\n\ninterface LoadingSequenceBinding {\n labelEl: HTMLElement;\n steps: string[];\n index: number;\n intervalId: ReturnType<typeof setInterval> | null;\n}\n\nconst CLIPBOARD_DATA_URL_IN_HTML = /data:image\\/(png|jpeg|jpg|webp);base64,[A-Za-z0-9+/=]+/gi;\n\n/** Turn clipboard items into an image File (PNG/JPEG/WebP). */\nasync function fileFromClipboardItems(items: ClipboardItem[]): Promise<File | null> {\n for (const clipItem of items) {\n for (const type of clipItem.types) {\n if (!type.startsWith('image/')) continue;\n try {\n const blob = await clipItem.getType(type);\n if (!blob || blob.size === 0) continue;\n const mime = type || blob.type || 'image/png';\n const ext = mime === 'image/png' ? 'png' : mime === 'image/webp' ? 'webp' : 'jpg';\n const name = `paste-${Date.now()}.${ext}`;\n return new File([blob], name, { type: mime });\n } catch {\n continue;\n }\n }\n }\n\n // Windows / Office / browser copy sometimes exposes images only inside text/html (data URLs).\n for (const clipItem of items) {\n if (!clipItem.types.includes('text/html')) continue;\n try {\n const blob = await clipItem.getType('text/html');\n const html = await blob.text();\n const matches = html.match(CLIPBOARD_DATA_URL_IN_HTML);\n const dataUrl = matches?.[0];\n if (!dataUrl || dataUrl.length > 5_000_000) continue;\n const res = await fetch(dataUrl);\n const out = await res.blob();\n if (!out || out.size === 0) continue;\n const mime = out.type || 'image/png';\n if (!['image/jpeg', 'image/png', 'image/webp'].includes(mime)) continue;\n const ext = mime === 'image/png' ? 'png' : mime === 'image/webp' ? 'webp' : 'jpg';\n return new File([out], `paste-${Date.now()}.${ext}`, { type: mime });\n } catch {\n continue;\n }\n }\n\n return null;\n}\n\n/**\n * Read image from system clipboard.\n * @param readPromise - Pass `navigator.clipboard.read()` from the **synchronous** click handler.\n * Chromium on Windows often requires starting `read()` in the same turn as the user gesture;\n * awaiting other work first can clear activation so `read()` yields nothing / rejects.\n */\nasync function readClipboardImageAsFile(readPromise?: Promise<ClipboardItem[]>): Promise<File | null> {\n try {\n // readPromise is always supplied by the click handler (started synchronously for Chromium\n // user-activation). The fallback only fires in non-Chromium environments where activation\n // is not required, so it will never be reached on the browsers that need the fix.\n const p = readPromise ?? (typeof navigator.clipboard?.read === 'function' ? navigator.clipboard.read() : null);\n if (!p) return null;\n const items = await p;\n return fileFromClipboardItems(items);\n } catch {\n /* unsupported or permission denied */\n }\n return null;\n}\n\nexport class ChatDrawer {\n private root: HTMLElement;\n private messagesEl: HTMLElement;\n private inputEl: HTMLTextAreaElement;\n private sendBtn: HTMLButtonElement;\n private _sendStopHandler: (() => void) | null = null;\n private i18n: ChatI18n;\n private onSend: (text: string, attachment?: File) => void;\n private _panelEl: HTMLElement;\n private _panelVisible = false;\n private _panelCollapsed = false;\n private _dividerPreviewEnabled = false;\n private _dividerEl: HTMLElement;\n private _dividerPreviewEl: HTMLElement;\n private _onPanelToggle: (() => void) | undefined = undefined;\n private _onHostShellSync: (() => void) | undefined = undefined;\n private _pendingAttachment: File | null = null;\n private _fileInput: HTMLInputElement;\n private _previewStrip: HTMLElement;\n private _previewName: HTMLElement;\n private _onAttachment: ((file: File) => void) | undefined = undefined;\n private _onRollback: ((messageId: string) => void) | undefined = undefined;\n private _onLinkClick: ((url: string) => void) | undefined = undefined;\n private _pillsEl: HTMLElement;\n private _kvkkSlot: HTMLElement;\n private _panelTopBar: PanelTopBar;\n private _userScrolledUp = false;\n private _scrollLockedUntil = 0;\n private _inputChipsEl: HTMLElement;\n private _thumbnailsColumn: ThumbnailsColumn;\n private _panelFloatingEl: HTMLElement;\n /** Mobile: overlay host for comparison dock (above panel scroll; avoids transformed panel containing block). */\n private _comparisonDockSlotEl: HTMLElement;\n /** Slot between panel top bar and main scroll content (desktop AI picks / analyzing strip). */\n private _panelAiZoneEl!: HTMLElement;\n private _favBadgeEl: HTMLElement | null = null;\n private _thinkingSteps: string[] = [];\n private _firstBotMessageIds: Set<string> = new Set();\n private _voiceInput: VoiceInput | null = null;\n private _micBtn: HTMLButtonElement | null = null;\n private _voiceEnabled = false;\n private _voiceLang = 'tr-TR';\n private _ignoreNextDividerClick = false;\n /** Cancels in-flight panel list scroll-to-top tween when a new one starts. */\n private _panelListScrollAnimToken = 0;\n private readonly _cleanups: Array<() => void> = [];\n private _focusTrapHandler: ((e: KeyboardEvent) => void) | null = null;\n private _previouslyFocusedElement: HTMLElement | null = null;\n private _conversationEl: HTMLElement | null = null;\n private readonly _options: ChatDrawerOptions;\n private _reopenPanelBtn: HTMLButtonElement | null = null;\n private _presentationFocusThreadId: string | null = null;\n private _formerMessagesBtn: HTMLButtonElement | null = null;\n private _programmaticScrollUntil = 0;\n private _userInteractionUntil = 0;\n private _touchStartY: number | null = null;\n private _presentationPinned = true;\n private _presentationUserInteracting = false;\n private _resizeRafId: number | null = null;\n private _cartBtn: HTMLButtonElement | null = null;\n private _attachWrapEl: HTMLElement | null = null;\n private _attachMenuEl: HTMLElement | null = null;\n private _attachBtn: HTMLButtonElement | null = null;\n private _attachMenuCleanup: (() => void) | null = null;\n private _attachMenuClickTimerId: number | null = null;\n private _typingLoadingBinding: LoadingSequenceBinding | null = null;\n private _panelLoadingBinding: LoadingSequenceBinding | null = null;\n private _panelAiZoneLoadingBinding: LoadingSequenceBinding | null = null;\n\n constructor(container: HTMLElement, options: ChatDrawerOptions) {\n this._options = options;\n this.i18n = { ...DEFAULT_I18N, ...options.i18n };\n this.onSend = options.onSend;\n if (options.onPanelToggle !== undefined) {\n this._onPanelToggle = options.onPanelToggle;\n }\n if (options.onHostShellSync !== undefined) {\n this._onHostShellSync = options.onHostShellSync;\n }\n if (options.onAttachment !== undefined) {\n this._onAttachment = options.onAttachment;\n }\n if (options.onRollback !== undefined) {\n this._onRollback = options.onRollback;\n }\n if (options.onLinkClick !== undefined) {\n this._onLinkClick = options.onLinkClick;\n }\n if (options.voiceEnabled) {\n this._voiceEnabled = true;\n }\n if (options.voiceLang !== undefined) {\n this._voiceLang = options.voiceLang;\n }\n\n this.root = document.createElement('div');\n this.root.className = 'gengage-chat-drawer gds-panel';\n this.root.dataset['gengagePart'] = 'chat-drawer';\n this.root.setAttribute('role', 'dialog');\n this.root.setAttribute('aria-label', this.i18n.headerTitle ?? 'Chat');\n this.root.setAttribute('aria-modal', 'true');\n\n const descId = 'gengage-chat-dialog-desc';\n const descEl = document.createElement('span');\n descEl.id = descId;\n descEl.className = 'gengage-sr-only';\n descEl.textContent = this.i18n.headerTitle ?? 'AI shopping assistant';\n this.root.appendChild(descEl);\n this.root.setAttribute('aria-describedby', descId);\n\n // Mobile drag handle — visual pill indicator (events attached to the full header below)\n let _handleEl: HTMLDivElement | null = null;\n {\n const handleEl = document.createElement('div');\n handleEl.className = 'gengage-chat-drawer-handle';\n handleEl.dataset['gengagePart'] = 'chat-drawer-handle';\n handleEl.setAttribute('aria-hidden', 'true');\n handleEl.style.pointerEvents = 'none'; // visual only; header receives the touch events\n _handleEl = handleEl;\n }\n\n // Header — branded dark bar\n const header = document.createElement('div');\n header.className = 'gengage-chat-header gds-shell-header';\n header.dataset['gengagePart'] = 'chat-header';\n\n const headerLeft = document.createElement('div');\n headerLeft.className = 'gengage-chat-header-left';\n headerLeft.dataset['gengagePart'] = 'chat-header-left';\n\n const avatarUrl = options.headerAvatarUrl ?? options.launcherImageUrl;\n const useLogoAvatar =\n typeof options.headerAvatarUrl === 'string' &&\n options.headerAvatarUrl.length > 0 &&\n options.headerAvatarUrl !== options.launcherImageUrl;\n if (avatarUrl) {\n const avatar = document.createElement('img');\n avatar.className = 'gengage-chat-header-avatar';\n if (useLogoAvatar) avatar.classList.add('gengage-chat-header-avatar--logo');\n avatar.dataset['gengagePart'] = 'chat-header-avatar';\n avatar.src = avatarUrl;\n avatar.alt = options.headerTitle ?? 'Assistant';\n headerLeft.appendChild(avatar);\n }\n\n const headerInfo = document.createElement('div');\n headerInfo.className = 'gengage-chat-header-info';\n headerInfo.dataset['gengagePart'] = 'chat-header-info';\n\n const titleRow = document.createElement('div');\n titleRow.className = 'gengage-chat-header-title-row';\n titleRow.dataset['gengagePart'] = 'chat-header-title-row';\n const title = document.createElement('span');\n title.className = 'gengage-chat-header-title';\n title.dataset['gengagePart'] = 'chat-header-title';\n title.textContent = options.headerTitle ?? this.i18n.headerTitle ?? 'Product Expert';\n titleRow.appendChild(title);\n\n if (options.headerBadge) {\n const badge = document.createElement('span');\n badge.className = 'gengage-chat-header-badge gds-badge gds-badge-brand';\n badge.dataset['gengagePart'] = 'chat-header-badge';\n badge.textContent = options.headerBadge;\n titleRow.appendChild(badge);\n }\n headerInfo.appendChild(titleRow);\n\n const powered = document.createElement('a');\n powered.className = 'gengage-chat-header-powered';\n powered.dataset['gengagePart'] = 'chat-header-powered-by';\n powered.href = 'https://gengage.ai/';\n powered.target = '_blank';\n powered.rel = 'noopener noreferrer';\n powered.innerHTML =\n `<svg viewBox=\"0 0 15 15\" fill=\"none\" aria-hidden=\"true\">` +\n `<path d=\"M15 5.88941C12.2201 5.88941 9.72762 7.14107 8.05571 9.11059H0C2.77991 9.11059 5.27238 7.85893 6.94429 5.88941H15Z\" fill=\"currentColor\"/>` +\n `<path d=\"M9.10964 0C9.10964 2.24394 8.29524 4.30038 6.94429 5.88941C5.27238 7.85962 2.77922 9.11059 0 9.11059V5.88941C3.24802 5.88941 5.89036 3.2465 5.89036 0H9.10964Z\" fill=\"currentColor\" fill-opacity=\"0.68\"/>` +\n `<path d=\"M15 5.88941V9.11059C11.752 9.11059 9.10964 11.7535 9.10964 15H5.89036C5.89036 12.7561 6.70476 10.6996 8.05571 9.11059C9.72762 7.14038 12.2208 5.88941 15 5.88941Z\" fill=\"currentColor\" fill-opacity=\"0.68\"/>` +\n `</svg>${this.i18n.poweredBy}`;\n headerInfo.appendChild(powered);\n\n headerLeft.appendChild(headerInfo);\n header.appendChild(headerLeft);\n\n const headerRight = document.createElement('div');\n headerRight.className = 'gengage-chat-header-right';\n headerRight.dataset['gengagePart'] = 'chat-header-actions';\n\n // Reopen-panel button — shown on mobile when the side panel is hidden but has content\n {\n const reopenBtn = document.createElement('button');\n reopenBtn.type = 'button';\n reopenBtn.className =\n 'gengage-chat-header-btn gengage-chat-header-btn--reopen-panel gds-btn gds-btn-ghost gds-icon-btn';\n reopenBtn.dataset['gengagePart'] = 'chat-header-reopen-panel';\n reopenBtn.setAttribute('aria-label', this.i18n.showPanelAriaLabel);\n reopenBtn.innerHTML = `<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/><line x1=\"9\" y1=\"3\" x2=\"9\" y2=\"21\"/></svg>`;\n reopenBtn.addEventListener('click', () => this._showMobilePanelFromBtn());\n headerRight.appendChild(reopenBtn);\n this._reopenPanelBtn = reopenBtn;\n }\n\n // Cart button — always a <button> so the onCartClick callback is always invoked\n // (handles session persistence before navigation when headerCartUrl is set).\n {\n const cartBtn = document.createElement('button');\n cartBtn.type = 'button';\n cartBtn.className = 'gengage-chat-header-btn gds-btn gds-btn-ghost gds-icon-btn';\n cartBtn.dataset['gengagePart'] = 'chat-header-cart';\n cartBtn.setAttribute('aria-label', this.i18n.cartAriaLabel);\n cartBtn.innerHTML = `<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"9\" cy=\"21\" r=\"1\"/><circle cx=\"20\" cy=\"21\" r=\"1\"/><path d=\"M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6\"/></svg>`;\n cartBtn.addEventListener('click', () => options.onCartClick?.());\n headerRight.appendChild(cartBtn);\n this._cartBtn = cartBtn;\n }\n\n // New Chat button (optional — reset conversation)\n if (options.onNewChat) {\n const newChatBtn = document.createElement('button');\n newChatBtn.className = 'gengage-chat-header-btn gengage-chat-new-chat gds-btn gds-btn-ghost gds-icon-btn';\n newChatBtn.dataset['gengagePart'] = 'chat-header-new-chat';\n newChatBtn.type = 'button';\n newChatBtn.setAttribute('aria-label', this.i18n.newChatButton);\n newChatBtn.title = this.i18n.newChatButton;\n newChatBtn.innerHTML = `<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 20h9\"/><path d=\"M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z\"/></svg>`;\n newChatBtn.addEventListener('click', () => options.onNewChat?.());\n headerRight.appendChild(newChatBtn);\n }\n\n const closeBtn = document.createElement('button');\n closeBtn.className = 'gengage-chat-close gds-btn gds-btn-ghost gds-icon-btn';\n closeBtn.dataset['gengagePart'] = 'chat-header-close';\n closeBtn.type = 'button';\n closeBtn.setAttribute('aria-label', this.i18n.closeButton);\n closeBtn.innerHTML = `<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>`;\n closeBtn.addEventListener('click', options.onClose);\n\n if (options.showHeaderFavorites) {\n const favBtn = document.createElement('button');\n favBtn.className = 'gengage-chat-header-btn gengage-chat-header-btn--fav gds-btn gds-btn-ghost gds-icon-btn';\n favBtn.dataset['gengagePart'] = 'chat-header-favorites';\n favBtn.type = 'button';\n favBtn.setAttribute('aria-label', this.i18n.favoritesAriaLabel);\n favBtn.innerHTML = `<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z\"/></svg>`;\n\n const badge = document.createElement('span');\n badge.className = 'gengage-chat-header-fav-badge';\n badge.dataset['gengagePart'] = 'chat-header-favorites-badge';\n badge.setAttribute('aria-hidden', 'true');\n badge.style.display = 'none';\n favBtn.appendChild(badge);\n this._favBadgeEl = badge;\n\n favBtn.addEventListener('click', () => {\n options.onFavoritesClick?.();\n });\n headerRight.appendChild(favBtn);\n }\n\n headerRight.appendChild(closeBtn);\n // Insert handle at the very top of header (before headerLeft / headerRight)\n if (_handleEl) header.insertBefore(_handleEl, header.firstChild);\n header.appendChild(headerRight);\n\n // Attach drag-to-dismiss events to the full header so any header tap-drag works.\n // Interactive children (buttons, links) are excluded so they keep normal tap behaviour.\n {\n const SNAP_THRESHOLD = 72;\n let dragStartY = 0;\n let dragDelta = 0;\n let dragging = false;\n\n const onDragStart = (e: TouchEvent) => {\n if (!(this._options.getMobileViewport?.() ?? window.innerWidth <= 768)) return;\n // Don't start drag if the touch landed on an interactive element\n const target = e.target as HTMLElement;\n if (target.closest('button, a, input, [role=\"button\"]')) return;\n const t = e.changedTouches?.[0];\n if (!t) return;\n dragStartY = t.clientY;\n dragDelta = 0;\n dragging = true;\n this.root.style.transition = 'none';\n };\n\n const onDragMove = (e: TouchEvent) => {\n if (!dragging) return;\n const t = e.changedTouches?.[0];\n if (!t) return;\n dragDelta = t.clientY - dragStartY;\n const currentState = options.getMobileState?.() ?? 'full';\n const clampedDelta = currentState === 'full' ? Math.max(0, dragDelta) : dragDelta;\n e.preventDefault();\n this.root.style.transform = `translateY(${clampedDelta}px)`;\n };\n\n const onDragEnd = () => {\n if (!dragging) return;\n dragging = false;\n const currentState = options.getMobileState?.() ?? 'full';\n\n let nextState: 'half' | 'full' | 'close';\n if (dragDelta > SNAP_THRESHOLD) {\n nextState = currentState === 'full' ? 'half' : 'close';\n } else if (dragDelta < -SNAP_THRESHOLD && currentState === 'half') {\n nextState = 'full';\n } else {\n nextState = currentState;\n }\n\n this.root.style.transition = '';\n if (nextState === 'close') {\n this.root.style.transform = 'translateY(100%)';\n setTimeout(() => {\n this.root.style.transform = '';\n options.onMobileSnap?.('close');\n }, 280);\n } else {\n this.root.style.transform = '';\n options.onMobileSnap?.(nextState);\n }\n dragDelta = 0;\n };\n\n const onDragCancel = () => {\n if (!dragging) return;\n dragging = false;\n dragDelta = 0;\n this.root.style.transition = '';\n this.root.style.transform = '';\n };\n\n header.addEventListener('touchstart', onDragStart, { passive: true });\n header.addEventListener('touchmove', onDragMove, { passive: false });\n header.addEventListener('touchend', onDragEnd, { passive: true });\n header.addEventListener('touchcancel', onDragCancel, { passive: true });\n this._cleanups.push(() => {\n header.removeEventListener('touchstart', onDragStart);\n header.removeEventListener('touchmove', onDragMove);\n header.removeEventListener('touchend', onDragEnd);\n header.removeEventListener('touchcancel', onDragCancel);\n });\n }\n\n // Body: flex container for panel + conversation\n const body = document.createElement('div');\n body.className = 'gengage-chat-body';\n body.dataset['gengagePart'] = 'chat-body';\n\n // Panel (hidden by default)\n this._panelEl = document.createElement('div');\n this._panelEl.className = 'gengage-chat-panel gds-panel';\n this._panelEl.dataset['gengagePart'] = 'chat-panel';\n\n // Panel top bar (navigation)\n this._panelTopBar = new PanelTopBar({\n onBack: () => options.onPanelBack?.(),\n onForward: () => options.onPanelForward?.(),\n onClose: () => {\n if (options.getMobileViewport?.() ?? false) {\n this.hideMobilePanel();\n } else {\n this.clearPanel();\n }\n options.onPanelClose?.();\n },\n backAriaLabel: this.i18n.backAriaLabel,\n forwardAriaLabel: this.i18n.forwardAriaLabel,\n closePanelAriaLabel: this.i18n.closePanelAriaLabel,\n });\n this._panelEl.appendChild(this._panelTopBar.getElement());\n\n // Panel scroll affordance — bottom fade gradient when content is scrollable\n const onPanelScroll = () => this._updateScrollAffordance();\n this._panelEl.addEventListener('scroll', onPanelScroll, { passive: true });\n this._cleanups.push(() => this._panelEl.removeEventListener('scroll', onPanelScroll));\n\n body.appendChild(this._panelEl);\n\n // Divider between panel and conversation\n this._dividerEl = document.createElement('div');\n this._dividerEl.className = 'gengage-chat-panel-divider gengage-chat-panel-divider--hidden';\n this._dividerEl.dataset['gengagePart'] = 'chat-panel-divider';\n this._dividerEl.setAttribute('role', 'separator');\n this._dividerEl.setAttribute('aria-label', this.i18n.togglePanelAriaLabel);\n this._dividerEl.setAttribute('title', this.i18n.togglePanelAriaLabel);\n this._dividerPreviewEl = document.createElement('div');\n this._dividerPreviewEl.className = 'gengage-chat-panel-divider-preview';\n this._dividerPreviewEl.dataset['gengagePart'] = 'chat-panel-divider-preview';\n this._dividerPreviewEl.setAttribute('aria-hidden', 'true');\n this._dividerEl.appendChild(this._dividerPreviewEl);\n const chevron = document.createElement('button');\n chevron.className = 'gengage-chat-panel-divider-toggle gds-btn gds-btn-ghost';\n chevron.dataset['gengagePart'] = 'chat-panel-divider-toggle';\n chevron.type = 'button';\n chevron.setAttribute('aria-label', this.i18n.togglePanelAriaLabel);\n chevron.setAttribute('title', this.i18n.togglePanelAriaLabel);\n chevron.textContent = '\\u00BB'; // » (collapse right)\n chevron.addEventListener('click', () => {\n if (this._ignoreNextDividerClick) {\n this._ignoreNextDividerClick = false;\n return;\n }\n this.togglePanel();\n this._onPanelToggle?.();\n });\n let touchStartX: number | null = null;\n let touchStartY: number | null = null;\n const swipeThreshold = 24;\n const onDividerTouchStart = (event: TouchEvent) => {\n if (!(this._options.getMobileViewport?.() ?? window.innerWidth <= 768)) return;\n const touch = event.changedTouches?.[0];\n if (!touch) return;\n touchStartX = touch.clientX;\n touchStartY = touch.clientY;\n };\n const onDividerTouchEnd = (event: TouchEvent) => {\n if (!(this._options.getMobileViewport?.() ?? window.innerWidth <= 768)) return;\n if (touchStartX === null || touchStartY === null) return;\n const touch = event.changedTouches?.[0];\n if (!touch) return;\n\n const deltaX = touch.clientX - touchStartX;\n const deltaY = touch.clientY - touchStartY;\n touchStartX = null;\n touchStartY = null;\n\n // Vertical swipe only. Swipe up collapses panel, swipe down expands.\n if (Math.abs(deltaY) < swipeThreshold || Math.abs(deltaY) < Math.abs(deltaX)) return;\n const nextCollapsed = deltaY < 0;\n if (nextCollapsed === this._panelCollapsed) return;\n\n this._ignoreNextDividerClick = true;\n this.setPanelCollapsed(nextCollapsed);\n this._onPanelToggle?.();\n };\n this._dividerEl.addEventListener('touchstart', onDividerTouchStart, { passive: true });\n this._dividerEl.addEventListener('touchend', onDividerTouchEnd, { passive: true });\n this._cleanups.push(() => {\n this._dividerEl.removeEventListener('touchstart', onDividerTouchStart);\n this._dividerEl.removeEventListener('touchend', onDividerTouchEnd);\n });\n this._dividerEl.appendChild(chevron);\n body.appendChild(this._dividerEl);\n\n // Conversation wrapper — header lives inside so it only spans chat width\n const conversation = document.createElement('div');\n conversation.className = 'gengage-chat-conversation';\n conversation.dataset['gengagePart'] = 'chat-conversation';\n this._conversationEl = conversation;\n conversation.appendChild(header);\n\n // Offline status bar (hidden by default, shown when navigator.onLine === false)\n const offlineBar = document.createElement('div');\n offlineBar.className = 'gengage-chat-offline-bar gds-evidence-card gds-evidence-card-warning';\n offlineBar.dataset['gengagePart'] = 'chat-offline-bar';\n offlineBar.setAttribute('role', 'status');\n offlineBar.setAttribute('aria-live', 'polite');\n offlineBar.textContent = this.i18n.offlineMessage;\n if (typeof navigator !== 'undefined' && !navigator.onLine) {\n offlineBar.classList.add('gengage-chat-offline-bar--visible');\n }\n conversation.appendChild(offlineBar);\n\n const onOffline = () => offlineBar.classList.add('gengage-chat-offline-bar--visible');\n const onOnline = () => offlineBar.classList.remove('gengage-chat-offline-bar--visible');\n window.addEventListener('offline', onOffline);\n window.addEventListener('online', onOnline);\n this._cleanups.push(() => {\n window.removeEventListener('offline', onOffline);\n window.removeEventListener('online', onOnline);\n });\n\n // KVKK banner slot (inserted above messages)\n this._kvkkSlot = document.createElement('div');\n this._kvkkSlot.className = 'gengage-chat-kvkk-slot';\n this._kvkkSlot.dataset['gengagePart'] = 'chat-kvkk-slot';\n conversation.appendChild(this._kvkkSlot);\n\n // Messages area (stable id for host tooling / getChatScrollElement registration)\n this.messagesEl = document.createElement('div');\n this.messagesEl.id = CHAT_SCROLL_ELEMENT_ID;\n this.messagesEl.className = 'gengage-chat-messages';\n this.messagesEl.dataset['gengagePart'] = 'chat-messages';\n this.messagesEl.setAttribute('role', 'log');\n this.messagesEl.setAttribute('aria-live', 'polite');\n this.messagesEl.setAttribute('aria-atomic', 'false');\n this.messagesEl.setAttribute('aria-label', this.i18n.chatMessagesAriaLabel);\n registerChatScrollElement(this.messagesEl);\n\n const formerBtn = document.createElement('button');\n formerBtn.type = 'button';\n formerBtn.className = 'gengage-chat-former-messages-btn gds-chip';\n formerBtn.dataset['gengagePart'] = 'chat-former-messages-button';\n formerBtn.textContent = this.i18n.showFormerMessagesButton;\n formerBtn.setAttribute('aria-label', this.i18n.showFormerMessagesButton);\n formerBtn.style.display = 'none';\n formerBtn.addEventListener('click', () => {\n this._options.presentation?.onReleasePresentationFocus?.();\n });\n this.messagesEl.appendChild(formerBtn);\n this._formerMessagesBtn = formerBtn;\n\n const markExplicitUserInteraction = () => {\n this._userInteractionUntil = Date.now() + 2000;\n };\n\n // Track user scroll position + presentation pin / interaction (aligned with legacy UX)\n let scrollRafPending = false;\n const pres = () => this._options.presentation;\n const onMessagesScroll = () => {\n if (scrollRafPending) return;\n scrollRafPending = true;\n requestAnimationFrame(() => {\n scrollRafPending = false;\n const { scrollTop, scrollHeight, clientHeight } = this.messagesEl;\n const distanceFromBottom = scrollHeight - scrollTop - clientHeight;\n this._userScrolledUp = distanceFromBottom > 10;\n\n const pinnedEnterThreshold = 32;\n const pinnedExitThreshold = 96;\n const previouslyPinned = this._presentationPinned;\n const pinned = previouslyPinned\n ? distanceFromBottom < pinnedExitThreshold\n : distanceFromBottom < pinnedEnterThreshold;\n\n const now = Date.now();\n const isProgrammaticScroll = now < this._programmaticScrollUntil;\n const explicitUserInteracting = !pinned && now < this._userInteractionUntil;\n const nextUserInteracting = isProgrammaticScroll ? false : explicitUserInteracting;\n\n if (pinned !== this._presentationPinned) {\n this._presentationPinned = pinned;\n pres()?.onPinnedToBottomChange?.(pinned);\n }\n if (nextUserInteracting !== this._presentationUserInteracting) {\n this._presentationUserInteracting = nextUserInteracting;\n pres()?.onUserInteractingChange?.(nextUserInteracting);\n }\n });\n };\n this.messagesEl.addEventListener('scroll', onMessagesScroll, { passive: true });\n this._cleanups.push(() => {\n this.messagesEl.removeEventListener('scroll', onMessagesScroll);\n });\n\n const onWheel = (e: WheelEvent) => {\n markExplicitUserInteraction();\n if (e.deltaY < -6 && this._presentationFocusThreadId) {\n this._options.presentation?.onFormerMessagesHint?.();\n }\n };\n this.messagesEl.addEventListener('wheel', onWheel, { passive: true });\n this._cleanups.push(() => this.messagesEl.removeEventListener('wheel', onWheel));\n\n const onTouchStart = (e: TouchEvent) => {\n markExplicitUserInteraction();\n this._touchStartY = e.touches[0]?.clientY ?? null;\n };\n const onTouchMove = (e: TouchEvent) => {\n markExplicitUserInteraction();\n const y = e.touches[0]?.clientY;\n const start = this._touchStartY;\n if (typeof y === 'number' && typeof start === 'number' && y - start > 10 && this._presentationFocusThreadId) {\n this._options.presentation?.onFormerMessagesHint?.();\n }\n };\n this.messagesEl.addEventListener('touchstart', onTouchStart, { passive: true });\n this.messagesEl.addEventListener('touchmove', onTouchMove, { passive: true });\n this._cleanups.push(() => {\n this.messagesEl.removeEventListener('touchstart', onTouchStart);\n this.messagesEl.removeEventListener('touchmove', onTouchMove);\n });\n\n conversation.appendChild(this.messagesEl);\n\n // Thumbnails column (right edge of panel — quick-scroll shortcuts for search results)\n this._thumbnailsColumn = new ThumbnailsColumn({\n onThumbnailClick: (threadId) => options.onThumbnailClick?.(threadId),\n });\n this._panelEl.appendChild(this._thumbnailsColumn.getElement());\n\n // Floating overlay: sticky zero-height anchor so absolutely-positioned overlays\n // (e.g. ChoicePrompter) stay fixed to the panel's visible area regardless of scroll.\n this._panelFloatingEl = document.createElement('div');\n this._panelFloatingEl.className = 'gengage-chat-panel-float';\n this._panelFloatingEl.dataset['gengagePart'] = 'chat-panel-floating-layer';\n this._panelEl.appendChild(this._panelFloatingEl);\n\n this._resetPanelAiZoneElement();\n\n // Suggestion pills row (between messages and input)\n this._pillsEl = document.createElement('div');\n this._pillsEl.className = 'gengage-chat-pills';\n this._pillsEl.dataset['gengagePart'] = 'chat-suggestion-pills';\n this._pillsEl.setAttribute('role', 'toolbar');\n this._pillsEl.setAttribute('aria-label', this.i18n.suggestionsAriaLabel);\n this._pillsEl.style.display = 'none';\n\n const pillsScroll = document.createElement('div');\n pillsScroll.className = 'gengage-chat-pills-scroll';\n pillsScroll.dataset['gengagePart'] = 'chat-suggestion-pills-scroll';\n this._pillsEl.appendChild(pillsScroll);\n\n const pillsArrow = document.createElement('button');\n pillsArrow.className = 'gengage-chat-pills-arrow gds-btn gds-btn-ghost';\n pillsArrow.dataset['gengagePart'] = 'chat-suggestion-pills-more';\n pillsArrow.type = 'button';\n pillsArrow.setAttribute('aria-label', this.i18n.moreSuggestionsAriaLabel);\n pillsArrow.textContent = '\\u203A'; // › single right-pointing angle\n pillsArrow.addEventListener('click', () => {\n pillsScroll.scrollBy({ left: 150, behavior: 'smooth' });\n });\n this._pillsEl.appendChild(pillsArrow);\n\n // Hide arrow when fully scrolled\n let pillsRafPending = false;\n const onPillsScroll = () => {\n if (pillsRafPending) return;\n pillsRafPending = true;\n requestAnimationFrame(() => {\n pillsRafPending = false;\n const atEnd = pillsScroll.scrollLeft + pillsScroll.clientWidth >= pillsScroll.scrollWidth - 4;\n pillsArrow.style.display = atEnd ? 'none' : '';\n });\n };\n pillsScroll.addEventListener('scroll', onPillsScroll, { passive: true });\n this._cleanups.push(() => {\n pillsScroll.removeEventListener('scroll', onPillsScroll);\n });\n\n conversation.appendChild(this._pillsEl);\n\n // Input-area chips (compact chips above input for search/info/review/similar)\n this._inputChipsEl = document.createElement('div');\n this._inputChipsEl.className = 'gengage-chat-input-chips';\n this._inputChipsEl.dataset['gengagePart'] = 'chat-input-chips';\n this._inputChipsEl.style.display = 'none';\n conversation.appendChild(this._inputChipsEl);\n\n // Input area\n const inputArea = document.createElement('div');\n inputArea.className = 'gengage-chat-input-area';\n inputArea.dataset['gengagePart'] = 'chat-input-area';\n\n this.inputEl = document.createElement('textarea');\n this.inputEl.className = 'gengage-chat-input';\n this.inputEl.dataset['gengagePart'] = 'chat-input';\n this.inputEl.rows = 1;\n this.inputEl.placeholder = this.i18n.inputPlaceholder;\n\n // Auto-expand on desktop as user types (capped at 120px)\n this.inputEl.addEventListener('input', () => {\n // Cancel any pending resize rAF to avoid queuing multiple reflows\n if (this._resizeRafId !== null) {\n cancelAnimationFrame(this._resizeRafId);\n }\n this._resizeRafId = requestAnimationFrame(() => {\n this._resizeRafId = null;\n this.inputEl.style.height = 'auto';\n this.inputEl.style.height = `${Math.min(this.inputEl.scrollHeight, 120)}px`;\n });\n this._updateSendEnabled();\n });\n\n // Enter submits; Shift+Enter inserts newline on desktop only\n this.inputEl.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') {\n const isMobile = this._options.getMobileViewport?.() ?? window.innerWidth <= 768;\n if (isMobile || !e.shiftKey) {\n e.preventDefault();\n this._submit();\n }\n // else: Shift+Enter on desktop → natural newline (no preventDefault)\n }\n });\n\n this.inputEl.addEventListener('paste', (e) => {\n const cd = e.clipboardData;\n if (!cd) return;\n let file: File | null = null;\n const f0 = cd.files?.[0];\n if (f0 && f0.type.startsWith('image/')) {\n file = f0;\n } else if (cd.items?.length) {\n for (let i = 0; i < cd.items.length; i++) {\n const item = cd.items[i];\n if (item?.kind === 'file' && item.type.startsWith('image/')) {\n const f = item.getAsFile();\n if (f) {\n file = f;\n break;\n }\n }\n }\n }\n if (file) {\n e.preventDefault();\n this._routeAttachmentFile(file);\n }\n });\n\n // Hidden file input\n this._fileInput = document.createElement('input');\n this._fileInput.type = 'file';\n this._fileInput.accept = 'image/jpeg,image/png,image/webp';\n this._fileInput.style.display = 'none';\n this._fileInput.addEventListener('change', () => {\n const file = this._fileInput.files?.[0];\n if (file) {\n this._routeAttachmentFile(file);\n }\n this._fileInput.value = '';\n });\n\n // Attach: camera button + popup (fotoğraf seç / panodan yapıştır)\n const attachWrap = document.createElement('div');\n attachWrap.className = 'gengage-chat-attach-wrap';\n attachWrap.dataset['gengagePart'] = 'chat-attach-wrap';\n this._attachWrapEl = attachWrap;\n\n const attachBtn = document.createElement('button');\n this._attachBtn = attachBtn;\n attachBtn.className = 'gengage-chat-attach-btn gds-btn gds-btn-ghost';\n attachBtn.dataset['gengagePart'] = 'chat-attach-button';\n attachBtn.type = 'button';\n attachBtn.setAttribute('aria-label', this.i18n.attachImageButton);\n attachBtn.setAttribute('aria-haspopup', 'menu');\n attachBtn.setAttribute('aria-expanded', 'false');\n attachBtn.innerHTML = `<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z\"/><circle cx=\"12\" cy=\"13\" r=\"4\"/></svg>`;\n attachBtn.addEventListener('click', (ev) => {\n ev.stopPropagation();\n this._toggleAttachMenu();\n });\n\n const attachMenu = document.createElement('div');\n this._attachMenuEl = attachMenu;\n attachMenu.className = 'gengage-chat-attach-menu gds-menu';\n attachMenu.dataset['gengagePart'] = 'chat-attach-menu';\n attachMenu.setAttribute('role', 'menu');\n attachMenu.setAttribute('hidden', '');\n\n const selectPhotoBtn = document.createElement('button');\n selectPhotoBtn.type = 'button';\n selectPhotoBtn.className = 'gengage-chat-attach-menu-item gds-btn gds-btn-ghost';\n selectPhotoBtn.dataset['gengagePart'] = 'chat-attach-menu-select-photo';\n selectPhotoBtn.setAttribute('role', 'menuitem');\n selectPhotoBtn.innerHTML =\n '<span class=\"gengage-chat-attach-menu-icon\" aria-hidden=\"true\">' +\n '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z\"/><circle cx=\"12\" cy=\"13\" r=\"4\"/></svg></span>' +\n `<span class=\"gengage-chat-attach-menu-label\">${this.i18n.attachMenuSelectPhoto}</span>`;\n selectPhotoBtn.addEventListener('click', (ev) => {\n ev.stopPropagation();\n this._closeAttachMenu();\n this._fileInput.click();\n });\n\n const sep = document.createElement('div');\n sep.className = 'gengage-chat-attach-menu-sep';\n sep.dataset['gengagePart'] = 'chat-attach-menu-separator';\n sep.setAttribute('aria-hidden', 'true');\n\n const pasteBtn = document.createElement('button');\n pasteBtn.type = 'button';\n pasteBtn.className = 'gengage-chat-attach-menu-item gds-btn gds-btn-ghost';\n pasteBtn.dataset['gengagePart'] = 'chat-attach-menu-paste';\n pasteBtn.setAttribute('role', 'menuitem');\n pasteBtn.innerHTML =\n '<span class=\"gengage-chat-attach-menu-icon\" aria-hidden=\"true\">' +\n '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">' +\n '<path d=\"M15 2H9a1 1 0 0 0-1 1v1a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1Z\"/>' +\n '<path d=\"M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2\"/>' +\n '<path d=\"M16 4h2a2 2 0 0 1 2 2v4\"/>' +\n '<path d=\"M21 14H11\"/>' +\n '<path d=\"m15 10-4 4 4 4\"/>' +\n '</svg></span>' +\n `<span class=\"gengage-chat-attach-menu-label\">${this.i18n.attachMenuPaste}</span>`;\n pasteBtn.addEventListener('click', (ev) => {\n ev.stopPropagation();\n // Start Clipboard API read synchronously in this handler (Chromium user-activation rule).\n const clipRead = typeof navigator.clipboard?.read === 'function' ? navigator.clipboard.read() : undefined;\n void this._pasteImageFromClipboardMenu(clipRead);\n });\n\n attachMenu.appendChild(selectPhotoBtn);\n attachMenu.appendChild(sep);\n attachMenu.appendChild(pasteBtn);\n attachWrap.appendChild(attachBtn);\n attachWrap.appendChild(attachMenu);\n\n // Attachment preview strip (hidden by default)\n this._previewStrip = document.createElement('div');\n this._previewStrip.className =\n 'gengage-chat-attachment-preview gengage-chat-attachment-preview--hidden gds-card-soft';\n this._previewStrip.dataset['gengagePart'] = 'chat-attachment-preview';\n const previewThumb = document.createElement('img');\n previewThumb.className = 'gengage-chat-attachment-preview-thumb';\n previewThumb.dataset['gengagePart'] = 'chat-attachment-preview-thumb';\n previewThumb.alt = '';\n this._previewName = document.createElement('span');\n this._previewName.className = 'gengage-chat-attachment-name';\n this._previewName.dataset['gengagePart'] = 'chat-attachment-preview-name';\n const removeBtn = document.createElement('button');\n removeBtn.className = 'gengage-chat-attachment-remove gds-btn gds-btn-ghost';\n removeBtn.dataset['gengagePart'] = 'chat-attachment-preview-remove';\n removeBtn.type = 'button';\n removeBtn.setAttribute('aria-label', this.i18n.removeAttachmentButton);\n removeBtn.textContent = '\\u00D7'; // multiplication sign (x)\n removeBtn.addEventListener('click', () => this.clearAttachment());\n this._previewStrip.appendChild(previewThumb);\n this._previewStrip.appendChild(this._previewName);\n this._previewStrip.appendChild(removeBtn);\n\n this.sendBtn = document.createElement('button');\n this.sendBtn.className = 'gengage-chat-send gds-btn gds-btn-primary';\n this.sendBtn.dataset['gengagePart'] = 'chat-send';\n this.sendBtn.type = 'button';\n this.sendBtn.disabled = true;\n this.sendBtn.setAttribute('aria-label', this.i18n.sendButton);\n this.sendBtn.dataset['tooltip'] = this.i18n.sendButton;\n this._renderSendButtonIcon('send');\n this.sendBtn.addEventListener('click', () => {\n if (this._sendStopHandler) {\n const onStop = this._sendStopHandler;\n this.hideStopButton();\n onStop();\n return;\n }\n this._submit();\n });\n\n // Drag-and-drop on input area\n inputArea.addEventListener('dragover', (e) => {\n e.preventDefault();\n inputArea.classList.add('gengage-chat-input-area--dragover');\n });\n inputArea.addEventListener('dragleave', () => {\n inputArea.classList.remove('gengage-chat-input-area--dragover');\n });\n inputArea.addEventListener('drop', (e) => {\n e.preventDefault();\n inputArea.classList.remove('gengage-chat-input-area--dragover');\n const file = e.dataTransfer?.files[0];\n if (file) {\n this._routeAttachmentFile(file);\n }\n });\n\n // Build pill container: [camera] [input] [mic?] [send]\n const pill = document.createElement('div');\n pill.className = 'gengage-chat-input-pill gds-input-shell';\n pill.dataset['gengagePart'] = 'chat-input-shell';\n pill.appendChild(attachWrap);\n pill.appendChild(this.inputEl);\n\n // Voice input mic button (Web Speech API STT)\n if (this._voiceEnabled && isVoiceInputSupported()) {\n this._micBtn = document.createElement('button');\n this._micBtn.className = 'gengage-chat-mic-btn gds-btn gds-btn-ghost';\n this._micBtn.dataset['gengagePart'] = 'chat-mic-button';\n this._micBtn.type = 'button';\n this._micBtn.setAttribute('aria-label', this.i18n.voiceButton);\n this._micBtn.innerHTML =\n '<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">' +\n '<path d=\"M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z\"/>' +\n '<path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>' +\n '<line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"23\"/>' +\n '<line x1=\"8\" y1=\"23\" x2=\"16\" y2=\"23\"/>' +\n '</svg>';\n this._micBtn.addEventListener('click', () => this._toggleVoice());\n pill.appendChild(this._micBtn);\n\n this._voiceInput = new VoiceInput(\n {\n onInterim: (text) => {\n if (this._resizeRafId !== null) {\n cancelAnimationFrame(this._resizeRafId);\n this._resizeRafId = null;\n }\n this.inputEl.value = text;\n this.inputEl.style.height = 'auto';\n this.inputEl.style.height = `${Math.min(this.inputEl.scrollHeight, 120)}px`;\n },\n onFinal: (text) => {\n this.inputEl.value = text;\n },\n onAutoSubmit: (text) => {\n this.inputEl.value = text;\n this._micBtn?.classList.remove('gengage-chat-mic-btn--active');\n this._submit();\n },\n onStateChange: (state) => {\n if (state === 'listening') {\n this._micBtn?.classList.add('gengage-chat-mic-btn--active');\n } else {\n this._micBtn?.classList.remove('gengage-chat-mic-btn--active');\n }\n },\n onError: (_code, _message) => {\n this._micBtn?.classList.remove('gengage-chat-mic-btn--active');\n },\n },\n { lang: this._voiceLang },\n );\n }\n\n pill.appendChild(this.sendBtn);\n\n inputArea.appendChild(this._previewStrip);\n inputArea.appendChild(this._fileInput);\n inputArea.appendChild(pill);\n conversation.appendChild(inputArea);\n\n body.appendChild(conversation);\n\n this._comparisonDockSlotEl = document.createElement('div');\n this._comparisonDockSlotEl.className = 'gengage-chat-comparison-dock-slot';\n this._comparisonDockSlotEl.dataset['gengagePart'] = 'comparison-dock-slot';\n body.appendChild(this._comparisonDockSlotEl);\n\n this.root.appendChild(body);\n\n // Horizontal swipe to toggle panel on mobile (GAP-101)\n this._setupHorizontalSwipe(conversation);\n this._setupHorizontalSwipe(this._panelEl);\n\n // Footer\n const footer = document.createElement('div');\n footer.className = 'gengage-chat-footer';\n footer.dataset['gengagePart'] = 'chat-footer';\n footer.textContent = this.i18n.poweredBy;\n this.root.appendChild(footer);\n\n // Escape key to close drawer\n const escapeHandler = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n options.onClose();\n }\n };\n this.root.addEventListener('keydown', escapeHandler);\n this._cleanups.push(() => this.root.removeEventListener('keydown', escapeHandler));\n\n container.appendChild(this.root);\n }\n\n addMessage(message: ChatMessage): void {\n const bubble = document.createElement('div');\n const bubbleRoleClass = message.role === 'assistant' ? 'gds-message-assistant' : 'gds-message-user';\n bubble.className = `gengage-chat-bubble gds-message ${bubbleRoleClass} gengage-chat-bubble--${message.role}`;\n bubble.dataset['gengagePart'] = message.role === 'assistant' ? 'chat-message-assistant' : 'chat-message-user';\n bubble.setAttribute('role', 'listitem');\n bubble.dataset['messageId'] = message.id;\n if (message.threadId) {\n bubble.dataset['threadId'] = message.threadId;\n }\n\n if (this._firstBotMessageIds.has(message.id)) {\n bubble.classList.add('gengage-chat-bubble--first');\n }\n\n if (message.attachment) {\n const thumbEl = document.createElement('img');\n thumbEl.className = 'gengage-chat-attachment-thumb';\n const blobUrl = URL.createObjectURL(message.attachment);\n thumbEl.src = blobUrl;\n thumbEl.alt = message.attachment.name;\n // Revoke blob URL once image loads (or errors) to free memory\n thumbEl.addEventListener('load', () => URL.revokeObjectURL(blobUrl), { once: true });\n thumbEl.addEventListener('error', () => URL.revokeObjectURL(blobUrl), { once: true });\n bubble.insertBefore(thumbEl, bubble.firstChild);\n }\n\n if (message.content) {\n const text = document.createElement('div');\n text.className = 'gengage-chat-bubble-text';\n text.dataset['gengagePart'] = 'chat-message-text';\n if (message.role === 'assistant') {\n text.innerHTML = sanitizeHtml(message.content);\n // Intercept all links in bot HTML\n if (this._onLinkClick) {\n const links = text.querySelectorAll('a[href]');\n for (const link of links) {\n link.addEventListener('click', (e) => {\n e.preventDefault();\n const href = link.getAttribute('href');\n if (href) {\n this._onLinkClick?.(href);\n }\n });\n }\n }\n } else {\n text.textContent = message.content; // User messages: always safe textContent\n }\n bubble.appendChild(text);\n }\n\n // Add rollback button to user message bubbles\n if (message.role === 'user' && this._onRollback) {\n const rollbackBtn = document.createElement('button');\n rollbackBtn.className = 'gengage-chat-rollback-btn gds-btn gds-btn-ghost';\n rollbackBtn.dataset['gengagePart'] = 'chat-message-rollback';\n rollbackBtn.type = 'button';\n rollbackBtn.setAttribute('aria-label', this.i18n.rollbackAriaLabel);\n rollbackBtn.title = this.i18n.rollbackAriaLabel;\n rollbackBtn.innerHTML = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"1 4 1 10 7 10\"/><path d=\"M3.51 15a9 9 0 1 0 2.13-9.36L1 10\"/></svg>`;\n rollbackBtn.addEventListener('click', (e) => {\n e.stopPropagation();\n this._onRollback?.(message.id);\n });\n bubble.appendChild(rollbackBtn);\n }\n\n this.messagesEl.appendChild(bubble);\n if (this._presentationFocusThreadId) {\n this._applyPresentationCollapsed();\n }\n this._scrollToBottom(message.role === 'user');\n }\n\n /** Remove one transcript bubble (e.g. superseded empty assistant placeholder). */\n removeMessageBubble(messageId: string): void {\n this._firstBotMessageIds.delete(messageId);\n this.messagesEl.querySelector(`[data-message-id=\"${CSS.escape(messageId)}\"]`)?.remove();\n if (this._presentationFocusThreadId) {\n this._applyPresentationCollapsed();\n }\n }\n\n showTypingIndicator(searchText?: string): void {\n this.removeTypingIndicator();\n const initialSteps =\n this._thinkingSteps.length > 0\n ? this._thinkingSteps.slice(-3)\n : searchText\n ? [searchText]\n : this.i18n.loadingSequenceGeneric;\n const { root, binding } = this._createLoadingSequence(\n 'chat',\n initialSteps,\n 'chat-typing-indicator',\n 'gengage-chat-typing',\n );\n root.dataset['typing'] = 'true';\n this._typingLoadingBinding = binding;\n\n this.messagesEl.appendChild(root);\n this._scrollToBottom(true);\n }\n\n /** Accumulate a new thinking step (shown as a checklist in the typing indicator). */\n addThinkingStep(text: string): void {\n const normalized = text.trim();\n if (!normalized) return;\n if (this._thinkingSteps[this._thinkingSteps.length - 1] === normalized) return;\n this._thinkingSteps.push(normalized);\n this._thinkingSteps = this._thinkingSteps.slice(-3);\n if (this._typingLoadingBinding) {\n this._applyLoadingSteps(this._typingLoadingBinding, this._thinkingSteps, true);\n }\n if (this._panelLoadingBinding) {\n this._applyLoadingSteps(this._panelLoadingBinding, this._thinkingSteps, true);\n }\n if (this._panelAiZoneLoadingBinding) {\n this._applyLoadingSteps(this._panelAiZoneLoadingBinding, this._thinkingSteps, true);\n }\n }\n\n setThinkingSteps(steps: string[]): void {\n const normalized = steps\n .map((step) => step.trim())\n .filter(Boolean)\n .slice(-3);\n if (normalized.length === 0) return;\n this._thinkingSteps = normalized;\n if (this._typingLoadingBinding) {\n this._applyLoadingSteps(this._typingLoadingBinding, this._thinkingSteps, true);\n }\n if (this._panelLoadingBinding) {\n this._applyLoadingSteps(this._panelLoadingBinding, this._thinkingSteps, true);\n }\n if (this._panelAiZoneLoadingBinding) {\n this._applyLoadingSteps(this._panelAiZoneLoadingBinding, this._thinkingSteps, true);\n }\n }\n\n removeTypingIndicator(): void {\n this._destroyLoadingBinding(this._typingLoadingBinding);\n this._typingLoadingBinding = null;\n const existing = this.messagesEl.querySelector('.gengage-chat-typing');\n existing?.remove();\n this._thinkingSteps = [];\n this.hideStopButton();\n }\n\n /** Show a \"Stop generating\" button below the typing indicator. */\n showStopButton(onStop: () => void): void {\n this._sendStopHandler = onStop;\n this.sendBtn.disabled = false;\n this.sendBtn.classList.add('gengage-chat-send--stop', 'gds-btn-secondary');\n this.sendBtn.classList.remove('gds-btn-primary');\n this.sendBtn.setAttribute('aria-label', this.i18n.stopGenerating);\n this.sendBtn.dataset['tooltip'] = this.i18n.stopGenerating;\n this._renderSendButtonIcon('stop');\n }\n\n /** Remove the stop-generating button if present. */\n hideStopButton(): void {\n this._sendStopHandler = null;\n this.sendBtn.classList.remove('gengage-chat-send--stop', 'gds-btn-secondary');\n this.sendBtn.classList.add('gds-btn-primary');\n this.sendBtn.setAttribute('aria-label', this.i18n.sendButton);\n this.sendBtn.dataset['tooltip'] = this.i18n.sendButton;\n this._renderSendButtonIcon('send');\n this._updateSendEnabled();\n }\n\n showError(message?: string, onRetry?: () => void): void {\n const errEl = document.createElement('div');\n errEl.className = 'gengage-chat-error';\n errEl.setAttribute('role', 'alert');\n const textEl = document.createElement('span');\n textEl.textContent = message ?? this.i18n.errorMessage;\n errEl.appendChild(textEl);\n\n if (onRetry) {\n const retryBtn = document.createElement('button');\n retryBtn.className = 'gengage-chat-error-retry';\n retryBtn.textContent = this.i18n.retryButton ?? 'Retry';\n retryBtn.addEventListener('click', () => {\n errEl.remove();\n onRetry();\n });\n errEl.appendChild(retryBtn);\n }\n\n this.messagesEl.appendChild(errEl);\n this._scrollToBottom(true);\n }\n\n /** Show error with recovery action pills (\"Try again\" + \"Ask something else\"). */\n showErrorWithRecovery(message: string, actions: { onRetry: () => void; onNewQuestion: () => void }): void {\n this.showError(message);\n this.setRecoveryPills(actions);\n }\n\n /** Recovery pills only — error copy is shown as a normal assistant message. */\n showRecoveryPillsOnly(actions: { onRetry: () => void; onNewQuestion: () => void }): void {\n this.setRecoveryPills(actions);\n }\n\n private setRecoveryPills(actions: { onRetry: () => void; onNewQuestion: () => void }): void {\n this.setPills([\n { label: this.i18n.tryAgainButton, onAction: actions.onRetry },\n { label: this.i18n.askSomethingElseButton, onAction: actions.onNewQuestion },\n ]);\n }\n\n clearMessages(): void {\n const former = this._formerMessagesBtn;\n for (const child of [...this.messagesEl.children]) {\n if (child !== former) child.remove();\n }\n }\n\n /** Replace suggestion pills. Pass empty array to hide. */\n setPills(\n pills: Array<{ label: string; onAction: () => void; icon?: string; image?: string; description?: string }>,\n ): void {\n const scroll = this._pillsEl.querySelector('.gengage-chat-pills-scroll');\n if (!scroll) return;\n while (scroll.firstChild) scroll.removeChild(scroll.firstChild);\n\n if (pills.length === 0) {\n this._pillsEl.style.display = 'none';\n return;\n }\n\n this._pillsEl.style.display = '';\n for (const pill of pills) {\n const btn = document.createElement('button');\n btn.className = pill.image\n ? 'gengage-chat-pill gds-chip gds-chip-active gengage-chat-pill--rich'\n : 'gengage-chat-pill gds-chip gds-chip-active';\n btn.type = 'button';\n\n if (pill.icon) {\n const svgHtml = SUGGESTED_ACTION_ICONS[pill.icon] ?? DEFAULT_ACTION_ICON;\n const iconSpan = document.createElement('span');\n iconSpan.className = 'gengage-chat-pill-icon';\n iconSpan.innerHTML = svgHtml;\n btn.appendChild(iconSpan);\n }\n\n if (pill.image && isSafeImageUrl(pill.image)) {\n const img = document.createElement('img');\n img.className = 'gengage-chat-pill-img';\n img.src = pill.image;\n img.alt = '';\n btn.appendChild(img);\n }\n\n const textWrap = document.createElement('span');\n textWrap.className = 'gengage-chat-pill-text';\n textWrap.textContent = pill.label;\n btn.appendChild(textWrap);\n\n if (pill.description) {\n const desc = document.createElement('span');\n desc.className = 'gengage-chat-pill-desc';\n const descId = `pill-desc-${Math.random().toString(36).slice(2, 9)}`;\n desc.id = descId;\n desc.textContent = pill.description;\n btn.appendChild(desc);\n btn.setAttribute('aria-describedby', descId);\n }\n\n btn.addEventListener('click', () => pill.onAction());\n scroll.appendChild(btn);\n }\n\n // Show/hide arrow based on overflow\n const arrow = this._pillsEl.querySelector('.gengage-chat-pills-arrow') as HTMLElement | null;\n if (arrow) {\n requestAnimationFrame(() => {\n arrow.style.display = scroll.scrollWidth > scroll.clientWidth ? '' : 'none';\n });\n }\n }\n\n focusInput(): void {\n this.inputEl.focus();\n }\n\n showKvkkBanner(html: string, onDismiss: () => void): void {\n this._kvkkSlot.innerHTML = '';\n const banner = createKvkkBanner({ htmlContent: html, onDismiss, closeAriaLabel: this.i18n.closeAriaLabel });\n this._kvkkSlot.appendChild(banner);\n }\n\n hideKvkkBanner(): void {\n this._kvkkSlot.innerHTML = '';\n }\n\n /** True when the KVKK banner is mounted (user has not dismissed it yet). */\n isKvkkBannerVisible(): boolean {\n return this._kvkkSlot.childNodes.length > 0;\n }\n\n getElement(): HTMLElement {\n return this.root;\n }\n\n /** Stage a file attachment for sending. Shows preview. */\n stageAttachment(file: File): void {\n this._pendingAttachment = file;\n this._previewName.textContent = file.name;\n const thumb = this._previewStrip.querySelector('.gengage-chat-attachment-preview-thumb') as HTMLImageElement;\n if (thumb) {\n // Revoke previous blob URL to prevent memory leak\n if (thumb.src && thumb.src.startsWith('blob:')) {\n URL.revokeObjectURL(thumb.src);\n }\n thumb.src = URL.createObjectURL(file);\n }\n this._previewStrip.classList.remove('gengage-chat-attachment-preview--hidden');\n this._updateSendEnabled();\n }\n\n /** Remove the staged attachment and hide preview. */\n clearAttachment(): void {\n const thumb = this._previewStrip.querySelector('.gengage-chat-attachment-preview-thumb') as HTMLImageElement;\n if (thumb?.src) {\n URL.revokeObjectURL(thumb.src);\n thumb.src = '';\n }\n this._pendingAttachment = null;\n this._previewStrip.classList.add('gengage-chat-attachment-preview--hidden');\n this._updateSendEnabled();\n }\n\n private _routeAttachmentFile(file: File): void {\n if (this._onAttachment) {\n try {\n this._onAttachment(file);\n } catch (err) {\n console.error('[gengage:chat] Attachment callback error:', err);\n }\n } else {\n this.stageAttachment(file);\n }\n }\n\n private _closeAttachMenu(): void {\n if (!this._attachMenuEl) return;\n this._attachMenuEl.setAttribute('hidden', '');\n this._attachBtn?.setAttribute('aria-expanded', 'false');\n if (this._attachMenuClickTimerId !== null) {\n clearTimeout(this._attachMenuClickTimerId);\n this._attachMenuClickTimerId = null;\n }\n if (this._attachMenuCleanup) {\n this._attachMenuCleanup();\n this._attachMenuCleanup = null;\n }\n }\n\n private _openAttachMenu(): void {\n if (!this._attachMenuEl) return;\n this._attachMenuEl.removeAttribute('hidden');\n this._attachBtn?.setAttribute('aria-expanded', 'true');\n const onDocCapture = (e: MouseEvent): void => {\n if (this._attachWrapEl?.contains(e.target as Node)) return;\n this._closeAttachMenu();\n };\n const onEsc = (e: KeyboardEvent): void => {\n if (e.key === 'Escape') {\n e.stopPropagation();\n this._closeAttachMenu();\n }\n };\n this._attachMenuClickTimerId = window.setTimeout(() => {\n this._attachMenuClickTimerId = null;\n document.addEventListener('click', onDocCapture, true);\n }, 0);\n document.addEventListener('keydown', onEsc, true);\n this._attachMenuCleanup = () => {\n document.removeEventListener('click', onDocCapture, true);\n document.removeEventListener('keydown', onEsc, true);\n };\n }\n\n private _toggleAttachMenu(): void {\n if (!this._attachMenuEl) return;\n if (this._attachMenuEl.hasAttribute('hidden')) {\n this._openAttachMenu();\n } else {\n this._closeAttachMenu();\n }\n }\n\n private async _pasteImageFromClipboardMenu(clipRead?: Promise<ClipboardItem[]>): Promise<void> {\n const file = await readClipboardImageAsFile(clipRead);\n if (file) {\n this._routeAttachmentFile(file);\n this._closeAttachMenu();\n return;\n }\n dispatch('gengage:global:error', {\n message: this.i18n.clipboardNoImageMessage,\n source: 'chat' as const,\n });\n this._closeAttachMenu();\n }\n\n /** Get the currently staged attachment file, or null. */\n getPendingAttachment(): File | null {\n return this._pendingAttachment;\n }\n\n /**\n * Desktop: area above the main panel body for “analyzing” + AITopPicks / AIGroupingCards\n * so they are not duplicated in the chat column.\n */\n setPanelAiZoneState(\n state: 'hidden' | 'analyzing' | 'results',\n options?: { resultEl?: HTMLElement; analyzingLabel?: string },\n ): void {\n if (!this._panelAiZoneEl.isConnected) return;\n this._destroyLoadingBinding(this._panelAiZoneLoadingBinding);\n this._panelAiZoneLoadingBinding = null;\n if (state === 'hidden') {\n this._panelAiZoneEl.innerHTML = '';\n this._panelAiZoneEl.setAttribute('hidden', '');\n return;\n }\n this._panelAiZoneEl.removeAttribute('hidden');\n if (state === 'analyzing') {\n this._panelAiZoneEl.innerHTML = '';\n const fallbackSequence = [\n options?.analyzingLabel ?? this.i18n.aiAnalysisAnalyzingLabel,\n ...this.i18n.loadingSequencePanel,\n ];\n const { root, binding } = this._createLoadingSequence(\n 'panel',\n this._thinkingSteps.length > 0 ? this._thinkingSteps.slice(-3) : fallbackSequence,\n 'panel-ai-zone-loading',\n 'gengage-chat-panel-ai-zone-inner',\n );\n this._panelAiZoneLoadingBinding = binding;\n this._panelAiZoneEl.appendChild(root);\n } else if (state === 'results' && options?.resultEl) {\n this._panelAiZoneEl.innerHTML = '';\n this._panelAiZoneEl.appendChild(options.resultEl);\n }\n }\n\n private _resetPanelAiZoneElement(): void {\n this._panelAiZoneEl = document.createElement('div');\n this._panelAiZoneEl.className = 'gengage-chat-panel-ai-zone';\n this._panelAiZoneEl.setAttribute('hidden', '');\n }\n\n private _emitHostShellSync(): void {\n this._onHostShellSync?.();\n }\n\n private _syncPanelTopBarFromContent(contentEl: HTMLElement): void {\n const gridHead = contentEl.querySelector<HTMLElement>('.gengage-chat-product-grid-head');\n if (gridHead) {\n const titleEl = gridHead.querySelector<HTMLElement>('.gengage-chat-product-grid-head-title');\n const actionsEl = gridHead.querySelector<HTMLElement>('.gengage-chat-product-grid-head-actions');\n if (titleEl?.textContent?.trim()) {\n const derivedTitle = titleEl.textContent.trim();\n contentEl.dataset['gengagePanelDerivedTitle'] = derivedTitle;\n this._panelTopBar.setTitle(derivedTitle);\n }\n if (actionsEl) {\n actionsEl.classList.add('gengage-chat-panel-topbar-toolbar-host');\n this._panelTopBar.setActions(actionsEl);\n } else {\n this._panelTopBar.setActions(null);\n }\n gridHead.remove();\n return;\n }\n this._syncPanelTopBarTitleFromContent(contentEl);\n }\n\n private _syncPanelTopBarTitleFromContent(contentEl: HTMLElement): void {\n const derivedTitle = contentEl.dataset['gengagePanelDerivedTitle'];\n if (derivedTitle?.trim()) {\n this._panelTopBar.setTitle(derivedTitle.trim());\n return;\n }\n const titleCandidate = contentEl.querySelector<HTMLElement>(\n '.gengage-chat-product-details-title, .gengage-chat-product-details-similars-heading, .gengage-chat-ai-top-picks-title',\n );\n const titleText = titleCandidate?.textContent?.trim();\n if (titleText) {\n this._panelTopBar.setTitle(titleText);\n }\n }\n\n /** Replace panel content and show the panel. */\n setPanelContent(el: HTMLElement): void {\n this._destroyLoadingBinding(this._panelLoadingBinding);\n this._panelLoadingBinding = null;\n this._destroyLoadingBinding(this._panelAiZoneLoadingBinding);\n this._panelAiZoneLoadingBinding = null;\n const wasVisible = this._panelVisible;\n // Only apply opacity crossfade when swapping content in an already-visible panel.\n // Applying it on first-show would hide the slide-in animation (opacity:0 masks the transform).\n if (wasVisible) {\n this._panelEl.classList.add('gengage-chat-panel--transitioning');\n }\n this._panelEl.innerHTML = '';\n this._resetPanelAiZoneElement();\n this._panelEl.appendChild(this._panelTopBar.getElement());\n this._panelEl.appendChild(this._panelAiZoneEl);\n this._panelTopBar.setActions(null);\n this._panelEl.appendChild(el);\n this._panelEl.appendChild(this._thumbnailsColumn.getElement());\n this._panelEl.appendChild(this._panelFloatingEl);\n this._syncPanelTopBarFromContent(el);\n this._dividerEl.classList.remove('gengage-chat-panel-divider--hidden');\n if (!this._panelVisible) {\n this._panelVisible = true;\n this._panelEl.classList.add('gengage-chat-panel--visible');\n this.root.classList.add('gengage-chat-drawer--with-panel');\n }\n if (this._panelCollapsed) {\n this._panelEl.classList.add('gengage-chat-panel--collapsed');\n }\n this._syncDividerPreview();\n requestAnimationFrame(() => {\n this._panelEl.classList.remove('gengage-chat-panel--transitioning');\n this._updateScrollAffordance();\n this._smoothScrollPanelListToTop();\n });\n // New content always reopens the panel — hide the reopen button\n if (this._reopenPanelBtn) this._reopenPanelBtn.style.display = 'none';\n this._emitHostShellSync();\n }\n\n /** Append content to the panel without replacing existing content. */\n appendPanelContent(el: HTMLElement): void {\n const thumb = this._thumbnailsColumn.getElement();\n const ref = thumb.parentElement === this._panelEl ? thumb : this._panelFloatingEl;\n this._panelEl.insertBefore(el, ref);\n this._syncPanelTopBarFromContent(this.getPanelContentElement() ?? el);\n this._dividerEl.classList.remove('gengage-chat-panel-divider--hidden');\n if (!this._panelVisible) {\n this._panelVisible = true;\n this._panelEl.classList.add('gengage-chat-panel--visible');\n this.root.classList.add('gengage-chat-drawer--with-panel');\n }\n this._syncDividerPreview();\n this._emitHostShellSync();\n }\n\n /** Return the panel element's content child (after topbar), or null. */\n getPanelContentElement(): HTMLElement | null {\n const children = this._panelEl.children;\n for (let i = 0; i < children.length; i++) {\n const child = children[i] as HTMLElement;\n if (\n child.classList.contains('gengage-chat-panel-topbar') ||\n child.classList.contains('gengage-chat-panel-ai-zone') ||\n child.classList.contains('gengage-chat-thumbnails-column') ||\n child.classList.contains('gengage-chat-panel-float')\n ) {\n continue;\n }\n return child;\n }\n return null;\n }\n\n /** Whether the panel is currently visible (may be empty). */\n isPanelVisible(): boolean {\n return this._panelVisible;\n }\n\n /** Whether the panel is currently visible and has rendered content (beyond topbar + thumbnails column). */\n hasPanelContent(): boolean {\n return this._panelVisible && this.getPanelContentElement() !== null;\n }\n\n /** Whether panel currently shows loading skeleton blocks. */\n isPanelLoading(): boolean {\n return this._panelEl.querySelector('.gengage-chat-panel-skeleton') !== null;\n }\n\n /** Show loading skeleton in the panel. Variant depends on contentType hint. */\n showPanelLoading(contentType?: string): void {\n this._destroyLoadingBinding(this._panelLoadingBinding);\n this._panelLoadingBinding = null;\n this._dividerEl.classList.remove('gengage-chat-panel-divider--hidden');\n this._panelEl.innerHTML = '';\n this._resetPanelAiZoneElement();\n this._panelEl.appendChild(this._panelTopBar.getElement());\n this._panelEl.appendChild(this._panelAiZoneEl);\n const skeleton = document.createElement('div');\n skeleton.className = 'gengage-chat-panel-skeleton';\n const panelSequence =\n contentType === 'comparisonTable' ? this.i18n.loadingSequenceComparison : this.i18n.loadingSequencePanel;\n const { root: panelStatus, binding: panelBinding } = this._createLoadingSequence(\n 'panel',\n this._thinkingSteps.length > 0 ? this._thinkingSteps.slice(-3) : panelSequence,\n 'panel-loading-status',\n 'gengage-chat-panel-loading-status',\n );\n this._panelLoadingBinding = panelBinding;\n\n switch (contentType) {\n case 'productDetails': {\n skeleton.appendChild(panelStatus);\n // Tall image placeholder + text lines\n const imgBlock = document.createElement('div');\n imgBlock.className = 'gengage-chat-panel-skeleton-block gengage-chat-panel-skeleton-block--image';\n skeleton.appendChild(imgBlock);\n for (let i = 0; i < 3; i++) {\n const line = document.createElement('div');\n line.className = 'gengage-chat-panel-skeleton-block gengage-chat-panel-skeleton-block--text';\n skeleton.appendChild(line);\n }\n break;\n }\n case 'productList':\n case 'groupList': {\n skeleton.appendChild(panelStatus);\n // 2x3 grid of small card placeholders\n const grid = document.createElement('div');\n grid.className = 'gengage-chat-panel-skeleton-grid';\n for (let i = 0; i < 6; i++) {\n const card = document.createElement('div');\n card.className = 'gengage-chat-panel-skeleton-block gengage-chat-panel-skeleton-block--card';\n grid.appendChild(card);\n }\n skeleton.appendChild(grid);\n break;\n }\n case 'comparisonTable': {\n skeleton.classList.add('gengage-chat-panel-skeleton--comparison');\n skeleton.appendChild(panelStatus);\n const root = document.createElement('div');\n root.className = 'gengage-chat-comparison gengage-chat-comparison--skeleton';\n root.setAttribute('aria-busy', 'true');\n\n // Önerilen seçim kartı — gerçek .gengage-chat-comparison-recommended ile aynı kutu\n const rec = document.createElement('div');\n rec.className = 'gengage-chat-comparison-recommended';\n const recLabel = document.createElement('div');\n recLabel.className = 'gengage-chat-comparison-skeleton-shimmer gengage-chat-comparison-skeleton-rec-label';\n rec.appendChild(recLabel);\n const recBody = document.createElement('div');\n recBody.className = 'gengage-chat-comparison-recommended-body';\n const recImg = document.createElement('div');\n recImg.className = 'gengage-chat-comparison-skeleton-shimmer gengage-chat-comparison-skeleton-rec-img';\n recImg.setAttribute('aria-hidden', 'true');\n const recInfo = document.createElement('div');\n recInfo.className = 'gengage-chat-comparison-recommended-info';\n for (let i = 0; i < 2; i++) {\n const t = document.createElement('div');\n t.className = 'gengage-chat-comparison-skeleton-shimmer gengage-chat-comparison-skeleton-rec-title';\n if (i === 1) t.classList.add('gengage-chat-comparison-skeleton-rec-title--short');\n recInfo.appendChild(t);\n }\n const recPrice = document.createElement('div');\n recPrice.className = 'gengage-chat-comparison-skeleton-shimmer gengage-chat-comparison-skeleton-rec-price';\n recInfo.appendChild(recPrice);\n recBody.appendChild(recImg);\n recBody.appendChild(recInfo);\n rec.appendChild(recBody);\n const hl = document.createElement('div');\n hl.className = 'gengage-chat-comparison-highlights';\n const hlLab = document.createElement('div');\n hlLab.className = 'gengage-chat-comparison-skeleton-shimmer gengage-chat-comparison-skeleton-hl-label';\n hl.appendChild(hlLab);\n const hlUl = document.createElement('ul');\n hlUl.className = 'gengage-chat-comparison-skeleton-hl-list';\n for (let i = 0; i < 3; i++) {\n const li = document.createElement('li');\n const line = document.createElement('div');\n line.className = 'gengage-chat-comparison-skeleton-shimmer gengage-chat-comparison-skeleton-hl-line';\n if (i === 1) line.classList.add('gengage-chat-comparison-skeleton-hl-line--medium');\n if (i === 2) line.classList.add('gengage-chat-comparison-skeleton-hl-line--short');\n li.appendChild(line);\n hlUl.appendChild(li);\n }\n hl.appendChild(hlUl);\n rec.appendChild(hl);\n root.appendChild(rec);\n\n // Temel farklar — .gengage-chat-comparison-key-differences\n const kd = document.createElement('div');\n kd.className = 'gengage-chat-comparison-key-differences';\n const kdH = document.createElement('div');\n kdH.className = 'gengage-chat-comparison-skeleton-shimmer gengage-chat-comparison-skeleton-kd-heading';\n kd.appendChild(kdH);\n const kdContent = document.createElement('div');\n kdContent.className = 'gengage-chat-comparison-key-differences-content';\n for (let i = 0; i < 4; i++) {\n const line = document.createElement('div');\n line.className = 'gengage-chat-comparison-skeleton-shimmer gengage-chat-comparison-skeleton-kd-line';\n kdContent.appendChild(line);\n }\n kd.appendChild(kdContent);\n root.appendChild(kd);\n\n // Özel durumlar çubuğu — gerçek special ile aynı dolgu/kenar\n const special = document.createElement('div');\n special.className = 'gengage-chat-comparison-special gengage-chat-comparison-special--skeleton';\n const specialInner = document.createElement('div');\n specialInner.className =\n 'gengage-chat-comparison-skeleton-shimmer gengage-chat-comparison-skeleton-special-line';\n special.appendChild(specialInner);\n root.appendChild(special);\n\n // Tablo özeti — thead (görsel + isim + fiyat) + birkaç attribute satırı\n const tableWrap = document.createElement('div');\n tableWrap.className = 'gengage-chat-comparison-skeleton-table-wrap';\n const thead = document.createElement('div');\n thead.className = 'gengage-chat-comparison-skeleton-table-head';\n const corner = document.createElement('div');\n corner.className = 'gengage-chat-comparison-skeleton-table-corner';\n thead.appendChild(corner);\n for (let c = 0; c < 3; c++) {\n const col = document.createElement('div');\n col.className = 'gengage-chat-comparison-skeleton-table-col';\n const thImg = document.createElement('div');\n thImg.className = 'gengage-chat-comparison-skeleton-shimmer gengage-chat-comparison-skeleton-table-th-img';\n const thName = document.createElement('div');\n thName.className = 'gengage-chat-comparison-skeleton-shimmer gengage-chat-comparison-skeleton-table-th-name';\n const thPrice = document.createElement('div');\n thPrice.className =\n 'gengage-chat-comparison-skeleton-shimmer gengage-chat-comparison-skeleton-table-th-price';\n col.appendChild(thImg);\n col.appendChild(thName);\n col.appendChild(thPrice);\n thead.appendChild(col);\n }\n tableWrap.appendChild(thead);\n for (let r = 0; r < 3; r++) {\n const row = document.createElement('div');\n row.className = 'gengage-chat-comparison-skeleton-table-row';\n const labelCell = document.createElement('div');\n labelCell.className = 'gengage-chat-comparison-skeleton-shimmer gengage-chat-comparison-skeleton-table-label';\n row.appendChild(labelCell);\n for (let c = 0; c < 3; c++) {\n const cell = document.createElement('div');\n cell.className = 'gengage-chat-comparison-skeleton-shimmer gengage-chat-comparison-skeleton-table-cell';\n row.appendChild(cell);\n }\n tableWrap.appendChild(row);\n }\n root.appendChild(tableWrap);\n\n skeleton.appendChild(root);\n break;\n }\n default: {\n skeleton.appendChild(panelStatus);\n // Generic: 3 blocks (existing behavior)\n for (let i = 0; i < 3; i++) {\n const block = document.createElement('div');\n block.className = 'gengage-chat-panel-skeleton-block';\n skeleton.appendChild(block);\n }\n break;\n }\n }\n\n this._panelEl.appendChild(skeleton);\n this._panelEl.appendChild(this._thumbnailsColumn.getElement());\n this._panelEl.appendChild(this._panelFloatingEl);\n if (!this._panelVisible) {\n this._panelVisible = true;\n this._panelEl.classList.add('gengage-chat-panel--visible');\n this.root.classList.add('gengage-chat-drawer--with-panel');\n }\n this._syncDividerPreview();\n this._emitHostShellSync();\n }\n\n /** Update the panel top bar navigation state. */\n updatePanelTopBar(canBack: boolean, canForward: boolean, title: string): void {\n // On mobile the back button always closes the side-panel overlay, so keep it active\n const isMobile = this._options.getMobileViewport?.() ?? false;\n this._panelTopBar.update(isMobile ? true : canBack, canForward, title);\n const contentEl = this.getPanelContentElement();\n if (contentEl) {\n this._syncPanelTopBarTitleFromContent(contentEl);\n }\n }\n\n getPanelTopBarTitle(): string {\n return this._panelTopBar.getTitle();\n }\n\n /** Update the favorites badge count. Pass 0 to hide the badge. */\n updateFavoritesBadge(count: number): void {\n if (!this._favBadgeEl) return;\n if (count > 0) {\n this._favBadgeEl.textContent = count > 99 ? '99+' : String(count);\n this._favBadgeEl.style.display = '';\n } else {\n this._favBadgeEl.style.display = 'none';\n }\n }\n\n /**\n * Hide the panel and clear its content. Always hides — even in force-expanded mode.\n * Callers: _hideDrawer (stale panel cleanup), stream onDone (loading skeleton cleanup),\n * thread navigation (no snapshot to restore). All require full hide.\n * Keeps `_panelCollapsed` untouched so user collapse preference survives future panel renders.\n */\n clearPanel(): void {\n this._destroyLoadingBinding(this._panelLoadingBinding);\n this._panelLoadingBinding = null;\n this._destroyLoadingBinding(this._panelAiZoneLoadingBinding);\n this._panelAiZoneLoadingBinding = null;\n this._panelEl.innerHTML = '';\n this._resetPanelAiZoneElement();\n this._panelEl.appendChild(this._panelTopBar.getElement());\n this._panelEl.appendChild(this._panelAiZoneEl);\n this._panelEl.appendChild(this._thumbnailsColumn.getElement());\n this._panelEl.appendChild(this._panelFloatingEl);\n this._panelTopBar.setActions(null);\n this._panelVisible = false;\n this._panelEl.classList.remove('gengage-chat-panel--visible', 'gengage-chat-panel--collapsed');\n this.root.classList.remove('gengage-chat-drawer--with-panel');\n this._dividerEl.classList.add('gengage-chat-panel-divider--hidden');\n this._dividerPreviewEnabled = false;\n this._syncDividerPreview();\n if (this._reopenPanelBtn) this._reopenPanelBtn.style.display = 'none';\n this.setComparisonDockContent(null);\n this._emitHostShellSync();\n }\n\n /**\n * Mobile-only slot (see CSS): pins the comparison dock above panel scroll.\n * Pass null to clear.\n */\n setComparisonDockContent(el: HTMLElement | null): void {\n this._comparisonDockSlotEl.replaceChildren();\n if (el) this._comparisonDockSlotEl.appendChild(el);\n }\n\n /**\n * On mobile: hide the side panel overlay without clearing its content.\n * Shows the reopen button in the header so the user can slide the panel back in.\n */\n hideMobilePanel(): void {\n if (!this._panelVisible) return;\n this._panelVisible = false;\n this._panelEl.classList.remove('gengage-chat-panel--visible');\n if (this._reopenPanelBtn) this._reopenPanelBtn.style.display = 'flex';\n this._emitHostShellSync();\n }\n\n private _showMobilePanelFromBtn(): void {\n if (this._panelVisible) return;\n this._panelVisible = true;\n this._panelEl.classList.add('gengage-chat-panel--visible');\n if (this._reopenPanelBtn) this._reopenPanelBtn.style.display = 'none';\n this._emitHostShellSync();\n }\n\n /** Expand panel without locking — user can still toggle via divider. */\n expandPanel(): void {\n this._panelCollapsed = false;\n this._panelEl.classList.remove('gengage-chat-panel--collapsed');\n if (!this._panelVisible) {\n this._panelVisible = true;\n this._panelEl.classList.add('gengage-chat-panel--visible');\n this.root.classList.add('gengage-chat-drawer--with-panel');\n }\n this._syncDividerPreview();\n this._emitHostShellSync();\n }\n\n /**\n * Ensure the panel starts expanded (panelMode: 'expanded').\n * Users can still collapse/expand via the divider chevron.\n */\n setForceExpanded(): void {\n this._panelCollapsed = false;\n this._panelEl.classList.remove('gengage-chat-panel--collapsed');\n // Show panel immediately even if empty\n if (!this._panelVisible) {\n this._panelVisible = true;\n this._panelEl.classList.add('gengage-chat-panel--visible');\n this.root.classList.add('gengage-chat-drawer--with-panel');\n }\n this._dividerEl.classList.remove('gengage-chat-panel-divider--hidden');\n this._syncDividerPreview();\n this._emitHostShellSync();\n }\n\n /**\n * After new list/grid content is mounted, scroll the left panel toward the top smoothly.\n * InnerHTML resets scrollTop to 0, so we nudge down first; a rAF tween (ease-out quint) replaces\n * native smooth scroll for a softer deceleration.\n */\n private _smoothScrollPanelListToTop(): void {\n const panel = this._panelEl;\n const reduceMotion =\n typeof window !== 'undefined' && (window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches ?? false);\n\n if (reduceMotion) {\n panel.scrollTop = 0;\n return;\n }\n\n this._panelListScrollAnimToken += 1;\n const token = this._panelListScrollAnimToken;\n\n requestAnimationFrame(() => {\n if (token !== this._panelListScrollAnimToken) return;\n const maxScroll = Math.max(0, panel.scrollHeight - panel.clientHeight);\n if (maxScroll <= 0) return;\n\n const startTop = Math.min(160, Math.max(48, maxScroll * 0.28));\n panel.scrollTop = startTop;\n\n const durationMs = Math.min(720, Math.max(380, 320 + Math.sqrt(startTop) * 28));\n const t0 = performance.now();\n\n const easeOutQuint = (t: number) => 1 - (1 - t) ** 5;\n\n const step = (now: number) => {\n if (token !== this._panelListScrollAnimToken) return;\n const elapsed = now - t0;\n const linear = Math.min(1, elapsed / durationMs);\n const eased = easeOutQuint(linear);\n panel.scrollTop = startTop * (1 - eased);\n if (linear < 1) {\n requestAnimationFrame(step);\n } else {\n panel.scrollTop = 0;\n }\n };\n requestAnimationFrame(step);\n });\n }\n\n /** Update scroll affordance (bottom fade gradient) on the panel. */\n private _updateScrollAffordance(): void {\n const panel = this._panelEl;\n const atBottom = panel.scrollTop + panel.clientHeight >= panel.scrollHeight - 10;\n panel.classList.toggle('gengage-chat-panel--has-scroll', !atBottom && panel.scrollHeight > panel.clientHeight);\n panel.classList.toggle('gengage-chat-panel--scrolled', panel.scrollTop > 88);\n }\n\n /** Horizontal swipe on conversation/panel areas to toggle the panel (mobile only). */\n private _setupHorizontalSwipe(el: HTMLElement): void {\n let startX = 0;\n let startY = 0;\n\n const onTouchStart = (e: TouchEvent) => {\n if (!(this._options.getMobileViewport?.() ?? window.innerWidth <= 768)) return;\n const t = e.touches[0];\n if (!t) return;\n startX = t.clientX;\n startY = t.clientY;\n };\n\n const onTouchEnd = (e: TouchEvent) => {\n if (!(this._options.getMobileViewport?.() ?? window.innerWidth <= 768)) return;\n const t = e.changedTouches[0];\n if (!t) return;\n const dx = t.clientX - startX;\n const dy = t.clientY - startY;\n // Only trigger if horizontal movement > 50px and dominant direction\n if (Math.abs(dx) > 50 && Math.abs(dx) > Math.abs(dy) * 2) {\n this.togglePanel();\n this._onPanelToggle?.();\n }\n };\n\n el.addEventListener('touchstart', onTouchStart, { passive: true });\n el.addEventListener('touchend', onTouchEnd, { passive: true });\n this._cleanups.push(() => {\n el.removeEventListener('touchstart', onTouchStart);\n el.removeEventListener('touchend', onTouchEnd);\n });\n }\n\n /** Toggle panel between collapsed and expanded. */\n togglePanel(): void {\n this.setPanelCollapsed(!this._panelCollapsed);\n }\n\n /** Whether the panel is currently collapsed by the user. */\n isPanelCollapsed(): boolean {\n return this._panelCollapsed;\n }\n\n /** Programmatically set panel collapsed state. */\n setPanelCollapsed(collapsed: boolean): void {\n this._panelCollapsed = collapsed;\n if (collapsed) {\n this._panelEl.classList.add('gengage-chat-panel--collapsed');\n } else {\n this._panelEl.classList.remove('gengage-chat-panel--collapsed');\n }\n const chevronBtn = this._dividerEl.querySelector('.gengage-chat-panel-divider-toggle');\n if (chevronBtn) {\n chevronBtn.textContent = collapsed ? '\\u00AB' : '\\u00BB'; // « (expand left) or » (collapse right)\n }\n this._syncDividerPreview();\n }\n\n setDividerPreviewEnabled(enabled: boolean): void {\n this._dividerPreviewEnabled = enabled;\n this._syncDividerPreview();\n }\n\n /** Save panel collapsed state to sessionStorage. */\n persistPanelState(accountId: string): void {\n try {\n const key = `gengage:panel:${accountId}`;\n if (this._panelCollapsed) {\n sessionStorage.setItem(key, 'collapsed');\n } else {\n sessionStorage.removeItem(key);\n }\n } catch {\n // sessionStorage may be unavailable in restricted environments\n }\n }\n\n /** Restore panel collapsed state from sessionStorage. Returns true when restored as collapsed. */\n restorePanelState(accountId: string): boolean {\n try {\n const key = `gengage:panel:${accountId}`;\n if (sessionStorage.getItem(key) === 'collapsed') {\n this._panelCollapsed = true;\n return true;\n }\n } catch {\n // sessionStorage may be unavailable in restricted environments\n }\n return false;\n }\n\n private _createLoadingSequence(\n variant: 'chat' | 'panel',\n steps: string[],\n part: string,\n className: string,\n ): { root: HTMLElement; binding: LoadingSequenceBinding } {\n const root = document.createElement('div');\n root.className = `${className} gds-progress-loader ${variant === 'chat' ? 'gds-progress-loader-chat' : 'gds-progress-loader-panel'}`;\n root.dataset['gengagePart'] = part;\n root.setAttribute('role', 'status');\n root.setAttribute('aria-live', 'polite');\n\n const label = document.createElement('span');\n label.className =\n variant === 'chat'\n ? 'gengage-chat-typing-text gds-progress-label'\n : 'gengage-chat-panel-loading-label gds-progress-label';\n root.appendChild(label);\n\n const dots = document.createElement('span');\n dots.className = variant === 'chat' ? 'gengage-chat-typing-dots gds-progress-dots' : 'gds-progress-dots';\n dots.setAttribute('aria-hidden', 'true');\n for (let i = 0; i < 3; i++) {\n const dot = document.createElement('span');\n dot.className = 'gds-progress-dot';\n dots.appendChild(dot);\n }\n root.appendChild(dots);\n\n const binding: LoadingSequenceBinding = {\n labelEl: label,\n steps: [],\n index: 0,\n intervalId: null,\n };\n this._applyLoadingSteps(binding, steps);\n return { root, binding };\n }\n\n private _applyLoadingSteps(binding: LoadingSequenceBinding, steps: string[], forceLatest = false): void {\n const normalized = steps\n .map((s) => s.trim())\n .filter(Boolean)\n .slice(-3);\n const fallback = [this.i18n.loadingMessage];\n binding.steps = normalized.length > 0 ? normalized : fallback;\n this._clearLoadingBindingInterval(binding);\n binding.index = forceLatest ? binding.steps.length - 1 : 0;\n binding.labelEl.textContent = binding.steps[binding.index]!;\n\n if (!forceLatest && binding.steps.length > 1) {\n binding.intervalId = setInterval(() => {\n if (binding.index >= binding.steps.length - 1) {\n this._clearLoadingBindingInterval(binding);\n return;\n }\n binding.index += 1;\n binding.labelEl.textContent = binding.steps[binding.index]!;\n if (binding.index >= binding.steps.length - 1) {\n this._clearLoadingBindingInterval(binding);\n }\n }, LOADING_STEP_INTERVAL_MS);\n }\n }\n\n private _clearLoadingBindingInterval(binding: LoadingSequenceBinding | null): void {\n if (binding?.intervalId) {\n clearInterval(binding.intervalId);\n binding.intervalId = null;\n }\n }\n\n private _destroyLoadingBinding(binding: LoadingSequenceBinding | null): void {\n this._clearLoadingBindingInterval(binding);\n }\n\n private _updateSendEnabled(): void {\n if (this._sendStopHandler) {\n this.sendBtn.disabled = false;\n return;\n }\n const hasContent = this.inputEl.value.trim().length > 0 || this._pendingAttachment !== null;\n this.sendBtn.disabled = !hasContent;\n }\n\n private _renderSendButtonIcon(mode: 'send' | 'stop'): void {\n this.sendBtn.innerHTML =\n mode === 'stop'\n ? '<span class=\"gengage-chat-send-stop-icon\" aria-hidden=\"true\"></span>'\n : `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M2.01 21L23 12 2.01 3 2 10l15 2-15 2z\"/></svg>`;\n }\n\n private _submit(): void {\n const text = this.inputEl.value.trim();\n const attachment = this._pendingAttachment;\n if (!text && !attachment) return;\n // Match send-button behaviour: while \"Stop generating\" is active, Enter must run\n // the same abort + cleanup as Stop before starting a new request. Otherwise the\n // prior stream's placeholder and bridge state drift from _sendAction's abort.\n if (this._sendStopHandler) {\n const onStop = this._sendStopHandler;\n this.hideStopButton();\n onStop();\n }\n this.onSend(text, attachment ?? undefined);\n this.inputEl.value = '';\n this.inputEl.style.height = 'auto'; // Reset textarea height after submit\n this.clearAttachment();\n this._updateSendEnabled();\n }\n\n private _toggleVoice(): void {\n if (!this._voiceInput) return;\n if (this._voiceInput.state === 'listening') {\n const text = this._voiceInput.stop();\n if (text.trim()) {\n this.inputEl.value = text;\n this._submit();\n }\n } else {\n this.inputEl.value = '';\n this._voiceInput.start();\n }\n }\n\n /** Lock auto-scroll for 500ms after session history restore to prevent visual jump. */\n lockScrollForRestore(): void {\n this._scrollLockedUntil = Date.now() + 500;\n }\n\n /** Scroll to bottom only if user hasn't scrolled up. Force=true always scrolls. */\n private _scrollToBottom(force = false): void {\n if (!force && this._userScrolledUp) return;\n if (!force && this._options.presentation?.shouldBlockSoftAutoScroll?.()) return;\n if (!force && Date.now() < this._scrollLockedUntil) return;\n requestAnimationFrame(() => {\n this.messagesEl.scrollTop = this.messagesEl.scrollHeight;\n this._userScrolledUp = false;\n });\n }\n\n /** Public method for typewriter ticks — scrolls only if user is near bottom. */\n scrollToBottomIfNeeded(): void {\n this._scrollToBottom(false);\n }\n\n /** Update a bot message's text content in the DOM (e.g. for fallback messages). */\n updateBotMessage(messageId: string, html: string): void {\n const bubble = this.messagesEl.querySelector(`[data-message-id=\"${CSS.escape(messageId)}\"]`);\n if (!bubble) return;\n let textEl = bubble.querySelector('.gengage-chat-bubble-text');\n if (!textEl) {\n textEl = document.createElement('div');\n textEl.className = 'gengage-chat-bubble-text';\n bubble.appendChild(textEl);\n }\n textEl.innerHTML = sanitizeHtml(html);\n this._scrollToBottom(false);\n }\n\n /** Mark a message as the first bot message in its thread (for special styling). */\n markFirstBotMessage(messageId: string): void {\n this._firstBotMessageIds.add(messageId);\n const bubble = this.messagesEl.querySelector(`[data-message-id=\"${CSS.escape(messageId)}\"]`);\n if (bubble) {\n bubble.classList.add('gengage-chat-bubble--first');\n }\n }\n\n /** Scroll to the first message of the last thread (for restore targeting). */\n scrollToLastThread(): void {\n const allBubbles = this.messagesEl.querySelectorAll('[data-thread-id]');\n if (allBubbles.length === 0) {\n this._scrollToBottom(true);\n return;\n }\n const lastThreadId = allBubbles[allBubbles.length - 1]!.getAttribute('data-thread-id');\n if (!lastThreadId) {\n this._scrollToBottom(true);\n return;\n }\n this._programmaticScrollUntil = Date.now() + 700;\n const target = this.messagesEl.querySelector(`[data-thread-id=\"${CSS.escape(lastThreadId)}\"]`);\n if (target) {\n requestAnimationFrame(() => {\n target.scrollIntoView({ block: 'start', behavior: 'auto' });\n this._userScrolledUp = false;\n });\n } else {\n this._scrollToBottom(true);\n }\n }\n\n /**\n * Smooth scroll transcript so the given thread’s first bubble is near the top.\n * Used by centralized presentation scroll requests.\n */\n scrollThreadIntoView(threadId: string, behavior: ScrollBehavior = 'smooth'): boolean {\n const matches = this.messagesEl.querySelectorAll(`[data-thread-id=\"${CSS.escape(threadId)}\"]`);\n let target: HTMLElement | null = null;\n for (let i = 0; i < matches.length; i++) {\n const el = matches[i];\n if (!(el instanceof HTMLElement)) continue;\n if (el.classList.contains('gengage-chat-bubble--presentation-collapsed')) continue;\n target = el;\n break;\n }\n if (!target && matches.length > 0 && matches[0] instanceof HTMLElement) {\n target = matches[0];\n }\n if (!target) return false;\n const topInset = 20;\n const nextTop = Math.max(target.offsetTop - topInset, 0);\n this._programmaticScrollUntil = Date.now() + 700;\n this._scrollMessagesTo(nextTop, behavior);\n return true;\n }\n\n /** Programmatic scroll to bottom (e.g. host bridge) — bypasses “user scrolled up” until next frame. */\n scrollToBottomPresentation(behavior: ScrollBehavior = 'smooth'): void {\n this._programmaticScrollUntil = Date.now() + 700;\n requestAnimationFrame(() => {\n this._scrollMessagesTo(this.messagesEl.scrollHeight, behavior);\n this._userScrolledUp = false;\n });\n }\n\n private _scrollMessagesTo(top: number, behavior: ScrollBehavior): void {\n if (typeof this.messagesEl.scrollTo === 'function') {\n this.messagesEl.scrollTo({ top, behavior });\n return;\n }\n this.messagesEl.scrollTop = top;\n }\n\n /** Collapse transcript to a single thread (null = show full history). */\n setPresentationFocus(threadId: string | null): void {\n this._presentationFocusThreadId = threadId;\n this._applyPresentationCollapsed();\n }\n\n setFormerMessagesButtonVisible(visible: boolean): void {\n if (this._formerMessagesBtn) {\n this._formerMessagesBtn.style.display = visible ? '' : 'none';\n }\n }\n\n private _applyPresentationCollapsed(): void {\n const focus = this._presentationFocusThreadId;\n this.messagesEl.querySelectorAll<HTMLElement>('[data-thread-id]').forEach((el) => {\n const tid = el.dataset['threadId'];\n if (!tid) return;\n if (focus && tid !== focus) {\n el.classList.add('gengage-chat-bubble--presentation-collapsed');\n } else {\n el.classList.remove('gengage-chat-bubble--presentation-collapsed');\n }\n });\n }\n\n /** Call after inline chat DOM (e.g. ProductSummaryCard) is appended — reapplies thread collapse. */\n refreshPresentationCollapsed(): void {\n if (this._presentationFocusThreadId) {\n this._applyPresentationCollapsed();\n }\n }\n\n /** Set compact input-area chips (search/info/review shortcuts above input). */\n setInputAreaChips(chips: Array<{ label: string; onAction: () => void; icon?: string }>): void {\n this._inputChipsEl.innerHTML = '';\n if (chips.length === 0) {\n this._inputChipsEl.style.display = 'none';\n return;\n }\n this._inputChipsEl.style.display = '';\n for (const chip of chips) {\n const btn = document.createElement('button');\n btn.className = 'gengage-chat-input-chip gds-chip';\n btn.type = 'button';\n\n // Icon (SVG from icon map, falls back to generic arrow for unknown names)\n if (chip.icon) {\n const svgHtml = SUGGESTED_ACTION_ICONS[chip.icon] ?? DEFAULT_ACTION_ICON;\n const iconSpan = document.createElement('span');\n iconSpan.className = 'gengage-chat-input-chip-icon';\n iconSpan.innerHTML = svgHtml;\n btn.appendChild(iconSpan);\n }\n\n const label = document.createElement('span');\n label.textContent = chip.label;\n btn.appendChild(label);\n\n btn.addEventListener('click', () => chip.onAction());\n this._inputChipsEl.appendChild(btn);\n }\n }\n\n /** Clear input-area chips. */\n clearInputAreaChips(): void {\n this._inputChipsEl.innerHTML = '';\n this._inputChipsEl.style.display = 'none';\n }\n\n setThumbnails(entries: ThumbnailEntry[]): void {\n this._thumbnailsColumn.setEntries(entries);\n if (entries.length > 0) {\n this._thumbnailsColumn.show();\n } else {\n this._thumbnailsColumn.hide();\n }\n this._renderDividerPreview(entries);\n this._syncDividerPreview();\n }\n\n hideThumbnails(): void {\n this._thumbnailsColumn.hide();\n this._renderDividerPreview([]);\n this._syncDividerPreview();\n }\n\n private _renderDividerPreview(entries: ThumbnailEntry[]): void {\n this._dividerPreviewEl.innerHTML = '';\n\n const seen = new Set<string>();\n const previewEntries: ThumbnailEntry[] = [];\n for (let i = entries.length - 1; i >= 0; i--) {\n const entry = entries[i];\n if (!entry || seen.has(entry.sku) || !isSafeImageUrl(entry.imageUrl)) continue;\n seen.add(entry.sku);\n previewEntries.push(entry);\n if (previewEntries.length >= 3) break;\n }\n previewEntries.reverse();\n\n for (const entry of previewEntries) {\n const thumb = document.createElement('span');\n thumb.className = 'gengage-chat-panel-divider-preview-thumb';\n const img = document.createElement('img');\n img.className = 'gengage-chat-panel-divider-preview-img';\n img.src = entry.imageUrl;\n img.alt = '';\n img.width = 48;\n img.height = 48;\n thumb.appendChild(img);\n this._dividerPreviewEl.appendChild(thumb);\n }\n }\n\n private _syncDividerPreview(): void {\n const hasPreview = this._dividerPreviewEl.childElementCount > 0;\n const isActive =\n this._dividerPreviewEnabled &&\n hasPreview &&\n this._panelCollapsed &&\n !this._dividerEl.classList.contains('gengage-chat-panel-divider--hidden');\n this._dividerEl.classList.toggle('gengage-chat-panel-divider--preview-active', isActive);\n }\n\n /** Activate focus trap — Tab/Shift+Tab cycles within the drawer. */\n trapFocus(): void {\n this._previouslyFocusedElement = document.activeElement as HTMLElement | null;\n this.releaseFocus();\n\n const handler = (e: KeyboardEvent) => {\n if (e.key !== 'Tab') return;\n const focusable = this.root.querySelectorAll<HTMLElement>(\n 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\n );\n if (focusable.length === 0) return;\n const first = focusable[0]!;\n const last = focusable[focusable.length - 1]!;\n\n // Use getRootNode() to resolve activeElement inside Shadow DOM\n const rootNode = this.root.getRootNode();\n const active = rootNode instanceof ShadowRoot ? rootNode.activeElement : document.activeElement;\n\n if (e.shiftKey) {\n if (active === first || !this.root.contains(active)) {\n e.preventDefault();\n last.focus();\n }\n } else {\n if (active === last || !this.root.contains(active)) {\n e.preventDefault();\n first.focus();\n }\n }\n };\n\n this._focusTrapHandler = handler;\n this.root.addEventListener('keydown', handler);\n }\n\n /** Release the focus trap and restore previously focused element. */\n releaseFocus(): void {\n if (this._focusTrapHandler) {\n this.root.removeEventListener('keydown', this._focusTrapHandler);\n this._focusTrapHandler = null;\n }\n if (this._previouslyFocusedElement) {\n try {\n this._previouslyFocusedElement.focus();\n } catch {\n // Element may no longer be in the DOM\n }\n this._previouslyFocusedElement = null;\n }\n }\n\n /** Briefly animate the cart icon button to signal a successful add-to-cart. */\n flashCartBadge(): void {\n if (!this._cartBtn) return;\n // Restart animation by removing then re-adding the class after a reflow\n this._cartBtn.classList.remove('gengage-chat-header-btn--cart-flash');\n void this._cartBtn.offsetWidth;\n this._cartBtn.classList.add('gengage-chat-header-btn--cart-flash');\n this._cartBtn.addEventListener(\n 'animationend',\n () => {\n this._cartBtn?.classList.remove('gengage-chat-header-btn--cart-flash');\n },\n { once: true },\n );\n }\n\n /** Show a temporary success toast inside the shadow root. */\n showCartToast(message: string): void {\n const existing = this.root.querySelector('.gengage-chat-cart-toast');\n existing?.remove();\n const toast = document.createElement('div');\n toast.className = 'gengage-chat-cart-toast';\n toast.setAttribute('role', 'status');\n toast.setAttribute('aria-live', 'polite');\n toast.textContent = message;\n this.root.appendChild(toast);\n // Force reflow then add visible class for animation\n void toast.offsetWidth;\n toast.classList.add('gengage-chat-cart-toast--visible');\n setTimeout(() => {\n toast.classList.remove('gengage-chat-cart-toast--visible');\n setTimeout(() => toast.remove(), 300);\n }, 2500);\n }\n\n /** Clean up event listeners and child resources (VoiceInput, timers). */\n destroy(): void {\n registerChatScrollElement(null);\n this.releaseFocus();\n if (this._resizeRafId !== null) {\n cancelAnimationFrame(this._resizeRafId);\n this._resizeRafId = null;\n }\n this._destroyLoadingBinding(this._typingLoadingBinding);\n this._typingLoadingBinding = null;\n this._destroyLoadingBinding(this._panelLoadingBinding);\n this._panelLoadingBinding = null;\n this._destroyLoadingBinding(this._panelAiZoneLoadingBinding);\n this._panelAiZoneLoadingBinding = null;\n this._closeAttachMenu();\n for (const cleanup of this._cleanups) cleanup();\n this._cleanups.length = 0;\n this._voiceInput?.destroy();\n this._voiceInput = null;\n }\n}\n","export interface LauncherOptions {\n onClick: () => void;\n svgMarkup?: string;\n /** Full-size image URL — renders launcher as an image button (no circular bg). */\n imageUrl?: string;\n ariaLabel?: string;\n hideMobile?: boolean;\n mobileBreakpoint?: number;\n tooltip?: string;\n}\n\n/**\n * Result of createLauncher — the container wraps the button and exposes\n * content-area slots where the QNA widget or host page can inject engagement\n * actions (buying-hesitation questions, \"Find Similar\" buttons, etc.).\n */\nexport interface LauncherElements {\n /** Outer container — append this to the DOM. */\n container: HTMLElement;\n /** The clickable FAB button. */\n button: HTMLButtonElement;\n /** Slot above the button (primary QNA actions). */\n contentArea: HTMLElement;\n /** Slot below the button (secondary content). */\n contentAreaBottom: HTMLElement;\n}\n\nconst DEFAULT_SVG = `<svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"3\" y=\"7\" width=\"18\" height=\"13\" rx=\"3\" fill=\"currentColor\" opacity=\"0.15\"/>\n <rect x=\"3\" y=\"7\" width=\"18\" height=\"13\" rx=\"3\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <circle cx=\"9\" cy=\"13\" r=\"1.5\" fill=\"currentColor\"/>\n <circle cx=\"15\" cy=\"13\" r=\"1.5\" fill=\"currentColor\"/>\n <path d=\"M9.5 17C10.3 17.6 11.1 18 12 18C12.9 18 13.7 17.6 14.5 17\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n <path d=\"M12 7V4\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n <circle cx=\"12\" cy=\"3\" r=\"1\" fill=\"currentColor\"/>\n <path d=\"M1 12V14\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n <path d=\"M23 12V14\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n</svg>`;\n\nexport function createLauncher(options: LauncherOptions): LauncherElements {\n // Container holds content areas + button\n const container = document.createElement('div');\n container.className = 'gengage-chat-launcher-container';\n container.dataset['gengagePart'] = 'chat-launcher-container';\n\n // Content area above button (QNA actions, buying-hesitation questions)\n const contentArea = document.createElement('div');\n contentArea.className = 'gengage-chat-launcher-content-area';\n contentArea.dataset['gengagePart'] = 'chat-launcher-content-primary';\n container.appendChild(contentArea);\n\n // The FAB button\n const button = document.createElement('button');\n button.type = 'button';\n button.setAttribute('aria-label', options.ariaLabel ?? 'Open chat');\n\n if (options.imageUrl) {\n button.className = 'gengage-chat-launcher gengage-chat-launcher--image-mode';\n const img = document.createElement('img');\n img.src = options.imageUrl;\n img.alt = '';\n img.draggable = false;\n button.appendChild(img);\n } else {\n button.className = 'gengage-chat-launcher';\n button.innerHTML = options.svgMarkup ?? DEFAULT_SVG;\n }\n button.dataset['gengagePart'] = 'chat-launcher-button';\n\n if (options.tooltip !== undefined) {\n const tooltipEl = document.createElement('span');\n tooltipEl.className = 'gengage-chat-launcher-tooltip';\n tooltipEl.dataset['gengagePart'] = 'chat-launcher-tooltip';\n tooltipEl.textContent = options.tooltip;\n button.appendChild(tooltipEl);\n }\n\n if (options.hideMobile) {\n container.dataset['hideMobile'] = '1';\n }\n if (options.mobileBreakpoint !== undefined) {\n container.dataset['mobileBreakpoint'] = String(options.mobileBreakpoint);\n }\n\n button.addEventListener('click', options.onClick);\n container.appendChild(button);\n\n // Content area below button (secondary content)\n const contentAreaBottom = document.createElement('div');\n contentAreaBottom.className = 'gengage-chat-launcher-content-area-bottom';\n contentAreaBottom.dataset['gengagePart'] = 'chat-launcher-content-secondary';\n container.appendChild(contentAreaBottom);\n\n return { container, button, contentArea, contentAreaBottom };\n}\n","const ALLOWED_AUDIO_TYPES = new Set([\n 'audio/ogg',\n 'audio/mpeg',\n 'audio/mp3',\n 'audio/wav',\n 'audio/webm',\n 'audio/aac',\n 'audio/mp4',\n]);\n\n/** Active audio elements tracked for bulk cleanup. */\nconst activeAudioElements = new Set<HTMLAudioElement>();\n\n/** Release an audio element: pause, revoke, and remove from tracking set. */\nfunction releaseAudio(audio: HTMLAudioElement): void {\n audio.pause();\n audio.removeAttribute('src');\n audio.load(); // Releases the media resource\n activeAudioElements.delete(audio);\n}\n\n/** Returned by `playTtsAudio` on successful playback initiation. */\nexport interface AudioHandle {\n /** Stop playback immediately. Safe to call multiple times. */\n stop: () => void;\n}\n\n/**\n * Plays a base64-encoded audio clip.\n * Returns an `AudioHandle` that can stop playback, or `null` if playback\n * could not be initiated (blocked by browser, unsupported environment, etc.).\n */\nexport function playTtsAudio(base64: string, contentType = 'audio/ogg'): AudioHandle | null {\n // Strip parameters like '; codecs=opus' before checking allowlist\n const baseType = contentType.split(';')[0]!.trim();\n if (!ALLOWED_AUDIO_TYPES.has(baseType)) return null;\n try {\n const audio = new Audio(`data:${contentType};base64,${base64}`);\n activeAudioElements.add(audio);\n audio.addEventListener('ended', () => releaseAudio(audio), { once: true });\n audio.play().catch(() => {\n // Autoplay blocked by browser — release immediately\n releaseAudio(audio);\n });\n return {\n stop: () => releaseAudio(audio),\n };\n } catch {\n // Unsupported environment\n return null;\n }\n}\n\n/** Stop and release all active TTS audio elements. */\nexport function destroyAllTtsAudio(): void {\n for (const audio of activeAudioElements) {\n releaseAudio(audio);\n }\n}\n","/**\n * Configurable price formatting.\n *\n * Defaults to Turkish locale (dot thousands, comma decimal, TL suffix),\n * but renders whole TL amounts without kurus for a cleaner retail UI.\n * Configure via widget config `pricing` field for any locale/currency.\n */\n\nexport interface PriceFormatConfig {\n /** ISO 4217 currency code (e.g. 'TRY', 'USD'). Used by analytics only — not consumed by formatPrice(). */\n currencyCode?: string;\n /** Currency symbol. Default: 'TL' */\n currencySymbol?: string;\n /** Where to place the symbol. Default: 'suffix' */\n currencyPosition?: 'prefix' | 'suffix';\n /** Separator between thousands. Default: '.' (Turkish) */\n thousandsSeparator?: string;\n /** Decimal point character. Default: ',' (Turkish) */\n decimalSeparator?: string;\n /** Whether to show decimal part for whole numbers. Default: false */\n alwaysShowDecimals?: boolean;\n}\n\nconst TURKISH_DEFAULTS: Required<PriceFormatConfig> = {\n currencyCode: 'TRY',\n currencySymbol: 'TL',\n currencyPosition: 'suffix',\n thousandsSeparator: '.',\n decimalSeparator: ',',\n alwaysShowDecimals: false,\n};\n\n/**\n * Formats a raw numeric price string into the configured locale format.\n *\n * Examples (default Turkish):\n * \"17990\" → \"17.990 TL\"\n * \"17990.5\" → \"17.990,50 TL\"\n *\n * Examples (GBP prefix):\n * \"17990\" with { currencySymbol: '£', currencyPosition: 'prefix', thousandsSeparator: ',', decimalSeparator: '.' }\n * → \"£17,990\"\n *\n * Returns the input as-is if it's not a valid number.\n */\nexport function formatPrice(raw: string, config?: PriceFormatConfig): string {\n const num = Number(raw);\n if (!Number.isFinite(num) || num < 0) return raw;\n\n const resolved = { ...TURKISH_DEFAULTS, ...config };\n\n const isWholeAmount = Math.abs(num % 1) < Number.EPSILON;\n const shouldShowDecimals = resolved.alwaysShowDecimals || !isWholeAmount;\n const fixed = shouldShowDecimals ? num.toFixed(2) : num.toFixed(0);\n const dotIdx = fixed.indexOf('.');\n const intPart = dotIdx === -1 ? fixed : fixed.slice(0, dotIdx);\n const decPart = dotIdx === -1 ? undefined : fixed.slice(dotIdx + 1);\n\n // Add thousands separators to integer part\n const withSeparators = intPart.replace(/\\B(?=(\\d{3})+(?!\\d))/g, resolved.thousandsSeparator);\n\n let formatted: string;\n if (decPart !== undefined) {\n formatted = `${withSeparators}${resolved.decimalSeparator}${decPart}`;\n } else {\n formatted = withSeparators;\n }\n\n if (resolved.currencySymbol) {\n if (resolved.currencyPosition === 'prefix') {\n return `${resolved.currencySymbol}${formatted}`;\n }\n return `${formatted} ${resolved.currencySymbol}`;\n }\n\n return formatted;\n}\n","/**\n * ComparisonTable — renders a product comparison table with recommended pick,\n * attribute rows, highlights, and optional special cases.\n *\n * XSS safety: All text is set via textContent. Image URLs are validated\n * for safe protocols before being assigned to img.src.\n */\n\nimport { sanitizeHtml, isSafeImageUrl } from '../../common/safe-html.js';\nimport { formatPrice } from '../../common/price-formatter.js';\nimport type { PriceFormatConfig } from '../../common/price-formatter.js';\n\n/**\n * Fallback display names for common e-commerce product attributes.\n * Used when the backend sends raw field names (e.g., \"screen_size\")\n * and no locale-specific criteriaLabels map is provided via i18n.\n */\nconst CRITERIA_DISPLAY_NAMES: Record<string, string> = {\n screen_size: 'Screen Size',\n weight: 'Weight',\n battery_capacity: 'Battery Capacity',\n battery_life: 'Battery Life',\n storage: 'Storage',\n memory: 'Memory',\n ram: 'RAM',\n processor: 'Processor',\n camera: 'Camera',\n resolution: 'Resolution',\n display_type: 'Display Type',\n refresh_rate: 'Refresh Rate',\n color: 'Color',\n material: 'Material',\n dimensions: 'Dimensions',\n warranty: 'Warranty',\n connectivity: 'Connectivity',\n water_resistance: 'Water Resistance',\n operating_system: 'Operating System',\n brand: 'Brand',\n model: 'Model',\n price: 'Price',\n energy_class: 'Energy Class',\n noise_level: 'Noise Level',\n capacity: 'Capacity',\n power: 'Power',\n voltage: 'Voltage',\n width: 'Width',\n height: 'Height',\n depth: 'Depth',\n};\n\n/**\n * Map a raw criteria field name to a human-readable label.\n * Checks locale-specific `criteriaLabels` first (from i18n), then the\n * built-in fallback map, then applies a formatting heuristic.\n */\nexport function formatCriteriaName(rawName: string, criteriaLabels?: Record<string, string>): string {\n return (\n criteriaLabels?.[rawName] ??\n CRITERIA_DISPLAY_NAMES[rawName] ??\n rawName.replace(/_/g, ' ').replace(/^\\w/, (c) => c.toUpperCase())\n );\n}\n\nexport interface ComparisonProduct {\n sku: string;\n name: string;\n price: string;\n imageUrl?: string | undefined;\n rating?: number | undefined;\n reviewCount?: number | undefined;\n}\n\nexport interface ComparisonAttribute {\n label: string;\n values: string[];\n}\n\nexport interface ComparisonTableI18n {\n comparisonHeading?: string;\n recommendedChoiceLabel?: string;\n highlightsLabel?: string;\n keyDifferencesLabel?: string;\n specialCasesLabel?: string;\n viewMoreLabel?: string;\n addToCartButton?: string;\n /** Locale-specific attribute display names (e.g., { screen_size: 'Screen Size' }). */\n criteriaLabels?: Record<string, string>;\n}\n\nexport interface ComparisonTableOptions {\n recommended: ComparisonProduct;\n products: ComparisonProduct[];\n attributes: ComparisonAttribute[];\n highlights: string[];\n specialCases?: string[] | undefined;\n onProductClick: (params: { sku: string; name: string }) => void;\n onAddToCart?: ((sku: string) => void) | undefined;\n recommendedText?: string | undefined;\n winnerHits?: Record<string, { positive?: string[]; negative?: string[] }> | undefined;\n productActions?: Record<string, { title: string; type: string; payload?: unknown }> | undefined;\n keyDifferencesHtml?: string | undefined;\n i18n?: ComparisonTableI18n | undefined;\n pricing?: PriceFormatConfig | undefined;\n}\n\nfunction hasRenderablePrice(raw: string | undefined): raw is string {\n if (typeof raw !== 'string') return false;\n const num = Number(raw);\n return Number.isFinite(num) && num > 0;\n}\n\nfunction hasRenderableRating(raw: number | string | undefined): raw is number | string {\n const num = typeof raw === 'number' ? raw : typeof raw === 'string' ? Number(raw) : NaN;\n return Number.isFinite(num) && num > 0;\n}\n\nfunction createRatingBadge(raw: number | string): HTMLElement {\n const value = typeof raw === 'number' ? raw : Number(raw);\n const badge = document.createElement('div');\n badge.className = 'gengage-chat-comparison-recommended-rating';\n badge.innerHTML =\n '<span class=\"gengage-chat-comparison-recommended-rating-icon\" aria-hidden=\"true\">' +\n '<svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 3.6l2.58 5.23 5.77.84-4.17 4.07.98 5.75L12 16.78l-5.16 2.71.99-5.75L3.66 9.67l5.76-.84L12 3.6z\"/></svg>' +\n '</span>';\n const label = document.createElement('span');\n label.className = 'gengage-chat-comparison-recommended-rating-value';\n label.textContent = value.toFixed(1);\n badge.appendChild(label);\n return badge;\n}\n\nexport function renderComparisonTable(options: ComparisonTableOptions): HTMLElement {\n const { recommended, products, attributes, highlights, specialCases, onProductClick, i18n } = options;\n\n const container = document.createElement('div');\n container.className = 'gengage-chat-comparison';\n container.dataset['gengagePart'] = 'comparison-dialog';\n container.setAttribute('role', 'dialog');\n container.setAttribute('aria-label', i18n?.comparisonHeading ?? 'Comparison Results');\n\n // Recommended card\n if (recommended) {\n const recCard = document.createElement('article');\n recCard.className = 'gengage-chat-comparison-recommended gds-card';\n recCard.dataset['gengagePart'] = 'comparison-recommended-card';\n\n const recLabel = document.createElement('div');\n recLabel.className = 'gengage-chat-comparison-recommended-label';\n recLabel.textContent = i18n?.recommendedChoiceLabel ?? 'Recommended Choice';\n recCard.appendChild(recLabel);\n\n const recBody = document.createElement('div');\n recBody.className = 'gengage-chat-comparison-recommended-body';\n recBody.classList.add('gds-clickable');\n recBody.tabIndex = 0;\n recBody.setAttribute('role', 'button');\n recBody.setAttribute('aria-label', recommended.name);\n\n const media = document.createElement('div');\n media.className = 'gengage-chat-comparison-recommended-media';\n if (recommended.imageUrl && isSafeImageUrl(recommended.imageUrl)) {\n const img = document.createElement('img');\n img.src = recommended.imageUrl;\n img.alt = recommended.name;\n img.loading = 'lazy';\n img.addEventListener(\n 'error',\n () => {\n img.style.display = 'none';\n },\n { once: true },\n );\n media.appendChild(img);\n } else {\n const placeholder = document.createElement('div');\n placeholder.className = 'gengage-chat-comparison-recommended-placeholder';\n placeholder.setAttribute('aria-hidden', 'true');\n media.appendChild(placeholder);\n }\n recBody.appendChild(media);\n\n const info = document.createElement('div');\n info.className = 'gengage-chat-comparison-recommended-info';\n const title = document.createElement('div');\n title.className = 'gengage-chat-comparison-recommended-title';\n title.textContent = recommended.name;\n info.appendChild(title);\n const meta = document.createElement('div');\n meta.className = 'gengage-chat-comparison-recommended-meta';\n if (hasRenderableRating(recommended.rating)) {\n meta.appendChild(createRatingBadge(recommended.rating!));\n }\n if (hasRenderablePrice(recommended.price)) {\n const price = document.createElement('div');\n price.className = 'gengage-chat-comparison-recommended-price';\n price.textContent = formatPrice(recommended.price, options.pricing);\n meta.appendChild(price);\n }\n if (meta.childElementCount > 0) info.appendChild(meta);\n if (options.recommendedText) {\n const recExplanation = document.createElement('p');\n recExplanation.className = 'gengage-chat-comparison-recommended-text';\n recExplanation.innerHTML = sanitizeHtml(options.recommendedText);\n info.appendChild(recExplanation);\n }\n recBody.appendChild(info);\n\n const openRecommended = (): void => {\n onProductClick({ sku: recommended.sku, name: recommended.name });\n };\n recBody.addEventListener('click', openRecommended);\n recBody.addEventListener('keydown', (event) => {\n if (event.key !== 'Enter' && event.key !== ' ') return;\n event.preventDefault();\n openRecommended();\n });\n\n recCard.appendChild(recBody);\n\n // Supporting bullets\n if (highlights.length > 0) {\n const hlSection = document.createElement('div');\n hlSection.className = 'gengage-chat-comparison-highlights';\n hlSection.dataset['gengagePart'] = 'comparison-highlights';\n const hlLabel = document.createElement('div');\n hlLabel.className = 'gengage-chat-comparison-highlights-label';\n hlLabel.textContent = i18n?.highlightsLabel ?? 'Key Highlights';\n hlSection.appendChild(hlLabel);\n const ul = document.createElement('ul');\n for (const hl of highlights) {\n const li = document.createElement('li');\n li.textContent = hl;\n ul.appendChild(li);\n }\n hlSection.appendChild(ul);\n recCard.appendChild(hlSection);\n }\n\n container.appendChild(recCard);\n }\n\n // Key Differences section\n if (options.keyDifferencesHtml) {\n const kdSection = document.createElement('details');\n kdSection.className = 'gengage-chat-comparison-key-differences';\n kdSection.dataset['gengagePart'] = 'comparison-key-differences';\n const kdSummary = document.createElement('summary');\n kdSummary.className = 'gengage-chat-comparison-key-differences-summary';\n const kdLabel = document.createElement('span');\n kdLabel.className = 'gengage-chat-comparison-key-differences-summary-label';\n kdLabel.textContent = i18n?.keyDifferencesLabel ?? 'Key Differences';\n const kdMeta = document.createElement('span');\n kdMeta.className = 'gengage-chat-comparison-key-differences-summary-meta';\n kdMeta.textContent = i18n?.viewMoreLabel ?? 'Show More';\n kdSummary.appendChild(kdLabel);\n kdSummary.appendChild(kdMeta);\n kdSection.appendChild(kdSummary);\n const kdContent = document.createElement('div');\n kdContent.className = 'gengage-chat-comparison-key-differences-content';\n kdContent.innerHTML = sanitizeHtml(formatKeyDifferences(options.keyDifferencesHtml));\n kdSection.appendChild(kdContent);\n container.appendChild(kdSection);\n }\n\n // Special cases (expandable)\n if (specialCases && specialCases.length > 0) {\n const special = document.createElement('details');\n special.className = 'gengage-chat-comparison-special gds-evidence-card gds-evidence-card-warning';\n special.dataset['gengagePart'] = 'comparison-special-cases';\n const summary = document.createElement('summary');\n summary.textContent = i18n?.specialCasesLabel ?? 'For Special Cases';\n special.appendChild(summary);\n const list = document.createElement('ul');\n for (const sc of specialCases) {\n appendSpecialCaseListItems(list, sc);\n }\n if (list.childElementCount > 0) {\n special.appendChild(list);\n }\n container.appendChild(special);\n }\n\n // Comparison table\n if (products.length > 0 && attributes.length > 0) {\n const table = document.createElement('table');\n table.className = 'gengage-chat-comparison-table gds-comparison-table';\n\n // Header row: empty cell + product columns\n const thead = document.createElement('thead');\n const headerRow = document.createElement('tr');\n const emptyTh = document.createElement('th');\n headerRow.appendChild(emptyTh);\n for (const product of products) {\n const th = document.createElement('th');\n if (product.sku === recommended?.sku) {\n th.className = 'gengage-chat-comparison-selected gds-comparison-table-winner-cell';\n }\n const headerCell = document.createElement('div');\n headerCell.className =\n 'gengage-chat-comparison-table-header-cell gengage-chat-comparison-table-header-cell--clickable gds-clickable';\n headerCell.tabIndex = 0;\n headerCell.setAttribute('role', 'button');\n headerCell.setAttribute('aria-label', product.name);\n headerCell.title = product.name;\n const openProduct = (): void => {\n onProductClick({ sku: product.sku, name: product.name });\n };\n headerCell.addEventListener('click', openProduct);\n headerCell.addEventListener('keydown', (event) => {\n if (event.key !== 'Enter' && event.key !== ' ') return;\n event.preventDefault();\n openProduct();\n });\n if (product.imageUrl && isSafeImageUrl(product.imageUrl)) {\n const img = document.createElement('img');\n img.src = product.imageUrl;\n img.alt = product.name;\n img.loading = 'lazy';\n img.addEventListener(\n 'error',\n () => {\n img.style.display = 'none';\n },\n { once: true },\n );\n headerCell.appendChild(img);\n } else {\n const placeholder = document.createElement('div');\n placeholder.className = 'gengage-chat-comparison-table-header-img-placeholder';\n placeholder.setAttribute('aria-hidden', 'true');\n headerCell.appendChild(placeholder);\n }\n const name = document.createElement('div');\n name.className = 'gengage-chat-comparison-table-product-name';\n name.textContent = product.name;\n headerCell.appendChild(name);\n if (hasRenderablePrice(product.price)) {\n const prc = document.createElement('div');\n prc.className = 'gengage-chat-comparison-table-price';\n prc.textContent = formatPrice(product.price, options.pricing);\n headerCell.appendChild(prc);\n }\n th.appendChild(headerCell);\n headerRow.appendChild(th);\n }\n thead.appendChild(headerRow);\n table.appendChild(thead);\n\n // Attribute rows\n const tbody = document.createElement('tbody');\n for (const attr of attributes) {\n const row = document.createElement('tr');\n const labelTd = document.createElement('td');\n labelTd.className = 'gengage-chat-comparison-label';\n labelTd.textContent = formatCriteriaName(attr.label, i18n?.criteriaLabels);\n row.appendChild(labelTd);\n for (let i = 0; i < attr.values.length; i++) {\n const td = document.createElement('td');\n if (products[i]?.sku === recommended?.sku) {\n td.className = 'gengage-chat-comparison-selected gds-comparison-table-winner-cell';\n }\n td.textContent = attr.values[i] ?? '';\n row.appendChild(td);\n }\n tbody.appendChild(row);\n }\n table.appendChild(tbody);\n // Wrap in a scrollable container so the table scrolls independently\n const tableWrapper = document.createElement('div');\n tableWrapper.className = 'gengage-chat-comparison-table-wrapper';\n tableWrapper.dataset['gengagePart'] = 'comparison-table-wrapper';\n tableWrapper.appendChild(table);\n container.appendChild(tableWrapper);\n }\n\n // Focus trap: keep Tab cycling within the comparison dialog\n container.addEventListener('keydown', (e) => {\n if (e.key !== 'Tab') return;\n const focusables = container.querySelectorAll<HTMLElement>(\n 'button, [href], input, [tabindex]:not([tabindex=\"-1\"])',\n );\n if (focusables.length === 0) return;\n const first = focusables[0]!;\n const last = focusables[focusables.length - 1]!;\n if (e.shiftKey && document.activeElement === first) {\n e.preventDefault();\n last.focus();\n } else if (!e.shiftKey && document.activeElement === last) {\n e.preventDefault();\n first.focus();\n }\n });\n\n return container;\n}\n\nfunction formatKeyDifferences(text: string): string {\n // If the backend already sent HTML with list elements, pass through as-is\n if (/<[uo]l[\\s>]/i.test(text) || /<li[\\s>]/i.test(text)) return text;\n const lines = text.split('\\n').filter((l) => l.trim());\n if (lines.length <= 1) return text;\n return '<ul>' + lines.map((l) => `<li>${l.trim()}</li>`).join('') + '</ul>';\n}\n\nfunction appendSpecialCaseListItems(list: HTMLUListElement, raw: string): void {\n const sanitized = sanitizeHtml(raw);\n if (!sanitized) return;\n\n const template = document.createElement('template');\n template.innerHTML = sanitized;\n const nestedItems = Array.from(template.content.querySelectorAll('li'));\n if (nestedItems.length > 0) {\n for (const nestedItem of nestedItems) {\n const li = document.createElement('li');\n li.innerHTML = sanitizeHtml(nestedItem.innerHTML);\n list.appendChild(li);\n }\n return;\n }\n\n const li = document.createElement('li');\n if (looksLikeHtml(raw)) {\n li.innerHTML = sanitized;\n } else {\n li.textContent = raw;\n }\n list.appendChild(li);\n}\n\nfunction looksLikeHtml(text: string): boolean {\n return /<\\/?[a-z][\\s\\S]*>/i.test(text);\n}\n","/**\n * UI Utilities for the Gengage Chat components.\n */\n\n/**\n * Creates a Lucide-style SVG icon from paths.\n * @param paths SVG path strings (the 'd' attribute).\n * @param size Icon width/height in pixels.\n * @returns An SVGElement.\n */\nexport function createLucideIcon(paths: string[], size = 18): SVGElement {\n const ns = 'http://www.w3.org/2000/svg';\n const svg = document.createElementNS(ns, 'svg');\n svg.setAttribute('width', String(size));\n svg.setAttribute('height', String(size));\n svg.setAttribute('viewBox', '0 0 24 24');\n svg.setAttribute('fill', 'none');\n svg.setAttribute('stroke', 'currentColor');\n svg.setAttribute('stroke-width', '2');\n svg.setAttribute('stroke-linecap', 'round');\n svg.setAttribute('stroke-linejoin', 'round');\n svg.setAttribute('aria-hidden', 'true');\n\n for (const d of paths) {\n const path = document.createElementNS(ns, 'path');\n path.setAttribute('d', d);\n svg.appendChild(path);\n }\n\n return svg;\n}\n","/**\n * ReviewHighlights — subject-first review intelligence panel.\n *\n * Subjects are neutral topics (e.g., \"Build quality\").\n * Sentiment is represented by per-subject positive/negative mention counts.\n * Clicking a subject shows short snippets, not full review bodies.\n */\n\nimport type { UIElement } from '../../common/types.js';\nimport { createLucideIcon } from '../utils/ui.js';\n\ntype ReviewTone = 'positive' | 'negative' | 'neutral';\n\ninterface ReviewItem {\n review_class?: string;\n review_text?: string;\n review_rating?: string | number;\n review_tag?: string;\n}\n\ninterface ReviewSnippet {\n text: string;\n tone: ReviewTone;\n rating?: string | number;\n}\n\ninterface SubjectAggregate {\n key: string;\n label: string;\n mentions: number;\n positive: number;\n negative: number;\n neutral: number;\n snippets: ReviewSnippet[];\n}\n\nconst DEFAULT_SUBJECT = 'General';\nconst MAX_SNIPPET_CHARS = 220;\nconst MAX_SNIPPETS_PER_SUBJECT = 6;\n\nfunction normalizeTags(tag: unknown): string[] {\n if (typeof tag !== 'string') return [DEFAULT_SUBJECT];\n const split = tag\n .split(/[,;|/]+/)\n .map((part) => part.trim().replace(/\\s+/g, ' '))\n .filter((part) => part.length > 0);\n if (split.length === 0) return [DEFAULT_SUBJECT];\n return Array.from(new Set(split.map((part) => part.toLocaleLowerCase()))).map((lowered) => {\n const original = split.find((part) => part.toLocaleLowerCase() === lowered);\n return original ?? DEFAULT_SUBJECT;\n });\n}\n\nfunction normalizeTone(tone: unknown): ReviewTone {\n if (tone === 'positive' || tone === 'negative' || tone === 'neutral') return tone;\n return 'neutral';\n}\n\nfunction normalizeSnippet(text: unknown): string {\n if (typeof text !== 'string') return '';\n const normalized = text.trim().replace(/\\s+/g, ' ');\n if (normalized.length <= MAX_SNIPPET_CHARS) return normalized;\n return `${normalized.slice(0, MAX_SNIPPET_CHARS - 3).trimEnd()}...`;\n}\n\nfunction subjectTone(subject: SubjectAggregate): ReviewTone {\n if (subject.positive > subject.negative) return 'positive';\n if (subject.negative > subject.positive) return 'negative';\n return 'neutral';\n}\n\nfunction subjectToneIcon(tone: ReviewTone): SVGElement {\n if (tone === 'positive') {\n return createLucideIcon(['M7 17 17 7', 'M7 7h10v10'], 14);\n }\n if (tone === 'negative') {\n return createLucideIcon(['M7 7 17 17', 'M17 7v10H7'], 14);\n }\n return createLucideIcon(['M5 12h14'], 14);\n}\n\nfunction mentionText(\n count: number,\n singularLabel: string = '1 customer mentions',\n pluralLabel: string = 'customers mention',\n): string {\n return count === 1 ? singularLabel : `${count} ${pluralLabel}`;\n}\n\nexport function renderReviewHighlights(\n element: UIElement,\n options?: {\n emptyReviewsMessage?: string | undefined;\n reviewFilterPositive?: string | undefined;\n reviewFilterNegative?: string | undefined;\n reviewCustomersMentionSingular?: string | undefined;\n reviewCustomersMentionPlural?: string | undefined;\n reviewSubjectsHeading?: string | undefined;\n },\n): HTMLElement {\n const container = document.createElement('div');\n container.className = 'gengage-chat-review-highlights';\n\n const reviews = element.props?.['reviews'];\n if (!Array.isArray(reviews) || reviews.length === 0) {\n const empty = document.createElement('div');\n empty.className = 'gengage-chat-review-empty';\n empty.textContent = options?.emptyReviewsMessage ?? 'No review summary found.';\n container.appendChild(empty);\n return container;\n }\n\n const rawItems: ReviewItem[] = reviews.filter(\n (r): r is Record<string, unknown> => r !== null && typeof r === 'object',\n ) as ReviewItem[];\n\n const subjectsMap = new Map<string, SubjectAggregate>();\n for (const item of rawItems) {\n const tone = normalizeTone(item.review_class);\n const snippet = normalizeSnippet(item.review_text);\n const labels = normalizeTags(item.review_tag);\n\n for (const label of labels) {\n const key = label.toLocaleLowerCase();\n let subject = subjectsMap.get(key);\n if (!subject) {\n subject = {\n key,\n label,\n mentions: 0,\n positive: 0,\n negative: 0,\n neutral: 0,\n snippets: [],\n };\n subjectsMap.set(key, subject);\n }\n\n subject.mentions += 1;\n if (tone === 'positive') subject.positive += 1;\n else if (tone === 'negative') subject.negative += 1;\n else subject.neutral += 1;\n\n if (snippet.length > 0 && subject.snippets.length < MAX_SNIPPETS_PER_SUBJECT) {\n const alreadyExists = subject.snippets.some((entry) => entry.text === snippet);\n if (!alreadyExists) {\n const snippetEntry: ReviewSnippet = {\n text: snippet,\n tone,\n };\n if (item.review_rating !== undefined && String(item.review_rating).length > 0) {\n snippetEntry.rating = item.review_rating;\n }\n subject.snippets.push(snippetEntry);\n }\n }\n }\n }\n\n const subjects = Array.from(subjectsMap.values()).sort((a, b) => {\n if (b.mentions !== a.mentions) return b.mentions - a.mentions;\n return a.label.localeCompare(b.label);\n });\n\n if (subjects.length === 0) {\n const empty = document.createElement('div');\n empty.className = 'gengage-chat-review-empty';\n empty.textContent = options?.emptyReviewsMessage ?? 'No review summary found.';\n container.appendChild(empty);\n return container;\n }\n\n const positiveLabel = (options?.reviewFilterPositive ?? 'positive').toLowerCase();\n const negativeLabel = (options?.reviewFilterNegative ?? 'negative').toLowerCase();\n\n const subjectHeading = document.createElement('div');\n subjectHeading.className = 'gengage-chat-review-subjects-heading';\n subjectHeading.textContent = options?.reviewSubjectsHeading ?? 'Select to learn more';\n container.appendChild(subjectHeading);\n\n let activeKey = subjects[0]?.key ?? null;\n\n const subjectRow = document.createElement('div');\n subjectRow.className = 'gengage-chat-review-subjects gds-toolbar gds-toolbar-compact';\n\n const detailCard = document.createElement('section');\n detailCard.className = 'gengage-chat-review-detail gds-evidence-card';\n\n const renderDetail = (): void => {\n while (detailCard.firstChild) detailCard.removeChild(detailCard.firstChild);\n if (!activeKey) return;\n const active = subjects.find((subject) => subject.key === activeKey);\n if (!active) return;\n\n const metaRow = document.createElement('div');\n metaRow.className = 'gengage-chat-review-detail-meta';\n\n const mentions = document.createElement('span');\n mentions.className = 'gengage-chat-review-detail-mentions';\n mentions.textContent = `${mentionText(\n active.mentions,\n options?.reviewCustomersMentionSingular,\n options?.reviewCustomersMentionPlural,\n )} \"${active.label}\"`;\n metaRow.appendChild(mentions);\n\n if (active.positive > 0) {\n const pos = document.createElement('span');\n pos.className = 'gengage-chat-review-detail-positive';\n pos.textContent = `${active.positive} ${positiveLabel}`;\n metaRow.appendChild(pos);\n }\n\n if (active.negative > 0) {\n const neg = document.createElement('span');\n neg.className = 'gengage-chat-review-detail-negative';\n neg.textContent = `${active.negative} ${negativeLabel}`;\n metaRow.appendChild(neg);\n }\n\n detailCard.appendChild(metaRow);\n\n const snippets = document.createElement('div');\n snippets.className = 'gengage-chat-review-snippets';\n\n for (const snippet of active.snippets) {\n const line = document.createElement('article');\n line.className = 'gengage-chat-review-snippet';\n line.dataset['tone'] = snippet.tone;\n\n const text = document.createElement('div');\n text.className = 'gengage-chat-review-snippet-text';\n text.textContent = `\"${snippet.text}\"`;\n line.appendChild(text);\n\n if (snippet.rating !== undefined && String(snippet.rating).length > 0) {\n const rating = document.createElement('div');\n rating.className = 'gengage-chat-review-snippet-rating';\n rating.textContent = `\\u2605 ${String(snippet.rating)}`;\n line.appendChild(rating);\n }\n\n snippets.appendChild(line);\n }\n\n detailCard.appendChild(snippets);\n };\n\n const refreshSubjectSelection = (): void => {\n for (const node of subjectRow.querySelectorAll<HTMLElement>('.gengage-chat-review-subject')) {\n const isActive = node.dataset['subjectKey'] === activeKey;\n node.classList.toggle('gengage-chat-review-subject--active', isActive);\n node.classList.toggle('gds-chip-active', isActive);\n node.setAttribute('aria-pressed', String(isActive));\n }\n };\n\n for (const subject of subjects) {\n const tone = subjectTone(subject);\n const chip = document.createElement('button');\n chip.type = 'button';\n chip.className = 'gengage-chat-review-subject gds-chip';\n chip.dataset['subjectKey'] = subject.key;\n chip.dataset['tone'] = tone;\n\n const icon = document.createElement('span');\n icon.className = 'gengage-chat-review-subject-icon';\n icon.appendChild(subjectToneIcon(tone));\n chip.appendChild(icon);\n\n const label = document.createElement('span');\n label.className = 'gengage-chat-review-subject-label';\n label.textContent = subject.label;\n chip.appendChild(label);\n\n const count = document.createElement('span');\n count.className = 'gengage-chat-review-subject-count';\n count.textContent = `(${subject.mentions})`;\n chip.appendChild(count);\n\n chip.addEventListener('click', () => {\n activeKey = subject.key;\n refreshSubjectSelection();\n renderDetail();\n });\n\n subjectRow.appendChild(chip);\n }\n\n container.appendChild(subjectRow);\n refreshSubjectSelection();\n renderDetail();\n container.appendChild(detailCard);\n return container;\n}\n","/**\n * Shared product rendering utilities.\n *\n * Extracted from chat/renderUISpec and simrel/ProductCard to eliminate\n * duplication and provide consistent behavior across all widgets.\n */\n\n/** Clamp a rating value to the 0–5 range. Returns 0 for NaN/non-finite. */\nexport function clampRating(value: number): number {\n if (!Number.isFinite(value)) return 0;\n return Math.max(0, Math.min(5, value));\n}\n\n/** Clamp a discount percentage to the 0–100 range, rounded to integer. Returns 0 for NaN/non-finite. */\nexport function clampDiscount(value: number): number {\n if (!Number.isFinite(value)) return 0;\n return Math.max(0, Math.min(100, Math.round(value)));\n}\n\n/**\n * Render a star rating string with full, half, and empty stars.\n *\n * @param rating - A numeric rating (will be clamped to 0–5).\n * @param halfStars - Whether to render half-star characters. Defaults to true.\n * @returns A string like \"★★★½☆\" or \"★★★☆☆\" (without half-stars).\n */\nexport function renderStarRating(rating: number, halfStars: boolean = true): string {\n const clamped = clampRating(rating);\n if (halfStars) {\n const full = Math.floor(clamped);\n const half = clamped - full >= 0.5 ? 1 : 0;\n const empty = 5 - full - half;\n return '\\u2605'.repeat(full) + (half ? '\\u00BD' : '') + '\\u2606'.repeat(empty);\n }\n const rounded = Math.round(clamped);\n return '\\u2605'.repeat(rounded) + '\\u2606'.repeat(5 - rounded);\n}\n\n/**\n * Create a star rating DOM element with proper half-filled star rendering.\n *\n * Uses a CSS-clipped full star overlaid on an empty star for the half-star,\n * giving a visually accurate half-filled appearance instead of the \"½\" character.\n *\n * @param rating - A numeric rating (will be clamped to 0–5).\n * @returns An HTMLSpanElement containing the star icons.\n */\nexport function createStarRatingElement(rating: number): HTMLSpanElement {\n const clamped = clampRating(rating);\n const full = Math.floor(clamped);\n const hasHalf = clamped - full >= 0.5;\n const empty = 5 - full - (hasHalf ? 1 : 0);\n\n const container = document.createElement('span');\n container.className = 'gengage-star-rating';\n container.setAttribute('role', 'img');\n container.setAttribute('aria-label', `${clamped.toFixed(1)} out of 5 stars`);\n\n if (full > 0) {\n container.appendChild(document.createTextNode('\\u2605'.repeat(full)));\n }\n\n if (hasHalf) {\n const halfStar = document.createElement('span');\n halfStar.className = 'gengage-star-half';\n halfStar.textContent = '\\u2606';\n const filled = document.createElement('span');\n filled.textContent = '\\u2605';\n halfStar.appendChild(filled);\n container.appendChild(halfStar);\n }\n\n if (empty > 0) {\n container.appendChild(document.createTextNode('\\u2606'.repeat(empty)));\n }\n\n return container;\n}\n\n/**\n * Attach a one-time error handler that hides the image on load failure.\n *\n * Works with any HTMLImageElement. Hides the element by setting\n * `display: none` so layout doesn't break from broken images.\n */\nexport function addImageErrorHandler(img: HTMLImageElement): void {\n img.addEventListener(\n 'error',\n () => {\n img.style.display = 'none';\n },\n { once: true },\n );\n}\n","import type { ChatUISpecRenderContext, ProductPriceOriginalStyle } from '../types.js';\nimport { isSafeImageUrl } from '../../common/safe-html.js';\nimport { addImageErrorHandler } from '../../common/product-utils.js';\n\nexport function discountReasonFromProduct(product: Record<string, unknown> | undefined): string | undefined {\n if (!product) return undefined;\n const raw =\n product['discountReason'] ?? product['discount_reason'] ?? product['campaignReason'] ?? product['campaign_reason'];\n if (typeof raw !== 'string') return undefined;\n const t = raw.trim();\n return t.length > 0 ? t : undefined;\n}\n\nexport function campaignReasonForDisplay(\n ctx: ChatUISpecRenderContext,\n product: Record<string, unknown> | undefined,\n): string | undefined {\n if (ctx.productPriceUi?.showCampaignReason !== true) return undefined;\n return discountReasonFromProduct(product);\n}\n\n/**\n * Per-product `originalPriceStyle` / `price_original_style` override config.\n * Default `strikethrough`; `inline` uses a separator row without a strike line.\n */\nexport function resolveOriginalPriceStyle(\n ctx: ChatUISpecRenderContext,\n product: Record<string, unknown> | undefined,\n): ProductPriceOriginalStyle {\n const fromProduct = product?.['originalPriceStyle'] ?? product?.['price_original_style'];\n if (fromProduct === 'inline' || fromProduct === 'strikethrough') return fromProduct;\n const fromConfig = ctx.productPriceUi?.originalPriceStyle;\n if (fromConfig === 'inline' || fromConfig === 'strikethrough') return fromConfig;\n return 'strikethrough';\n}\n\nexport function createCampaignReasonElement(text: string): HTMLDivElement {\n const el = document.createElement('div');\n el.className = 'gengage-chat-campaign-reason';\n el.textContent = text;\n return el;\n}\n\nexport function resolveCampaignBadgeLogoUrl(\n ctx: ChatUISpecRenderContext,\n product: Record<string, unknown> | undefined,\n): string | undefined {\n const keys = ['campaignReasonLogoUrl', 'campaign_reason_logo_url', 'discountBadgeLogoUrl', 'discount_badge_logo_url'];\n for (const k of keys) {\n const v = product?.[k];\n if (typeof v === 'string' && v.trim()) {\n const u = v.trim();\n if (isSafeImageUrl(u)) return u;\n }\n }\n const fromConfig = ctx.productPriceUi?.campaignBadgeLogoUrl;\n if (typeof fromConfig === 'string' && fromConfig.trim()) {\n const u = fromConfig.trim();\n if (isSafeImageUrl(u)) return u;\n }\n return undefined;\n}\n\n/**\n * Single bordered badge: optional logo, campaign line (body text), sale price — gradient border, transparent fill.\n */\nexport function createCampaignPriceBadge(options: {\n reasonText: string;\n salePriceFormatted: string;\n logoUrl?: string;\n}): HTMLElement {\n const badge = document.createElement('div');\n badge.className = 'gengage-chat-campaign-price-badge';\n badge.dataset['gengagePart'] = 'campaign-price-badge';\n\n if (options.logoUrl) {\n const logoWrap = document.createElement('div');\n logoWrap.className = 'gengage-chat-campaign-price-badge__logo';\n const img = document.createElement('img');\n img.alt = '';\n img.loading = 'lazy';\n // logoUrl is validated by isSafeImageUrl() in resolveCampaignBadgeLogoUrl before reaching here.\n img.src = options.logoUrl;\n addImageErrorHandler(img);\n logoWrap.appendChild(img);\n badge.appendChild(logoWrap);\n }\n\n const body = document.createElement('div');\n body.className = 'gengage-chat-campaign-price-badge__body';\n\n const reason = document.createElement('div');\n reason.className = 'gengage-chat-campaign-price-badge__reason';\n reason.textContent = options.reasonText;\n\n const sale = document.createElement('span');\n sale.className = 'gengage-chat-campaign-price-badge__sale';\n sale.textContent = options.salePriceFormatted;\n\n body.appendChild(reason);\n body.appendChild(sale);\n badge.appendChild(body);\n return badge;\n}\n","/**\n * AI Top Picks renderer.\n *\n * DOM/layout parity with robot-engine-lean MainPane/AIAnalysisZone TopPicksResults:\n * article: role badge, image, title, rating, price, labels, hover CTA.\n */\n\nimport type { UIElement, ActionPayload } from '../../common/types.js';\nimport type { ChatUISpecRenderContext } from '../types.js';\nimport { formatPrice } from '../../common/price-formatter.js';\nimport { isSafeImageUrl, safeSetAttribute } from '../../common/safe-html.js';\nimport { addImageErrorHandler } from '../../common/product-utils.js';\nimport {\n campaignReasonForDisplay,\n createCampaignPriceBadge,\n createCampaignReasonElement,\n resolveCampaignBadgeLogoUrl,\n resolveOriginalPriceStyle,\n} from './product-price-layout.js';\n\ninterface SentimentLabel {\n label: string;\n sentiment?: 'positive' | 'negative' | 'neutral';\n}\n\ninterface AITopPickItem {\n product: Record<string, unknown>;\n role?: string;\n reason?: string;\n labels?: SentimentLabel[];\n expertQualityScore?: number;\n reviewHighlight?: string;\n action?: ActionPayload;\n}\n\nfunction resolveActionSku(item: AITopPickItem): string | null {\n const productSku = item.product['sku'];\n if (typeof productSku === 'string' && productSku.length > 0) return productSku;\n const payload = item.action?.payload;\n if (payload && typeof payload === 'object' && 'sku' in payload && typeof payload.sku === 'string') {\n return payload.sku;\n }\n return null;\n}\n\nconst ROLE_LABELS: Record<string, string> = {\n winner: 'roleWinner',\n best_value: 'roleBestValue',\n best_alternative: 'roleBestAlternative',\n};\n\nfunction getRoleLabel(role: string | undefined, i18n: ChatUISpecRenderContext['i18n']): string | null {\n if (!role || !i18n) return null;\n const key = ROLE_LABELS[role];\n if (!key) return role;\n return (i18n as Record<string, string>)[key] ?? role;\n}\n\nfunction renderRatingRow(product: Record<string, unknown>): HTMLElement | null {\n const raw = product['rating'];\n const num = typeof raw === 'number' ? raw : typeof raw === 'string' ? parseFloat(raw) : NaN;\n if (Number.isNaN(num) || num <= 0) return null;\n const row = document.createElement('div');\n row.className = 'gengage-chat-ai-toppick-rating';\n row.dataset['gengagePart'] = 'ai-top-pick-rating';\n const icon = document.createElement('span');\n icon.className = 'gengage-chat-ai-toppick-rating-icon';\n icon.setAttribute('aria-hidden', 'true');\n icon.innerHTML =\n '<svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 3.6l2.58 5.23 5.77.84-4.17 4.07.98 5.75L12 16.78l-5.16 2.71.99-5.75L3.66 9.67l5.76-.84L12 3.6z\"/></svg>';\n const value = document.createElement('span');\n value.className = 'gengage-chat-ai-toppick-rating-value';\n value.textContent = num.toFixed(1);\n row.appendChild(icon);\n row.appendChild(value);\n return row;\n}\n\n/** Image-focused media with the same lightweight hover actions as listing cards. */\nfunction appendTopPickMedia(\n item: AITopPickItem,\n alt: string,\n target: HTMLElement,\n ctx: ChatUISpecRenderContext,\n options?: { skipOverlayActions?: boolean },\n): void {\n const media = document.createElement('div');\n media.className = 'gengage-chat-ai-toppick-media';\n media.dataset['gengagePart'] = 'ai-top-pick-media';\n\n const product = item.product;\n const imageUrl = product['imageUrl'] as string | undefined;\n if (imageUrl && isSafeImageUrl(imageUrl)) {\n const img = document.createElement('img');\n img.className = 'gengage-chat-ai-toppick-img';\n img.dataset['gengagePart'] = 'ai-top-pick-image';\n safeSetAttribute(img, 'src', imageUrl);\n img.loading = 'lazy';\n img.alt = alt;\n addImageErrorHandler(img);\n media.appendChild(img);\n }\n\n const sku = resolveActionSku(item);\n if (sku && !options?.skipOverlayActions) {\n const imgActions = document.createElement('div');\n imgActions.className = 'gengage-chat-product-card-img-actions';\n\n if (ctx.onFavoriteToggle) {\n const heart = document.createElement('button');\n heart.className = 'gengage-chat-favorite-btn';\n heart.type = 'button';\n heart.dataset.gengageFavoriteSku = sku;\n heart.setAttribute('aria-label', ctx.i18n?.addToFavoritesLabel ?? 'Add to favorites');\n const isFav = ctx.favoritedSkus?.has(sku) ?? false;\n if (isFav) heart.classList.add('gengage-chat-favorite-btn--active');\n const svgFill = isFav ? 'currentColor' : 'none';\n heart.innerHTML = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"${svgFill}\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z\"/></svg>`;\n heart.addEventListener('click', (e) => {\n e.stopPropagation();\n heart.classList.toggle('gengage-chat-favorite-btn--active');\n heart\n .querySelector('svg')\n ?.setAttribute(\n 'fill',\n heart.classList.contains('gengage-chat-favorite-btn--active') ? 'currentColor' : 'none',\n );\n ctx.onFavoriteToggle?.(sku, product);\n });\n imgActions.appendChild(heart);\n }\n\n const findSimilarLabel = ctx.i18n?.findSimilarLabel ?? 'Find Similar';\n const pill = document.createElement('button');\n pill.className = 'gengage-chat-find-similar-pill';\n pill.type = 'button';\n pill.setAttribute('aria-label', findSimilarLabel);\n pill.dataset['tooltip'] = findSimilarLabel;\n pill.innerHTML =\n `<span class=\"gengage-chat-find-similar-pill-icon\" aria-hidden=\"true\">` +\n `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.25\" stroke-linecap=\"round\" stroke-linejoin=\"round\">` +\n `<circle cx=\"10.5\" cy=\"10.5\" r=\"6.5\"/>` +\n `<path d=\"M16 16l5.5 5.5\"/>` +\n `</svg></span>`;\n const pillText = document.createElement('span');\n pillText.className = 'gengage-chat-find-similar-pill-text';\n pillText.textContent = findSimilarLabel;\n pill.appendChild(pillText);\n pill.addEventListener('click', (e) => {\n e.stopPropagation();\n ctx.onAction({\n title: findSimilarLabel,\n type: 'findSimilar',\n payload: { sku, ...(imageUrl ? { image_url: imageUrl } : {}) },\n });\n });\n imgActions.appendChild(pill);\n\n media.appendChild(imgActions);\n }\n\n target.appendChild(media);\n}\n\nfunction appendPriceRow(product: Record<string, unknown>, body: HTMLElement, ctx: ChatUISpecRenderContext): void {\n const price = product['price'] as string | undefined;\n const originalPrice = product['originalPrice'] as string | undefined;\n if (!price) return;\n\n const campaignReason = campaignReasonForDisplay(ctx, product);\n const listStyle = resolveOriginalPriceStyle(ctx, product);\n const hasDiscount = !!(originalPrice && originalPrice !== price);\n const frameDiscounted = !!(campaignReason && hasDiscount);\n const logoUrl = resolveCampaignBadgeLogoUrl(ctx, product);\n\n const priceRow = document.createElement('div');\n priceRow.className = 'gengage-chat-ai-toppick-price';\n priceRow.dataset['gengagePart'] = 'ai-top-pick-price';\n\n if (frameDiscounted) {\n const badge = createCampaignPriceBadge({\n reasonText: campaignReason!,\n salePriceFormatted: formatPrice(price, ctx.pricing),\n ...(logoUrl !== undefined ? { logoUrl } : {}),\n });\n if (hasDiscount && listStyle === 'inline') {\n priceRow.classList.add('gengage-chat-ai-toppick-price--inline');\n priceRow.appendChild(badge);\n const sep = document.createElement('span');\n sep.className = 'gengage-chat-ai-toppick-price-sep';\n sep.setAttribute('aria-hidden', 'true');\n const orig = document.createElement('span');\n orig.className = 'gengage-chat-ai-toppick-original-price';\n orig.textContent = formatPrice(originalPrice!, ctx.pricing);\n priceRow.appendChild(sep);\n priceRow.appendChild(orig);\n } else {\n const orig = document.createElement('span');\n orig.className = 'gengage-chat-ai-toppick-original-price';\n orig.textContent = formatPrice(originalPrice!, ctx.pricing);\n priceRow.appendChild(orig);\n priceRow.appendChild(document.createTextNode(' '));\n priceRow.appendChild(badge);\n }\n body.appendChild(priceRow);\n return;\n }\n\n if (hasDiscount && listStyle === 'inline') {\n priceRow.classList.add('gengage-chat-ai-toppick-price--inline');\n const cur = document.createElement('span');\n cur.className = 'gengage-chat-ai-toppick-price-current';\n cur.textContent = formatPrice(price, ctx.pricing);\n const sep = document.createElement('span');\n sep.className = 'gengage-chat-ai-toppick-price-sep';\n sep.setAttribute('aria-hidden', 'true');\n const orig = document.createElement('span');\n orig.className = 'gengage-chat-ai-toppick-original-price';\n orig.textContent = formatPrice(originalPrice!, ctx.pricing);\n priceRow.appendChild(cur);\n priceRow.appendChild(sep);\n priceRow.appendChild(orig);\n } else if (hasDiscount) {\n const orig = document.createElement('span');\n orig.className = 'gengage-chat-ai-toppick-original-price';\n orig.textContent = formatPrice(originalPrice!, ctx.pricing);\n priceRow.appendChild(orig);\n priceRow.appendChild(document.createTextNode(' '));\n const current = document.createElement('span');\n current.className = 'gengage-chat-ai-toppick-price-current';\n current.textContent = formatPrice(price, ctx.pricing);\n priceRow.appendChild(current);\n } else {\n const current = document.createElement('span');\n current.className = 'gengage-chat-ai-toppick-price-current';\n current.textContent = formatPrice(price, ctx.pricing);\n priceRow.appendChild(current);\n }\n\n if (campaignReason) {\n const stack = document.createElement('div');\n stack.className = 'gengage-chat-ai-toppick-price-stack';\n stack.appendChild(createCampaignReasonElement(campaignReason));\n stack.appendChild(priceRow);\n body.appendChild(stack);\n } else {\n body.appendChild(priceRow);\n }\n}\n\nfunction appendWinnerEvidence(item: AITopPickItem, body: HTMLElement): void {\n const reason = typeof item.reason === 'string' ? item.reason.trim() : '';\n const reviewHighlight = typeof item.reviewHighlight === 'string' ? item.reviewHighlight.trim() : '';\n if (!reason && !reviewHighlight) return;\n\n if (reason) {\n const reasonEl = document.createElement('p');\n reasonEl.className = 'gengage-chat-ai-toppick-reason';\n reasonEl.dataset['gengagePart'] = 'ai-top-pick-reason';\n reasonEl.textContent = reason;\n body.appendChild(reasonEl);\n }\n\n if (reviewHighlight) {\n const reviewEl = document.createElement('div');\n reviewEl.className = 'gengage-chat-ai-toppick-review';\n reviewEl.dataset['gengagePart'] = 'ai-top-pick-review';\n reviewEl.textContent = reviewHighlight;\n body.appendChild(reviewEl);\n }\n}\n\n/**\n * Single card layout matching lean TopPicksResults (all picks use the same structure).\n * `--winner` / `--compact` class names kept for tests (highlight vs secondary border).\n */\nfunction renderPickCard(item: AITopPickItem, ctx: ChatUISpecRenderContext, isWinner: boolean): HTMLElement {\n const card = document.createElement('div');\n card.className = isWinner\n ? 'gengage-chat-ai-toppick-card gengage-chat-ai-toppick-card--winner gds-card'\n : 'gengage-chat-ai-toppick-card gengage-chat-ai-toppick-card--compact gds-card';\n card.dataset['gengagePart'] = isWinner ? 'ai-top-pick-card-winner' : 'ai-top-pick-card';\n const product = item.product;\n const sku = resolveActionSku(item);\n const url = (product['url'] as string) ?? '';\n const cartCode = product['cartCode'] as string | undefined;\n const inStock = product['inStock'];\n const hasCart = !!(sku && cartCode && inStock !== false);\n const action = item.action;\n if (sku && ctx.onProductClick) {\n card.classList.add('gds-clickable');\n card.addEventListener('click', (e) => {\n if ((e.target as HTMLElement).closest('.gengage-chat-ai-toppick-cta')) return;\n if ((e.target as HTMLElement).closest('.gengage-chat-favorite-btn')) return;\n if ((e.target as HTMLElement).closest('.gengage-chat-find-similar-pill')) return;\n ctx.onProductClick?.({ sku, url });\n });\n } else if (action) {\n card.classList.add('gds-clickable');\n card.addEventListener('click', (e) => {\n if ((e.target as HTMLElement).closest('.gengage-chat-ai-toppick-cta')) return;\n if ((e.target as HTMLElement).closest('.gengage-chat-favorite-btn')) return;\n if ((e.target as HTMLElement).closest('.gengage-chat-find-similar-pill')) return;\n ctx.onAction(action);\n });\n }\n const alt = (product['name'] as string) || 'Product image';\n const compactMobile = !isWinner && ctx.isMobile === true;\n\n /* Lean: span.absolute.-top-2.5.left-3 — no wrapper */\n const roleLabel = isWinner\n ? (getRoleLabel(item.role, ctx.i18n) ?? ctx.i18n?.roleWinner ?? 'TOP MATCH')\n : getRoleLabel(item.role, ctx.i18n);\n if (roleLabel && !compactMobile) {\n const badge = document.createElement('span');\n badge.className = 'gengage-chat-ai-toppick-badge gds-badge';\n badge.dataset['gengagePart'] = 'ai-top-pick-role-badge';\n badge.textContent = roleLabel;\n card.appendChild(badge);\n }\n\n const topRow = document.createElement('div');\n topRow.className = 'gengage-chat-ai-toppick-top-row';\n topRow.dataset['gengagePart'] = 'ai-top-pick-top-row';\n appendTopPickMedia(item, alt, topRow, ctx, { skipOverlayActions: compactMobile });\n\n const body = document.createElement('div');\n body.className = 'gengage-chat-ai-toppick-body';\n body.dataset['gengagePart'] = 'ai-top-pick-body';\n\n if (roleLabel && compactMobile) {\n const roleLine = document.createElement('div');\n roleLine.className = 'gengage-chat-ai-toppick-role-line';\n roleLine.dataset['gengagePart'] = 'ai-top-pick-role-line';\n roleLine.textContent = roleLabel;\n body.appendChild(roleLine);\n }\n\n const name = product['name'] as string | undefined;\n if (name) {\n const nameEl = document.createElement('div');\n nameEl.className = 'gengage-chat-ai-toppick-name';\n nameEl.dataset['gengagePart'] = 'ai-top-pick-name';\n nameEl.textContent = name;\n body.appendChild(nameEl);\n }\n\n const ratingRow = renderRatingRow(product);\n if (ratingRow) body.appendChild(ratingRow);\n\n /* Lean order: price before labels */\n appendPriceRow(product, body, ctx);\n\n if (item.labels && item.labels.length > 0) {\n body.appendChild(renderSentimentChips(item.labels));\n }\n\n topRow.appendChild(body);\n card.appendChild(topRow);\n\n const detail = document.createElement('div');\n detail.className = 'gengage-chat-ai-toppick-detail';\n detail.dataset['gengagePart'] = 'ai-top-pick-detail';\n if (isWinner) {\n appendWinnerEvidence(item, detail);\n } else {\n const snippet = typeof item.reason === 'string' ? item.reason.trim() : '';\n if (snippet) {\n const snippetEl = document.createElement('p');\n snippetEl.className = 'gengage-chat-ai-toppick-snippet';\n snippetEl.dataset['gengagePart'] = 'ai-top-pick-snippet';\n snippetEl.textContent = snippet;\n detail.appendChild(snippetEl);\n }\n }\n if (detail.childNodes.length > 0) {\n card.appendChild(detail);\n }\n\n const wantSpinner = !!(sku && ctx.topPicksLoadingSku === sku);\n const showCta = (hasCart || action) && !compactMobile;\n\n if (wantSpinner || hasCart || action) {\n const spinner = document.createElement('div');\n spinner.className = 'gengage-chat-ai-toppick-spinner';\n spinner.dataset['gengagePart'] = 'ai-top-pick-spinner';\n spinner.style.display = wantSpinner ? '' : 'none';\n card.appendChild(spinner);\n\n if (showCta) {\n const cta = document.createElement('button');\n cta.className = 'gengage-chat-ai-toppick-cta gds-btn gds-btn-primary';\n cta.dataset['gengagePart'] = 'ai-top-pick-cta';\n cta.type = 'button';\n cta.textContent = hasCart\n ? (ctx.i18n?.addToCartButton ?? 'Add to Cart')\n : (ctx.i18n?.viewDetails ?? 'View Details');\n cta.addEventListener('click', (e) => {\n e.stopPropagation();\n if (hasCart) {\n ctx.onAction({\n title: ctx.i18n?.addToCartButton ?? 'Add to Cart',\n type: 'addToCart',\n payload: { sku: sku!, cartCode: cartCode!, quantity: 1 },\n });\n return;\n }\n if (!action) return;\n if (action.type === 'findSimilar' && sku && ctx.onProductClick) {\n ctx.onProductClick({ sku, url });\n return;\n }\n ctx.onAction(action);\n });\n card.appendChild(cta);\n }\n }\n\n return card;\n}\n\nexport function renderAITopPicks(element: UIElement, ctx: ChatUISpecRenderContext): HTMLElement {\n const container = document.createElement('div');\n container.className = 'gengage-chat-ai-top-picks';\n container.dataset['gengagePart'] = 'ai-top-picks';\n\n const suggestions = (element.props?.['suggestions'] ?? []) as AITopPickItem[];\n if (suggestions.length === 0) return container;\n\n const title = document.createElement('h3');\n title.className = 'gengage-chat-ai-top-picks-title';\n title.dataset['gengagePart'] = 'ai-top-picks-title';\n title.textContent = ctx.i18n?.aiTopPicksTitle ?? 'Top Picks';\n container.appendChild(title);\n\n const scrollRow = document.createElement('div');\n scrollRow.className = 'gengage-chat-ai-top-picks-scroll';\n scrollRow.dataset['gengagePart'] = 'ai-top-picks-scroll';\n\n const first = suggestions[0]!;\n /* First suggestion always uses winner/highlight card chrome (parity: i === 0 in old loop). */\n scrollRow.appendChild(renderPickCard(first, ctx, true));\n\n if (suggestions.length > 1) {\n const rest = document.createElement('div');\n rest.className = 'gengage-chat-ai-top-picks-rest';\n rest.dataset['gengagePart'] = 'ai-top-picks-rest';\n for (let i = 1; i < suggestions.length; i++) {\n const suggestion = suggestions[i]!;\n const isWinner = suggestion.role === 'winner';\n rest.appendChild(renderPickCard(suggestion, ctx, isWinner));\n }\n scrollRow.appendChild(rest);\n }\n\n container.appendChild(scrollRow);\n return container;\n}\n\nfunction renderSentimentChips(labels: SentimentLabel[]): HTMLElement {\n const container = document.createElement('div');\n container.className = 'gengage-chat-ai-toppick-labels';\n container.dataset['gengagePart'] = 'ai-top-pick-labels';\n for (const label of labels) {\n const chip = document.createElement('span');\n chip.className = 'gengage-chat-ai-toppick-label gds-chip';\n chip.dataset['gengagePart'] = 'ai-top-pick-label';\n chip.dataset['sentiment'] = label.sentiment ?? 'neutral';\n chip.textContent = label.label;\n container.appendChild(chip);\n }\n return container;\n}\n","/**\n * Grounding Review Card renderer.\n *\n * Renders a clickable card for review grounding data with title,\n * review count, and a CTA arrow. The entire card is clickable.\n *\n * XSS safety: All text is set via textContent. No innerHTML.\n */\n\nimport type { UIElement, ActionPayload } from '../../common/types.js';\nimport type { ChatUISpecRenderContext } from '../types.js';\n\nimport { createLucideIcon } from '../utils/ui.js';\n\nexport function renderGroundingReviewCard(element: UIElement, ctx: ChatUISpecRenderContext): HTMLElement {\n const container = document.createElement('div');\n container.className = 'gengage-chat-grounding-review gds-evidence-card';\n container.dataset['gengagePart'] = 'grounding-review-card';\n\n const props = element.props ?? {};\n const title = props['title'] as string | undefined;\n const reviewCount = props['reviewCount'] as string | undefined;\n const action = props['action'] as ActionPayload | undefined;\n const ctaLabel = ctx.i18n?.groundingReviewCta ?? 'Read Reviews';\n\n // Icon\n const icon = document.createElement('span');\n icon.className = 'gengage-chat-grounding-review-icon';\n icon.appendChild(\n createLucideIcon(['M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z', 'M8 9h8', 'M8 13h6']),\n );\n container.appendChild(icon);\n\n const body = document.createElement('div');\n body.className = 'gengage-chat-grounding-review-body';\n body.dataset['gengagePart'] = 'grounding-review-body';\n\n // Title\n const titleEl = document.createElement('div');\n titleEl.className = 'gengage-chat-grounding-review-title';\n titleEl.dataset['gengagePart'] = 'grounding-review-title';\n titleEl.textContent = title ?? ctx.i18n?.customerReviewsTitle ?? 'Customer Reviews';\n body.appendChild(titleEl);\n\n // Subtitle (review count)\n if (reviewCount) {\n const subtitle = document.createElement('div');\n subtitle.className = 'gengage-chat-grounding-review-subtitle';\n subtitle.dataset['gengagePart'] = 'grounding-review-subtitle';\n const template = ctx.i18n?.groundingReviewSubtitle ?? '{count} yorum mevcut';\n subtitle.textContent = template.replace('{count}', reviewCount);\n body.appendChild(subtitle);\n }\n\n container.appendChild(body);\n\n // CTA arrow\n const cta = document.createElement('span');\n cta.className = 'gengage-chat-grounding-review-cta';\n cta.dataset['gengagePart'] = 'grounding-review-cta';\n const ctaLabelEl = document.createElement('span');\n ctaLabelEl.textContent = ctaLabel;\n cta.appendChild(ctaLabelEl);\n const ctaIcon = document.createElement('span');\n ctaIcon.className = 'gengage-chat-grounding-review-cta-icon';\n ctaIcon.appendChild(createLucideIcon(['M5 12h14', 'M15 8l4 4-4 4'], 14));\n cta.appendChild(ctaIcon);\n container.appendChild(cta);\n\n // Make entire card clickable\n if (action) {\n container.classList.add('gds-clickable');\n container.setAttribute('role', 'button');\n container.setAttribute('tabindex', '0');\n container.addEventListener('click', () => ctx.onAction(action));\n container.addEventListener('keydown', (e: KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n ctx.onAction(action);\n }\n });\n }\n\n return container;\n}\n","/**\n * AI Grouping Cards renderer.\n *\n * Desktop: compact card with image, name, and labels.\n * Mobile: simple button list with arrow prefix (images hidden via CSS).\n *\n * Note: The media-rail layout is similar to `AISuggestedSearchCards` but with different tokens;\n * a shared primitive may be extracted later if the visual system stabilizes.\n *\n * XSS safety: All text is set via textContent. Image URLs are validated\n * for safe protocols. No innerHTML.\n */\n\nimport type { UIElement, ActionPayload } from '../../common/types.js';\nimport type { ChatUISpecRenderContext } from '../types.js';\nimport { isSafeImageUrl } from '../../common/safe-html.js';\n\ninterface GroupingEntry {\n name: string;\n image?: string;\n description?: string;\n labels?: string[];\n action: ActionPayload;\n}\n\nfunction normalizeGroupingAction(entry: GroupingEntry): ActionPayload {\n if (entry.action.type !== 'findSimilar') return entry.action;\n const payload =\n entry.action.payload && typeof entry.action.payload === 'object'\n ? (entry.action.payload as Record<string, unknown>)\n : null;\n const text =\n (typeof payload?.['input'] === 'string' && payload['input'].trim()) ||\n (typeof payload?.['text'] === 'string' && payload['text'].trim()) ||\n entry.name.trim();\n if (!text) return entry.action;\n\n const normalizedPayload: Record<string, unknown> = {\n text,\n is_suggested_text: 1,\n };\n if (typeof payload?.['sku'] === 'string' && payload['sku'].trim()) {\n normalizedPayload['sku'] = payload['sku'];\n }\n if (Array.isArray(payload?.['group_skus'])) {\n const groupSkus = payload['group_skus'].filter((sku): sku is string => typeof sku === 'string' && sku.length > 0);\n if (groupSkus.length > 0) normalizedPayload['group_skus'] = groupSkus;\n }\n return {\n title: entry.action.title,\n type: 'inputText',\n payload: normalizedPayload,\n };\n}\n\nexport function renderAIGroupingCards(element: UIElement, ctx: ChatUISpecRenderContext): HTMLElement {\n const container = document.createElement('div');\n container.className = 'gengage-chat-grouping-cards';\n container.dataset['gengagePart'] = 'ai-grouping-cards';\n\n const entries = (element.props?.['entries'] ?? []) as GroupingEntry[];\n if (entries.length === 0) return container;\n\n const customTitle = element.props?.['sectionTitle'];\n const sectionTitle =\n typeof customTitle === 'string' && customTitle.trim().length > 0\n ? customTitle.trim()\n : ctx.i18n?.aiBrowseCategoriesTitle;\n if (sectionTitle) {\n const heading = document.createElement('h3');\n heading.className = 'gengage-chat-grouping-section-title';\n heading.textContent = sectionTitle;\n container.appendChild(heading);\n }\n\n const scrollRow = document.createElement('div');\n scrollRow.className = 'gengage-chat-grouping-cards-scroll';\n scrollRow.dataset['gengagePart'] = 'ai-grouping-cards-scroll';\n\n for (const entry of entries) {\n const card = document.createElement('div');\n card.className = 'gengage-chat-grouping-card gds-card';\n card.dataset['gengagePart'] = 'ai-grouping-card';\n card.classList.add('gds-clickable');\n card.addEventListener('click', () => ctx.onAction(normalizeGroupingAction(entry)));\n\n // Image — intrinsic size; CSS sets panel vs chat dimensions\n if (entry.image && isSafeImageUrl(entry.image)) {\n const img = document.createElement('img');\n img.className = 'gengage-chat-grouping-card-img';\n img.dataset['gengagePart'] = 'ai-grouping-card-image';\n img.src = entry.image;\n img.alt = entry.name;\n img.width = 64;\n img.height = 64;\n card.appendChild(img);\n }\n\n const body = document.createElement('div');\n body.className = 'gengage-chat-grouping-card-body';\n body.dataset['gengagePart'] = 'ai-grouping-card-body';\n\n const nameEl = document.createElement('span');\n nameEl.className = 'gengage-chat-grouping-card-name';\n nameEl.dataset['gengagePart'] = 'ai-grouping-card-name';\n nameEl.textContent = entry.name;\n body.appendChild(nameEl);\n\n if (entry.labels && entry.labels.length > 0) {\n const labelsEl = document.createElement('div');\n labelsEl.className = 'gengage-chat-grouping-card-labels';\n labelsEl.dataset['gengagePart'] = 'ai-grouping-card-labels';\n for (const label of entry.labels.slice(0, 2)) {\n const chip = document.createElement('span');\n chip.className = 'gengage-chat-grouping-card-label gds-chip';\n chip.textContent = label;\n labelsEl.appendChild(chip);\n }\n body.appendChild(labelsEl);\n }\n\n card.appendChild(body);\n\n // Mobile prefix arrow (shown via CSS only on mobile)\n const arrow = document.createElement('span');\n arrow.className = 'gengage-chat-grouping-card-arrow';\n arrow.textContent = '\\u21B3';\n card.insertBefore(arrow, card.firstChild);\n\n scrollRow.appendChild(card);\n }\n\n container.appendChild(scrollRow);\n\n return container;\n}\n","/**\n * AI Suggested Search Cards renderer.\n *\n * Cards with a larger image and a single clear title line.\n * Secondary / tertiary supporting lines are intentionally omitted in chat so\n * the module reads as a compact, visual category chooser.\n *\n * XSS safety: All text is set via textContent. Image URLs are validated\n * for safe protocols. No innerHTML.\n */\n\nimport type { UIElement, ActionPayload } from '../../common/types.js';\nimport type { ChatUISpecRenderContext } from '../types.js';\nimport { isSafeImageUrl } from '../../common/safe-html.js';\n\ninterface SuggestedSearchEntry {\n shortName: string;\n image?: string;\n action: ActionPayload;\n}\n\nexport function renderAISuggestedSearchCards(element: UIElement, ctx: ChatUISpecRenderContext): HTMLElement {\n const container = document.createElement('div');\n container.className = 'gengage-chat-suggested-search-cards';\n container.dataset['gengagePart'] = 'ai-suggested-search-cards';\n\n const entries = (element.props?.['entries'] ?? []) as SuggestedSearchEntry[];\n if (entries.length === 0) return container;\n\n for (const entry of entries) {\n const card = document.createElement('div');\n card.className = 'gengage-chat-suggested-search-card gds-card';\n card.dataset['gengagePart'] = 'ai-suggested-search-card';\n card.classList.add('gds-clickable');\n card.addEventListener('click', () => ctx.onAction(entry.action));\n\n // Image\n if (entry.image && isSafeImageUrl(entry.image)) {\n const img = document.createElement('img');\n img.className = 'gengage-chat-suggested-search-card-img';\n img.dataset['gengagePart'] = 'ai-suggested-search-card-image';\n img.src = entry.image;\n img.alt = entry.shortName;\n img.width = 64;\n img.height = 64;\n card.appendChild(img);\n }\n\n const body = document.createElement('div');\n body.className = 'gengage-chat-suggested-search-card-body';\n body.dataset['gengagePart'] = 'ai-suggested-search-card-body';\n\n const nameEl = document.createElement('div');\n nameEl.className = 'gengage-chat-suggested-search-card-name';\n nameEl.dataset['gengagePart'] = 'ai-suggested-search-card-name';\n nameEl.textContent = entry.shortName;\n body.appendChild(nameEl);\n\n card.appendChild(body);\n container.appendChild(card);\n }\n\n return container;\n}\n","/**\n * Slim bottom-docked comparison bar for the main panel.\n *\n * Keeps compare mode visible without the oversized floating prompt.\n */\n\nimport type { ChatUISpecRenderContext } from '../types.js';\n\nexport function renderFloatingComparisonButton(selectedSkus: string[], ctx: ChatUISpecRenderContext): HTMLElement {\n const canCompare = selectedSkus.length >= 2;\n const label = ctx.i18n?.compareSelected ?? 'Compare';\n const warning = ctx.comparisonSelectionWarning;\n\n const dock = document.createElement('div');\n dock.className = 'gengage-chat-comparison-floating-btn';\n dock.dataset['gengagePart'] = 'comparison-dock';\n\n const summary = document.createElement('div');\n summary.className = 'gengage-chat-comparison-floating-summary';\n\n const count = document.createElement('span');\n count.className = 'gengage-chat-comparison-floating-count';\n count.textContent = String(selectedSkus.length);\n summary.appendChild(count);\n\n const text = document.createElement('div');\n text.className = 'gengage-chat-comparison-floating-copy';\n\n const title = document.createElement('div');\n title.className = 'gengage-chat-comparison-floating-title';\n title.textContent = canCompare\n ? `${label} (${selectedSkus.length})`\n : (ctx.i18n?.compareMinHint ?? 'Select at least 2 products');\n text.appendChild(title);\n\n if (warning) {\n const warningEl = document.createElement('div');\n warningEl.className = 'gengage-chat-comparison-floating-warning';\n warningEl.setAttribute('role', 'status');\n warningEl.setAttribute('aria-live', 'polite');\n warningEl.textContent = warning;\n text.appendChild(warningEl);\n }\n\n summary.appendChild(text);\n dock.appendChild(summary);\n\n const action = document.createElement('button');\n action.className = 'gengage-chat-comparison-floating-action gds-btn gds-btn-primary';\n action.type = 'button';\n action.textContent = label;\n action.disabled = !canCompare;\n if (!canCompare) action.classList.add('gengage-chat-comparison-floating-action--disabled');\n action.addEventListener('click', () => {\n if (!canCompare) return;\n ctx.onAction({\n title: label,\n type: 'getComparisonTable',\n payload: { sku_list: [...selectedSkus] },\n });\n });\n dock.appendChild(action);\n\n if (ctx.onToggleComparisonSku) {\n const close = document.createElement('button');\n close.className = 'gengage-chat-comparison-floating-close gds-btn gds-btn-ghost gds-icon-btn';\n close.dataset['gengagePart'] = 'comparison-dock-close';\n close.type = 'button';\n close.setAttribute('aria-label', ctx.i18n?.closeAriaLabel ?? 'Close');\n close.innerHTML =\n '<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" aria-hidden=\"true\"><path d=\"M18 6L6 18\"/><path d=\"M6 6l12 12\"/></svg>';\n close.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n ctx.onToggleComparisonSku?.('');\n });\n dock.appendChild(close);\n }\n\n return dock;\n}\n","/**\n * Renders a Pros & Cons list for a product.\n *\n * XSS safety: All text is set via textContent. No innerHTML.\n */\n\nexport function renderProsAndCons(element: { props?: Record<string, unknown> }): HTMLElement {\n const container = document.createElement('div');\n container.className = 'gengage-chat-pros-cons gds-card-soft';\n container.dataset['gengagePart'] = 'pros-cons';\n\n const productName = element.props?.['productName'] as string | undefined;\n if (productName) {\n const heading = document.createElement('h4');\n heading.className = 'gengage-chat-pros-cons-heading';\n heading.textContent = productName;\n container.appendChild(heading);\n }\n\n const pros = element.props?.['pros'] as string[] | undefined;\n const cons = element.props?.['cons'] as string[] | undefined;\n\n if (pros && pros.length > 0) {\n const prosList = document.createElement('ul');\n prosList.className = 'gengage-chat-pros-cons-list';\n prosList.dataset['gengagePart'] = 'pros-list';\n for (const pro of pros) {\n const li = document.createElement('li');\n li.className = 'gengage-chat-pros-cons-item';\n li.dataset['gengagePart'] = 'pros-item';\n const icon = document.createElement('span');\n icon.className = 'gengage-chat-pros-cons-icon gengage-chat-pros-cons-icon--pro';\n icon.textContent = '\\u2713';\n li.appendChild(icon);\n const text = document.createElement('span');\n text.textContent = pro;\n li.appendChild(text);\n prosList.appendChild(li);\n }\n container.appendChild(prosList);\n }\n\n if (cons && cons.length > 0) {\n const consList = document.createElement('ul');\n consList.className = 'gengage-chat-pros-cons-list';\n consList.dataset['gengagePart'] = 'cons-list';\n for (const con of cons) {\n const li = document.createElement('li');\n li.className = 'gengage-chat-pros-cons-item';\n li.dataset['gengagePart'] = 'cons-item';\n const icon = document.createElement('span');\n icon.className = 'gengage-chat-pros-cons-icon gengage-chat-pros-cons-icon--con';\n icon.textContent = '\\u2717';\n li.appendChild(icon);\n const text = document.createElement('span');\n text.textContent = con;\n li.appendChild(text);\n consList.appendChild(li);\n }\n container.appendChild(consList);\n }\n\n return container;\n}\n","/**\n * CategoriesContainer — renders grouped product lists with tab navigation\n * and optional filter tags.\n *\n * Backend sends `groupList` with `{ group_list: [{ group_name, product_list }], filter_tags }`.\n * The protocol adapter normalizes this into a CategoriesContainer UISpec element.\n *\n * XSS safety: All text is set via textContent. No innerHTML.\n */\n\nimport type { UIElement } from '../../common/types.js';\nimport type { ChatUISpecRenderContext } from '../types.js';\nimport type { NormalizedProduct } from '../../common/protocol-adapter.js';\nimport { isSafeImageUrl } from '../../common/safe-html.js';\nimport { formatPrice } from '../../common/price-formatter.js';\nimport { addImageErrorHandler } from '../../common/product-utils.js';\n\ninterface GroupData {\n groupName: string;\n products: NormalizedProduct[];\n}\n\ninterface FilterTagData {\n title: string;\n action?: { title: string; type: string; payload?: unknown };\n}\n\nexport function renderCategoriesContainer(element: UIElement, context: ChatUISpecRenderContext): HTMLElement {\n const groups = (element.props?.['groups'] as GroupData[] | undefined) ?? [];\n const filterTags = (element.props?.['filterTags'] as FilterTagData[] | undefined) ?? [];\n\n const container = document.createElement('div');\n container.className = 'gengage-chat-categories';\n container.dataset['gengagePart'] = 'categories-container';\n\n if (groups.length === 0) return container;\n\n // Tab bar — WAI-ARIA tablist pattern\n const tabBar = document.createElement('div');\n tabBar.className = 'gengage-chat-categories-tabs gds-toolbar';\n tabBar.dataset['gengagePart'] = 'categories-tab-bar';\n tabBar.setAttribute('role', 'tablist');\n\n const tabs: HTMLButtonElement[] = [];\n const panels: HTMLElement[] = [];\n\n const activateTab = (index: number): void => {\n for (let j = 0; j < tabs.length; j++) {\n const isActive = j === index;\n tabs[j]!.classList.toggle('gengage-chat-categories-tab--active', isActive);\n tabs[j]!.classList.toggle('is-active', isActive);\n tabs[j]!.setAttribute('aria-selected', String(isActive));\n tabs[j]!.tabIndex = isActive ? 0 : -1;\n panels[j]!.style.display = isActive ? '' : 'none';\n }\n };\n\n for (let i = 0; i < groups.length; i++) {\n const group = groups[i]!;\n const tabId = `gengage-cat-tab-${i}`;\n const panelId = `gengage-cat-panel-${i}`;\n\n // Tab button\n const tab = document.createElement('button');\n tab.className = 'gengage-chat-categories-tab gds-tab';\n tab.type = 'button';\n tab.dataset['gengagePart'] = 'categories-tab';\n tab.id = tabId;\n tab.setAttribute('role', 'tab');\n tab.setAttribute('aria-controls', panelId);\n tab.setAttribute('aria-selected', String(i === 0));\n tab.tabIndex = i === 0 ? 0 : -1;\n if (i === 0) tab.classList.add('gengage-chat-categories-tab--active', 'is-active');\n tab.textContent = group.groupName;\n\n tab.addEventListener('click', () => activateTab(i));\n tab.addEventListener('keydown', (e: KeyboardEvent) => {\n let next = -1;\n if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {\n next = (i + 1) % groups.length;\n } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {\n next = (i - 1 + groups.length) % groups.length;\n } else if (e.key === 'Home') {\n next = 0;\n } else if (e.key === 'End') {\n next = groups.length - 1;\n }\n if (next >= 0) {\n e.preventDefault();\n activateTab(next);\n tabs[next]!.focus();\n }\n });\n\n tabs.push(tab);\n tabBar.appendChild(tab);\n\n // Product grid panel\n const panel = document.createElement('div');\n panel.className = 'gengage-chat-categories-grid';\n panel.dataset['gengagePart'] = 'categories-panel';\n panel.id = panelId;\n panel.setAttribute('role', 'tabpanel');\n panel.setAttribute('aria-labelledby', tabId);\n if (i !== 0) panel.style.display = 'none';\n\n for (const product of group.products) {\n const card = renderCategoryProductCard(product, context);\n panel.appendChild(card);\n }\n\n panels.push(panel);\n }\n\n container.appendChild(tabBar);\n for (const panel of panels) container.appendChild(panel);\n\n // Filter tags\n if (filterTags.length > 0) {\n const tagsContainer = document.createElement('div');\n tagsContainer.className = 'gengage-chat-categories-filter-tags gds-toolbar';\n tagsContainer.dataset['gengagePart'] = 'categories-filter-tags';\n\n for (const tag of filterTags) {\n const tagBtn = document.createElement('button');\n tagBtn.className = 'gengage-chat-categories-filter-tag gds-chip';\n tagBtn.type = 'button';\n tagBtn.dataset['gengagePart'] = 'categories-filter-tag';\n tagBtn.textContent = tag.title;\n if (tag.action) {\n tagBtn.addEventListener('click', () => {\n context.onAction(tag.action!);\n });\n }\n tagsContainer.appendChild(tagBtn);\n }\n\n container.appendChild(tagsContainer);\n }\n\n return container;\n}\n\nfunction renderCategoryProductCard(product: NormalizedProduct, ctx: ChatUISpecRenderContext): HTMLElement {\n const card = document.createElement('div');\n card.className = 'gengage-chat-product-card gds-card gds-product-card gds-card-interactive';\n card.dataset['gengagePart'] = 'categories-product-card';\n\n if (product.imageUrl && isSafeImageUrl(product.imageUrl)) {\n const img = document.createElement('img');\n img.className = 'gengage-chat-product-card-img';\n img.src = product.imageUrl;\n img.alt = product.name;\n img.loading = 'lazy';\n addImageErrorHandler(img);\n card.appendChild(img);\n }\n\n const body = document.createElement('div');\n body.className = 'gengage-chat-product-card-body';\n\n const nameEl = document.createElement('div');\n nameEl.className = 'gengage-chat-product-card-name';\n nameEl.textContent = product.name;\n body.appendChild(nameEl);\n\n if (product.price) {\n const priceEl = document.createElement('div');\n priceEl.className = 'gengage-chat-product-card-price';\n priceEl.textContent = formatPrice(product.price, ctx.pricing);\n body.appendChild(priceEl);\n }\n\n card.appendChild(body);\n\n // Click → show product details\n if (ctx.onProductSelect || ctx.onAction) {\n card.classList.add('gds-clickable');\n card.addEventListener('click', () => {\n if (product.sku) {\n ctx.onAction({\n title: product.name,\n type: 'launchSingleProduct',\n payload: { sku: product.sku },\n });\n return;\n }\n ctx.onProductSelect?.(product as unknown as Record<string, unknown>);\n });\n }\n\n return card;\n}\n","/**\n * Renders a handoff notice when the backend escalates to a human agent.\n *\n * XSS safety: All text is set via textContent. No innerHTML.\n */\n\nimport type { ChatUISpecRenderContext } from '../types.js';\n\nexport function renderHandoffNotice(\n element: { props?: Record<string, unknown> },\n context: ChatUISpecRenderContext,\n): HTMLElement {\n const container = document.createElement('div');\n container.className = 'gengage-chat-handoff-notice gds-evidence-card gds-evidence-card-warning';\n container.dataset['gengagePart'] = 'handoff-notice';\n container.setAttribute('role', 'alert');\n\n const icon = document.createElement('span');\n icon.className = 'gengage-chat-handoff-notice-icon';\n icon.textContent = '\\u{1F464}'; // 👤\n icon.setAttribute('aria-hidden', 'true');\n container.appendChild(icon);\n\n const heading = document.createElement('h4');\n heading.className = 'gengage-chat-handoff-notice-heading';\n heading.textContent = context.i18n?.handoffHeading ?? 'Transferring to a support agent';\n container.appendChild(heading);\n\n const summary = element.props?.['summary'] as string | undefined;\n if (summary) {\n const summaryEl = document.createElement('p');\n summaryEl.className = 'gengage-chat-handoff-notice-summary';\n summaryEl.textContent = summary;\n container.appendChild(summaryEl);\n }\n\n return container;\n}\n","/**\n * Compact horizontal product card for inline chat-pane rendering.\n *\n * Production parity: mirrors the prior engine's `LaunchSingleProduct` component.\n * Renders when `productDetails` arrives — the full ProductDetailsPanel goes\n * to the left panel while this compact summary appears inline in chat messages.\n *\n * Layout: [image 64×64] [name · rating · price] [View link]\n */\n\nimport type { UIElement } from '../../common/types.js';\nimport type { ChatUISpecRenderContext } from '../types.js';\nimport { formatPrice } from '../../common/price-formatter.js';\nimport { isSafeUrl, safeSetAttribute } from '../../common/safe-html.js';\nimport { addImageErrorHandler, createStarRatingElement } from '../../common/product-utils.js';\nimport {\n campaignReasonForDisplay,\n createCampaignPriceBadge,\n createCampaignReasonElement,\n resolveCampaignBadgeLogoUrl,\n resolveOriginalPriceStyle,\n} from './product-price-layout.js';\n\nexport function renderProductSummaryCard(element: UIElement, ctx: ChatUISpecRenderContext): HTMLElement {\n const product = (element.props?.['product'] ?? element.props) as Record<string, unknown> | undefined;\n\n const card = document.createElement('div');\n card.className = 'gengage-chat-product-summary gds-card';\n card.dataset['gengagePart'] = 'product-summary-card';\n if (!product) return card;\n\n // Make entire card clickable to open product in panel\n card.classList.add('gds-clickable');\n card.addEventListener('click', (e) => {\n if ((e.target as HTMLElement).closest('a')) return;\n ctx.onProductSelect?.(product);\n });\n\n // --- Image (left side) ---\n const imageUrl = product['imageUrl'] as string | undefined;\n if (imageUrl && isSafeUrl(imageUrl)) {\n const imgWrap = document.createElement('div');\n imgWrap.className = 'gengage-chat-product-summary__image';\n imgWrap.dataset['gengagePart'] = 'product-summary-image';\n const img = document.createElement('img');\n img.loading = 'lazy';\n safeSetAttribute(img, 'src', imageUrl);\n const name = product['name'] as string | undefined;\n img.alt = name || 'Product image';\n addImageErrorHandler(img);\n imgWrap.appendChild(img);\n card.appendChild(imgWrap);\n }\n\n // --- Content (right side) ---\n const content = document.createElement('div');\n content.className = 'gengage-chat-product-summary__content';\n content.dataset['gengagePart'] = 'product-summary-content';\n\n // Product name (brand + name)\n const brand = product['brand'] as string | undefined;\n const name = product['name'] as string | undefined;\n if (name) {\n const nameEl = document.createElement('div');\n nameEl.className = 'gengage-chat-product-summary__name';\n nameEl.dataset['gengagePart'] = 'product-summary-name';\n // Only prepend brand if name doesn't already start with it\n const needsBrand = brand && !name.toLowerCase().startsWith(brand.toLowerCase());\n const fullName = needsBrand ? `${brand} ${name}` : name;\n nameEl.textContent = fullName;\n nameEl.title = fullName;\n content.appendChild(nameEl);\n }\n\n // Rating\n const rating = product['rating'];\n const reviewCount = product['reviewCount'];\n if (typeof rating === 'number' && Number.isFinite(rating) && rating > 0) {\n const ratingRow = document.createElement('div');\n ratingRow.className = 'gengage-chat-product-summary__rating';\n ratingRow.dataset['gengagePart'] = 'product-summary-rating';\n ratingRow.appendChild(createStarRatingElement(rating));\n if (typeof reviewCount === 'number' && Number.isFinite(reviewCount)) {\n const count = document.createElement('span');\n count.className = 'gengage-chat-product-summary__review-count';\n count.textContent = ` (${reviewCount})`;\n ratingRow.appendChild(count);\n }\n content.appendChild(ratingRow);\n }\n\n // Price row (+ optional campaign line)\n const price = product['price'] as string | undefined;\n const originalPrice = product['originalPrice'] as string | undefined;\n if (price) {\n const campaignReason = campaignReasonForDisplay(ctx, product);\n const listStyle = resolveOriginalPriceStyle(ctx, product);\n const hasDiscount = !!(originalPrice && originalPrice !== price);\n\n const priceRow = document.createElement('div');\n priceRow.className = 'gengage-chat-product-summary__price';\n priceRow.dataset['gengagePart'] = 'product-summary-price';\n\n const frameDiscounted = !!(campaignReason && hasDiscount);\n const logoUrl = resolveCampaignBadgeLogoUrl(ctx, product);\n\n if (frameDiscounted) {\n const badge = createCampaignPriceBadge({\n reasonText: campaignReason!,\n salePriceFormatted: formatPrice(price, ctx.pricing),\n ...(logoUrl !== undefined ? { logoUrl } : {}),\n });\n if (hasDiscount && listStyle === 'inline') {\n priceRow.classList.add('gengage-chat-product-summary__price--inline');\n priceRow.appendChild(badge);\n const sep = document.createElement('span');\n sep.className = 'gengage-chat-product-summary__price-sep';\n sep.setAttribute('aria-hidden', 'true');\n const orig = document.createElement('span');\n orig.className = 'gengage-chat-product-summary__price-original';\n orig.textContent = formatPrice(originalPrice!, ctx.pricing);\n priceRow.appendChild(sep);\n priceRow.appendChild(orig);\n } else {\n const orig = document.createElement('span');\n orig.className = 'gengage-chat-product-summary__price-original';\n orig.textContent = formatPrice(originalPrice!, ctx.pricing);\n priceRow.appendChild(orig);\n priceRow.appendChild(document.createTextNode(' '));\n priceRow.appendChild(badge);\n }\n content.appendChild(priceRow);\n } else {\n if (hasDiscount && listStyle === 'inline') {\n priceRow.classList.add('gengage-chat-product-summary__price--inline');\n const cur = document.createElement('span');\n cur.className = 'gengage-chat-product-summary__price-current';\n cur.textContent = formatPrice(price, ctx.pricing);\n const sep = document.createElement('span');\n sep.className = 'gengage-chat-product-summary__price-sep';\n sep.setAttribute('aria-hidden', 'true');\n const orig = document.createElement('span');\n orig.className = 'gengage-chat-product-summary__price-original';\n orig.textContent = formatPrice(originalPrice!, ctx.pricing);\n priceRow.appendChild(cur);\n priceRow.appendChild(sep);\n priceRow.appendChild(orig);\n } else if (hasDiscount) {\n const orig = document.createElement('span');\n orig.className = 'gengage-chat-product-summary__price-original';\n orig.textContent = formatPrice(originalPrice!, ctx.pricing);\n priceRow.appendChild(orig);\n priceRow.appendChild(document.createTextNode(' '));\n const current = document.createElement('span');\n current.className = 'gengage-chat-product-summary__price-current';\n current.textContent = formatPrice(price, ctx.pricing);\n priceRow.appendChild(current);\n } else {\n const current = document.createElement('span');\n current.className = 'gengage-chat-product-summary__price-current';\n current.textContent = formatPrice(price, ctx.pricing);\n priceRow.appendChild(current);\n }\n if (campaignReason) {\n const stack = document.createElement('div');\n stack.className = 'gengage-chat-product-summary__price-stack';\n stack.appendChild(createCampaignReasonElement(campaignReason));\n stack.appendChild(priceRow);\n content.appendChild(stack);\n } else {\n content.appendChild(priceRow);\n }\n }\n }\n\n card.appendChild(content);\n\n // --- CTA link (right edge) ---\n const url = product['url'] as string | undefined;\n if (url && isSafeUrl(url)) {\n const cta = document.createElement('a');\n cta.className = 'gengage-chat-product-summary__cta gds-chip gds-chip-active';\n cta.dataset['gengagePart'] = 'product-summary-cta';\n safeSetAttribute(cta, 'href', url);\n safeSetAttribute(cta, 'target', '_blank');\n safeSetAttribute(cta, 'rel', 'noopener noreferrer');\n cta.textContent = ctx.i18n?.productCtaLabel ?? 'View';\n card.appendChild(cta);\n }\n\n return card;\n}\n","/**\n * Renders a json-render UISpec into DOM elements.\n *\n * XSS safety: All text is set via textContent. URLs are validated for safe protocols.\n * No innerHTML is used anywhere in this module.\n */\n\nimport type { UISpec, UIElement, ActionPayload } from '../../common/types.js';\nimport { renderUISpecWithRegistry } from '../../common/renderer/index.js';\nimport type { UISpecDomRegistry, UISpecDomUnknownRenderer } from '../../common/renderer/index.js';\nimport type { ChatUISpecRenderContext, ProductSortState } from '../types.js';\nimport { formatPrice } from '../../common/price-formatter.js';\nimport type { PriceFormatConfig } from '../../common/price-formatter.js';\nimport { renderComparisonTable } from './ComparisonTable.js';\nimport type { ComparisonProduct, ComparisonAttribute } from './ComparisonTable.js';\nimport { renderReviewHighlights as renderReviewHighlightsComponent } from './ReviewHighlights.js';\nimport { renderAITopPicks } from './AITopPicks.js';\nimport { renderGroundingReviewCard } from './GroundingReviewCard.js';\nimport { renderAIGroupingCards } from './AIGroupingCards.js';\nimport { renderAISuggestedSearchCards } from './AISuggestedSearchCards.js';\nimport { renderFloatingComparisonButton } from './FloatingComparisonButton.js';\nimport { renderProsAndCons } from './ProsAndCons.js';\nimport { renderCategoriesContainer } from './CategoriesContainer.js';\nimport { renderHandoffNotice } from './HandoffNotice.js';\nimport { renderProductSummaryCard } from './ProductSummaryCard.js';\nimport { isSafeUrl, safeSetAttribute } from '../../common/safe-html.js';\nimport {\n clampRating,\n clampDiscount,\n addImageErrorHandler,\n createStarRatingElement,\n} from '../../common/product-utils.js';\nimport {\n campaignReasonForDisplay,\n createCampaignPriceBadge,\n createCampaignReasonElement,\n resolveCampaignBadgeLogoUrl,\n resolveOriginalPriceStyle,\n} from './product-price-layout.js';\nimport type { ProductPriceOriginalStyle } from '../types.js';\n\nexport type UISpecRenderContext = ChatUISpecRenderContext;\n\nexport type ChatUISpecRegistry = UISpecDomRegistry<UISpecRenderContext>;\n\nexport type { PriceFormatConfig };\n\n/** @deprecated Use context.isMobile instead. Kept as fallback for custom renderers. */\nfunction isMobileViewport(): boolean {\n return window.innerWidth < 768;\n}\n\nconst DEFAULT_CHAT_UI_SPEC_REGISTRY: ChatUISpecRegistry = {\n ActionButtons: ({ element, context }) => renderActionButtons(element, context),\n ActionButton: ({ element, context }) => renderActionButton(element, context),\n ProductCard: ({ element, context }) => renderProductCard(element, context),\n ProductDetailsPanel: ({ element, context }) => renderProductDetailsPanel(element, context),\n ProductGrid: ({ element, spec, renderElement, context }) => renderProductGrid(element, spec, renderElement, context),\n ReviewHighlights: ({ element, context }) =>\n renderReviewHighlightsComponent(element, {\n emptyReviewsMessage: context.i18n?.emptyReviewsMessage,\n reviewFilterPositive: context.i18n?.reviewFilterPositive,\n reviewFilterNegative: context.i18n?.reviewFilterNegative,\n reviewCustomersMentionSingular: context.i18n?.reviewCustomersMentionSingular,\n reviewCustomersMentionPlural: context.i18n?.reviewCustomersMentionPlural,\n reviewSubjectsHeading: context.i18n?.reviewSubjectsHeading,\n }),\n ComparisonTable: ({ element, context }) => renderComparisonTableElement(element, context),\n AITopPicks: ({ element, context }) => renderAITopPicks(element, context),\n GroundingReviewCard: ({ element, context }) => renderGroundingReviewCard(element, context),\n AIGroupingCards: ({ element, context }) => renderAIGroupingCards(element, context),\n AISuggestedSearchCards: ({ element, context }) => renderAISuggestedSearchCards(element, context),\n ProsAndCons: ({ element }) => renderProsAndCons(element),\n CategoriesContainer: ({ element, context }) => renderCategoriesContainer(element, context),\n HandoffNotice: ({ element, context }) => renderHandoffNotice(element, context),\n ProductSummaryCard: ({ element, context }) => renderProductSummaryCard(element, context),\n Divider: ({ element }) => renderDivider(element),\n};\n\nexport const defaultChatUnknownUISpecRenderer: UISpecDomUnknownRenderer<UISpecRenderContext> = ({\n element,\n renderElement,\n}) => {\n if (import.meta.env?.DEV) {\n console.warn(`[gengage] Unknown ui_spec component type: ${element.type}`);\n }\n if (!element.children || element.children.length === 0) {\n return null;\n }\n const wrapper = document.createElement('div');\n for (const childId of element.children) {\n const rendered = renderElement(childId);\n if (rendered) wrapper.appendChild(rendered);\n }\n return wrapper;\n};\n\nexport function createDefaultChatUISpecRegistry(): ChatUISpecRegistry {\n return { ...DEFAULT_CHAT_UI_SPEC_REGISTRY };\n}\n\nexport function renderUISpec(\n spec: UISpec,\n ctx: UISpecRenderContext,\n registry = DEFAULT_CHAT_UI_SPEC_REGISTRY,\n unknownRenderer: UISpecDomUnknownRenderer<UISpecRenderContext> = defaultChatUnknownUISpecRenderer,\n): HTMLElement {\n return renderUISpecWithRegistry({\n spec,\n context: ctx,\n registry,\n containerClassName: 'gengage-chat-uispec',\n unknownRenderer,\n });\n}\n\nfunction renderActionButtons(element: UIElement, ctx: UISpecRenderContext): HTMLElement {\n const container = document.createElement('div');\n container.className = 'gengage-chat-action-buttons';\n\n const buttons = element.props?.['buttons'] as Array<{ label: string; action: ActionPayload }> | undefined;\n\n if (buttons) {\n for (const btn of buttons) {\n const button = document.createElement('button');\n button.className = 'gengage-chat-action-btn';\n button.textContent = btn.label;\n button.addEventListener('click', () => ctx.onAction(btn.action));\n container.appendChild(button);\n }\n }\n\n return container;\n}\n\nfunction renderActionButton(element: UIElement, ctx: UISpecRenderContext): HTMLElement {\n const button = document.createElement('button');\n button.className = 'gengage-chat-action-btn';\n const label = element.props?.['label'];\n if (typeof label === 'string') button.textContent = label;\n const action = element.props?.['action'] as ActionPayload | undefined;\n if (action) {\n button.addEventListener('click', () => ctx.onAction(action));\n }\n return button;\n}\n\nfunction fillProductCardPriceBlock(\n priceBlock: HTMLElement,\n ctx: UISpecRenderContext,\n product: Record<string, unknown>,\n price: string,\n originalPrice: string | undefined,\n): void {\n priceBlock.replaceChildren();\n const style = resolveOriginalPriceStyle(ctx, product);\n const hasDiscount = !!(originalPrice && price && originalPrice !== price);\n const campaignReason = campaignReasonForDisplay(ctx, product);\n const frameDiscounted = !!(campaignReason && hasDiscount);\n const logoUrl = resolveCampaignBadgeLogoUrl(ctx, product);\n if (!price || parseFloat(price) <= 0) return;\n\n if (frameDiscounted) {\n const badge = createCampaignPriceBadge({\n reasonText: campaignReason!,\n salePriceFormatted: formatPrice(price, ctx.pricing),\n ...(logoUrl !== undefined ? { logoUrl } : {}),\n });\n if (hasDiscount && style === 'inline') {\n priceBlock.classList.add('gengage-chat-product-card-price-block--inline');\n const row = document.createElement('div');\n row.className = 'gengage-chat-product-card-price-row';\n row.appendChild(badge);\n const sep = document.createElement('span');\n sep.className = 'gengage-chat-product-card-price-sep';\n sep.setAttribute('aria-hidden', 'true');\n const orig = document.createElement('span');\n orig.className = 'gengage-chat-product-card-original-price';\n orig.textContent = formatPrice(originalPrice!, ctx.pricing);\n row.appendChild(sep);\n row.appendChild(orig);\n priceBlock.appendChild(row);\n return;\n }\n priceBlock.classList.remove('gengage-chat-product-card-price-block--inline');\n priceBlock.appendChild(badge);\n if (hasDiscount) {\n priceBlock.appendChild(document.createTextNode(' '));\n const orig = document.createElement('span');\n orig.className = 'gengage-chat-product-card-original-price';\n orig.textContent = formatPrice(originalPrice!, ctx.pricing);\n priceBlock.appendChild(orig);\n }\n return;\n }\n\n if (hasDiscount && style === 'inline') {\n priceBlock.classList.add('gengage-chat-product-card-price-block--inline');\n const row = document.createElement('div');\n row.className = 'gengage-chat-product-card-price-row';\n const cur = document.createElement('span');\n cur.className = 'gengage-chat-product-card-current-price';\n cur.textContent = formatPrice(price, ctx.pricing);\n const sep = document.createElement('span');\n sep.className = 'gengage-chat-product-card-price-sep';\n sep.setAttribute('aria-hidden', 'true');\n const orig = document.createElement('span');\n orig.className = 'gengage-chat-product-card-original-price';\n orig.textContent = formatPrice(originalPrice, ctx.pricing);\n row.appendChild(cur);\n row.appendChild(sep);\n row.appendChild(orig);\n priceBlock.appendChild(row);\n return;\n }\n\n priceBlock.classList.remove('gengage-chat-product-card-price-block--inline');\n const current = document.createElement('span');\n current.className = 'gengage-chat-product-card-current-price';\n current.textContent = formatPrice(price, ctx.pricing);\n priceBlock.appendChild(current);\n if (hasDiscount) {\n priceBlock.appendChild(document.createTextNode(' '));\n const orig = document.createElement('span');\n orig.className = 'gengage-chat-product-card-original-price';\n orig.textContent = formatPrice(originalPrice!, ctx.pricing);\n priceBlock.appendChild(orig);\n }\n}\n\nfunction fillProductDetailsPriceRow(\n priceRow: HTMLElement,\n ctx: UISpecRenderContext,\n product: Record<string, unknown>,\n price: string,\n originalPrice: string | undefined,\n style: ProductPriceOriginalStyle,\n hasDiscount: boolean,\n): void {\n priceRow.classList.remove('gengage-chat-product-details-price--inline');\n priceRow.replaceChildren();\n priceRow.className = 'gengage-chat-product-details-price';\n const campaignReason = campaignReasonForDisplay(ctx, product);\n const frameDiscounted = !!(campaignReason && hasDiscount);\n const logoUrl = resolveCampaignBadgeLogoUrl(ctx, product);\n if (frameDiscounted) {\n const badge = createCampaignPriceBadge({\n reasonText: campaignReason!,\n salePriceFormatted: formatPrice(price, ctx.pricing),\n ...(logoUrl !== undefined ? { logoUrl } : {}),\n });\n if (hasDiscount && style === 'inline') {\n priceRow.classList.add('gengage-chat-product-details-price--inline');\n priceRow.appendChild(badge);\n const sep = document.createElement('span');\n sep.className = 'gengage-chat-product-details-price-sep';\n sep.setAttribute('aria-hidden', 'true');\n const oldP = document.createElement('span');\n oldP.className = 'gengage-chat-product-details-original-price';\n oldP.textContent = formatPrice(originalPrice!, ctx.pricing);\n priceRow.appendChild(sep);\n priceRow.appendChild(oldP);\n } else {\n if (hasDiscount) {\n const oldPrice = document.createElement('span');\n oldPrice.className = 'gengage-chat-product-details-original-price';\n oldPrice.textContent = formatPrice(originalPrice!, ctx.pricing);\n priceRow.appendChild(oldPrice);\n priceRow.appendChild(document.createTextNode(' '));\n }\n priceRow.appendChild(badge);\n }\n } else if (hasDiscount && style === 'inline') {\n priceRow.classList.add('gengage-chat-product-details-price--inline');\n const cur = document.createElement('span');\n cur.className = 'gengage-chat-product-details-current-price';\n cur.textContent = formatPrice(price, ctx.pricing);\n const sep = document.createElement('span');\n sep.className = 'gengage-chat-product-details-price-sep';\n sep.setAttribute('aria-hidden', 'true');\n const oldP = document.createElement('span');\n oldP.className = 'gengage-chat-product-details-original-price';\n oldP.textContent = formatPrice(originalPrice!, ctx.pricing);\n priceRow.appendChild(cur);\n priceRow.appendChild(sep);\n priceRow.appendChild(oldP);\n } else {\n if (hasDiscount) {\n const oldPrice = document.createElement('span');\n oldPrice.className = 'gengage-chat-product-details-original-price';\n oldPrice.textContent = formatPrice(originalPrice!, ctx.pricing);\n priceRow.appendChild(oldPrice);\n priceRow.appendChild(document.createTextNode(' '));\n }\n const currentPrice = document.createElement('span');\n currentPrice.className = 'gengage-chat-product-details-current-price';\n currentPrice.textContent = formatPrice(price, ctx.pricing);\n priceRow.appendChild(currentPrice);\n }\n\n const discountPercent = productNumber(product, 'discountPercent', 'price_discount_rate');\n if (typeof discountPercent === 'number' && discountPercent > 0) {\n const discountBadge = document.createElement('span');\n discountBadge.className = 'gengage-chat-product-details-discount-badge';\n discountBadge.textContent = `%${clampDiscount(discountPercent)}`;\n priceRow.appendChild(discountBadge);\n }\n}\n\nfunction renderProductCard(element: UIElement, ctx: UISpecRenderContext): HTMLElement {\n const card = document.createElement('div');\n card.className = 'gengage-chat-product-card gds-card gds-product-card gds-card-interactive';\n\n // Product data may be nested under `product` prop (adapter) or flat in props\n const product = (element.props?.['product'] ?? element.props) as Record<string, unknown> | undefined;\n if (!product) return card;\n\n // Store SKU as data attribute for comparison mode DOM refresh\n const productSku = product['sku'] as string | undefined;\n if (productSku) card.dataset['sku'] = productSku;\n const action = element.props?.['action'] as ActionPayload | undefined;\n\n // Make card clickable to show detail in panel (disabled in comparison select mode)\n if (ctx.onProductSelect || action) {\n card.classList.add('gds-clickable');\n card.addEventListener('click', (e) => {\n // Check live DOM: if card is inside a comparison wrapper, mode is active\n if (card.parentElement?.classList.contains('gengage-chat-comparison-select-wrapper')) return;\n if ((e.target as HTMLElement).closest('.gengage-chat-product-card-atc')) return;\n if ((e.target as HTMLElement).closest('.gengage-chat-product-card-cta')) return;\n if (action) {\n ctx.onAction(action);\n return;\n }\n ctx.onProductSelect?.(product);\n });\n }\n\n const imageUrl = product['imageUrl'] as string | undefined;\n if (imageUrl && isSafeUrl(imageUrl)) {\n const imgWrapper = document.createElement('div');\n imgWrapper.className = 'gengage-chat-product-card-img-wrapper';\n\n const img = document.createElement('img');\n img.className = 'gengage-chat-product-card-img';\n img.loading = 'lazy';\n safeSetAttribute(img, 'src', imageUrl);\n const name = product['name'] as string | undefined;\n if (name) img.alt = name;\n addImageErrorHandler(img);\n imgWrapper.appendChild(img);\n\n // Discount badge (top-left of image)\n const discountPercent = product['discountPercent'] as number | undefined;\n if (typeof discountPercent === 'number' && discountPercent > 0) {\n const badge = document.createElement('span');\n badge.className = 'gengage-chat-product-card-discount-badge';\n badge.textContent = `%${clampDiscount(discountPercent)}`;\n imgWrapper.appendChild(badge);\n }\n\n const imgActions = document.createElement('div');\n imgActions.className = 'gengage-chat-product-card-img-actions';\n\n // Favorite (top of action stack — matches retail card reference)\n const favSku = product['sku'] as string | undefined;\n if (favSku && ctx.onFavoriteToggle) {\n const heart = document.createElement('button');\n heart.className = 'gengage-chat-favorite-btn';\n heart.type = 'button';\n heart.dataset.gengageFavoriteSku = favSku;\n heart.setAttribute('aria-label', ctx.i18n?.addToFavoritesLabel ?? 'Add to favorites');\n const isFav = ctx.favoritedSkus?.has(favSku) ?? false;\n if (isFav) heart.classList.add('gengage-chat-favorite-btn--active');\n const svgFill = isFav ? 'currentColor' : 'none';\n heart.innerHTML = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"${svgFill}\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z\"/></svg>`;\n heart.addEventListener('click', (e) => {\n e.stopPropagation();\n heart.classList.toggle('gengage-chat-favorite-btn--active');\n const svg = heart.querySelector('svg');\n if (svg) {\n svg.setAttribute(\n 'fill',\n heart.classList.contains('gengage-chat-favorite-btn--active') ? 'currentColor' : 'none',\n );\n }\n ctx.onFavoriteToggle!(favSku, product);\n });\n imgActions.appendChild(heart);\n }\n\n // Find similar — icon button + visually hidden label (panel: always visible stack)\n const findSimilarSku = product['sku'] as string | undefined;\n const findSimilarLabel = ctx.i18n?.findSimilarLabel ?? 'Find Similar';\n if (findSimilarSku) {\n const pill = document.createElement('button');\n pill.className = 'gengage-chat-find-similar-pill';\n pill.type = 'button';\n pill.setAttribute('aria-label', findSimilarLabel);\n pill.dataset['tooltip'] = findSimilarLabel;\n pill.innerHTML =\n `<span class=\"gengage-chat-find-similar-pill-icon\" aria-hidden=\"true\">` +\n `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.25\" stroke-linecap=\"round\" stroke-linejoin=\"round\">` +\n `<circle cx=\"10.5\" cy=\"10.5\" r=\"6.5\"/>` +\n `<path d=\"M16 16l5.5 5.5\"/>` +\n `</svg></span>`;\n const pillText = document.createElement('span');\n pillText.className = 'gengage-chat-find-similar-pill-text';\n pillText.textContent = findSimilarLabel;\n pill.appendChild(pillText);\n pill.addEventListener('click', (e) => {\n e.stopPropagation();\n ctx.onAction({\n title: findSimilarLabel,\n type: 'findSimilar',\n payload: { sku: findSimilarSku, ...(imageUrl ? { image_url: imageUrl } : {}) },\n });\n });\n imgActions.appendChild(pill);\n }\n\n if (imgActions.childElementCount > 0) {\n imgWrapper.appendChild(imgActions);\n }\n\n card.appendChild(imgWrapper);\n }\n\n const body = document.createElement('div');\n body.className = 'gengage-chat-product-card-body';\n\n const brand = product['brand'] as string | undefined;\n if (brand) {\n const brandEl = document.createElement('div');\n brandEl.className = 'gengage-chat-product-card-brand';\n brandEl.textContent = brand;\n body.appendChild(brandEl);\n }\n\n const rating = product['rating'];\n const reviewCount = product['reviewCount'];\n const price = product['price'] as string | undefined;\n const originalPrice = product['originalPrice'] as string | undefined;\n const priceAsync = product['price_async'] as boolean | undefined;\n const campaignReason = campaignReasonForDisplay(ctx, product);\n const priceStyle = resolveOriginalPriceStyle(ctx, product);\n const hasCardDiscount = !!(originalPrice && price && originalPrice !== price && parseFloat(price) > 0);\n const useCardPriceStack = !!(campaignReason || (hasCardDiscount && priceStyle === 'inline'));\n\n const hasNumericRating = typeof rating === 'number' && Number.isFinite(rating) && rating > 0;\n const metaRow = document.createElement('div');\n metaRow.className = 'gengage-chat-product-card-meta-row';\n\n const priceBlock = document.createElement('div');\n // Keep .gengage-chat-product-card-price for tests and legacy selectors\n priceBlock.className = 'gengage-chat-product-card-price gengage-chat-product-card-price-block';\n\n const priceOuter: HTMLElement = useCardPriceStack\n ? (() => {\n const stack = document.createElement('div');\n stack.className = 'gengage-chat-product-card-price-stack';\n if (campaignReason) stack.appendChild(createCampaignReasonElement(campaignReason));\n stack.appendChild(priceBlock);\n return stack;\n })()\n : priceBlock;\n\n if (priceAsync === true) {\n const skeleton = document.createElement('span');\n skeleton.className = 'gengage-chat-price-skeleton';\n priceBlock.appendChild(skeleton);\n setTimeout(() => {\n if (!skeleton.parentElement) return;\n if (price && parseFloat(price) > 0) {\n fillProductCardPriceBlock(priceBlock, ctx, product, price, product['originalPrice'] as string | undefined);\n } else {\n skeleton.remove();\n }\n }, 300);\n } else if (price && parseFloat(price) > 0) {\n fillProductCardPriceBlock(priceBlock, ctx, product, price, originalPrice);\n }\n\n if (priceBlock.childElementCount > 0 || priceAsync === true) {\n metaRow.appendChild(priceOuter);\n }\n\n if (hasNumericRating) {\n const ratingCompact = document.createElement('div');\n ratingCompact.className = 'gengage-chat-product-card-rating gengage-chat-product-card-rating-compact';\n const rc = clampRating(rating);\n const labelParts = [`${rc.toFixed(1)}`, 'out of 5 stars'];\n if (typeof reviewCount === 'number' && Number.isFinite(reviewCount)) {\n labelParts.push(`(${reviewCount} reviews)`);\n }\n ratingCompact.setAttribute('aria-label', labelParts.join(' '));\n const starEl = document.createElement('span');\n starEl.className = 'gengage-chat-product-card-rating-compact-star';\n starEl.setAttribute('aria-hidden', 'true');\n starEl.innerHTML =\n '<svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 3.6l2.58 5.23 5.77.84-4.17 4.07.98 5.75L12 16.78l-5.16 2.71.99-5.75L3.66 9.67l5.76-.84L12 3.6z\"/></svg>';\n const valEl = document.createElement('span');\n valEl.className = 'gengage-chat-product-card-rating-compact-value';\n valEl.textContent = rc.toFixed(1);\n ratingCompact.appendChild(starEl);\n ratingCompact.appendChild(valEl);\n metaRow.appendChild(ratingCompact);\n }\n\n if (metaRow.childElementCount === 0) {\n metaRow.classList.add('gengage-chat-product-card-meta-row--empty');\n metaRow.setAttribute('aria-hidden', 'true');\n }\n body.appendChild(metaRow);\n\n const name = product['name'] as string | undefined;\n if (name) {\n const nameEl = document.createElement('div');\n nameEl.className = 'gengage-chat-product-card-name';\n nameEl.textContent = name;\n nameEl.title = name;\n body.appendChild(nameEl);\n }\n\n // Stock indicator\n const inStock = product['inStock'];\n if (typeof inStock === 'boolean') {\n const stock = document.createElement('div');\n stock.className = `gengage-chat-product-card-stock ${inStock ? 'is-in-stock' : 'is-out-of-stock'}`;\n stock.textContent = inStock\n ? (ctx.i18n?.inStockLabel ?? 'In Stock')\n : (ctx.i18n?.outOfStockLabel ?? 'Out of Stock');\n body.appendChild(stock);\n }\n\n card.appendChild(body);\n\n const url = product['url'] as string | undefined;\n const sku = product['sku'] as string | undefined;\n const cartCode = product['cartCode'] as string | undefined;\n\n const hasCart = !!(cartCode && sku && inStock !== false);\n const ctaLabel = ctx.i18n?.productCtaLabel ?? 'View';\n\n if (hasCart) {\n const buyFooter = document.createElement('div');\n buyFooter.className = 'gengage-chat-product-card-buy-footer';\n\n const trigger = document.createElement('button');\n trigger.type = 'button';\n trigger.className = 'gengage-chat-product-card-buy-trigger';\n trigger.textContent = ctaLabel;\n trigger.addEventListener('click', (e) => {\n e.stopPropagation();\n ctx.onAction({\n title: ctx.i18n?.addToCartButton ?? ctaLabel,\n type: 'addToCart',\n payload: { sku: sku!, cartCode: cartCode!, quantity: 1 },\n });\n });\n\n buyFooter.appendChild(trigger);\n card.appendChild(buyFooter);\n } else if (action) {\n const cta = document.createElement('button');\n cta.className = 'gengage-chat-product-card-cta';\n cta.type = 'button';\n cta.textContent = action.type === 'launchSingleProduct' ? ctaLabel : action.title || ctaLabel;\n cta.addEventListener('click', (e) => {\n if (card.parentElement?.classList.contains('gengage-chat-comparison-select-wrapper')) {\n e.stopPropagation();\n return;\n }\n ctx.onAction(action);\n });\n card.appendChild(cta);\n } else if (url && isSafeUrl(url)) {\n const cta = document.createElement('a');\n cta.className = 'gengage-chat-product-card-cta';\n safeSetAttribute(cta, 'href', url);\n safeSetAttribute(cta, 'target', '_blank');\n safeSetAttribute(cta, 'rel', 'noopener noreferrer');\n cta.textContent = ctaLabel;\n cta.addEventListener('click', (e) => {\n if (card.parentElement?.classList.contains('gengage-chat-comparison-select-wrapper')) {\n e.preventDefault();\n e.stopPropagation();\n return;\n }\n if (ctx.onProductClick && sku) {\n e.preventDefault();\n ctx.onProductClick({ sku, url });\n }\n });\n card.appendChild(cta);\n }\n\n // Wrap with checkbox overlay when comparison select mode is active\n if (ctx.comparisonSelectMode && sku && ctx.onToggleComparisonSku) {\n const wrapper = document.createElement('div');\n wrapper.className = 'gengage-chat-comparison-select-wrapper';\n const isSelected = ctx.comparisonSelectedSkus?.includes(sku) ?? false;\n if (isSelected) wrapper.classList.add('gengage-chat-comparison-select-wrapper--selected');\n\n const productName = (product['name'] as string | undefined) ?? sku;\n const hintText =\n ctx.i18n?.comparisonSelectCardHint ?? 'Tap anywhere on the card to add or remove it from comparison.';\n wrapper.setAttribute('role', 'group');\n wrapper.setAttribute('aria-label', `${String(productName)}. ${hintText}`);\n\n const toggle = document.createElement('button');\n toggle.type = 'button';\n toggle.className = 'gengage-chat-comparison-checkbox';\n toggle.dataset['selected'] = isSelected ? 'true' : 'false';\n toggle.setAttribute('aria-pressed', isSelected ? 'true' : 'false');\n const icon = document.createElement('span');\n icon.className = 'gengage-chat-comparison-checkbox-icon';\n icon.innerHTML = isSelected\n ? '<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 6L9 17l-5-5\"/></svg>'\n : '<span class=\"gengage-chat-comparison-checkbox-dot\"></span>';\n const label = document.createElement('span');\n label.className = 'gengage-chat-comparison-checkbox-label';\n label.textContent = isSelected\n ? (ctx.i18n?.comparisonSelectedLabel ?? 'Selected')\n : (ctx.i18n?.comparisonSelectLabel ?? 'Select to compare');\n toggle.appendChild(icon);\n toggle.appendChild(label);\n toggle.addEventListener('click', (e) => {\n e.stopPropagation();\n ctx.onToggleComparisonSku?.(sku);\n });\n\n const hint = document.createElement('div');\n hint.className = 'gengage-chat-comparison-card-hint';\n hint.setAttribute('aria-hidden', 'true');\n hint.textContent = hintText;\n\n // Clicking anywhere on the card toggles comparison selection — no product detail navigation.\n // Do NOT manually flip checkbox.checked here: onToggleComparisonSku triggers\n // _refreshComparisonUI which syncs checkbox state from the canonical Set.\n wrapper.addEventListener('click', (e) => {\n if ((e.target as HTMLElement).closest('.gengage-chat-comparison-checkbox')) return;\n e.stopPropagation();\n ctx.onToggleComparisonSku?.(sku);\n });\n\n wrapper.appendChild(toggle);\n wrapper.appendChild(hint);\n wrapper.appendChild(card);\n return wrapper;\n }\n\n return card;\n}\n\n/* clampRating, clampDiscount, addImageErrorHandler, renderStarRating\n are imported from ../../common/product-utils.js */\n\ntype ProductFeatureEntry = { key: string; value: string };\ntype ProductDescriptionContent = { text: string; html?: string };\n\nconst COLOR_VARIANT_NAMES = new Set(['color', 'colour', 'renk', 'renk kodu', 'color code']);\nconst SIZE_VARIANT_NAMES = new Set(['size', 'beden', 'boyut']);\nconst FINISH_VARIANT_NAMES = new Set(['finish', 'bitiş', 'bitişi']);\nconst VARIANT_ARRAY_KEYS = [\n 'variants',\n 'variantOptions',\n 'variant_options',\n 'productVariants',\n 'product_variants',\n 'options',\n];\nconst PRODUCT_DESCRIPTION_ALLOWED_TAGS = new Set([\n 'H2',\n 'H3',\n 'H4',\n 'P',\n 'BR',\n 'UL',\n 'OL',\n 'LI',\n 'STRONG',\n 'B',\n 'EM',\n 'I',\n]);\nconst PRODUCT_DESCRIPTION_BLOCKED_TAGS = new Set(['SCRIPT', 'STYLE', 'NOSCRIPT', 'IFRAME', 'OBJECT', 'EMBED']);\n\nfunction productString(product: Record<string, unknown>, ...keys: string[]): string | undefined {\n for (const key of keys) {\n const value = product[key];\n if (typeof value === 'string') {\n const trimmed = value.trim();\n if (trimmed.length > 0) return trimmed;\n }\n }\n return undefined;\n}\n\nfunction productNumber(product: Record<string, unknown>, ...keys: string[]): number | undefined {\n for (const key of keys) {\n const value = product[key];\n if (typeof value === 'number' && Number.isFinite(value)) return value;\n if (typeof value === 'string') {\n const parsed = Number(value.replace(',', '.'));\n if (Number.isFinite(parsed)) return parsed;\n }\n }\n return undefined;\n}\n\nfunction productBoolean(product: Record<string, unknown>, ...keys: string[]): boolean | undefined {\n for (const key of keys) {\n const value = product[key];\n if (typeof value === 'boolean') return value;\n }\n return undefined;\n}\n\nfunction productRecord(product: Record<string, unknown>, ...keys: string[]): Record<string, unknown> | undefined {\n for (const key of keys) {\n const value = product[key];\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n }\n return undefined;\n}\n\nfunction productStringArray(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return value.filter((item): item is string => typeof item === 'string' && item.trim().length > 0);\n}\n\nfunction productImageList(product: Record<string, unknown>): string[] {\n const urls = [\n ...productStringArray(product['images']),\n productString(product, 'imageUrl', 'image_url', 'image'),\n ].filter((url): url is string => !!url && isSafeUrl(url));\n return Array.from(new Set(urls));\n}\n\nfunction htmlToPlainText(html: string): string {\n if (typeof DOMParser !== 'undefined') {\n const doc = new DOMParser().parseFromString(html, 'text/html');\n return (doc.body.textContent ?? '').replace(/\\s+/g, ' ').trim();\n }\n return html\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction productDescription(product: Record<string, unknown>): ProductDescriptionContent | undefined {\n const html = productString(product, 'description_html', 'descriptionHtml');\n if (html) {\n const text = htmlToPlainText(html);\n if (text) return { text, html };\n }\n\n const description = productString(product, 'description');\n return description ? { text: description } : undefined;\n}\n\nfunction normalizeProductFeatures(value: unknown): ProductFeatureEntry[] {\n if (Array.isArray(value)) {\n return value\n .map((entry): ProductFeatureEntry | null => {\n if (!entry || typeof entry !== 'object') return null;\n const record = entry as Record<string, unknown>;\n const key = productString(record, 'key', 'name', 'label', 'title');\n const rawValue = record['value'];\n const val =\n typeof rawValue === 'string' || typeof rawValue === 'number' || typeof rawValue === 'boolean'\n ? String(rawValue).trim()\n : undefined;\n if (!key || !val) return null;\n return { key, value: val };\n })\n .filter((entry): entry is ProductFeatureEntry => entry !== null);\n }\n\n if (value && typeof value === 'object') {\n return Object.entries(value as Record<string, unknown>)\n .map(([key, rawValue]): ProductFeatureEntry | null => {\n const val =\n typeof rawValue === 'string' || typeof rawValue === 'number' || typeof rawValue === 'boolean'\n ? String(rawValue).trim()\n : undefined;\n if (!key || !val) return null;\n return { key, value: val };\n })\n .filter((entry): entry is ProductFeatureEntry => entry !== null);\n }\n\n return [];\n}\n\nfunction productFeatureEntries(product: Record<string, unknown>): ProductFeatureEntry[] {\n const features = normalizeProductFeatures(product['features']);\n if (features.length > 0) return features;\n return normalizeProductFeatures(product['specifications']);\n}\n\nfunction productSpecifications(\n product: Record<string, unknown>,\n): Record<string, string> | Array<{ key: string; value: string }> | undefined {\n const explicit = product['specifications'];\n const explicitEntries = normalizeProductFeatures(explicit);\n if (explicitEntries.length > 0) {\n return Array.isArray(explicit)\n ? explicitEntries\n : Object.fromEntries(explicitEntries.map((item) => [item.key, item.value]));\n }\n\n const featureEntries = normalizeProductFeatures(product['features']);\n return featureEntries.length > 0 ? featureEntries : undefined;\n}\n\nfunction variantString(variant: Record<string, unknown>, ...keys: string[]): string | undefined {\n return productString(variant, ...keys);\n}\n\nfunction variantNumber(variant: Record<string, unknown>, ...keys: string[]): number | undefined {\n return productNumber(variant, ...keys);\n}\n\nfunction variantDisplayLabel(variant: Record<string, unknown>): string | undefined {\n return variantString(\n variant,\n 'value',\n 'option_value',\n 'attribute_value',\n 'label',\n 'title',\n 'name',\n 'variant_name',\n 'sku',\n );\n}\n\nfunction variantTypeName(variant: Record<string, unknown>): string | undefined {\n const explicitType = variantString(variant, 'type', 'attribute', 'option_name', 'attribute_name');\n if (explicitType) return explicitType;\n return variantString(variant, 'value') ? variantString(variant, 'name', 'variant_name') : undefined;\n}\n\nfunction isColorVariant(variant: Record<string, unknown>): boolean {\n const typeName = variantTypeName(variant)?.toLowerCase();\n return !!(\n productString(variant, 'color', 'colour', 'color_hex', 'hex', 'swatch', 'swatchColor') ||\n (typeName && COLOR_VARIANT_NAMES.has(typeName))\n );\n}\n\nfunction safeCssColor(value: string | undefined): string | undefined {\n if (!value) return undefined;\n const trimmed = value.trim();\n if (!trimmed || trimmed.includes(';')) return undefined;\n if (typeof CSS !== 'undefined' && CSS.supports?.('color', trimmed)) return trimmed;\n if (/^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(trimmed)) return trimmed;\n return undefined;\n}\n\nfunction variantSwatchColor(variant: Record<string, unknown>): string | undefined {\n const explicit = safeCssColor(variantString(variant, 'swatchColor', 'swatch', 'color_hex', 'hex', 'color', 'colour'));\n if (explicit) return explicit;\n if (!isColorVariant(variant)) return undefined;\n return safeCssColor(variantDisplayLabel(variant));\n}\n\nfunction variantImage(variant: Record<string, unknown>): string | undefined {\n return variantString(variant, 'image', 'imageUrl', 'image_url', 'swatchImage', 'swatch_image');\n}\n\nfunction variantPrice(variant: Record<string, unknown>): number | string | undefined {\n return (\n variantNumber(variant, 'price_discounted', 'priceDiscounted') ??\n variantString(variant, 'price_discounted', 'priceDiscounted') ??\n variantNumber(variant, 'price') ??\n variantString(variant, 'price')\n );\n}\n\nfunction isVariantFacetName(key: string): boolean {\n const normalized = key.trim().toLowerCase();\n return (\n COLOR_VARIANT_NAMES.has(normalized) || SIZE_VARIANT_NAMES.has(normalized) || FINISH_VARIANT_NAMES.has(normalized)\n );\n}\n\nfunction canonicalVariantFacetName(key: string): string {\n const normalized = key.trim().toLowerCase();\n if (normalized === 'renk kodu') return 'Renk';\n if (normalized === 'color code') return 'Color';\n return key.trim();\n}\n\nfunction recordVariantArrays(product: Record<string, unknown>): Array<Record<string, unknown>> | undefined {\n for (const key of VARIANT_ARRAY_KEYS) {\n const value = product[key];\n if (!Array.isArray(value)) continue;\n const variants = value.filter(\n (item): item is Record<string, unknown> => !!item && typeof item === 'object' && !Array.isArray(item),\n );\n if (variants.length > 0) return variants;\n }\n return undefined;\n}\n\nfunction productVariants(product: Record<string, unknown>): Array<Record<string, unknown>> {\n const explicitVariants = recordVariantArrays(product);\n if (explicitVariants) return explicitVariants;\n\n const sku = productString(product, 'sku');\n const inStock = productBoolean(product, 'inStock', 'in_stock');\n const fallbackVariants: Array<Record<string, unknown>> = [];\n const seen = new Set<string>();\n\n const addVariant = (key: string, value: unknown) => {\n if (!isVariantFacetName(key)) return;\n if (typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean') return;\n const variantValue = String(value).trim();\n if (!variantValue) return;\n const variantName = canonicalVariantFacetName(key);\n const dedupeKey = `${variantName.toLowerCase()}:${variantValue.toLowerCase()}`;\n if (seen.has(dedupeKey)) return;\n seen.add(dedupeKey);\n fallbackVariants.push({\n name: variantName,\n value: variantValue,\n sku,\n inStock,\n });\n };\n\n const facetHits = productRecord(product, 'facetHits', 'facet_hits');\n if (facetHits) {\n for (const [key, value] of Object.entries(facetHits)) {\n addVariant(key, value);\n }\n }\n\n for (const feature of productFeatureEntries(product)) {\n addVariant(feature.key, feature.value);\n }\n\n return fallbackVariants;\n}\n\nfunction variantSectionLabel(variants: Array<Record<string, unknown>>, ctx: UISpecRenderContext): string {\n const types = Array.from(new Set(variants.map(variantTypeName).filter((label): label is string => !!label)));\n if (types.length === 1) return `${variants.length} ${types[0]}`;\n return ctx.i18n?.variantsLabel ?? 'Variants';\n}\n\nfunction renderProductDetailsPanel(element: UIElement, ctx: UISpecRenderContext): HTMLElement {\n const panel = document.createElement('article');\n panel.className = 'gengage-chat-product-details-panel';\n\n const product = (element.props?.['product'] ?? element.props) as Record<string, unknown> | undefined;\n if (!product) return panel;\n\n const name = productString(product, 'name');\n const brand = productString(product, 'brand');\n const sku = productString(product, 'sku');\n const cartCode = productString(product, 'cartCode', 'cart_code');\n const price = productString(product, 'price');\n const originalPrice = productString(product, 'originalPrice', 'price_original');\n const priceAsync = productBoolean(product, 'price_async');\n const campaignReason = campaignReasonForDisplay(ctx, product);\n const priceStyleDetails = resolveOriginalPriceStyle(ctx, product);\n const hasDiscountDetails = !!(originalPrice && price && originalPrice !== price);\n const inStock = productBoolean(product, 'inStock', 'in_stock');\n const reviewCount = productNumber(product, 'reviewCount', 'review_count');\n const rating = productNumber(product, 'rating');\n const safeImages = productImageList(product);\n const featureEntries = productFeatureEntries(product).slice(0, 4);\n\n // Image gallery or single image\n if (safeImages.length > 1) {\n // Gallery with thumbnails + prev/next arrows\n const media = document.createElement('div');\n media.className =\n 'gengage-chat-product-details-media gengage-chat-product-details-gallery gengage-chat-product-details-img-wrap';\n\n const mainImg = document.createElement('img');\n mainImg.className = 'gengage-chat-product-details-img';\n safeSetAttribute(mainImg, 'src', safeImages[0]!);\n mainImg.alt = name ?? 'Product image';\n addImageErrorHandler(mainImg);\n media.appendChild(mainImg);\n\n const thumbStrip = document.createElement('div');\n thumbStrip.className = 'gengage-chat-product-gallery-thumbs';\n\n const MAX_VISIBLE_THUMBNAILS = 6;\n let activeThumb: HTMLElement | null = null;\n let activeThumbIdx = 0;\n\n const i18n = ctx.i18n;\n const prevLabel = i18n?.galleryPrevAriaLabel ?? 'Previous image';\n const nextLabel = i18n?.galleryNextAriaLabel ?? 'Next image';\n\n const navSvg = (dir: 'prev' | 'next') =>\n dir === 'prev'\n ? '<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"15 18 9 12 15 6\"/></svg>'\n : '<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"9 18 15 12 9 6\"/></svg>';\n\n const prevBtn = document.createElement('button');\n prevBtn.type = 'button';\n prevBtn.className =\n 'gengage-chat-product-gallery-nav gengage-chat-product-gallery-nav--prev gds-btn gds-btn-ghost gds-icon-btn';\n prevBtn.setAttribute('aria-label', prevLabel);\n prevBtn.innerHTML = navSvg('prev');\n\n const nextBtn = document.createElement('button');\n nextBtn.type = 'button';\n nextBtn.className =\n 'gengage-chat-product-gallery-nav gengage-chat-product-gallery-nav--next gds-btn gds-btn-ghost gds-icon-btn';\n nextBtn.setAttribute('aria-label', nextLabel);\n nextBtn.innerHTML = navSvg('next');\n\n const updateNavDisabled = (): void => {\n prevBtn.disabled = activeThumbIdx <= 0;\n nextBtn.disabled = activeThumbIdx >= safeImages.length - 1;\n };\n\n const gotoIndex = (nextIdx: number): void => {\n if (nextIdx < 0 || nextIdx >= safeImages.length || nextIdx === activeThumbIdx) return;\n const nextUrl = safeImages[nextIdx];\n if (!nextUrl) return;\n safeSetAttribute(mainImg, 'src', nextUrl);\n const thumbEls = thumbStrip.querySelectorAll('.gengage-chat-product-gallery-thumb');\n if (activeThumb) activeThumb.classList.remove('gengage-chat-product-gallery-thumb--active');\n if (nextIdx < MAX_VISIBLE_THUMBNAILS && thumbEls[nextIdx]) {\n (thumbEls[nextIdx] as HTMLElement).classList.add('gengage-chat-product-gallery-thumb--active');\n activeThumb = thumbEls[nextIdx] as HTMLElement;\n } else {\n activeThumb = null;\n }\n activeThumbIdx = nextIdx;\n updateNavDisabled();\n };\n\n for (let i = 0; i < safeImages.length; i++) {\n const imgUrl = safeImages[i]!;\n if (i >= MAX_VISIBLE_THUMBNAILS) break;\n const thumb = document.createElement('img');\n thumb.className = 'gengage-chat-product-gallery-thumb';\n if (i === 0) {\n thumb.classList.add('gengage-chat-product-gallery-thumb--active');\n activeThumb = thumb;\n }\n safeSetAttribute(thumb, 'src', imgUrl);\n thumb.alt = `${name ?? 'Product'} ${i + 1}`;\n thumb.width = 48;\n thumb.height = 48;\n addImageErrorHandler(thumb);\n thumb.addEventListener('click', () => {\n gotoIndex(i);\n });\n thumbStrip.appendChild(thumb);\n }\n\n // \"+N more\" indicator when thumbnails exceed limit\n if (safeImages.length > MAX_VISIBLE_THUMBNAILS) {\n const more = document.createElement('span');\n more.className = 'gengage-chat-product-gallery-thumb-more';\n more.textContent = `+${safeImages.length - MAX_VISIBLE_THUMBNAILS}`;\n thumbStrip.appendChild(more);\n }\n\n prevBtn.addEventListener('click', (e) => {\n e.stopPropagation();\n gotoIndex(activeThumbIdx - 1);\n });\n nextBtn.addEventListener('click', (e) => {\n e.stopPropagation();\n gotoIndex(activeThumbIdx + 1);\n });\n updateNavDisabled();\n\n // Touch swipe gesture for gallery navigation\n let touchStartX = 0;\n const SWIPE_THRESHOLD = 50;\n\n mainImg.addEventListener(\n 'touchstart',\n (e: TouchEvent) => {\n touchStartX = e.changedTouches[0]!.clientX;\n },\n { passive: true },\n );\n\n mainImg.addEventListener('touchend', (e: TouchEvent) => {\n const touchEndX = e.changedTouches[0]!.clientX;\n const diff = touchStartX - touchEndX;\n if (Math.abs(diff) < SWIPE_THRESHOLD) return;\n\n const nextIdx =\n diff > 0\n ? Math.min(activeThumbIdx + 1, safeImages.length - 1) // swipe left → next\n : Math.max(activeThumbIdx - 1, 0); // swipe right → prev\n\n gotoIndex(nextIdx);\n });\n\n media.appendChild(prevBtn);\n media.appendChild(nextBtn);\n media.appendChild(thumbStrip);\n\n panel.appendChild(media);\n } else if (safeImages.length === 1) {\n // Single image fallback\n const media = document.createElement('div');\n media.className = 'gengage-chat-product-details-media gengage-chat-product-details-img-wrap';\n const img = document.createElement('img');\n img.className = 'gengage-chat-product-details-img';\n img.loading = 'lazy';\n safeSetAttribute(img, 'src', safeImages[0]!);\n addImageErrorHandler(img);\n img.alt = name ?? 'Product image';\n media.appendChild(img);\n\n panel.appendChild(media);\n }\n\n const content = document.createElement('div');\n content.className = 'gengage-chat-product-details-content';\n\n if (brand && (!name || !name.toLowerCase().startsWith(brand.toLowerCase()))) {\n const brandEl = document.createElement('div');\n brandEl.className = 'gengage-chat-product-details-brand';\n brandEl.textContent = brand;\n content.appendChild(brandEl);\n }\n\n if (name) {\n const title = document.createElement('h3');\n title.className = 'gengage-chat-product-details-title';\n title.textContent = name;\n title.title = name;\n content.appendChild(title);\n }\n\n if (typeof rating === 'number' && Number.isFinite(rating) && rating > 0) {\n const ratingRow = document.createElement(sku ? 'button' : 'div');\n ratingRow.className = 'gengage-chat-product-details-rating';\n if (sku) {\n (ratingRow as HTMLButtonElement).type = 'button';\n ratingRow.classList.add('gengage-chat-product-details-rating--clickable');\n ratingRow.setAttribute('aria-label', ctx.i18n?.groundingReviewCta ?? 'Read Reviews');\n ratingRow.addEventListener('click', () => {\n ctx.onAction({\n title: ctx.i18n?.customerReviewsTitle ?? 'Customer Reviews',\n type: 'reviewSummary',\n payload: { sku },\n });\n });\n }\n ratingRow.appendChild(createStarRatingElement(rating));\n const ratingValue = document.createElement('span');\n ratingValue.className = 'gengage-chat-product-details-rating-value';\n ratingValue.textContent = clampRating(rating).toFixed(1);\n ratingRow.appendChild(ratingValue);\n if (typeof reviewCount === 'number' && Number.isFinite(reviewCount)) {\n const count = document.createElement('span');\n count.className = 'gengage-chat-product-details-review-count';\n count.textContent = ` (${reviewCount})`;\n ratingRow.appendChild(count);\n }\n content.appendChild(ratingRow);\n }\n\n {\n let priceAppendTarget: HTMLElement = content;\n if (campaignReason) {\n const stack = document.createElement('div');\n stack.className = 'gengage-chat-product-details-price-stack';\n stack.appendChild(createCampaignReasonElement(campaignReason));\n content.appendChild(stack);\n priceAppendTarget = stack;\n }\n\n const priceRow = document.createElement('div');\n priceRow.className = 'gengage-chat-product-details-price';\n\n if (priceAsync === true) {\n const skeleton = document.createElement('span');\n skeleton.className = 'gengage-chat-price-skeleton';\n priceRow.appendChild(skeleton);\n priceAppendTarget.appendChild(priceRow);\n setTimeout(() => {\n if (!skeleton.parentElement) return;\n if (price && parseFloat(price) > 0) {\n fillProductDetailsPriceRow(\n priceRow,\n ctx,\n product,\n price,\n originalPrice,\n priceStyleDetails,\n hasDiscountDetails,\n );\n } else {\n const host = priceRow.parentElement;\n priceRow.remove();\n if (host?.classList.contains('gengage-chat-product-details-price-stack')) {\n host.remove();\n }\n }\n }, 300);\n } else if (price && parseFloat(price) > 0) {\n fillProductDetailsPriceRow(priceRow, ctx, product, price, originalPrice, priceStyleDetails, hasDiscountDetails);\n priceAppendTarget.appendChild(priceRow);\n } else if (campaignReason) {\n const host = priceAppendTarget;\n if (host.classList.contains('gengage-chat-product-details-price-stack')) {\n host.remove();\n }\n }\n }\n\n if (typeof inStock === 'boolean') {\n const stock = document.createElement('div');\n stock.className = `gengage-chat-product-details-stock ${inStock ? 'is-in-stock' : 'is-out-of-stock'}`;\n stock.textContent = inStock\n ? (ctx.i18n?.inStockLabel ?? 'In Stock')\n : (ctx.i18n?.outOfStockLabel ?? 'Out of Stock');\n content.appendChild(stock);\n }\n\n // Promotion badges (e.g. \"Free Shipping\", \"Flash Sale\") — max 3\n const promotions = product['promotions'] as string[] | undefined;\n if (promotions && promotions.length > 0) {\n const promoBadges = document.createElement('div');\n promoBadges.className = 'gengage-chat-product-details-promos';\n for (const promo of promotions.slice(0, 3)) {\n if (!promo || /%(0(\\.0+)?)\\s/.test(promo)) continue; // skip zero-value badges\n const badge = document.createElement('span');\n badge.className = 'gengage-chat-product-details-promo-badge';\n badge.textContent = promo;\n badge.title = promo;\n promoBadges.appendChild(badge);\n }\n if (promoBadges.childElementCount > 0) content.appendChild(promoBadges);\n }\n\n if (featureEntries.length > 0) {\n const facts = document.createElement('dl');\n facts.className = 'gengage-chat-product-details-facts';\n for (const feature of featureEntries) {\n const item = document.createElement('div');\n item.className = 'gengage-chat-product-details-fact';\n const key = document.createElement('dt');\n key.textContent = feature.key;\n const val = document.createElement('dd');\n val.textContent = feature.value;\n item.appendChild(key);\n item.appendChild(val);\n facts.appendChild(item);\n }\n content.appendChild(facts);\n }\n\n // Variant selector\n const variants = productVariants(product);\n if (variants.length > 0) {\n const variantSection = document.createElement('div');\n variantSection.className = 'gengage-chat-product-variants';\n\n const variantHeading = document.createElement('div');\n variantHeading.className = 'gengage-chat-product-variants-label';\n variantHeading.textContent = variantSectionLabel(variants, ctx);\n variantSection.appendChild(variantHeading);\n\n const variantList = document.createElement('div');\n variantList.className = 'gengage-chat-product-variants-list';\n\n for (const variant of variants) {\n const variantName = variantDisplayLabel(variant);\n const variantSku = variantString(variant, 'sku');\n if (!variantName && !variantSku) continue;\n\n const btn = document.createElement('button');\n btn.className = 'gengage-chat-product-variant-btn gds-chip';\n btn.type = 'button';\n const labelText = variantName ?? variantSku ?? '';\n btn.title = labelText;\n const variantInStock = productBoolean(variant, 'in_stock', 'inStock');\n if (variantSku && sku && variantSku === sku) {\n btn.classList.add('gengage-chat-product-variant-btn--active');\n btn.setAttribute('aria-pressed', 'true');\n } else {\n btn.setAttribute('aria-pressed', 'false');\n }\n if (variantInStock === false) {\n btn.classList.add('gengage-chat-product-variant-btn--out');\n btn.disabled = true;\n }\n\n const swatchImage = variantImage(variant);\n const swatchColor = variantSwatchColor(variant);\n if (swatchImage && isSafeUrl(swatchImage)) {\n const swatch = document.createElement('img');\n swatch.className = 'gengage-chat-product-variant-swatch gengage-chat-product-variant-swatch--image';\n safeSetAttribute(swatch, 'src', swatchImage);\n swatch.alt = '';\n swatch.setAttribute('aria-hidden', 'true');\n addImageErrorHandler(swatch);\n btn.appendChild(swatch);\n } else if (swatchColor) {\n const swatch = document.createElement('span');\n swatch.className = 'gengage-chat-product-variant-swatch';\n swatch.setAttribute('aria-hidden', 'true');\n swatch.style.backgroundColor = swatchColor;\n btn.appendChild(swatch);\n }\n\n const label = document.createElement('span');\n label.className = 'gengage-chat-product-variant-label';\n label.textContent = labelText;\n btn.appendChild(label);\n\n const nextVariantPrice = variantPrice(variant);\n if (nextVariantPrice && String(nextVariantPrice) !== String(price)) {\n const priceEl = document.createElement('span');\n priceEl.className = 'gengage-chat-product-variant-price';\n priceEl.textContent = formatPrice(String(nextVariantPrice), ctx.pricing);\n btn.appendChild(priceEl);\n }\n\n if (variantSku && variantSku !== sku) {\n const productName = name ?? '';\n const variantHuman =\n (typeof variant['value'] === 'string' ? variant['value'].trim() : '') ||\n (typeof variant['name'] === 'string' ? variant['name'].trim() : '') ||\n (typeof variant['variant_name'] === 'string' ? variant['variant_name'].trim() : '') ||\n '';\n const launchTitle =\n productName.length > 0\n ? variantHuman.length > 0 && variantHuman !== productName\n ? `${productName} (${variantHuman})`\n : productName\n : labelText;\n\n btn.addEventListener('click', () => {\n ctx.onAction({\n title: launchTitle,\n type: 'launchVariant',\n payload: { sku: variantSku },\n });\n });\n }\n variantList.appendChild(btn);\n }\n\n if (variantList.childElementCount > 0) {\n variantSection.appendChild(variantList);\n content.appendChild(variantSection);\n }\n }\n\n const actionRow = document.createElement('div');\n actionRow.className = 'gengage-chat-product-details-actions';\n\n const action = element.props?.['action'] as ActionPayload | undefined;\n if (action) {\n const actionBtn = document.createElement('button');\n actionBtn.className = 'gengage-chat-product-details-cta gds-btn gds-btn-primary';\n actionBtn.type = 'button';\n actionBtn.textContent = action.title || ctx.i18n?.productCtaLabel || 'View';\n actionBtn.addEventListener('click', () => ctx.onAction(action));\n actionRow.appendChild(actionBtn);\n }\n\n // Add to Cart — direct add with quantity 1\n if (cartCode && sku && inStock !== false) {\n const addToCartBtn = document.createElement('button');\n addToCartBtn.className = 'gengage-chat-product-details-atc gds-btn gds-btn-primary';\n addToCartBtn.type = 'button';\n addToCartBtn.textContent = ctx.i18n?.addToCartButton ?? 'Add to Cart';\n addToCartBtn.addEventListener('click', () => {\n ctx.onAction({\n title: ctx.i18n?.addToCartButton ?? 'Add to Cart',\n type: 'addToCart',\n payload: { sku, cartCode, quantity: 1 },\n });\n });\n actionRow.appendChild(addToCartBtn);\n }\n\n const url = productString(product, 'url');\n if (!action && url && isSafeUrl(url)) {\n const cta = document.createElement('a');\n cta.className = 'gengage-chat-product-details-cta gds-btn gds-btn-secondary';\n safeSetAttribute(cta, 'href', url);\n safeSetAttribute(cta, 'target', '_blank');\n safeSetAttribute(cta, 'rel', 'noopener noreferrer');\n cta.textContent = ctx.i18n?.viewOnSiteLabel ?? ctx.i18n?.productCtaLabel ?? 'View on Site';\n cta.addEventListener('click', (e) => {\n if (ctx.onProductClick && sku) {\n e.preventDefault();\n ctx.onProductClick({ sku, url });\n }\n });\n actionRow.appendChild(cta);\n }\n\n // Share button — copies product URL or triggers native share\n const shareUrl = url;\n if (shareUrl && isSafeUrl(shareUrl)) {\n const shareBtn = document.createElement('button');\n shareBtn.className = 'gengage-chat-product-details-share gds-btn gds-btn-ghost gds-icon-btn';\n shareBtn.type = 'button';\n const shareLabel = ctx.i18n?.shareButton ?? 'Share';\n shareBtn.title = shareLabel;\n shareBtn.setAttribute('aria-label', shareLabel);\n const svgNS = 'http://www.w3.org/2000/svg';\n const svg = document.createElementNS(svgNS, 'svg');\n svg.setAttribute('width', '18');\n svg.setAttribute('height', '18');\n svg.setAttribute('viewBox', '0 0 24 24');\n svg.setAttribute('fill', 'none');\n svg.setAttribute('stroke', 'currentColor');\n svg.setAttribute('stroke-width', '2');\n svg.setAttribute('stroke-linecap', 'round');\n svg.setAttribute('stroke-linejoin', 'round');\n function addCircle(cx: string, cy: string): void {\n const c = document.createElementNS(svgNS, 'circle');\n c.setAttribute('cx', cx);\n c.setAttribute('cy', cy);\n c.setAttribute('r', '3');\n svg.appendChild(c);\n }\n function addLine(x1: string, y1: string, x2: string, y2: string): void {\n const l = document.createElementNS(svgNS, 'line');\n l.setAttribute('x1', x1);\n l.setAttribute('y1', y1);\n l.setAttribute('x2', x2);\n l.setAttribute('y2', y2);\n svg.appendChild(l);\n }\n addCircle('18', '5');\n addCircle('6', '12');\n addCircle('18', '19');\n addLine('8.59', '13.51', '15.42', '17.49');\n addLine('15.41', '6.51', '8.59', '10.49');\n shareBtn.appendChild(svg);\n shareBtn.addEventListener('click', async () => {\n try {\n if (navigator.share) {\n await navigator.share({ title: name ?? '', url: shareUrl });\n } else if (navigator.clipboard) {\n await navigator.clipboard.writeText(shareUrl);\n shareBtn.classList.add('gengage-chat-product-details-share--copied');\n setTimeout(() => shareBtn.classList.remove('gengage-chat-product-details-share--copied'), 1500);\n }\n } catch {\n // Share cancelled or clipboard write denied — ignore\n }\n });\n actionRow.appendChild(shareBtn);\n }\n\n if (actionRow.childElementCount > 0) {\n content.appendChild(actionRow);\n }\n\n panel.appendChild(content);\n\n // Product detail tabs: \"Product Info\" / \"Specifications\"\n const description = productDescription(product);\n const specifications = productSpecifications(product);\n if (description || specifications) {\n panel.appendChild(renderProductDetailTabs(description, specifications, ctx));\n }\n\n return panel;\n}\n\nfunction sanitizeProductDescriptionNode(node: Node): Node | null {\n if (node.nodeType === Node.TEXT_NODE) {\n return document.createTextNode(node.textContent ?? '');\n }\n\n if (node.nodeType !== Node.ELEMENT_NODE) return null;\n\n const element = node as Element;\n const tagName = element.tagName.toUpperCase();\n if (PRODUCT_DESCRIPTION_BLOCKED_TAGS.has(tagName)) return null;\n\n if (!PRODUCT_DESCRIPTION_ALLOWED_TAGS.has(tagName)) {\n const fragment = document.createDocumentFragment();\n for (const child of Array.from(element.childNodes)) {\n const sanitized = sanitizeProductDescriptionNode(child);\n if (sanitized) fragment.appendChild(sanitized);\n }\n return fragment;\n }\n\n const sanitizedElement = document.createElement(tagName.toLowerCase());\n for (const child of Array.from(element.childNodes)) {\n const sanitized = sanitizeProductDescriptionNode(child);\n if (sanitized) sanitizedElement.appendChild(sanitized);\n }\n return sanitizedElement;\n}\n\nfunction appendPlainProductDescription(target: HTMLElement, text: string): void {\n const paragraphs = text\n .split(/\\n{2,}/)\n .map((paragraph) => paragraph.replace(/\\s+/g, ' ').trim())\n .filter(Boolean);\n\n if (paragraphs.length === 0) return;\n\n for (const paragraph of paragraphs) {\n const p = document.createElement('p');\n p.textContent = paragraph;\n target.appendChild(p);\n }\n}\n\nfunction appendProductDescription(target: HTMLElement, description: ProductDescriptionContent): void {\n target.classList.add('gengage-chat-product-description');\n\n if (description.html && typeof DOMParser !== 'undefined') {\n const doc = new DOMParser().parseFromString(description.html, 'text/html');\n const nodes = Array.from(doc.body.childNodes)\n .map((node) => sanitizeProductDescriptionNode(node))\n .filter((node): node is Node => !!node && (node.nodeType === Node.ELEMENT_NODE || !!node.textContent?.trim()));\n\n if (nodes.length > 0) {\n for (const node of nodes) target.appendChild(node);\n return;\n }\n }\n\n appendPlainProductDescription(target, description.text);\n}\n\nfunction renderProductDetailTabs(\n description: ProductDescriptionContent | undefined,\n specifications: Record<string, string> | Array<{ key: string; value: string }> | undefined,\n ctx: UISpecRenderContext,\n): HTMLElement {\n const container = document.createElement('div');\n container.className = 'gengage-chat-product-detail-tabs';\n\n const tabBar = document.createElement('div');\n tabBar.className = 'gengage-chat-product-detail-tab-bar gds-toolbar';\n\n const tabPanels: HTMLElement[] = [];\n\n // Product Info tab\n if (description) {\n const tab = document.createElement('button');\n tab.className = 'gengage-chat-product-detail-tab gds-tab gengage-chat-product-detail-tab--active is-active';\n tab.type = 'button';\n tab.setAttribute('aria-selected', 'true');\n tab.textContent = ctx.i18n?.productInfoTab ?? 'Product Info';\n tabBar.appendChild(tab);\n\n const panel = document.createElement('div');\n panel.className = 'gengage-chat-product-detail-tab-panel';\n appendProductDescription(panel, description);\n tabPanels.push(panel);\n }\n\n // Specifications tab\n if (specifications) {\n const tab = document.createElement('button');\n tab.className = `gengage-chat-product-detail-tab gds-tab${!description ? ' gengage-chat-product-detail-tab--active is-active' : ''}`;\n tab.type = 'button';\n tab.setAttribute('aria-selected', description ? 'false' : 'true');\n tab.textContent = ctx.i18n?.specificationsTab ?? 'Specifications';\n tabBar.appendChild(tab);\n\n const panel = document.createElement('div');\n panel.className = 'gengage-chat-product-detail-tab-panel';\n if (description) {\n panel.style.display = 'none';\n }\n\n const table = document.createElement('table');\n table.className = 'gengage-chat-product-specs-table';\n const entries = Array.isArray(specifications)\n ? specifications\n : Object.entries(specifications).map(([key, value]) => ({ key, value }));\n for (const entry of entries) {\n const row = document.createElement('tr');\n const keyCell = document.createElement('td');\n keyCell.className = 'gengage-chat-product-specs-key';\n keyCell.textContent = entry.key;\n const valCell = document.createElement('td');\n valCell.className = 'gengage-chat-product-specs-value';\n valCell.textContent = entry.value;\n row.appendChild(keyCell);\n row.appendChild(valCell);\n table.appendChild(row);\n }\n panel.appendChild(table);\n tabPanels.push(panel);\n }\n\n // Wire up tab switching\n const tabs = tabBar.querySelectorAll('.gengage-chat-product-detail-tab');\n tabs.forEach((tabEl, idx) => {\n tabEl.addEventListener('click', () => {\n tabs.forEach((t) => {\n t.classList.remove('gengage-chat-product-detail-tab--active', 'is-active');\n t.setAttribute('aria-selected', 'false');\n });\n tabEl.classList.add('gengage-chat-product-detail-tab--active', 'is-active');\n tabEl.setAttribute('aria-selected', 'true');\n tabPanels.forEach((p, pIdx) => {\n p.style.display = pIdx === idx ? '' : 'none';\n });\n });\n });\n\n container.appendChild(tabBar);\n for (const p of tabPanels) container.appendChild(p);\n return container;\n}\n\n/** Lucide-style stroke icons (matches ChatDrawer / header SVGs). */\ntype ProductSortIconKind = 'related' | 'priceAsc' | 'priceDesc';\n\nfunction productSortIconSvgHtml(kind: ProductSortIconKind): string {\n const a =\n 'width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"';\n switch (kind) {\n case 'related':\n return `<svg ${a} aria-hidden=\"true\"><path d=\"M13 2 3 14h9l-1 8 10-12h-9l1-8z\"/></svg>`;\n case 'priceAsc':\n return `<svg ${a} aria-hidden=\"true\"><polyline points=\"22 7 13.5 15.5 8.5 10.5 2 17\"/><polyline points=\"16 7 22 7 22 13\"/></svg>`;\n case 'priceDesc':\n return `<svg ${a} aria-hidden=\"true\"><polyline points=\"22 17 13.5 8.5 8.5 13.5 2 7\"/><polyline points=\"16 17 22 17 22 11\"/></svg>`;\n default:\n return '';\n }\n}\n\nfunction productSortChevronSvgHtml(): string {\n return `<svg class=\"gengage-chat-product-sort-chevron\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"6 9 12 15 18 9\"/></svg>`;\n}\n\nfunction productSortCheckSvgHtml(): string {\n return `<svg class=\"gengage-chat-product-sort-check\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"20 6 9 17 4 12\"/></svg>`;\n}\n\n/** Left-right arrows (compare / swap), Lucide arrow-left-right style. */\nfunction comparisonToggleIconSvgHtml(): string {\n const a =\n 'width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"';\n return `<svg ${a} aria-hidden=\"true\"><path d=\"M8 3 4 7l4 4\"/><path d=\"M16 21l4-4-4-4\"/><path d=\"M4 7h16\"/><path d=\"M20 17H4\"/></svg>`;\n}\n\nfunction productSortStatesEqual(a: ProductSortState, b: ProductSortState): boolean {\n return a.type === b.type && a.direction === b.direction;\n}\n\nfunction getSortedChildIds(childIds: string[], spec: UISpec, sort?: ProductSortState): string[] {\n if (!sort || sort.type === 'related') return childIds;\n\n const withPrice = childIds.map((id) => {\n const el = spec.elements[id];\n const product = el?.props?.['product'] as Record<string, unknown> | undefined;\n const price = product ? Number(product['price']) : NaN;\n return { id, price: Number.isFinite(price) ? price : Infinity };\n });\n\n withPrice.sort((a, b) => {\n if (a.price === Infinity && b.price === Infinity) return 0;\n if (a.price === Infinity) return 1;\n if (b.price === Infinity) return -1;\n return sort.direction === 'desc' ? b.price - a.price : a.price - b.price;\n });\n\n return withPrice.map((x) => x.id);\n}\n\nfunction resortGrid(grid: HTMLElement, childIds: string[], spec: UISpec, sort: ProductSortState): void {\n const sorted = getSortedChildIds(childIds, spec, sort);\n // Build map from element ID data attribute to DOM element\n const childMap = new Map<string, HTMLElement>();\n for (const child of Array.from(grid.children) as HTMLElement[]) {\n const elId = child.dataset['elementId'];\n if (elId) childMap.set(elId, child);\n }\n\n for (const id of sorted) {\n const el = childMap.get(id);\n if (el) grid.appendChild(el);\n }\n}\n\nfunction renderProductGrid(\n element: UIElement,\n spec: UISpec,\n renderElement: (elementId: string) => HTMLElement | null,\n ctx?: UISpecRenderContext,\n): HTMLElement {\n const wrapper = document.createElement('div');\n wrapper.className = 'gengage-chat-product-grid-wrapper';\n\n const childIds = element.children ?? [];\n const grid = document.createElement('div');\n grid.className = 'gengage-chat-product-grid';\n\n const inlineHead = ctx?.panelProductListHeading;\n const hasSortToolbar = childIds.length > 1 && ctx?.onSortChange;\n\n // Sort + compare toolbar (only when >1 children and context has sort support)\n if (hasSortToolbar) {\n const toolbar = document.createElement('div');\n toolbar.className = 'gengage-chat-product-sort-toolbar';\n toolbar.setAttribute('role', 'toolbar');\n toolbar.setAttribute('aria-label', ctx.i18n?.sortToolbarAriaLabel ?? 'Sort products');\n\n let currentSort: ProductSortState = ctx.productSort ?? { type: 'related' };\n\n const sortOptions: Array<{ label: string; sortState: ProductSortState; icon: ProductSortIconKind }> = [\n { label: ctx.i18n?.sortRelated ?? 'Related', sortState: { type: 'related' }, icon: 'related' },\n {\n label: ctx.i18n?.sortPriceAsc ?? 'Price ↑',\n sortState: { type: 'price', direction: 'asc' },\n icon: 'priceAsc',\n },\n {\n label: ctx.i18n?.sortPriceDesc ?? 'Price ↓',\n sortState: { type: 'price', direction: 'desc' },\n icon: 'priceDesc',\n },\n ];\n\n const dropdown = document.createElement('div');\n dropdown.className = 'gengage-chat-product-sort-dropdown';\n\n const trigger = document.createElement('button');\n trigger.type = 'button';\n trigger.className = 'gengage-chat-product-sort-trigger gds-btn gds-btn-ghost';\n trigger.setAttribute('aria-haspopup', 'listbox');\n trigger.setAttribute('aria-expanded', 'false');\n const sortAria = ctx.i18n?.sortToolbarAriaLabel ?? 'Sort products';\n\n const triggerIcon = document.createElement('span');\n triggerIcon.className = 'gengage-chat-product-sort-trigger-icon';\n const triggerLabel = document.createElement('span');\n triggerLabel.className = 'gengage-chat-product-sort-trigger-label';\n\n const syncTriggerFromSort = (s: ProductSortState): void => {\n const opt = sortOptions.find((o) => productSortStatesEqual(o.sortState, s)) ?? sortOptions[0]!;\n triggerLabel.textContent = opt.label;\n triggerIcon.innerHTML = productSortIconSvgHtml(opt.icon);\n dropdown.dataset['sortIcon'] = opt.icon;\n trigger.setAttribute('aria-label', `${sortAria}: ${opt.label}`);\n trigger.title = opt.label;\n };\n syncTriggerFromSort(currentSort);\n\n const chevWrap = document.createElement('span');\n chevWrap.className = 'gengage-chat-product-sort-trigger-chevron';\n chevWrap.innerHTML = productSortChevronSvgHtml();\n\n trigger.appendChild(triggerIcon);\n trigger.appendChild(triggerLabel);\n trigger.appendChild(chevWrap);\n\n const menu = document.createElement('div');\n menu.className = 'gengage-chat-product-sort-menu gds-menu';\n menu.hidden = true;\n menu.setAttribute('role', 'listbox');\n menu.setAttribute('aria-label', sortAria);\n\n const doc = toolbar.ownerDocument;\n let menuOverlayAbort: AbortController | null = null;\n\n const closeSortMenu = (): void => {\n menu.hidden = true;\n dropdown.classList.remove('gengage-chat-product-sort-dropdown--open');\n trigger.setAttribute('aria-expanded', 'false');\n menuOverlayAbort?.abort();\n menuOverlayAbort = null;\n };\n\n /** Bubble-phase click avoids fighting trigger mousedown/pointerdown (menu kapanıp tekrar açılması). */\n const onSortMenuOutsideClick = (e: MouseEvent): void => {\n if (!dropdown.classList.contains('gengage-chat-product-sort-dropdown--open')) return;\n if (dropdown.contains(e.target as Node)) return;\n closeSortMenu();\n };\n\n const onSortMenuEscape = (e: KeyboardEvent): void => {\n if (e.key === 'Escape') {\n e.preventDefault();\n closeSortMenu();\n }\n };\n\n const openSortMenu = (): void => {\n menu.hidden = false;\n dropdown.classList.add('gengage-chat-product-sort-dropdown--open');\n trigger.setAttribute('aria-expanded', 'true');\n menuOverlayAbort = new AbortController();\n const { signal } = menuOverlayAbort;\n doc.addEventListener('click', onSortMenuOutsideClick, { signal });\n doc.addEventListener('keydown', onSortMenuEscape, { capture: true, signal });\n };\n\n trigger.addEventListener('click', (e) => {\n e.stopPropagation();\n if (dropdown.classList.contains('gengage-chat-product-sort-dropdown--open')) {\n closeSortMenu();\n } else {\n openSortMenu();\n }\n });\n\n for (const opt of sortOptions) {\n const optionBtn = document.createElement('button');\n optionBtn.type = 'button';\n optionBtn.className = 'gengage-chat-product-sort-option gds-menu-option';\n optionBtn.setAttribute('role', 'option');\n const isActive = productSortStatesEqual(currentSort, opt.sortState);\n optionBtn.setAttribute('aria-selected', isActive ? 'true' : 'false');\n if (isActive) optionBtn.classList.add('gengage-chat-product-sort-option--active', 'gds-menu-option-active');\n const sortKey = opt.sortState.type === 'related' ? 'related' : `price-${opt.sortState.direction ?? ''}`;\n optionBtn.dataset['sortKey'] = sortKey;\n\n const oIcon = document.createElement('span');\n oIcon.className = 'gengage-chat-product-sort-option-icon';\n oIcon.innerHTML = productSortIconSvgHtml(opt.icon);\n\n const oLabel = document.createElement('span');\n oLabel.className = 'gengage-chat-product-sort-option-label';\n oLabel.textContent = opt.label;\n\n const oCheck = document.createElement('span');\n oCheck.className = 'gengage-chat-product-sort-option-check';\n oCheck.innerHTML = productSortCheckSvgHtml();\n oCheck.setAttribute('aria-hidden', 'true');\n if (!isActive) oCheck.classList.add('gengage-chat-product-sort-option-check--hidden');\n\n optionBtn.appendChild(oIcon);\n optionBtn.appendChild(oLabel);\n optionBtn.appendChild(oCheck);\n\n optionBtn.addEventListener('click', () => {\n currentSort = opt.sortState;\n ctx.onSortChange?.(opt.sortState);\n resortGrid(grid, childIds, spec, opt.sortState);\n menu.querySelectorAll('.gengage-chat-product-sort-option').forEach((el) => {\n const btn = el as HTMLButtonElement;\n const active = btn.dataset['sortKey'] === sortKey;\n btn.classList.toggle('gengage-chat-product-sort-option--active', active);\n btn.classList.toggle('gds-menu-option-active', active);\n btn.setAttribute('aria-selected', active ? 'true' : 'false');\n const check = btn.querySelector('.gengage-chat-product-sort-option-check');\n check?.classList.toggle('gengage-chat-product-sort-option-check--hidden', !active);\n });\n syncTriggerFromSort(opt.sortState);\n closeSortMenu();\n });\n\n menu.appendChild(optionBtn);\n }\n\n dropdown.appendChild(trigger);\n dropdown.appendChild(menu);\n toolbar.appendChild(dropdown);\n\n if (ctx.onToggleComparisonSku) {\n const compareBtn = document.createElement('button');\n compareBtn.className = 'gengage-chat-comparison-toggle-btn gds-btn gds-btn-ghost';\n compareBtn.type = 'button';\n if (ctx.comparisonSelectMode) {\n compareBtn.classList.add('gengage-chat-comparison-toggle-btn--active');\n }\n if (ctx.isStreaming) {\n compareBtn.classList.add('gengage-chat-comparison-toggle-btn--hidden');\n }\n const compareIcon = document.createElement('span');\n compareIcon.className = 'gengage-chat-comparison-toggle-icon';\n compareIcon.innerHTML = comparisonToggleIconSvgHtml();\n const compareLabel = document.createElement('span');\n compareLabel.className = 'gengage-chat-comparison-toggle-label';\n const compareText = ctx.i18n?.compareSelected ?? 'Compare';\n compareLabel.textContent = compareText;\n compareBtn.setAttribute('aria-label', compareText);\n compareBtn.title = compareText;\n compareBtn.appendChild(compareIcon);\n compareBtn.appendChild(compareLabel);\n compareBtn.addEventListener('click', () => {\n ctx.onToggleComparisonSku?.('');\n });\n toolbar.appendChild(compareBtn);\n }\n\n if (inlineHead) {\n toolbar.classList.add('gengage-chat-product-sort-toolbar--inline');\n const head = document.createElement('div');\n head.className = 'gengage-chat-product-grid-head';\n const titleEl = document.createElement('span');\n titleEl.className = 'gengage-chat-product-grid-head-title';\n titleEl.textContent = inlineHead;\n head.appendChild(titleEl);\n const actions = document.createElement('div');\n actions.className = 'gengage-chat-product-grid-head-actions';\n actions.appendChild(toolbar);\n head.appendChild(actions);\n wrapper.appendChild(head);\n } else {\n wrapper.appendChild(toolbar);\n }\n } else if (inlineHead) {\n const head = document.createElement('div');\n head.className = 'gengage-chat-product-grid-head';\n const titleEl = document.createElement('span');\n titleEl.className = 'gengage-chat-product-grid-head-title';\n titleEl.textContent = inlineHead;\n head.appendChild(titleEl);\n wrapper.appendChild(head);\n }\n\n const sortedIds = getSortedChildIds(childIds, spec, ctx?.productSort);\n for (const childId of sortedIds) {\n if (!spec.elements[childId]) continue;\n const rendered = renderElement(childId);\n if (rendered) {\n rendered.dataset['elementId'] = childId;\n grid.appendChild(rendered);\n }\n }\n\n // Mobile variant: horizontal scroll\n if (ctx?.isMobile ?? isMobileViewport()) {\n grid.classList.add('gengage-chat-product-grid--mobile');\n }\n\n wrapper.appendChild(grid);\n\n // \"View More\" button (only when endOfList is not true)\n const endOfList = element.props?.['endOfList'] as boolean | undefined;\n if (endOfList !== true && childIds.length > 0) {\n const viewMoreTitle = ctx?.i18n?.viewMoreLabel ?? 'Show More';\n const viewMoreBtn = document.createElement('button');\n viewMoreBtn.className = 'gengage-chat-product-grid-view-more';\n viewMoreBtn.type = 'button';\n viewMoreBtn.textContent = viewMoreTitle;\n viewMoreBtn.addEventListener('click', () => {\n ctx?.onAction({ title: viewMoreTitle, type: 'moreProductList', payload: {} });\n });\n wrapper.appendChild(viewMoreBtn);\n }\n\n // Floating comparison dock — desktop: inside grid wrapper; mobile: index _refreshComparisonUI mounts into drawer slot\n const isMobileGrid = ctx?.isMobile ?? isMobileViewport();\n if (ctx?.comparisonSelectMode && ctx.comparisonSelectedSkus && !isMobileGrid) {\n const floatingBtn = renderFloatingComparisonButton(ctx.comparisonSelectedSkus, ctx);\n wrapper.appendChild(floatingBtn);\n }\n\n return wrapper;\n}\n\nfunction renderComparisonTableElement(element: UIElement, ctx: UISpecRenderContext): HTMLElement {\n const props = element.props ?? {};\n const keyDifferencesHtml = props['keyDifferencesHtml'] as string | undefined;\n const recommended = props['recommended'] as ComparisonProduct | undefined;\n const products = (props['products'] as ComparisonProduct[] | undefined) ?? [];\n const attributes = (props['attributes'] as ComparisonAttribute[] | undefined) ?? [];\n const highlights = (props['highlights'] as string[] | undefined) ?? [];\n const specialCases = props['specialCases'] as string[] | undefined;\n const recommendedText = props['recommendedText'] as string | undefined;\n const winnerHits = props['winnerHits'] as Record<string, { positive?: string[]; negative?: string[] }> | undefined;\n const productActions = props['productActions'] as\n | Record<string, { title: string; type: string; payload?: unknown }>\n | undefined;\n\n if (!recommended) {\n const fallback = document.createElement('div');\n return fallback;\n }\n\n const options: import('./ComparisonTable.js').ComparisonTableOptions = {\n recommended,\n products,\n attributes,\n highlights,\n specialCases,\n onProductClick: ({ sku, name }) => {\n ctx.onProductClick?.({ sku, url: '', name });\n },\n pricing: ctx.pricing,\n };\n if (recommendedText !== undefined) options.recommendedText = recommendedText;\n if (winnerHits !== undefined) options.winnerHits = winnerHits;\n if (productActions !== undefined) options.productActions = productActions;\n if (keyDifferencesHtml !== undefined) options.keyDifferencesHtml = keyDifferencesHtml;\n if (ctx.i18n) {\n options.i18n = {\n comparisonHeading: ctx.i18n.panelTitleComparisonResults,\n recommendedChoiceLabel: ctx.i18n.recommendedChoiceLabel,\n highlightsLabel: ctx.i18n.highlightsLabel,\n keyDifferencesLabel: ctx.i18n.keyDifferencesLabel,\n viewMoreLabel: ctx.i18n.viewMoreLabel,\n specialCasesLabel: ctx.i18n.specialCasesLabel,\n addToCartButton: ctx.i18n.addToCartButton,\n };\n }\n\n const el = renderComparisonTable(options);\n\n // Mobile variant\n if (ctx.isMobile ?? isMobileViewport()) {\n el.classList.add('gengage-chat-comparison--mobile');\n }\n\n return el;\n}\n\nfunction renderDivider(element: UIElement): HTMLElement {\n const hr = document.createElement('hr');\n hr.className = 'gengage-chat-divider';\n const label = element.props?.['label'] as string | undefined;\n if (label) {\n const wrapper = document.createElement('div');\n wrapper.className = 'gengage-chat-divider-wrapper';\n const labelEl = document.createElement('span');\n labelEl.className = 'gengage-chat-divider-label';\n labelEl.textContent = label;\n wrapper.appendChild(hr);\n wrapper.appendChild(labelEl);\n const hr2 = document.createElement('hr');\n hr2.className = 'gengage-chat-divider';\n wrapper.appendChild(hr2);\n return wrapper;\n }\n return hr;\n}\n","/**\n * Block-by-block HTML typewriter effect.\n *\n * Parses sanitised HTML into top-level blocks and reveals them one at a time\n * with a configurable stagger delay. Respects prefers-reduced-motion.\n */\n\nexport interface TypewriterOptions {\n container: HTMLElement;\n html: string;\n /** Delay in ms between block reveals (default: 30). */\n delayMs?: number;\n /** Called after each block is revealed — useful for scroll tracking. */\n onTick?: () => void;\n /** Called when all blocks have been revealed. */\n onComplete?: () => void;\n}\n\nexport interface TypewriterHandle {\n /** Skip animation and show all content immediately. */\n complete(): void;\n /** Cancel animation, leave content as-is. */\n cancel(): void;\n readonly isRunning: boolean;\n}\n\nconst BLOCK_ELEMENTS = new Set([\n 'P',\n 'DIV',\n 'H1',\n 'H2',\n 'H3',\n 'H4',\n 'H5',\n 'H6',\n 'LI',\n 'UL',\n 'OL',\n 'BLOCKQUOTE',\n 'PRE',\n 'TABLE',\n 'SECTION',\n 'HR',\n 'FIGURE',\n 'FIGCAPTION',\n 'DL',\n 'DT',\n 'DD',\n]);\n\n/**\n * Split parsed DOM children into logical blocks for reveal animation.\n * Block-level elements each become their own block.\n * Adjacent inline/text nodes are grouped together.\n */\nfunction splitIntoBlocks(nodes: NodeList): Node[][] {\n const blocks: Node[][] = [];\n let currentInline: Node[] = [];\n\n for (const node of nodes) {\n if (node.nodeType === Node.ELEMENT_NODE && BLOCK_ELEMENTS.has((node as Element).tagName)) {\n // Flush any pending inline group\n if (currentInline.length > 0) {\n blocks.push(currentInline);\n currentInline = [];\n }\n blocks.push([node]);\n } else {\n currentInline.push(node);\n }\n }\n\n if (currentInline.length > 0) {\n blocks.push(currentInline);\n }\n\n return blocks;\n}\n\nfunction containsTable(nodes: Node[][]): boolean {\n for (const block of nodes) {\n for (const node of block) {\n if (\n node.nodeType === Node.ELEMENT_NODE &&\n ((node as Element).tagName === 'TABLE' || (node as Element).querySelector?.('table'))\n ) {\n return true;\n }\n }\n }\n return false;\n}\n\nfunction prefersReducedMotion(): boolean {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return false;\n return window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n}\n\nexport function typewriteHtml(options: TypewriterOptions): TypewriterHandle {\n const { container, html, delayMs = 30, onTick, onComplete } = options;\n\n // Parse HTML into DOM nodes\n const template = document.createElement('template');\n template.innerHTML = html;\n const blocks = splitIntoBlocks(template.content.childNodes);\n\n // Skip animation for: reduced motion, single block, contains table, empty\n if (prefersReducedMotion() || blocks.length <= 1 || containsTable(blocks)) {\n container.innerHTML = html;\n onComplete?.();\n return { complete() {}, cancel() {}, isRunning: false };\n }\n\n // Clear container and start reveal\n container.innerHTML = '';\n let currentIndex = 0;\n let timerId: ReturnType<typeof setTimeout> | null = null;\n let running = true;\n\n function revealNext(): void {\n if (!running || currentIndex >= blocks.length) {\n running = false;\n onComplete?.();\n return;\n }\n\n const block = blocks[currentIndex]!;\n const wrapper = document.createElement('span');\n wrapper.className = 'gengage-chat-typewriter-block';\n for (const node of block) {\n wrapper.appendChild(node.cloneNode(true));\n }\n container.appendChild(wrapper);\n\n currentIndex++;\n onTick?.();\n\n if (currentIndex < blocks.length) {\n timerId = setTimeout(revealNext, delayMs);\n } else {\n running = false;\n onComplete?.();\n }\n }\n\n // Start the first reveal immediately\n revealNext();\n\n return {\n complete() {\n if (!running) return;\n if (timerId !== null) clearTimeout(timerId);\n running = false;\n container.innerHTML = html;\n onComplete?.();\n },\n cancel() {\n if (timerId !== null) clearTimeout(timerId);\n running = false;\n },\n get isRunning() {\n return running;\n },\n };\n}\n","/**\n * Product mention linker.\n *\n * After sanitiseHtml() renders bot text, this module walks text nodes and\n * wraps product name occurrences with clickable links that dispatch a\n * `launchSingleProduct` action.\n *\n * XSS safety: Uses DOM text-node manipulation only — no innerHTML.\n */\n\nexport interface ProductMention {\n sku: string;\n short_name: string;\n}\n\nexport interface ProductMentionLinkerOptions {\n container: HTMLElement;\n mentions: ProductMention[];\n onProductClick: (sku: string) => void;\n}\n\nfunction isWordChar(char: string | undefined): boolean {\n return char !== undefined && /[\\p{L}\\p{N}_]/u.test(char);\n}\n\n/**\n * Walk text nodes in `container` and wrap occurrences of each mention's\n * `short_name` with a clickable `<a>` element.\n *\n * Only the first occurrence of each mention is linked to avoid visual clutter.\n */\nexport function linkProductMentions(options: ProductMentionLinkerOptions): void {\n const { container, mentions, onProductClick } = options;\n if (mentions.length === 0) return;\n\n // Build a map of lowercase short_name → mention for case-insensitive matching\n const mentionMap = new Map<string, ProductMention>();\n for (const m of mentions) {\n if (m.short_name.length === 0) continue;\n mentionMap.set(m.short_name.toLowerCase(), m);\n }\n\n if (mentionMap.size === 0) return;\n\n // Process one mention at a time, re-walking the tree each time\n // (DOM mutations invalidate the walker)\n for (const [lowerName, mention] of mentionMap) {\n const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);\n let node = walker.nextNode();\n let found = false;\n\n while (node && !found) {\n const text = node.textContent ?? '';\n const idx = text.toLowerCase().indexOf(lowerName);\n if (idx === -1) {\n node = walker.nextNode();\n continue;\n }\n\n const prevChar = idx > 0 ? text[idx - 1] : undefined;\n const nextChar = text[idx + mention.short_name.length];\n if (isWordChar(prevChar) || isWordChar(nextChar)) {\n node = walker.nextNode();\n continue;\n }\n\n const before = text.slice(0, idx);\n const match = text.slice(idx, idx + mention.short_name.length);\n const after = text.slice(idx + mention.short_name.length);\n\n const parent = node.parentNode;\n if (!parent) {\n node = walker.nextNode();\n continue;\n }\n\n const link = document.createElement('a');\n link.className = 'gengage-product-mention';\n link.textContent = match;\n link.href = '#';\n link.addEventListener('click', (e) => {\n e.preventDefault();\n onProductClick(mention.sku);\n });\n\n if (before) parent.insertBefore(document.createTextNode(before), node);\n parent.insertBefore(link, node);\n if (after) parent.insertBefore(document.createTextNode(after), node);\n parent.removeChild(node);\n\n found = true;\n }\n }\n}\n","/**\n * Classifies suggested actions into input-area chips vs message-flow pills.\n *\n * Input-area chips: compact shortcuts above the input field (search, info, review, similar).\n * Message-flow pills: larger interactive cards in the suggestion row.\n */\n\nconst INPUT_AREA_ICONS = new Set(['search', 'info', 'review', 'similar']);\nconst INPUT_AREA_TYPES = new Set([\n 'quickAnswer',\n 'reviewSummary',\n 'searchDiscovery',\n 'launchDiscovery',\n 'exploreTogetherV2',\n]);\n\nexport function isInputAreaAction(btn: { icon?: string; action?: { type?: string } }): boolean {\n if (btn.icon && INPUT_AREA_ICONS.has(btn.icon)) return true;\n if (btn.action?.type && INPUT_AREA_TYPES.has(btn.action.type)) return true;\n return false;\n}\n","/**\n * Floating card that suggests the user try comparison mode.\n *\n * Shows in the panel bottom-right when a ProductGrid is displayed and\n * comparison mode is not active.\n */\n\nconst SESSION_STORAGE_KEY = 'gengage_choice_prompter_dismissed';\n/** Eski sürümler — clearChoicePrompterDismissState kaldırır. */\nconst LEGACY_GLOBAL_DISMISS_KEY = 'gengage_choice_prompter_dismissed_global';\n\nexport interface ChoicePrompterOptions {\n heading: string;\n suggestion: string;\n ctaLabel: string;\n threadId: string;\n onCtaClick: () => void;\n onDismiss?: () => void;\n dismissAriaLabel?: string;\n}\n\nexport function createChoicePrompter(options: ChoicePrompterOptions): HTMLElement {\n const card = document.createElement('div');\n card.className = 'gengage-chat-choice-prompter gds-card';\n card.dataset['gengagePart'] = 'choice-prompter';\n\n const copy = document.createElement('div');\n copy.className = 'gengage-chat-choice-prompter-copy';\n\n const headingEl = document.createElement('div');\n headingEl.className = 'gengage-chat-choice-prompter-heading';\n headingEl.textContent = options.heading;\n copy.appendChild(headingEl);\n\n const suggestionEl = document.createElement('div');\n suggestionEl.className = 'gengage-chat-choice-prompter-suggestion';\n suggestionEl.textContent = options.suggestion;\n copy.appendChild(suggestionEl);\n\n card.appendChild(copy);\n\n const cta = document.createElement('button');\n cta.type = 'button';\n cta.className = 'gengage-chat-choice-prompter-cta gds-btn gds-btn-primary';\n cta.dataset['gengagePart'] = 'choice-prompter-cta';\n cta.textContent = options.ctaLabel;\n cta.addEventListener('click', () => {\n markDismissed(options.threadId);\n card.remove();\n options.onCtaClick();\n });\n card.appendChild(cta);\n\n const dismiss = document.createElement('button');\n dismiss.type = 'button';\n dismiss.className = 'gengage-chat-choice-prompter-dismiss';\n dismiss.dataset['gengagePart'] = 'choice-prompter-dismiss';\n dismiss.textContent = '\\u00D7'; // × close\n dismiss.setAttribute('aria-label', options.dismissAriaLabel ?? 'Dismiss');\n dismiss.addEventListener('click', () => {\n markDismissed(options.threadId);\n card.remove();\n options.onDismiss?.();\n });\n card.appendChild(dismiss);\n\n return card;\n}\n\n/** Yeni kullanıcı araması başlarken çağrılır — kart tekrar gösterilebilir. */\nexport function clearChoicePrompterDismissState(): void {\n try {\n sessionStorage.removeItem(SESSION_STORAGE_KEY);\n sessionStorage.removeItem(LEGACY_GLOBAL_DISMISS_KEY);\n } catch {\n /* */\n }\n}\n\nexport function isChoicePrompterDismissed(threadId: string): boolean {\n try {\n const raw = sessionStorage.getItem(SESSION_STORAGE_KEY);\n if (!raw) return false;\n const dismissed: string[] = JSON.parse(raw);\n return dismissed.includes(threadId);\n } catch {\n return false;\n }\n}\n\nfunction markDismissed(threadId: string): void {\n try {\n const raw = sessionStorage.getItem(SESSION_STORAGE_KEY);\n const dismissed: string[] = raw ? (JSON.parse(raw) as string[]) : [];\n if (!dismissed.includes(threadId)) {\n dismissed.push(threadId);\n }\n sessionStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(dismissed));\n } catch {\n // sessionStorage unavailable — silently ignore\n }\n}\n\n/** Toolbar “Karşılaştır” gibi dış aksiyonlarda CTA ile aynı dismiss kaydı. */\nexport function recordChoicePrompterDismissedForThread(threadId: string): void {\n if (!threadId) return;\n markDismissed(threadId);\n}\n","/**\n * IndexedDB persistence for chat sessions, backend context, and message payloads.\n *\n * Three stores:\n * - `sessions` — full session state (messages, thread cursors, timestamps)\n * - `context` — per-thread backend context snapshots (compound key)\n * - `payload` — message UISpec payloads (lean message pattern)\n *\n * All operations are best-effort: IndexedDB unavailability is non-fatal.\n */\n\nimport type { SerializableChatMessage } from '../chat/types.js';\nimport type { UISpec } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Data shapes stored in each object store\n// ---------------------------------------------------------------------------\n\nexport interface SessionData {\n userId: string;\n appId: string;\n sessionId: string;\n messages: SerializableChatMessage[];\n currentThreadId: string | null;\n lastThreadId: string | null;\n createdAt: string;\n /** Thread IDs that produced panel content, in creation order. */\n panelThreads?: string[] | undefined;\n /** Product thumbnails for ThumbnailsColumn. */\n thumbnailEntries?: Array<{ sku: string; imageUrl: string; threadId: string }> | undefined;\n /** Serialized panel HTML keyed by bot message ID (for panel snapshot restore). */\n panelSnapshotHtml?: Record<string, string> | undefined;\n /** SKU active when session was saved — prevents cross-SKU restore. */\n sku?: string | undefined;\n}\n\nexport interface ContextData {\n sessionId: string;\n threadId: string;\n context: import('./types.js').BackendContext;\n}\n\nexport interface PayloadData {\n threadId: string;\n messageId: string;\n uiSpec: UISpec;\n}\n\nexport interface FavoriteData {\n userId: string;\n appId: string;\n sku: string;\n name?: string | undefined;\n imageUrl?: string | undefined;\n price?: string | undefined;\n savedAt: string;\n}\n\n// ---------------------------------------------------------------------------\n// GengageIndexedDB\n// ---------------------------------------------------------------------------\n\nconst DB_NAME = 'gengage_assistant';\nconst DB_VERSION = 3;\n\nconst STORE_SESSIONS = 'sessions';\nconst STORE_CONTEXT = 'context';\nconst STORE_PAYLOAD = 'payload';\nconst STORE_FAVORITES = 'favorites';\n\n/**\n * Wrap an IDBRequest in a Promise.\n */\nfunction requestToPromise<T>(request: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n}\n\n/**\n * Wrap an IDBTransaction completion in a Promise.\n */\nfunction transactionComplete(tx: IDBTransaction): Promise<void> {\n return new Promise((resolve, reject) => {\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n tx.onabort = () => reject(tx.error ?? new DOMException('Transaction aborted'));\n });\n}\n\nexport class GengageIndexedDB {\n private _db: IDBDatabase | null = null;\n private _dbName: string;\n private _version: number;\n\n constructor(dbName: string = DB_NAME, version: number = DB_VERSION) {\n this._dbName = dbName;\n this._version = version;\n }\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n async open(): Promise<IDBDatabase> {\n if (this._db) return this._db;\n\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(this._dbName, this._version);\n\n request.onupgradeneeded = (event) => {\n const db = request.result;\n const oldVersion = (event as IDBVersionChangeEvent).oldVersion;\n\n if (oldVersion < 1) {\n // Fresh install — create stores with v3 schema directly\n db.createObjectStore(STORE_SESSIONS, { keyPath: ['userId', 'appId', 'sessionId'] });\n db.createObjectStore(STORE_CONTEXT, { keyPath: ['sessionId', 'threadId'] });\n const payloadStore = db.createObjectStore(STORE_PAYLOAD, {\n keyPath: ['threadId', 'messageId'],\n });\n payloadStore.createIndex('threadId', 'threadId', { unique: false });\n db.createObjectStore(STORE_FAVORITES, { keyPath: ['userId', 'appId', 'sku'] });\n }\n\n if (oldVersion >= 1 && oldVersion < 2) {\n // Upgrade from v1 — drop old stores, recreate with new keys\n if (db.objectStoreNames.contains(STORE_SESSIONS)) db.deleteObjectStore(STORE_SESSIONS);\n if (db.objectStoreNames.contains(STORE_PAYLOAD)) db.deleteObjectStore(STORE_PAYLOAD);\n db.createObjectStore(STORE_SESSIONS, { keyPath: ['userId', 'appId', 'sessionId'] });\n const payloadStore = db.createObjectStore(STORE_PAYLOAD, {\n keyPath: ['threadId', 'messageId'],\n });\n payloadStore.createIndex('threadId', 'threadId', { unique: false });\n // Ensure context store exists (may be absent in early v1 schemas)\n if (!db.objectStoreNames.contains(STORE_CONTEXT)) {\n db.createObjectStore(STORE_CONTEXT, { keyPath: ['sessionId', 'threadId'] });\n }\n }\n\n if (oldVersion < 3) {\n if (!db.objectStoreNames.contains(STORE_FAVORITES)) {\n db.createObjectStore(STORE_FAVORITES, { keyPath: ['userId', 'appId', 'sku'] });\n }\n }\n };\n\n request.onsuccess = () => {\n this._db = request.result;\n resolve(this._db);\n };\n\n request.onerror = () => reject(request.error);\n });\n }\n\n close(): void {\n this._db?.close();\n this._db = null;\n }\n\n // -------------------------------------------------------------------------\n // Sessions\n // -------------------------------------------------------------------------\n\n async saveSession(data: SessionData): Promise<void> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_SESSIONS, 'readwrite');\n tx.objectStore(STORE_SESSIONS).put(data);\n await transactionComplete(tx);\n }\n\n async loadSession(userId: string, appId: string, sessionId: string): Promise<SessionData | null> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_SESSIONS, 'readonly');\n const result = await requestToPromise(tx.objectStore(STORE_SESSIONS).get([userId, appId, sessionId]));\n return (result as SessionData | undefined) ?? null;\n }\n\n // -------------------------------------------------------------------------\n // Context (compound key: [sessionId, threadId])\n // -------------------------------------------------------------------------\n\n async saveContext(data: ContextData): Promise<void> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_CONTEXT, 'readwrite');\n tx.objectStore(STORE_CONTEXT).put(data);\n await transactionComplete(tx);\n }\n\n async loadContext(sessionId: string, threadId: string): Promise<ContextData | null> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_CONTEXT, 'readonly');\n const result = await requestToPromise(tx.objectStore(STORE_CONTEXT).get([sessionId, threadId]));\n return (result as ContextData | undefined) ?? null;\n }\n\n /**\n * Delete all context entries for a session whose threadId is lexicographically\n * greater than the given threadId. Used during rollback to prune future branches.\n *\n * Thread IDs are UUIDv7 (lexicographically sortable by time), so string\n * comparison is sufficient.\n */\n async deleteContextsAfterThread(sessionId: string, threadId: string): Promise<void> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_CONTEXT, 'readwrite');\n const store = tx.objectStore(STORE_CONTEXT);\n\n // Open cursor over all entries and filter by sessionId + threadId comparison\n const request = store.openCursor();\n\n await new Promise<void>((resolve, reject) => {\n request.onsuccess = () => {\n const cursor = request.result;\n if (!cursor) {\n resolve();\n return;\n }\n const entry = cursor.value as ContextData;\n if (entry.sessionId === sessionId && entry.threadId > threadId) {\n try {\n cursor.delete();\n } catch {\n // cursor.delete() may fail on read-only or version-change transactions — non-fatal\n }\n }\n cursor.continue();\n };\n request.onerror = () => reject(request.error);\n });\n\n await transactionComplete(tx);\n }\n\n /**\n * Load the most recent context for a session (latest threadId).\n * Uses lexicographic ordering of UUIDv7 threadIds for chronological sort.\n */\n async loadLatestContext(sessionId: string): Promise<ContextData | null> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_CONTEXT, 'readonly');\n const store = tx.objectStore(STORE_CONTEXT);\n\n // UUIDv7 thread IDs are ASCII hex+hyphens, so \\uffff is a safe upper bound\n const range = IDBKeyRange.bound([sessionId, ''], [sessionId, '\\uffff']);\n\n return new Promise((resolve, reject) => {\n const request = store.openCursor(range, 'prev');\n request.onsuccess = () => {\n const cursor = request.result;\n resolve(cursor ? (cursor.value as ContextData) : null);\n };\n request.onerror = () => reject(request.error);\n });\n }\n\n // -------------------------------------------------------------------------\n // Payload\n // -------------------------------------------------------------------------\n\n async savePayload(data: PayloadData): Promise<void> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_PAYLOAD, 'readwrite');\n tx.objectStore(STORE_PAYLOAD).put(data);\n await transactionComplete(tx);\n }\n\n async loadPayload(threadId: string, messageId: string): Promise<PayloadData | null> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_PAYLOAD, 'readonly');\n const result = await requestToPromise(tx.objectStore(STORE_PAYLOAD).get([threadId, messageId]));\n return (result as PayloadData | undefined) ?? null;\n }\n\n async loadPayloadsByThread(threadId: string): Promise<PayloadData[]> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_PAYLOAD, 'readonly');\n const index = tx.objectStore(STORE_PAYLOAD).index('threadId');\n const results: PayloadData[] = [];\n\n return new Promise((resolve, reject) => {\n const request = index.openCursor(IDBKeyRange.only(threadId));\n request.onsuccess = () => {\n const cursor = request.result;\n if (!cursor) {\n resolve(results);\n return;\n }\n results.push(cursor.value as PayloadData);\n cursor.continue();\n };\n request.onerror = () => reject(request.error);\n });\n }\n\n // -------------------------------------------------------------------------\n // Favorites\n // -------------------------------------------------------------------------\n\n async saveFavorite(data: FavoriteData): Promise<void> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_FAVORITES, 'readwrite');\n tx.objectStore(STORE_FAVORITES).put(data);\n await transactionComplete(tx);\n }\n\n async removeFavorite(userId: string, appId: string, sku: string): Promise<void> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_FAVORITES, 'readwrite');\n tx.objectStore(STORE_FAVORITES).delete([userId, appId, sku]);\n await transactionComplete(tx);\n }\n\n async loadFavorites(userId: string, appId: string): Promise<FavoriteData[]> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_FAVORITES, 'readonly');\n const all = await requestToPromise(tx.objectStore(STORE_FAVORITES).getAll());\n return (all as FavoriteData[]).filter((f) => f.userId === userId && f.appId === appId);\n }\n\n async isFavorite(userId: string, appId: string, sku: string): Promise<boolean> {\n const db = this._requireDb();\n const tx = db.transaction(STORE_FAVORITES, 'readonly');\n const result = await requestToPromise(tx.objectStore(STORE_FAVORITES).get([userId, appId, sku]));\n return result !== undefined;\n }\n\n // -------------------------------------------------------------------------\n // Internal\n // -------------------------------------------------------------------------\n\n private _requireDb(): IDBDatabase {\n if (!this._db) {\n throw new Error('GengageIndexedDB: database not open. Call open() first.');\n }\n return this._db;\n }\n}\n","/**\n * Extended Mode Manager — production lock-count system for panel extension.\n *\n * Controls whether the host page PDP area should be maximized or minimized\n * based on a combination of conditions: lock count, user visibility preference,\n * favorites mode, panel content type, and chat visibility.\n */\n\nexport type PanelContentType =\n | 'comparisonTable'\n | 'groupList'\n | 'productDetails'\n | 'productDetailsSimilars'\n | 'productList';\n\n/**\n * Base content types that always trigger panel extension.\n * `productDetails` is only added when `productDetailsInPanel` is true\n * (i.e. demo websites). Regular accounts show product details inline in ChatPane.\n */\nconst BASE_PANEL_CONTENT_TYPES: readonly PanelContentType[] = [\n 'comparisonTable',\n 'groupList',\n 'productDetailsSimilars',\n 'productList',\n];\n\nexport interface ExtendedModeManagerOptions {\n onChange: (extended: boolean) => void;\n /** Whether `productDetails` should trigger panel extension (default: false, true for demo sites). */\n productDetailsInPanel?: boolean;\n}\n\nexport class ExtendedModeManager {\n private _lockCount = 1; // starts locked\n private _hiddenByUser = false;\n private _lastPanelContentType: PanelContentType | null = null;\n private _chatShown = false;\n private _isFavoritesMode = false;\n private _lastExtended = false;\n private _onChange: (extended: boolean) => void;\n private _panelContentTypes: ReadonlySet<string>;\n\n constructor(options: ExtendedModeManagerOptions) {\n this._onChange = options.onChange;\n\n const types = new Set<string>(BASE_PANEL_CONTENT_TYPES);\n if (options.productDetailsInPanel) {\n types.add('productDetails');\n }\n this._panelContentTypes = types;\n }\n\n get isExtended(): boolean {\n return (\n this._lockCount === 0 &&\n !this._hiddenByUser &&\n !this._isFavoritesMode &&\n this._lastPanelContentType !== null &&\n this._panelContentTypes.has(this._lastPanelContentType) &&\n this._chatShown\n );\n }\n\n unlock(): void {\n if (this._lockCount > 0) {\n this._lockCount--;\n }\n this._checkStateChange();\n }\n\n lock(): void {\n this._lockCount++;\n this._checkStateChange();\n }\n\n setHiddenByUser(hidden: boolean): void {\n this._hiddenByUser = hidden;\n this._checkStateChange();\n }\n\n setChatShown(shown: boolean): void {\n this._chatShown = shown;\n this._checkStateChange();\n }\n\n setFavoritesMode(fav: boolean): void {\n this._isFavoritesMode = fav;\n this._checkStateChange();\n }\n\n setPanelContentType(type: PanelContentType | null): void {\n this._lastPanelContentType = type;\n this._checkStateChange();\n }\n\n private _checkStateChange(): void {\n const current = this.isExtended;\n if (current !== this._lastExtended) {\n this._lastExtended = current;\n this._onChange(current);\n }\n }\n}\n","/**\n * PanelManager — manages the two-pane panel state for the chat widget.\n *\n * Owns panel snapshots, snapshot types, active message highlighting,\n * panel thread navigation, topbar updates, and extended mode coordination.\n *\n * Extracted from chat/index.ts to improve cohesion and reduce file size.\n */\n\nimport type { ChatDrawer } from './components/ChatDrawer.js';\nimport type { CommunicationBridge } from '../common/communication-bridge.js';\nimport type { ExtendedModeManager, PanelContentType } from './extendedModeManager.js';\nimport type { ChatI18n } from './types.js';\nimport type { UISpec, UIElement } from '../common/types.js';\n\n/** Minimal interface the panel manager needs from its host widget. */\nexport interface PanelManagerDeps {\n drawer: () => ChatDrawer | null;\n shadow: () => ShadowRoot | null;\n currentThreadId: () => string | null;\n bridge: () => CommunicationBridge | null;\n extendedModeManager: () => ExtendedModeManager | null;\n i18n: () => ChatI18n;\n rollbackToThread: (threadId: string) => void;\n}\n\nexport class PanelManager {\n /** Panel content snapshots keyed by bot message ID for history navigation. */\n readonly snapshots = new Map<string, HTMLElement>();\n /** Rebuild functions keyed by message ID — produce fresh DOM with live event listeners. */\n private readonly _snapshotRebuilders = new Map<string, () => HTMLElement>();\n /** Component type for each panel snapshot (for topbar title restoration). */\n readonly snapshotTypes = new Map<string, string>();\n /** Currently active (highlighted) message ID in the chat pane. */\n activePanelMessageId: string | null = null;\n /** Current panel component type. */\n currentType: string | null = null;\n /** Thread IDs that have panel content, in order of creation. */\n threads: string[] = [];\n /** Action type that triggered the current stream (for panel title disambiguation). */\n lastActionType: string | null = null;\n\n constructor(private readonly deps: PanelManagerDeps) {}\n\n /**\n * Snapshot the current panel state for a message. Stores a rebuild function\n * (preferred — produces fresh DOM with live event listeners) alongside a\n * static DOM clone as fallback.\n */\n snapshotForMessage(messageId: string, rebuild?: (() => HTMLElement) | undefined): void {\n const drawer = this.deps.drawer();\n if (!drawer?.hasPanelContent()) return;\n // Never snapshot loading skeleton — it must not be persisted or restored\n if (drawer.isPanelLoading()) return;\n const contentEl = drawer.getPanelContentElement();\n if (!contentEl) return;\n const clone = contentEl.cloneNode(true) as HTMLElement;\n this.snapshots.set(messageId, clone);\n if (rebuild) this._snapshotRebuilders.set(messageId, rebuild);\n // Store the component type so topbar can be restored with the right title\n if (this.currentType) {\n this.snapshotTypes.set(messageId, this.currentType);\n }\n }\n\n /**\n * Restore the panel content snapshot associated with a given message ID.\n * Highlights the active message and de-highlights the previous one.\n * Also restores the panel topbar title for the snapshot's component type.\n * Returns true if the snapshot was found and restored.\n */\n restoreForMessage(messageId: string): boolean {\n // Prefer rebuild function (live event listeners) over static DOM clone\n const rebuild = this._snapshotRebuilders.get(messageId);\n const snapshot = this.snapshots.get(messageId);\n if (!rebuild && !snapshot) return false;\n\n const shadow = this.deps.shadow();\n const drawer = this.deps.drawer();\n\n // De-highlight previous active message\n if (this.activePanelMessageId) {\n const prev = shadow?.querySelector(`[data-message-id=\"${CSS.escape(this.activePanelMessageId)}\"]`);\n prev?.classList.remove('gengage-chat-bubble--active');\n }\n\n // Highlight the clicked message\n const current = shadow?.querySelector(`[data-message-id=\"${CSS.escape(messageId)}\"]`);\n current?.classList.add('gengage-chat-bubble--active');\n this.activePanelMessageId = messageId;\n\n // Restore panel content — prefer rebuild for live interactions, fallback to clone\n const el = rebuild ? rebuild() : (snapshot!.cloneNode(true) as HTMLElement);\n drawer?.setPanelContent(el);\n\n // Restore component type and topbar\n const snapshotType = this.snapshotTypes.get(messageId);\n drawer?.setDividerPreviewEnabled(snapshotType === 'ProductGrid');\n if (snapshotType) {\n this.currentType = snapshotType;\n const inlineGridHead = el.querySelector('.gengage-chat-product-grid-head-title');\n if (inlineGridHead && snapshotType === 'ProductGrid') {\n const currentThreadId = this.deps.currentThreadId();\n if (currentThreadId) {\n const idx = this.threads.indexOf(currentThreadId);\n const canBack = idx > 0;\n const canForward = idx >= 0 && idx < this.threads.length - 1;\n this.deps.drawer()?.updatePanelTopBar(canBack, canForward, '');\n }\n } else {\n this.updateTopBar(snapshotType);\n }\n }\n return true;\n }\n\n /**\n * Send maximize-pdp / minify-pdp bridge messages with production-matching delays.\n * Called by the extended mode manager when panel extension state changes.\n */\n notifyExtension(extended: boolean): void {\n const bridge = this.deps.bridge();\n if (extended) {\n setTimeout(() => bridge?.send('maximize-pdp', {}), 350);\n } else {\n setTimeout(() => bridge?.send('minify-pdp', {}), 200);\n }\n }\n\n /**\n * Derive panel title from UISpec root element type using i18n strings.\n * When the backend provides a `panelTitle` prop, it takes precedence.\n */\n titleForComponent(componentType: string, backendTitle?: string): string {\n if (backendTitle) return backendTitle;\n const i18n = this.deps.i18n();\n switch (componentType) {\n case 'ProductDetailsPanel':\n return i18n.panelTitleProductDetails;\n case 'ProductGrid':\n // User-typed queries produce search results; product actions produce similar products\n return isSearchLikeActionType(this.lastActionType)\n ? i18n.panelTitleSearchResults\n : i18n.panelTitleSimilarProducts;\n case 'ComparisonTable':\n return i18n.panelTitleComparisonResults;\n case 'AIGroupingCards':\n return i18n.panelTitleCategories;\n default:\n return '';\n }\n }\n\n /**\n * Update the panel top bar navigation state and title.\n * When the backend provides a `panelTitle`, it takes precedence over i18n defaults.\n */\n /** @param displayTitle When passed (including empty string), used instead of titleForComponent. */\n updateTopBar(componentType: string, backendTitle?: string, displayTitle?: string): void {\n const currentThreadId = this.deps.currentThreadId();\n if (!currentThreadId) return;\n const idx = this.threads.indexOf(currentThreadId);\n const canBack = idx > 0;\n const canForward = idx >= 0 && idx < this.threads.length - 1;\n const title = displayTitle !== undefined ? displayTitle : this.titleForComponent(componentType, backendTitle);\n this.deps.drawer()?.updatePanelTopBar(canBack, canForward, title);\n }\n\n /**\n * Set panel topbar title during loading (before actual panel content arrives).\n * Maps backend pending types to the same i18n titles used for final content.\n */\n updateTopBarForLoading(pendingType: string): void {\n const i18n = this.deps.i18n();\n const loadingTitleMap: Record<string, string> = {\n productDetails: i18n.panelTitleProductDetails,\n productList: isSearchLikeActionType(this.lastActionType)\n ? i18n.panelTitleSearchResults\n : i18n.panelTitleSimilarProducts,\n comparisonTable: i18n.panelTitleComparisonResults,\n groupList: i18n.panelTitleCategories,\n };\n const title = loadingTitleMap[pendingType] ?? '';\n if (title) {\n const currentThreadId = this.deps.currentThreadId();\n const idx = currentThreadId ? this.threads.indexOf(currentThreadId) : -1;\n const canBack = idx > 0;\n const canForward = idx >= 0 && idx < this.threads.length - 1;\n this.deps.drawer()?.updatePanelTopBar(canBack, canForward, title);\n }\n }\n\n /**\n * Map UISpec component types to PanelContentType for the extended mode manager.\n */\n updateExtendedMode(componentType: string): void {\n const mapping: Record<string, PanelContentType> = {\n ComparisonTable: 'comparisonTable',\n AIGroupingCards: 'groupList',\n ProductDetailsPanel: 'productDetails',\n ProductGrid: 'productList',\n };\n const panelType = mapping[componentType] ?? null;\n this.deps.extendedModeManager()?.setPanelContentType(panelType);\n }\n\n /** Navigate to the previous panel thread. */\n navigateBack(): void {\n const currentThreadId = this.deps.currentThreadId();\n if (!currentThreadId) return;\n const idx = this.threads.indexOf(currentThreadId);\n if (idx > 0) {\n const target = this.threads[idx - 1];\n if (target) this.deps.rollbackToThread(target);\n }\n }\n\n /** Navigate to the next panel thread. */\n navigateForward(): void {\n const currentThreadId = this.deps.currentThreadId();\n if (!currentThreadId) return;\n const idx = this.threads.indexOf(currentThreadId);\n if (idx >= 0 && idx < this.threads.length - 1) {\n const target = this.threads[idx + 1];\n if (target) this.deps.rollbackToThread(target);\n }\n }\n\n /**\n * Panel route shaping:\n * - product details => expanded LHS panel (`ProductDetailsPanel`)\n * - all other panel-routed specs keep their original component types\n */\n toPanelSpec(spec: UISpec): UISpec {\n const root = spec.elements[spec.root];\n if (!root || root.type !== 'ProductCard') return spec;\n\n const panelRoot: UIElement = {\n ...root,\n type: 'ProductDetailsPanel',\n };\n\n return {\n root: spec.root,\n elements: {\n ...spec.elements,\n [spec.root]: panelRoot,\n },\n };\n }\n\n destroy(): void {\n this.snapshots.clear();\n this._snapshotRebuilders.clear();\n this.snapshotTypes.clear();\n this.activePanelMessageId = null;\n this.currentType = null;\n this.threads = [];\n }\n}\n\nfunction isSearchLikeActionType(actionType: string | null): boolean {\n return actionType === 'user_message' || actionType === 'inputText';\n}\n\n/**\n * Decide how to apply incoming panel UISpec content.\n *\n * - `'replace'` — set as sole panel content (new search, comparison, product details)\n * - `'append'` — add below existing content (similar products grid after product details)\n * - `'appendSimilars'` — append via the dedicated similars helper\n */\nexport type PanelUpdateAction = 'replace' | 'append' | 'appendSimilars';\n\nexport function determinePanelUpdateAction(opts: {\n componentType: string;\n similarsAppend: boolean;\n currentPanelType: string | null;\n hasPanelContent: boolean;\n isPanelLoading: boolean;\n isFirstPanelContentInStream: boolean;\n}): PanelUpdateAction {\n // productDetailsSimilars: append to existing ProductDetailsPanel\n if (\n opts.similarsAppend &&\n opts.currentPanelType === 'ProductDetailsPanel' &&\n opts.hasPanelContent &&\n !opts.isPanelLoading\n ) {\n return 'appendSimilars';\n }\n\n // Append ProductGrid only when it follows other panel content in the SAME stream\n // (e.g. similar products below product details). A ProductGrid that is the first\n // panel content in a new stream (search results) must REPLACE.\n if (\n opts.componentType === 'ProductGrid' &&\n !opts.isFirstPanelContentInStream &&\n opts.hasPanelContent &&\n !opts.isPanelLoading\n ) {\n return 'append';\n }\n\n return 'replace';\n}\n","/**\n * SessionPersistence — IndexedDB persistence helpers for the chat widget.\n *\n * Encapsulates session save/load, payload caching, favorite toggling,\n * and URL navigation with pre-save.\n *\n * Extracted from chat/index.ts to improve cohesion and reduce file size.\n */\n\nimport type { GengageIndexedDB, FavoriteData } from '../common/indexed-db.js';\nimport type { CommunicationBridge } from '../common/communication-bridge.js';\nimport { isSafeUrl } from '../common/safe-html.js';\nimport type { BackendContext, UISpec } from '../common/types.js';\nimport type { ChatMessage, SerializableChatMessage } from './types.js';\nimport type { ThumbnailEntry } from './components/ThumbnailsColumn.js';\n\nexport type { FavoriteData };\n\nexport interface PersistSessionParams {\n userId: string;\n appId: string;\n sessionId: string;\n messages: ChatMessage[];\n currentThreadId: string | null;\n lastThreadId: string | null;\n chatCreatedAt: string;\n panelSnapshots: Map<string, HTMLElement>;\n panelThreads: string[];\n thumbnailEntries: ThumbnailEntry[];\n lastBackendContext: BackendContext | null;\n sku?: string | undefined;\n}\n\nexport class SessionPersistence {\n private _db: GengageIndexedDB | null;\n /** Favorited product SKUs (loaded from IDB). */\n readonly favoritedSkus: Set<string> = new Set();\n /** Full favorite data cache (loaded from IDB alongside favoritedSkus). */\n private _favoritesCache: Map<string, FavoriteData> = new Map();\n /** Async mutex: serializes persist() calls to prevent interleaved IDB writes. */\n private _persistLock: Promise<void> = Promise.resolve();\n\n constructor(db: GengageIndexedDB | null) {\n this._db = db;\n }\n\n get db(): GengageIndexedDB | null {\n return this._db;\n }\n\n set db(value: GengageIndexedDB | null) {\n this._db = value;\n }\n\n /**\n * Persist current session state to IndexedDB.\n * Called after each stream completion (onDone). Non-fatal on failure.\n */\n async persist(params: PersistSessionParams): Promise<void> {\n if (!this._db) return;\n // Serialize through mutex to prevent interleaved IDB writes\n const prev = this._persistLock;\n let unlock: () => void;\n this._persistLock = new Promise<void>((r) => {\n unlock = r;\n });\n await prev;\n try {\n await this._persistImpl(params);\n } finally {\n unlock!();\n }\n }\n\n private async _persistImpl(params: PersistSessionParams): Promise<void> {\n if (!this._db) return;\n\n const serializableMessages: SerializableChatMessage[] = params.messages.map((m) => {\n const sm: SerializableChatMessage = {\n id: m.id,\n role: m.role,\n timestamp: m.timestamp,\n status: m.status === 'streaming' ? 'done' : m.status,\n };\n if (m.threadId !== undefined) sm.threadId = m.threadId;\n if (m.content !== undefined) sm.content = m.content;\n if (m.silent) sm.silent = true;\n return sm;\n });\n\n // Serialize panel snapshots to HTML strings (never persist loading skeletons)\n const panelSnapshotHtml: Record<string, string> = {};\n for (const [msgId, el] of params.panelSnapshots) {\n if (el.querySelector('.gengage-chat-panel-skeleton')) continue;\n panelSnapshotHtml[msgId] = el.innerHTML;\n }\n\n await this._db.saveSession({\n userId: params.userId,\n appId: params.appId,\n sessionId: params.sessionId,\n messages: serializableMessages,\n currentThreadId: params.currentThreadId,\n lastThreadId: params.lastThreadId,\n createdAt: params.chatCreatedAt,\n panelThreads: params.panelThreads.length > 0 ? params.panelThreads : undefined,\n thumbnailEntries: params.thumbnailEntries.length > 0 ? params.thumbnailEntries : undefined,\n panelSnapshotHtml: Object.keys(panelSnapshotHtml).length > 0 ? panelSnapshotHtml : undefined,\n sku: params.sku,\n });\n\n // Save latest context snapshot for current thread\n if (params.lastBackendContext && params.currentThreadId) {\n await this._db.saveContext({\n sessionId: params.sessionId,\n threadId: params.currentThreadId,\n context: params.lastBackendContext,\n });\n }\n\n // Save UISpec payloads separately\n // Note: we intentionally do NOT delete m.uiSpec from the caller's live objects.\n // The caller's message array must remain intact for re-rendering and snapshot logic.\n for (const m of params.messages) {\n if (m.uiSpec && m.threadId) {\n await this._db.savePayload({\n threadId: m.threadId,\n messageId: m.id,\n uiSpec: m.uiSpec,\n });\n }\n }\n }\n\n /**\n * Persist session to IndexedDB, then navigate to the URL.\n * Sends an 'openURLInNewTab' bridge message so the host page can intercept\n * (e.g. for SPA routing), then navigates directly as fallback.\n *\n * Legacy compatibility: the prior engine navigated via window.location.href\n * after posting saveSessionAndOpenURL to the iframe. The clean-room runs in\n * the same window (Shadow DOM, not iframe), so it navigates directly.\n */\n async saveAndOpenURL(url: string, persistFn: () => Promise<void>, bridge: CommunicationBridge | null): Promise<void> {\n try {\n await persistFn();\n } catch {\n // Non-fatal — still navigate\n }\n // Notify host so SPAs can intercept (e.g. router.push instead of full reload).\n // NOTE: The bridge message is fire-and-forget. The subsequent location.href\n // assignment may trigger a page unload before the host processes the message.\n // This matches legacy behavior. SPAs that need to intercept\n // should handle the bridge event synchronously or call event.preventDefault()\n // on the gengage:navigate CustomEvent to suppress the fallback navigation.\n bridge?.send('openURLInNewTab', { url });\n if (isSafeUrl(url)) {\n window.location.href = url;\n }\n }\n\n /**\n * Load a UISpec payload from IndexedDB with retry logic.\n * Returns null if not found or all retries fail.\n */\n async loadPayload(threadId: string, messageId: string): Promise<UISpec | null> {\n if (!this._db) return null;\n\n for (let attempt = 0; attempt < 3; attempt++) {\n try {\n const payload = await this._db.loadPayload(threadId, messageId);\n if (payload) return payload.uiSpec;\n } catch {\n // Retry after delay\n }\n if (attempt < 2) {\n await new Promise((r) => setTimeout(r, 100));\n }\n }\n return null;\n }\n\n /**\n * Load favorited SKUs from IDB into the in-memory set.\n */\n async loadFavorites(userId: string, appId: string): Promise<void> {\n if (!this._db) return;\n try {\n const favs = await this._db.loadFavorites(userId, appId);\n for (const f of favs) {\n this.favoritedSkus.add(f.sku);\n this._favoritesCache.set(f.sku, f);\n }\n } catch {\n // Non-fatal — continue without favorites\n }\n }\n\n /** Returns favorited products ordered newest-first. */\n getFavoriteProducts(): FavoriteData[] {\n return [...this._favoritesCache.values()].sort((a, b) => {\n return (b.savedAt ?? '').localeCompare(a.savedAt ?? '');\n });\n }\n\n /**\n * Toggle a product's favorited state in IDB and in-memory set.\n */\n async toggleFavorite(userId: string, appId: string, sku: string, product: Record<string, unknown>): Promise<void> {\n // Always update in-memory state first — IDB is optional (persistence only)\n if (this.favoritedSkus.has(sku)) {\n this.favoritedSkus.delete(sku);\n this._favoritesCache.delete(sku);\n if (this._db) await this._db.removeFavorite(userId, appId, sku);\n } else {\n const data: FavoriteData = {\n userId,\n appId,\n sku,\n name: product['name'] as string | undefined,\n imageUrl: product['imageUrl'] as string | undefined,\n price: product['price'] as string | undefined,\n savedAt: new Date().toISOString(),\n };\n this.favoritedSkus.add(sku);\n this._favoritesCache.set(sku, data);\n if (this._db) await this._db.saveFavorite(data);\n }\n }\n\n close(): void {\n this._db?.close();\n this._db = null;\n }\n}\n","/**\n * Centralized chat presentation state (focus thread, scroll requests, read hints).\n * Purpose-aligned with the legacy Redux presentation slice, without Redux.\n */\n\nexport type GroupReadState = 'seen' | 'unseen' | 'active_unread' | 'overflow_unread';\n\nexport type PresentationGroupMeta = {\n id: string;\n threadId: string;\n readState: GroupReadState;\n status: 'streaming' | 'complete';\n updatedAt: number;\n};\n\nexport type ScrollRequest = {\n id: number;\n type: 'thread' | 'bottom';\n behavior: ScrollBehavior;\n threadId?: string;\n};\n\nconst assistantGroupId = (threadId: string) => `${threadId}:assistant`;\n\nexport class ChatPresentationState {\n /** Per-assistant-thread read / streaming metadata */\n groups: Record<string, PresentationGroupMeta> = {};\n pinnedToBottom = true;\n userInteracting = false;\n /** When set, transcript UI may collapse other threads until released */\n focusedThreadId: string | null = null;\n /** Drawer / widget considered visible */\n shown = false;\n lastAutoAnchoredGroupId: string | null = null;\n scrollRequest: ScrollRequest | null = null;\n private _nextScrollId = 1;\n\n setShown(shown: boolean): void {\n this.shown = shown;\n if (!shown) {\n this.userInteracting = false;\n this.pinnedToBottom = true;\n this.focusedThreadId = null;\n this.scrollRequest = null;\n }\n }\n\n reset(): void {\n this.groups = {};\n this.pinnedToBottom = true;\n this.userInteracting = false;\n this.focusedThreadId = null;\n this.lastAutoAnchoredGroupId = null;\n this.scrollRequest = null;\n }\n\n registerAssistantActivity(threadId: string): void {\n const id = assistantGroupId(threadId);\n const existing = this.groups[id];\n const nextRead: GroupReadState =\n this.shown && this.pinnedToBottom && !this.userInteracting ? 'active_unread' : 'unseen';\n\n this.groups[id] = {\n id,\n threadId,\n readState: existing?.readState === 'seen' ? nextRead : (existing?.readState ?? nextRead),\n status: 'streaming',\n updatedAt: Date.now(),\n };\n if (existing?.readState === 'seen' && this.lastAutoAnchoredGroupId === id) {\n this.lastAutoAnchoredGroupId = null;\n }\n }\n\n finalizeAssistantGroup(threadId: string): void {\n const id = assistantGroupId(threadId);\n const existing = this.groups[id];\n if (!existing) return;\n existing.status = 'complete';\n existing.updatedAt = Date.now();\n }\n\n setGroupReadStates(updates: Array<{ groupId: string; readState: GroupReadState }>): void {\n const t = Date.now();\n for (const { groupId, readState } of updates) {\n const g = this.groups[groupId];\n if (!g || g.readState === readState) continue;\n g.readState = readState;\n g.updatedAt = t;\n }\n }\n\n requestThreadFocus(threadId: string, behavior: ScrollBehavior = 'smooth'): void {\n this.focusedThreadId = threadId;\n this.scrollRequest = {\n id: this._nextScrollId++,\n type: 'thread',\n threadId,\n behavior,\n };\n }\n\n requestScrollToBottom(behavior: ScrollBehavior = 'smooth'): void {\n this.scrollRequest = {\n id: this._nextScrollId++,\n type: 'bottom',\n behavior,\n };\n }\n\n consumeScrollRequest(id: number): void {\n if (this.scrollRequest?.id === id) {\n this.scrollRequest = null;\n }\n }\n\n releaseFocusedThread(): void {\n this.focusedThreadId = null;\n }\n\n /** Align focus with rollback / thread without queuing scroll */\n setFocusedThreadId(threadId: string | null): void {\n this.focusedThreadId = threadId;\n }\n\n markGroupAutoAnchored(groupId: string): void {\n this.lastAutoAnchoredGroupId = groupId;\n }\n\n /** Block soft stream scroll when user pulled away from bottom */\n shouldBlockStreamAutoScroll(): boolean {\n return this.shown && this.userInteracting && !this.pinnedToBottom;\n }\n\n getAssistantReadState(threadId: string): GroupReadState | undefined {\n return this.groups[assistantGroupId(threadId)]?.readState;\n }\n}\n\n/**\n * Latest assistant “group” with unread semantics (for auto-anchor).\n */\nexport function getLatestUnreadAssistantThreadId(\n orderedThreadIds: string[],\n state: ChatPresentationState,\n): string | null {\n for (let i = orderedThreadIds.length - 1; i >= 0; i--) {\n const tid = orderedThreadIds[i]!;\n const rs = state.getAssistantReadState(tid);\n if (rs !== undefined && rs !== 'seen') {\n return tid;\n }\n }\n return null;\n}\n","/**\n * makePillLauncher — creates a pill-shaped chat launcher that combines an\n * avatar image with a text label, replacing the default circular FAB.\n *\n\n * });\n *\n * Or call `makePillLauncher` + `apply()` manually when you need custom control.\n */\n\nexport interface PillLauncherOptions {\n /** Text label shown beside the avatar inside the pill */\n label: string;\n /** Avatar image URL — also used as `launcherImageUrl` to activate image-mode */\n avatarUrl: string;\n /** Brand primary color — used for border and hover effects */\n primaryColor: string;\n /** Dark text / shadow color for the label and drop shadow (default: '#111827') */\n secondaryColor?: string;\n /** CSS font-family string for the label text */\n fontFamily?: string;\n /** CSS class name injected on the label span (default: 'gengage-pill-launcher-label') */\n labelClassName?: string;\n /** id for the <style> tag injected into the shadow root (default: 'gengage-pill-launcher-style') */\n styleId?: string;\n /** Mobile breakpoint in px (default: 768) */\n mobileBreakpoint?: number;\n /** Desktop pill width (default: '188px') */\n desktopWidth?: string;\n /** Desktop pill height (default: '60px') */\n desktopHeight?: string;\n /** Avatar icon diameter (default: '46px') */\n iconSize?: string;\n}\n\nexport interface PillLauncherKit {\n /**\n * Pass as `chat.launcherImageUrl` to `initOverlayWidgets`.\n * Equals `avatarUrl` — activates image-mode on the launcher button.\n */\n launcherImageUrl: string;\n /**\n * Call after `initOverlayWidgets` resolves.\n * Injects pill CSS into the widget shadow root, fixes the header avatar\n * class, and appends the text label span to the launcher button.\n * Retries via requestAnimationFrame for up to ~1.5 s.\n *\n * Pass the widget's shadow root when it is available to avoid a global DOM\n * scan — required when multiple GengageChat instances share the same page.\n */\n apply(targetShadow?: ShadowRoot): Promise<void>;\n}\n\nexport function makePillLauncher(options: PillLauncherOptions): PillLauncherKit {\n const {\n label,\n avatarUrl,\n primaryColor,\n secondaryColor = '#111827',\n fontFamily = 'inherit',\n labelClassName = 'gengage-pill-launcher-label',\n styleId = 'gengage-pill-launcher-style',\n mobileBreakpoint = 768,\n desktopWidth = '188px',\n desktopHeight = '60px',\n iconSize = '46px',\n } = options;\n\n const mobileWidth = `${parseInt(desktopWidth, 10) - 14}px`;\n const mobileHeight = `${parseInt(desktopHeight, 10) - 4}px`;\n const mobileIconSize = `${parseInt(iconSize, 10) - 4}px`;\n\n const css = `\n:host {\n --pill-primary: ${primaryColor};\n --pill-secondary: ${secondaryColor};\n --pill-font: ${fontFamily};\n}\n\nbutton[data-gengage-part=\"chat-launcher-button\"] {\n box-sizing: border-box;\n width: ${desktopWidth} !important;\n min-width: ${desktopWidth} !important;\n max-width: ${desktopWidth} !important;\n height: ${desktopHeight} !important;\n min-height: ${desktopHeight} !important;\n padding: 6px 7px 6px 22px !important;\n display: inline-flex !important;\n flex-direction: row-reverse !important;\n align-items: center !important;\n justify-content: space-between !important;\n gap: 12px !important;\n border-radius: 999px !important;\n border: 1px solid color-mix(in srgb, var(--pill-primary) 18%, white) !important;\n background: #ffffff !important;\n color: var(--pill-secondary) !important;\n box-shadow:\n 0 18px 46px color-mix(in srgb, var(--pill-primary) 18%, transparent),\n 0 4px 14px color-mix(in srgb, var(--pill-secondary) 8%, transparent) !important;\n overflow: visible !important;\n}\n\nbutton[data-gengage-part=\"chat-launcher-button\"]:hover {\n transform: translateY(-1px) !important;\n box-shadow:\n 0 22px 52px color-mix(in srgb, var(--pill-primary) 24%, transparent),\n 0 6px 18px color-mix(in srgb, var(--pill-secondary) 12%, transparent) !important;\n}\n\n.${labelClassName} {\n color: var(--pill-secondary);\n font: 500 15px/1.05 var(--pill-font);\n letter-spacing: -0.02em;\n white-space: nowrap;\n flex: 0 0 auto;\n pointer-events: none;\n}\n\nbutton[data-gengage-part=\"chat-launcher-button\"] img {\n width: ${iconSize} !important;\n height: ${iconSize} !important;\n flex: 0 0 ${iconSize} !important;\n border-radius: 999px;\n object-fit: cover;\n object-position: center;\n background: transparent;\n border: 0 !important;\n box-shadow: none !important;\n}\n\n@media (max-width: ${mobileBreakpoint}px) {\n button[data-gengage-part=\"chat-launcher-button\"] {\n width: ${mobileWidth} !important;\n min-width: ${mobileWidth} !important;\n max-width: ${mobileWidth} !important;\n height: ${mobileHeight} !important;\n min-height: ${mobileHeight} !important;\n padding-left: 20px !important;\n padding-right: 7px !important;\n }\n\n .${labelClassName} {\n font-size: 14px;\n }\n\n button[data-gengage-part=\"chat-launcher-button\"] img {\n width: ${mobileIconSize} !important;\n height: ${mobileIconSize} !important;\n flex-basis: ${mobileIconSize} !important;\n }\n}\n`.trim();\n\n const findShadowHost = (): HTMLElement | null => {\n for (const el of document.querySelectorAll('[data-gengage-widget]')) {\n if (!(el instanceof HTMLElement)) continue;\n const sr = el.shadowRoot;\n if (!sr) continue;\n if (sr.querySelector('[data-gengage-part=\"chat-launcher-button\"]') || sr.querySelector('.gengage-chat-root')) {\n return el;\n }\n }\n return null;\n };\n\n const injectStyle = (root: ShadowRoot): void => {\n if (root.getElementById(styleId)) return;\n const style = document.createElement('style');\n style.id = styleId;\n style.textContent = css;\n root.appendChild(style);\n };\n\n const escapedClassName = CSS.escape(labelClassName);\n\n const applyOnce = (root: ShadowRoot): boolean => {\n injectStyle(root);\n\n const launcher = root.querySelector('[data-gengage-part=\"chat-launcher-button\"]');\n if (!(launcher instanceof HTMLButtonElement)) return false;\n\n // Only strip the logo class when the header and launcher show the same image.\n // When headerAvatarUrl was explicitly set to a different asset (e.g. a rectangular\n // brand logo), the logo class is intentional and must be preserved.\n const headerAvatar = root.querySelector('[data-gengage-part=\"chat-header-avatar\"]');\n if (headerAvatar instanceof HTMLImageElement) {\n const launcherImg = root.querySelector<HTMLImageElement>('[data-gengage-part=\"chat-launcher-button\"] img');\n if (!launcherImg || headerAvatar.src === launcherImg.src) {\n headerAvatar.classList.remove('gengage-chat-header-avatar--logo');\n }\n }\n\n if (launcher.querySelector(`.${escapedClassName}`)) return true;\n\n launcher.setAttribute('aria-label', label);\n const labelEl = document.createElement('span');\n labelEl.className = labelClassName;\n labelEl.textContent = label;\n launcher.appendChild(labelEl);\n return true;\n };\n\n const apply = async (targetShadow?: ShadowRoot): Promise<void> => {\n // When the caller passes the widget's own shadow root, use it directly and\n // skip the global DOM scan — this prevents cross-instance interference when\n // multiple GengageChat widgets share the same page.\n if (targetShadow) {\n injectStyle(targetShadow);\n await Promise.resolve();\n for (let frame = 0; frame < 90; frame++) {\n if (applyOnce(targetShadow)) return;\n await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));\n }\n return;\n }\n\n // Fallback: scan the document for a matching chat shadow host (standalone use).\n const earlyHost = findShadowHost();\n if (earlyHost?.shadowRoot) injectStyle(earlyHost.shadowRoot);\n\n await Promise.resolve();\n\n for (let frame = 0; frame < 90; frame++) {\n const host = findShadowHost();\n const root = host?.shadowRoot ?? null;\n if (root) {\n injectStyle(root);\n if (applyOnce(root)) return;\n }\n await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));\n }\n };\n\n return { launcherImageUrl: avatarUrl, apply };\n}\n","import { isLikelyConnectivityIssue } from '../common/global-error-toast.js';\n\nconst HTTP_STATUS_PREFIX = /^HTTP\\s+\\d{3}\\b/i;\n\nconst INFRA_MESSAGE_PATTERNS = [\n /response body is null/i,\n /streaming not supported/i,\n /ECONNREFUSED/i,\n /ECONNRESET/i,\n /ETIMEDOUT/i,\n /socket hang up/i,\n /net::ERR_/i,\n /502\\s+bad gateway/i,\n /503\\s+service unavailable/i,\n /504\\s+gateway timeout/i,\n] as const;\n\nconst TECHNICAL_DUMP_PATTERNS = [\n /Traceback\\s*\\(/i,\n /\\bSyntaxError\\b/,\n /\\bReferenceError\\b/,\n /\\bTypeError\\s*:/,\n /\\bat\\s+[\\w./()[\\]<>-]+:\\d+:\\d+/,\n /\\n\\s+at\\s+\\w/,\n /undefined is not/i,\n /is not a function/i,\n /<anonymous>/i,\n /ChunkLoadError/i,\n /UnhandledPromiseRejection/i,\n] as const;\n\n/** Above this length, treat as dump / transport payload — show red strip instead of a chat bubble. */\nconst MAX_CHAT_BUBBLE_ERROR_LENGTH = 720;\n\n/**\n * Use the red inline error strip (+ recovery pills) instead of a normal assistant bubble when the\n * failure looks like transport, HTTP, or an obvious technical dump. User-facing backend messages\n * (e.g. output_text with is_error) stay in the conversation as a chat message.\n */\nexport function shouldShowStreamErrorAsRedStrip(err: Error, displayText: string): boolean {\n if (isLikelyConnectivityIssue(err)) return true;\n\n const raw = err.message.trim();\n if (HTTP_STATUS_PREFIX.test(raw)) return true;\n if (INFRA_MESSAGE_PATTERNS.some((p) => p.test(raw))) return true;\n\n const text = displayText.trim();\n if (text.length > MAX_CHAT_BUBBLE_ERROR_LENGTH) return true;\n\n const lines = text.split(/\\n/).length;\n if (lines >= 6) return true;\n if (TECHNICAL_DUMP_PATTERNS.some((p) => p.test(text))) return true;\n if (lines >= 3 && /\\bat\\s+/.test(text)) return true;\n\n return false;\n}\n","/**\n * KVKK notice filtering and caching helpers.\n *\n * KVKK (Kişisel Verilerin Korunması Kanunu) is Turkey's data protection law.\n * When the backend streams a response containing a KVKK notice, we:\n * 1. Strip the KVKK block from the visible bot text\n * 2. Show a banner on first encounter (per account)\n * 3. Mark it as shown in localStorage to avoid repeat banners\n */\n\nconst KVKK_STORAGE_KEY = 'gengage_kvkk_shown';\nconst KVKK_TEXT_MARKERS = ['kvkk', 'kişisel veri', 'kisisel veri'];\nconst KVKK_LAW_NUMBER_RE = /\\b6698\\b/;\n\nexport function containsKvkk(html: string): boolean {\n const lower = html.toLowerCase();\n return KVKK_TEXT_MARKERS.some((m) => lower.includes(m)) || KVKK_LAW_NUMBER_RE.test(lower);\n}\n\nexport function isKvkkShown(accountId: string): boolean {\n try {\n return localStorage.getItem(`${KVKK_STORAGE_KEY}_${accountId}`) === '1';\n } catch {\n return false;\n }\n}\n\nexport function markKvkkShown(accountId: string): void {\n try {\n localStorage.setItem(`${KVKK_STORAGE_KEY}_${accountId}`, '1');\n } catch {\n // localStorage unavailable — silently ignore\n }\n}\n\n/**\n * Strip the KVKK portion from bot HTML.\n * KVKK is typically wrapped in a `<div style=\"...\">` at the start.\n * We remove the first block-level element that contains a KVKK marker.\n */\nexport function stripKvkkBlock(html: string): string {\n const doc = new DOMParser().parseFromString(html, 'text/html');\n const body = doc.body;\n const children = Array.from(body.children);\n for (const child of children) {\n if (containsKvkk(child.textContent ?? '')) {\n child.remove();\n break; // Only strip the first KVKK block\n }\n }\n return body.innerHTML.trim();\n}\n\nexport function extractKvkkBlock(html: string): string | null {\n const doc = new DOMParser().parseFromString(html, 'text/html');\n for (const child of Array.from(doc.body.children)) {\n if (containsKvkk(child.textContent ?? '')) {\n return child.outerHTML;\n }\n }\n return null;\n}\n\nconst LOCALE_TO_LANGUAGE: Record<string, string> = {\n tr: 'TURKISH',\n en: 'ENGLISH',\n de: 'GERMAN',\n fr: 'FRENCH',\n};\n\nexport function localeToOutputLanguage(locale?: string): string {\n if (!locale) return 'TURKISH';\n return LOCALE_TO_LANGUAGE[locale.toLowerCase().slice(0, 2)] ?? 'TURKISH';\n}\n","@import '../../design-system/index.css';\n\n:host {\n all: initial;\n font-family: var(\n --gengage-font-family,\n 'Plus Jakarta Sans',\n -apple-system,\n BlinkMacSystemFont,\n 'Segoe UI',\n Roboto,\n sans-serif\n );\n font-size: var(--gengage-font-size, 14px);\n}\n\n.gengage-chat-root button,\n.gengage-chat-root [role='button'],\n.gengage-chat-root a {\n cursor: pointer;\n}\n\n/* Screen reader only — visually hidden, accessible to assistive tech */\n.gengage-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n}\n\n.gengage-chat-root {\n position: fixed;\n z-index: var(--gengage-z-index, 2147483647);\n\n /* Shared geometry tokens (overridable via theme) */\n --_gengage-chat-offset: var(--gengage-chat-offset, 20px);\n --_gengage-chat-launcher-bottom: var(--gengage-chat-launcher-bottom, 20px);\n --_gengage-chat-launcher-right: var(--gengage-chat-launcher-right, 20px);\n --_gengage-chat-launcher-size: var(--gengage-chat-launcher-size, 56px);\n --_gengage-chat-drawer-width: var(--gengage-chat-width, 400px);\n --_gengage-chat-radius: var(--gengage-chat-shell-radius, 1rem);\n --_gengage-chat-header-height: var(--gengage-chat-header-height, 72px);\n --_gengage-chat-conversation-width: var(--gengage-chat-conversation-width, 396px);\n --_gengage-chat-panel-min-width: var(--gengage-chat-panel-min-width, 320px);\n --_gengage-chat-panel-max-width: var(--gengage-chat-panel-max-width, 1200px);\n --_gengage-chat-input-height: var(--gengage-chat-input-height, 48px);\n --_gengage-chat-shadow: var(--gengage-chat-shadow, var(--shadow-3));\n\n /* ── Editorial Commerce — Semantic Color Tokens ────────────────────── */\n /* Tonal layering: borders defined by background shift, not strokes */\n --_gengage-border-color: var(--border-default);\n --_gengage-discount-color: var(--gengage-discount-color, var(--client-primary));\n /* Sale price: `--gengage-discounted-price-color` set on host when `productPriceUi.discountedPriceColor === 'client'`. */\n /* Campaign line: solid or gradient via optional `--gengage-campaign-reason-color-end`. */\n --_gengage-campaign-reason-start: var(--gengage-campaign-reason-color, var(--client-primary));\n --_gengage-campaign-reason-end: var(\n --gengage-campaign-reason-color-end,\n var(--gengage-campaign-reason-color, var(--client-primary))\n );\n --_gengage-success-color: var(--success);\n --_gengage-text-primary: var(--text-primary);\n --_gengage-text-secondary: var(--text-secondary);\n --_gengage-surface-secondary: var(--surface-card-soft);\n --_gengage-surface-muted: var(--surface-card-soft);\n --_gengage-surface-dark: var(--surface-shell);\n --_gengage-rating-color: var(--rating);\n --_gengage-error: var(--error);\n --_gengage-danger-color: var(--error);\n\n /* Ghost border (outline_variant @ 20% opacity — for input fields) */\n --_gengage-ghost-border: var(--gengage-ghost-border, color-mix(in srgb, var(--client-primary) 16%, transparent));\n}\n\n/* Full-viewport layer: dim + click-outside only when `.gengage-chat-root--maximized-host-chrome`\n (floating + side panel open, or overlay modal). Otherwise transparent and pointer-events none. */\n.gengage-chat-backdrop {\n display: none;\n position: fixed;\n inset: 0;\n z-index: 0;\n margin: 0;\n padding: 0;\n border: none;\n background: transparent;\n pointer-events: none;\n cursor: pointer;\n box-sizing: border-box;\n -webkit-tap-highlight-color: transparent;\n}\n\n.gengage-chat-root--open:not(.gengage-chat--inline) .gengage-chat-backdrop {\n display: block;\n pointer-events: none;\n background: var(--gengage-chat-float-backdrop, transparent);\n}\n\n.gengage-chat-root--open.gengage-chat-root--maximized-host-chrome:not(.gengage-chat--inline) .gengage-chat-backdrop {\n pointer-events: auto;\n background: var(--gengage-chat-float-backdrop, color-mix(in srgb, var(--surface-overlay) 72%, transparent));\n}\n\n.gengage-chat-root.gengage-chat--overlay.gengage-chat-root--open .gengage-chat-backdrop {\n background: var(--gengage-chat-overlay-backdrop, var(--surface-overlay));\n pointer-events: auto;\n}\n\n/* Launcher container — fixed-positioned wrapper for button + content areas */\n.gengage-chat-launcher-container {\n position: fixed;\n bottom: var(--_gengage-chat-launcher-bottom);\n right: var(--_gengage-chat-launcher-right);\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 10px;\n z-index: var(--gengage-z-index, 2147483647);\n}\n\n/* Content area slots — QNA widget or host page injects engagement actions here */\n.gengage-chat-launcher-content-area,\n.gengage-chat-launcher-content-area-bottom {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 8px;\n}\n\n/* Collapse empty content areas so they don't affect layout */\n.gengage-chat-launcher-content-area:empty,\n.gengage-chat-launcher-content-area-bottom:empty {\n display: none;\n}\n\n/* Hide content areas when drawer is open */\n.gengage-chat-root--open .gengage-chat-launcher-content-area,\n.gengage-chat-root--open .gengage-chat-launcher-content-area-bottom {\n display: none;\n}\n\n/* Hide launcher button on desktop when drawer is open — prevents overlap with send button */\n.gengage-chat-root--open .gengage-chat-launcher-container {\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.2s ease;\n}\n\n/* Launcher button */\n.gengage-chat-launcher {\n width: var(--_gengage-chat-launcher-size);\n height: var(--_gengage-chat-launcher-size);\n border-radius: 50%;\n border: 1px solid var(--ds-button-primary-border);\n background: var(--ds-button-primary-bg);\n color: var(--ds-button-primary-fg);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: var(--shadow-3);\n transition:\n transform 0.18s cubic-bezier(0.2, 0.8, 0.2, 1),\n box-shadow 0.18s ease;\n position: relative;\n flex-shrink: 0;\n}\n\n.gengage-chat-launcher:hover {\n transform: none;\n box-shadow: var(--shadow-3);\n}\n\n.gengage-chat-launcher svg {\n width: 28px;\n height: 28px;\n}\n\n/* Image-mode launcher — full-size image, no circular bg */\n.gengage-chat-launcher--image-mode {\n background: transparent;\n border: none;\n border-radius: 0;\n box-shadow: none;\n padding: 0;\n overflow: hidden;\n}\n\n.gengage-chat-launcher--image-mode:hover {\n box-shadow: none;\n}\n\n.gengage-chat-launcher--image-mode img {\n width: 100%;\n height: 100%;\n object-fit: contain;\n}\n\n.gengage-chat-launcher--hidden-mobile {\n display: none !important;\n}\n\n/* Launcher tooltip */\n.gengage-chat-launcher-tooltip {\n position: absolute;\n right: calc(100% + 12px);\n top: 50%;\n transform: translateY(-50%);\n padding: 6px 12px;\n border-radius: var(--radius-control);\n background: var(--surface-shell);\n color: var(--text-inverse);\n font-size: 13px;\n font-weight: 500;\n white-space: nowrap;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.2s ease;\n box-shadow: var(--shadow-2);\n}\n\n.gengage-chat-launcher-tooltip::after {\n content: '';\n position: absolute;\n left: 100%;\n top: 50%;\n transform: translateY(-50%);\n border: 5px solid transparent;\n border-left-color: var(--surface-shell);\n}\n\n.gengage-chat-launcher:hover .gengage-chat-launcher-tooltip {\n opacity: 1;\n}\n\n/* Hide tooltip when drawer is open */\n.gengage-chat-root--open .gengage-chat-launcher-tooltip {\n display: none;\n}\n\n/* Drawer — always right-anchored, full height, slide in/out from the right */\n.gengage-chat-drawer {\n position: fixed;\n top: 0;\n bottom: 0;\n right: 0;\n width: var(--_gengage-chat-drawer-width);\n max-height: -webkit-fill-available;\n max-height: 100dvh;\n border-radius: 0;\n background: var(--surface-elevated, var(--surface-card));\n border-left: 1px solid var(--border-default);\n box-shadow: var(--_gengage-chat-shadow);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n z-index: var(--gengage-z-index, 2147483647);\n /* Drawer slide-in animation */\n transform: translateX(0);\n transition:\n transform 0.25s cubic-bezier(0.4, 0, 0.2, 1),\n opacity 0.2s ease;\n opacity: 1;\n}\n\n.gengage-chat-root--mobile .gengage-chat-launcher-container {\n right: var(--gengage-chat-launcher-mobile-right, 16px);\n bottom: calc(var(--gengage-chat-launcher-mobile-bottom, 16px) + env(safe-area-inset-bottom, 0px));\n}\n\n.gengage-chat-root--mobile .gengage-chat-drawer {\n left: 0;\n right: 0;\n bottom: 0;\n width: 100%;\n max-width: none;\n max-height: -webkit-fill-available;\n max-height: 100dvh;\n border-radius: 16px 16px 0 0;\n border: 1px solid var(--border-default);\n border-bottom: none;\n box-shadow: var(--shadow-3);\n padding-bottom: env(safe-area-inset-bottom, 0px);\n background: var(--surface-elevated, var(--surface-card));\n}\n\n/* Drag handle pill — visible only on mobile */\n.gengage-chat-drawer-handle {\n display: none;\n}\n\n/* Handle lives inside the header on mobile — visual pill only, pointer-events disabled via JS */\n.gengage-chat-root--mobile .gengage-chat-drawer-handle {\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n top: 6px;\n left: 0;\n right: 0;\n height: 14px;\n pointer-events: none;\n z-index: 1;\n}\n\n/* Header row is the actual drag target on mobile */\n.gengage-chat-root--mobile .gengage-chat-header {\n cursor: grab;\n touch-action: none;\n user-select: none;\n -webkit-user-select: none;\n}\n\n/* Buttons/links inside the header keep normal pointer behaviour */\n.gengage-chat-root--mobile .gengage-chat-header button,\n.gengage-chat-root--mobile .gengage-chat-header a {\n cursor: pointer;\n touch-action: auto;\n user-select: auto;\n -webkit-user-select: auto;\n}\n\n.gengage-chat-root--mobile .gengage-chat-drawer-handle::before {\n content: '';\n width: 36px;\n height: 4px;\n border-radius: 999px;\n background: var(--border-strong);\n pointer-events: none;\n}\n\n.gengage-chat-root--mobile.gengage-chat-root--mobile-half .gengage-chat-drawer {\n top: auto;\n height: min(72dvh, 620px);\n max-height: min(72dvh, 620px);\n}\n\n.gengage-chat-root--mobile.gengage-chat-root--mobile-full .gengage-chat-drawer {\n top: 0;\n height: 100dvh;\n max-height: 100dvh;\n border-radius: 0;\n}\n\n.gengage-chat-root--mobile .gengage-chat-input-area {\n padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));\n border-top: 1px solid var(--border-default);\n transform: translateY(calc(-1 * var(--gengage-keyboard-offset, 0px)));\n}\n\n/* (panel-mode-in animation removed — drawer is always full-size, no entry animation needed) */\n\n/* Header — calm neutral shell; brand belongs in action and badge layers */\n.gengage-chat-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n box-sizing: border-box;\n height: var(--_gengage-chat-header-height);\n padding: 14px 16px;\n background: var(--gengage-chat-header-bg, var(--ds-header-bg));\n color: var(--gengage-chat-header-foreground, var(--ds-header-fg));\n box-shadow: none;\n border-bottom: 1px solid var(--gengage-chat-header-border-bottom, var(--ds-header-border));\n position: relative;\n z-index: 2;\n}\n\n.gengage-chat-header-left {\n display: flex;\n align-items: center;\n gap: 10px;\n min-width: 0;\n}\n\n.gengage-chat-header-avatar {\n width: 44px;\n height: 44px;\n border-radius: 999px;\n background: var(--surface-card-soft);\n border: 1px solid var(--border-default);\n object-fit: contain;\n flex-shrink: 0;\n}\n\n.gengage-chat-header-avatar--logo {\n width: auto;\n height: 45px;\n max-width: 92px;\n border-radius: 0;\n border: none;\n background: transparent;\n object-fit: contain;\n}\n\n.gengage-chat-header-info {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.gengage-chat-header-title-row {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n}\n\n.gengage-chat-header-title {\n display: block;\n font-weight: 700;\n font-size: 16px;\n line-height: 1.25;\n letter-spacing: 0.01em;\n color: inherit;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.gengage-chat-header-badge {\n display: inline-flex;\n align-items: center;\n min-height: 26px;\n padding: 0 10px;\n border-radius: var(--radius-pill);\n background: var(--ds-badge-brand-bg);\n color: var(--ds-badge-brand-fg);\n border: 1px solid var(--ds-badge-brand-border);\n font-size: 10px;\n font-weight: 800;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n line-height: 1.5;\n}\n\n.gengage-chat-header-powered {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n color: var(--ds-header-muted);\n text-decoration: none;\n transition: color 0.15s;\n line-height: 1.2;\n}\n\n.gengage-chat-header-powered:hover {\n color: var(--client-primary);\n}\n\n.gengage-chat-header-powered svg {\n width: 12px;\n height: 12px;\n opacity: 0.6;\n flex-shrink: 0;\n}\n\n.gengage-chat-header-right {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n}\n\n/* Header action buttons (cart, favorites) */\n.gengage-chat-header-btn {\n width: 42px;\n height: 42px;\n border-radius: var(--radius-control, 12px);\n background: var(--surface-card-soft);\n border: 1px solid var(--border-default);\n color: var(--text-primary);\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n text-decoration: none;\n line-height: 1;\n transition:\n background-color 0.15s ease,\n color 0.15s ease,\n border-color 0.15s ease;\n}\n\n.gengage-chat-header-btn:hover {\n background: var(--client-primary-subtle);\n color: var(--client-primary);\n border-color: color-mix(in srgb, var(--client-primary) 18%, white);\n}\n\n/* Favorites button with badge */\n.gengage-chat-header-btn--fav {\n position: relative;\n}\n\n/* Reopen-panel button: hidden by default, shown on mobile via JS when panel is hidden */\n.gengage-chat-header-btn--reopen-panel {\n display: none;\n}\n\n/* New-chat button: hide on mobile (reopen-panel button takes its role) */\n.gengage-chat-root--mobile .gengage-chat-new-chat {\n display: none;\n}\n\n.gengage-chat-header-fav-badge {\n position: absolute;\n top: -4px;\n right: -4px;\n min-width: 16px;\n height: 16px;\n border-radius: 999px;\n background: var(--error);\n color: var(--text-inverse);\n font-size: 9px;\n font-weight: 700;\n line-height: 16px;\n text-align: center;\n padding: 0 3px;\n pointer-events: none;\n}\n\n.gengage-chat-close {\n width: 42px;\n height: 42px;\n min-width: 42px;\n min-height: 42px;\n border-radius: var(--radius-control, 12px);\n background: var(--surface-card-soft);\n border: 1px solid var(--border-default);\n color: var(--text-primary);\n font-size: 18px;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n transition:\n background-color 0.15s ease,\n color 0.15s ease,\n border-color 0.15s ease;\n}\n\n.gengage-chat-close:hover {\n background: var(--client-primary-subtle);\n color: var(--client-primary);\n border-color: color-mix(in srgb, var(--client-primary) 18%, white);\n}\n\n/* Body: flex container for panel + conversation */\n.gengage-chat-body {\n display: flex;\n flex: 1;\n overflow: hidden;\n min-height: 0;\n position: relative; /* needed for mobile side-panel absolute positioning */\n}\n\n/* Panel (left) — hidden by default */\n.gengage-chat-panel {\n display: none;\n width: 0;\n overflow: hidden;\n transition: width 0.22s cubic-bezier(0.2, 0.72, 0.2, 1);\n}\n\n.gengage-chat-panel--visible {\n display: flex;\n flex-direction: column;\n position: relative;\n box-sizing: border-box;\n width: 440px;\n min-width: 440px;\n background: var(--gengage-chat-panel-bg, var(--surface-card));\n border-right: 1px solid var(--border-default);\n overflow-y: auto;\n padding: 16px;\n scrollbar-width: thin;\n scrollbar-color: color-mix(in srgb, var(--text-muted) 24%, transparent) transparent;\n}\n\n.gengage-chat-panel--visible::-webkit-scrollbar {\n width: 6px;\n}\n\n.gengage-chat-panel--visible::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.gengage-chat-panel--visible::-webkit-scrollbar-thumb {\n background: color-mix(in srgb, var(--text-muted) 24%, transparent);\n border-radius: 3px;\n}\n\n.gengage-chat-panel--visible::-webkit-scrollbar-thumb:hover {\n background: color-mix(in srgb, var(--text-muted) 36%, transparent);\n}\n\n/* Above main panel body: analyzing strip + AITopPicks / AIGroupingCards (desktop) */\n.gengage-chat-panel-ai-zone {\n flex-shrink: 0;\n width: 100%;\n box-sizing: border-box;\n padding: 8px 0 12px;\n margin-bottom: 4px;\n border-bottom: 1px solid var(--border-subtle);\n max-height: min(42vh, 320px);\n overflow: auto;\n}\n\n/* Category rail / AI Top Picks: title + scroll row */\n.gengage-chat-panel-ai-zone:has(.gengage-chat-grouping-cards) {\n max-height: min(52vh, 520px);\n}\n\n/* Top Picks: full height — no inner vertical scroll / clipped borders (lean grid parity) */\n.gengage-chat-panel-ai-zone:has(.gengage-chat-ai-top-picks) {\n max-height: none;\n overflow: visible;\n}\n\n.gengage-chat-panel-ai-zone[hidden] {\n display: none !important;\n}\n\n.gengage-chat-panel-ai-zone-inner {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.gengage-chat-panel-ai-zone-text {\n font-size: 13px;\n color: var(--text-muted);\n}\n\n/* Panel AI zone: grouping cards — title fixed row, cards in horizontal scroll rail */\n.gengage-chat-grouping-section-title {\n display: none;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-grouping-section-title {\n display: block;\n flex-shrink: 0;\n align-self: stretch;\n width: 100%;\n margin: 0 0 10px;\n font-size: 15px;\n font-weight: 700;\n color: var(--text-primary);\n letter-spacing: -0.01em;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-grouping-cards {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: 0;\n width: 100%;\n min-width: 0;\n max-height: none;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-grouping-cards-scroll {\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n align-items: stretch;\n gap: 10px;\n width: 100%;\n min-width: 0;\n overflow-x: auto;\n overflow-y: hidden;\n padding: 2px 2px 10px;\n margin: 0 -2px;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: thin;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-grouping-card {\n flex: 0 0 auto;\n width: 136px;\n min-width: 136px;\n max-width: 150px;\n box-sizing: border-box;\n flex-direction: column;\n align-items: stretch;\n gap: 8px;\n padding: 12px 10px 14px;\n background: var(--surface-card);\n border: 1px solid var(--border-default);\n border-radius: var(--radius-card);\n box-shadow: var(--shadow-1);\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-grouping-card:hover {\n border-color: color-mix(in srgb, var(--client-primary) 18%, var(--border-default));\n box-shadow: var(--shadow-2);\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-grouping-card-arrow {\n display: none;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-grouping-card-img {\n width: 100%;\n max-width: 88px;\n height: 72px;\n min-width: 0;\n min-height: 72px;\n margin: 0 auto;\n border-radius: 8px;\n object-fit: cover;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-grouping-card-body {\n flex-direction: column;\n align-items: center;\n text-align: center;\n gap: 4px;\n width: 100%;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-grouping-card-name {\n font-size: 13px;\n font-weight: 700;\n line-height: 1.25;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-grouping-card-desc {\n font-size: 11px;\n color: var(--text-muted);\n line-height: 1.3;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-grouping-card-labels {\n justify-content: center;\n margin-top: 2px;\n white-space: normal;\n text-overflow: unset;\n overflow: visible;\n line-height: 1.35;\n}\n\n/* Divider between panel and conversation */\n.gengage-chat-panel-divider {\n display: flex;\n align-items: center;\n width: 4px;\n cursor: pointer;\n background: var(--_gengage-border-color);\n position: relative;\n flex-shrink: 0;\n transition: background 0.15s;\n z-index: 2;\n}\n\n.gengage-chat-panel-divider:hover {\n background: var(--border-strong);\n}\n\n.gengage-chat-panel-divider--hidden {\n display: none;\n}\n\n.gengage-chat-panel-divider-toggle {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 28px;\n height: 48px;\n border: 1px solid var(--_gengage-border-color);\n border-radius: 6px;\n background: var(--surface-card);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n color: var(--text-secondary);\n box-shadow: var(--shadow-1);\n transition:\n background 0.15s,\n color 0.15s;\n padding: 0;\n line-height: 1;\n}\n\n.gengage-chat-panel-divider-toggle:hover {\n background: var(--surface-card-muted);\n color: var(--client-primary);\n}\n\n.gengage-chat-panel-divider-preview {\n position: absolute;\n top: 50%;\n left: 50%;\n width: 38px;\n height: 192px;\n display: none;\n flex-direction: column;\n gap: 2px;\n padding: 6px 4px;\n border-radius: 999px;\n overflow: hidden;\n pointer-events: none;\n transform: translate(-50%, -50%);\n border: 1px solid var(--border-default);\n background: color-mix(in srgb, var(--surface-elevated) 96%, transparent);\n box-shadow: var(--shadow-2);\n z-index: 1;\n}\n\n.gengage-chat-panel-divider-preview::after {\n content: '';\n position: absolute;\n inset: 0;\n background: linear-gradient(180deg, transparent, color-mix(in srgb, var(--surface-shell) 10%, transparent));\n pointer-events: none;\n}\n\n.gengage-chat-panel-divider-preview-thumb {\n position: relative;\n flex: 1 1 0;\n min-height: 0;\n border-radius: 999px;\n overflow: hidden;\n}\n\n.gengage-chat-panel-divider-preview-img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n filter: grayscale(0.55) saturate(0.8) brightness(0.92);\n transform: scale(1.14);\n opacity: 0.9;\n}\n\n/* Collapsed panel — compound selector for specificity over .gengage-chat-panel--visible */\n.gengage-chat-panel.gengage-chat-panel--collapsed {\n flex: 0 0 0px;\n width: 0;\n min-width: 0;\n max-width: 0;\n padding: 0;\n border-right: none;\n overflow: hidden;\n}\n\n/* When collapsed, keep toggle button fully visible inside the chat pane */\n.gengage-chat-panel--collapsed ~ .gengage-chat-panel-divider {\n width: 0;\n background: transparent;\n}\n\n.gengage-chat-panel--collapsed ~ .gengage-chat-panel-divider .gengage-chat-panel-divider-toggle {\n left: 50%;\n transform: translate(-50%, -50%);\n}\n\n.gengage-chat-panel--collapsed\n ~ .gengage-chat-panel-divider.gengage-chat-panel-divider--preview-active\n .gengage-chat-panel-divider-preview {\n display: flex;\n left: 50%;\n transform: translate(-50%, -50%);\n}\n\n/* Conversation (right) — always visible */\n.gengage-chat-conversation {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-width: 0;\n overflow: hidden;\n background: var(--surface-card);\n position: relative;\n z-index: 1;\n}\n\n/* Full-height, right-rail panel mode — overlay on top of the host page.\n Header stays inside the conversation column; the results panel is a\n natural drawer extension that slides out from the left of the chat.\n Conversation right edge is kept at the same offset as normal mode\n so the chat pane does not jump when the panel opens. */\n.gengage-chat-drawer--with-panel {\n width: auto;\n background: transparent;\n box-shadow: none;\n overflow: visible;\n}\n\n/* Header flows naturally inside conversation column in panel mode */\n.gengage-chat-drawer--with-panel .gengage-chat-header {\n border-radius: 0;\n flex-shrink: 0;\n}\n\n/* Body takes full height — no gap: panes stick together */\n.gengage-chat-drawer--with-panel .gengage-chat-body {\n flex: 1 1 0;\n min-height: 0;\n gap: 0;\n padding: 0;\n align-items: stretch;\n overflow: visible;\n}\n\n/* Panel: attached to conversation, rounded only on the left side */\n.gengage-chat-drawer--with-panel .gengage-chat-panel--visible {\n flex: 0 0 auto;\n width: clamp(var(--_gengage-chat-panel-min-width), 56vw, var(--_gengage-chat-panel-max-width));\n min-width: var(--_gengage-chat-panel-min-width);\n max-width: var(--_gengage-chat-panel-max-width);\n border-right: none;\n background: var(--gengage-chat-panel-bg, var(--surface-card));\n padding: 0 16px 16px;\n overflow-y: auto;\n margin: 0;\n border-radius: 0;\n box-shadow: none;\n max-height: 100dvh;\n}\n\n/* Divider in panel mode: invisible line, only the toggle button shows */\n.gengage-chat-drawer--with-panel .gengage-chat-panel-divider {\n width: 0;\n background: transparent;\n}\n\n/* Toggle button: \"band-aid\" connecting the two panes */\n.gengage-chat-drawer--with-panel .gengage-chat-panel-divider-toggle {\n width: 20px;\n height: 48px;\n border-radius: 10px;\n background: var(--surface-card);\n color: var(--client-primary);\n border: 1px solid color-mix(in srgb, var(--client-primary) 20%, var(--border-default));\n box-shadow: var(--shadow-1);\n font-size: 12px;\n z-index: 10;\n}\n\n.gengage-chat-drawer--with-panel .gengage-chat-panel-divider-toggle:hover {\n background: var(--client-primary-subtle);\n color: var(--client-primary);\n}\n\n.gengage-chat-drawer--with-panel .gengage-chat-panel-divider--preview-active .gengage-chat-panel-divider-toggle:hover {\n background: var(--client-primary-subtle);\n}\n\n.gengage-chat-drawer--with-panel .gengage-chat-conversation {\n flex: 0 0 var(--_gengage-chat-conversation-width);\n width: var(--_gengage-chat-conversation-width);\n border-left: none;\n background: var(--surface-card);\n box-shadow: var(--shadow-2);\n}\n\n/* Conversation sits flush against viewport edge in panel mode */\n.gengage-chat-drawer--with-panel .gengage-chat-messages {\n padding-right: 6px;\n scrollbar-width: thin;\n scrollbar-color: color-mix(in srgb, var(--text-muted) 22%, transparent) transparent;\n}\n\n.gengage-chat-drawer--with-panel .gengage-chat-messages::-webkit-scrollbar {\n width: 0;\n}\n\n/* In --with-panel mode, collapsed panel must override the visible sizing above */\n.gengage-chat-drawer--with-panel .gengage-chat-panel.gengage-chat-panel--collapsed {\n flex: 0 0 0px;\n width: 0;\n min-width: 0;\n max-width: 0;\n padding: 0;\n overflow: hidden;\n}\n\n/* When panel is collapsed, shrink drawer to just the conversation width */\n.gengage-chat-drawer--with-panel:has(.gengage-chat-panel--collapsed) {\n width: var(--_gengage-chat-conversation-width);\n background: var(--surface-elevated, var(--surface-card));\n box-shadow: var(--_gengage-chat-shadow);\n}\n\n.gengage-chat-drawer--with-panel .gengage-chat-footer {\n display: none;\n}\n\n/* Messages */\n.gengage-chat-messages {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 16px;\n min-height: 0;\n scroll-behavior: smooth;\n -webkit-overflow-scrolling: touch;\n overscroll-behavior: contain;\n touch-action: pan-y;\n}\n\n.gengage-chat-messages::-webkit-scrollbar {\n width: 6px;\n}\n.gengage-chat-messages::-webkit-scrollbar-track {\n background: transparent;\n}\n.gengage-chat-messages::-webkit-scrollbar-thumb {\n background: color-mix(in srgb, var(--text-muted) 22%, transparent);\n border-radius: 3px;\n}\n.gengage-chat-messages::-webkit-scrollbar-thumb:hover {\n background: color-mix(in srgb, var(--text-muted) 36%, transparent);\n}\n\n/* Thread focus: collapse older turns until user expands history */\n.gengage-chat-bubble--presentation-collapsed {\n display: none !important;\n}\n\n.gengage-chat-former-messages-btn {\n position: sticky;\n top: 0;\n z-index: 2;\n flex-shrink: 0;\n align-self: center;\n margin-bottom: 4px;\n padding: 8px 14px;\n border-radius: var(--radius-pill);\n border: 1px solid var(--ds-chip-border);\n background: color-mix(in srgb, var(--surface-card) 96%, transparent);\n color: var(--text-primary);\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n box-shadow: var(--shadow-1);\n}\n\n.gengage-chat-former-messages-btn:hover {\n background: var(--surface-card-muted);\n}\n\n.gengage-chat-bubble {\n position: relative;\n}\n\n@keyframes gengage-chat-msg-in {\n from {\n opacity: 0;\n transform: translateY(8px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n.gengage-chat-bubble--user {\n max-width: 85%;\n}\n\n.gengage-chat-bubble--assistant {\n max-width: 85%;\n margin-left: 2px;\n}\n\n/* Active message highlight (clicked to show its panel content) */\n.gengage-chat-bubble--active {\n outline: 2px solid var(--ds-message-active-outline);\n outline-offset: -2px;\n}\n\n.gengage-chat-bubble--assistant:empty {\n display: none;\n}\n\n/* Rich HTML content inside assistant bubbles */\n.gengage-chat-bubble--assistant .gengage-chat-bubble-text a {\n color: var(--client-primary);\n text-decoration: underline;\n}\n.gengage-chat-bubble--assistant .gengage-chat-bubble-text a:hover {\n text-decoration: none;\n}\n.gengage-chat-bubble--assistant .gengage-chat-bubble-text p {\n margin: 0 0 8px;\n}\n.gengage-chat-bubble--assistant .gengage-chat-bubble-text p:last-child {\n margin-bottom: 0;\n}\n.gengage-chat-bubble--assistant .gengage-chat-bubble-text ul,\n.gengage-chat-bubble--assistant .gengage-chat-bubble-text ol {\n margin: 6px 0;\n padding-left: 20px;\n}\n.gengage-chat-bubble--assistant .gengage-chat-bubble-text li {\n margin-bottom: 6px;\n line-height: 1.5;\n}\n.gengage-chat-bubble--assistant .gengage-chat-bubble-text code {\n background: color-mix(in srgb, var(--text-primary) 8%, transparent);\n padding: 1px 4px;\n border-radius: 6px;\n font-size: 0.9em;\n}\n.gengage-chat-bubble--assistant .gengage-chat-bubble-text pre {\n background: color-mix(in srgb, var(--surface-card) 78%, var(--surface-card-soft));\n border: 1px solid var(--border-subtle);\n padding: 10px 12px;\n border-radius: 12px;\n overflow-x: auto;\n margin: 4px 0;\n}\n.gengage-chat-bubble--assistant .gengage-chat-bubble-text table {\n border-collapse: collapse;\n margin: 4px 0;\n font-size: 0.9em;\n}\n.gengage-chat-bubble--assistant .gengage-chat-bubble-text th,\n.gengage-chat-bubble--assistant .gengage-chat-bubble-text td {\n border: 1px solid var(--border-default);\n padding: 4px 8px;\n}\n.gengage-chat-bubble--assistant .gengage-chat-bubble-text img {\n max-width: 100%;\n height: auto;\n border-radius: 12px;\n margin: 4px 0;\n}\n.gengage-chat-bubble--assistant .gengage-chat-bubble-text h1,\n.gengage-chat-bubble--assistant .gengage-chat-bubble-text h2,\n.gengage-chat-bubble--assistant .gengage-chat-bubble-text h3,\n.gengage-chat-bubble--assistant .gengage-chat-bubble-text h4,\n.gengage-chat-bubble--assistant .gengage-chat-bubble-text h5,\n.gengage-chat-bubble--assistant .gengage-chat-bubble-text h6 {\n margin: 8px 0 4px;\n line-height: 1.3;\n color: var(--text-primary);\n}\n.gengage-chat-bubble--assistant .gengage-chat-bubble-text blockquote {\n border-left: 3px solid var(--border-default);\n padding-left: 12px;\n margin: 4px 0;\n color: var(--text-secondary);\n}\n\n/* GAP-077: Prevent bot response text from clipping on narrow viewports */\n.gengage-chat-bubble--assistant .gengage-chat-bubble-text {\n overflow-wrap: break-word;\n}\n\n/* First bot message in thread — subtle accent */\n.gengage-chat-bubble--first {\n border-left: 1px solid var(--ds-message-assistant-border);\n}\n\n.gengage-chat-bubble--first .gengage-chat-bubble-text {\n font-size: inherit;\n}\n\n@media (max-width: 768px) {\n .gengage-chat-bubble--assistant .gengage-chat-bubble-text {\n line-height: 1.5;\n }\n\n /* GAP-054: Prevent long user messages from overflowing on mobile */\n .gengage-chat-bubble--user {\n max-width: calc(100% - 16px);\n word-break: break-word;\n }\n}\n\n/* Typing indicator */\n.gengage-chat-typing {\n align-self: flex-start;\n max-width: 85%;\n}\n\n.gengage-chat-typing-dots {\n flex-shrink: 0;\n}\n\n.gengage-chat-typing-text {\n flex: 1;\n min-width: 0;\n}\n\n.gengage-chat-panel-loading-status {\n margin-bottom: 16px;\n}\n\n/* Panel loading strip: stay inside padded panel (desktop + mobile) */\n.gengage-chat-panel-loading-status.gds-progress-loader-panel {\n max-width: 100%;\n min-width: 0;\n box-sizing: border-box;\n}\n\n/* Mobile: single-line label with ellipsis */\n.gengage-chat-root--mobile .gengage-chat-panel-loading-status.gds-progress-loader-panel {\n max-width: 100%;\n}\n\n.gengage-chat-root--mobile .gengage-chat-panel-loading-status .gengage-chat-panel-loading-label {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Offline status bar */\n.gengage-chat-offline-bar {\n display: none;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n font-size: 12px;\n line-height: 1.4;\n color: color-mix(in srgb, var(--warning) 72%, var(--text-primary));\n background: color-mix(in srgb, var(--warning) 10%, white);\n border-bottom: 1px solid color-mix(in srgb, var(--warning) 22%, var(--border-default));\n}\n.gengage-chat-offline-bar--visible {\n display: flex;\n}\n.gengage-chat-offline-bar::before {\n content: '';\n display: inline-block;\n width: 8px;\n height: 8px;\n background: var(--_gengage-rating-color);\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n/* Thinking steps (legacy checklist style kept for compatibility) */\n.gengage-chat-thinking-steps {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 4px 0;\n}\n\n.gengage-chat-thinking-step {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n color: var(--text-secondary);\n}\n\n.gengage-chat-thinking-step-marker {\n font-size: 12px;\n width: 16px;\n text-align: center;\n flex-shrink: 0;\n}\n\n.gengage-chat-thinking-step-marker--done {\n color: var(--_gengage-success-color);\n}\n\n.gengage-chat-thinking-step-marker--active {\n color: var(--ai-accent-start);\n animation: gengage-thinking-pulse 1.5s ease-in-out infinite;\n}\n\n@keyframes gengage-thinking-pulse {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.4;\n }\n}\n\n.gengage-chat-thinking-step-text {\n line-height: 1.4;\n}\n\n/* Error */\n.gengage-chat-error {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 10px;\n padding: 10px 14px;\n border-radius: var(--radius-control);\n background: color-mix(in srgb, var(--error) 10%, white);\n color: color-mix(in srgb, var(--error) 84%, var(--text-primary));\n font-size: 13px;\n text-align: center;\n border: 1px solid color-mix(in srgb, var(--error) 22%, var(--border-default));\n}\n\n.gengage-chat-error-retry {\n flex-shrink: 0;\n padding: 4px 12px;\n border: 1px solid color-mix(in srgb, var(--error) 24%, var(--border-default));\n border-radius: var(--radius-control);\n background: var(--surface-card);\n color: color-mix(in srgb, var(--error) 84%, var(--text-primary));\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition:\n background 0.15s,\n border-color 0.15s;\n}\n\n.gengage-chat-error-retry:hover {\n background: color-mix(in srgb, var(--error) 8%, white);\n border-color: color-mix(in srgb, var(--error) 32%, var(--border-default));\n}\n\n/* Input area — pill container matching production cockpit */\n.gengage-chat-input-area {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n flex-shrink: 0;\n gap: 0;\n padding: 10px 12px 12px;\n border-top: 1px solid var(--border-subtle);\n background: var(--surface-card);\n /* Keep attach menu (overflows upward) below the message list */\n position: relative;\n z-index: 2;\n}\n\n.gengage-chat-input-pill {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n min-width: 0;\n min-height: 52px;\n background: var(--surface-card);\n border: 1px solid var(--border-default);\n border-radius: 16px;\n padding: 6px 6px 6px 14px;\n /* overflow:visible — attach menu overflows upward; hidden would clip it */\n overflow: visible;\n transition:\n border-color 0.2s ease,\n box-shadow 0.2s ease;\n box-shadow: none;\n}\n\n.gengage-chat-input-pill:focus-within {\n border-color: color-mix(in srgb, var(--client-primary) 28%, white);\n box-shadow: 0 0 0 4px var(--client-focus-ring);\n}\n\n.gengage-chat-input {\n flex: 1;\n min-width: 0;\n min-height: 40px;\n max-height: 120px;\n padding: 0;\n border: none;\n border-radius: 0;\n font-size: 14px;\n font-family: inherit;\n line-height: 1.4;\n align-content: center;\n outline: none;\n background: transparent;\n color: var(--_gengage-text-primary);\n resize: none;\n overflow-y: hidden;\n}\n\n/* On mobile, keep single-line appearance and meet WCAG 2.5.5 touch targets */\n.gengage-chat-root--mobile .gengage-chat-input-pill {\n min-height: 52px;\n padding: 6px 6px 6px 14px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-input {\n min-height: 44px;\n max-height: 44px;\n font-size: 16px;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.gengage-chat-root--mobile .gengage-chat-input::placeholder {\n font-size: 15px;\n}\n\n.gengage-chat-input::placeholder {\n font-size: 14px;\n color: var(--text-muted);\n line-height: normal;\n}\n\n.gengage-chat-send {\n width: 40px;\n height: 40px;\n min-width: 40px;\n padding: 0;\n border: 1px solid var(--ds-button-primary-border);\n border-radius: 12px;\n background: var(--ds-button-primary-bg);\n color: var(--ds-button-primary-fg);\n font-weight: 700;\n font-size: 0;\n font-family: inherit;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n transition:\n background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1),\n box-shadow 0.2s ease,\n transform 0.15s ease;\n box-shadow: none;\n position: relative;\n}\n\n.gengage-chat-send:hover {\n background: var(--ds-button-primary-bg-hover);\n transform: none;\n box-shadow: none;\n}\n\n.gengage-chat-send:active {\n transform: translateY(0);\n box-shadow: none;\n}\n\n.gengage-chat-send svg {\n width: 18px;\n height: 18px;\n}\n\n.gengage-chat-send--stop {\n background: var(--surface-card-soft);\n border-color: color-mix(in srgb, var(--client-primary) 18%, var(--border-default));\n color: var(--text-primary);\n}\n\n.gengage-chat-send--stop:hover {\n background: color-mix(in srgb, var(--surface-card-soft) 86%, var(--client-primary-subtle));\n border-color: color-mix(in srgb, var(--client-primary) 24%, var(--border-default));\n color: var(--client-primary);\n}\n\n.gengage-chat-send-stop-icon {\n display: inline-block;\n width: 12px;\n height: 12px;\n border-radius: 4px;\n background: currentColor;\n}\n\n.gengage-chat-send--stop::after {\n content: attr(data-tooltip);\n position: absolute;\n right: calc(100% + 10px);\n top: 50%;\n transform: translateY(-50%);\n padding: 7px 10px;\n border-radius: 10px;\n background: color-mix(in srgb, var(--surface-shell) 98%, black);\n border: 1px solid color-mix(in srgb, var(--surface-shell) 60%, white);\n box-shadow: var(--shadow-2);\n color: var(--text-inverse);\n font-size: 12px;\n font-weight: 600;\n line-height: 1.2;\n white-space: nowrap;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.16s ease;\n z-index: 12;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .gengage-chat-send--stop:hover::after,\n .gengage-chat-send--stop:focus-visible::after {\n opacity: 1;\n }\n}\n\n.gengage-chat-send:disabled {\n background: var(--ds-neutral-200);\n border-color: var(--ds-neutral-200);\n opacity: 0.5;\n box-shadow: none;\n cursor: not-allowed;\n}\n\n/* Footer — minimal, clean */\n.gengage-chat-footer {\n padding: 4px 10px;\n text-align: center;\n font-size: 10px;\n color: var(--text-muted);\n border-top: 1px solid var(--border-subtle);\n background: var(--surface-card);\n}\n\n/* UI Spec rendered components */\n.gengage-chat-uispec {\n width: 100%;\n}\n\n.gengage-chat-uispec:empty {\n display: none;\n}\n\n.gengage-chat-uispec > * {\n animation: gengage-chat-widget-enter 0.16s cubic-bezier(0.2, 0.7, 0.2, 1) both;\n}\n\n@keyframes gengage-chat-widget-enter {\n from {\n opacity: 0;\n transform: translateY(5px) scale(0.99);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n\n.gengage-chat-action-buttons {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n padding: 6px 0 4px;\n /* Even distribution when buttons wrap to a new line */\n justify-content: flex-start;\n}\n\n/* When action buttons wrap, last orphan row fills available space */\n.gengage-chat-action-buttons::after {\n content: '';\n flex: 1 0 auto;\n}\n\n.gengage-chat-action-btn {\n flex: 0 0 auto;\n padding: 8px 16px;\n border: 1px solid var(--ds-button-primary-border);\n border-radius: var(--radius-pill, 999px);\n background: var(--ds-button-primary-bg);\n color: var(--ds-button-primary-fg);\n font-size: 13px;\n font-weight: 700;\n line-height: 1.25;\n font-family: inherit;\n cursor: pointer;\n white-space: nowrap;\n transition:\n filter 0.15s ease,\n transform 0.1s ease;\n}\n\n.gengage-chat-action-btn:hover {\n background: var(--ds-button-primary-bg-hover);\n}\n\n.gengage-chat-action-btn:active {\n transform: scale(0.97);\n}\n\n.gengage-chat-product-card {\n position: relative;\n display: flex;\n flex-direction: column;\n border: 1px solid var(--ds-card-border);\n border-radius: var(--ds-card-radius);\n overflow: hidden;\n background: var(--ds-card-bg);\n width: 160px;\n min-width: 160px;\n max-width: 160px;\n box-shadow: var(--ds-card-shadow);\n transition:\n transform 0.1s ease,\n box-shadow 0.2s ease;\n}\n\n.gengage-chat-product-card:hover {\n transform: translateY(-2px);\n box-shadow: var(--ds-card-shadow-hover);\n}\n\n.gengage-chat-product-card-img {\n width: 100%;\n height: 120px;\n object-fit: contain;\n display: block;\n background: var(--surface-card);\n padding: 8px;\n box-sizing: border-box;\n}\n\n.gengage-chat-product-card-body {\n display: flex;\n flex-direction: column;\n flex: 1;\n gap: 4px;\n padding: 8px 10px 10px;\n text-align: center;\n}\n\n.gengage-chat-product-card-name {\n font-size: 13px;\n font-weight: 600;\n color: var(--_gengage-text-primary);\n line-height: 1.35;\n margin-bottom: 0;\n min-height: calc(2 * 1.35em);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.gengage-chat-product-card-brand {\n font-size: 10px;\n color: var(--text-secondary);\n}\n\n.gengage-chat-product-card-rating {\n font-size: 11px;\n color: var(--_gengage-rating-color);\n line-height: 1;\n}\n\n.gengage-star-half {\n display: inline-block;\n position: relative;\n}\n\n.gengage-star-half > span {\n position: absolute;\n left: 0;\n top: 0;\n overflow: hidden;\n width: 0.5em;\n}\n\n.gengage-chat-product-card-review-count {\n color: var(--text-secondary);\n font-size: 10px;\n}\n\n.gengage-chat-product-card-price,\n.gengage-chat-product-card-price-block {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n justify-content: center;\n gap: 2px 6px;\n font-size: 16px;\n font-weight: 800;\n color: var(--text-primary);\n}\n\n.gengage-chat-product-card-current-price {\n font: inherit;\n font-weight: 800;\n color: var(--gengage-discounted-price-color, var(--text-primary));\n}\n\n.gengage-chat-product-card-original-price {\n text-decoration: line-through;\n color: var(--_gengage-text-secondary);\n font-weight: 400;\n font-size: 11px;\n margin-right: 0;\n}\n\n.gengage-chat-campaign-reason {\n max-width: 100%;\n font-size: 11px;\n font-weight: 700;\n line-height: 1.25;\n margin: 0 0 4px;\n letter-spacing: 0.02em;\n background: linear-gradient(90deg, var(--_gengage-campaign-reason-start), var(--_gengage-campaign-reason-end));\n -webkit-background-clip: text;\n background-clip: text;\n color: transparent;\n}\n\n@supports not (background-clip: text) {\n .gengage-chat-campaign-reason {\n background: none;\n color: var(--_gengage-campaign-reason-start);\n }\n\n .gengage-chat-campaign-price-badge__reason {\n background: none;\n color: var(--_gengage-campaign-reason-start);\n }\n}\n\n.gengage-chat-product-card-price-stack {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n min-width: 0;\n width: 100%;\n}\n\n.gengage-chat-product-card-price-row {\n display: flex;\n flex-direction: row;\n align-items: baseline;\n flex-wrap: wrap;\n gap: 0 6px;\n}\n\n.gengage-chat-product-card-price-sep {\n display: inline-block;\n width: 1px;\n height: 0.95em;\n margin: 0 1px;\n background: var(--border-subtle);\n align-self: center;\n flex-shrink: 0;\n}\n\n.gengage-chat-product-card-price-block--inline .gengage-chat-product-card-original-price {\n text-decoration: none;\n font-size: 16px;\n font-weight: 500;\n color: var(--gengage-inline-original-price-color, var(--text-secondary));\n}\n\n.gengage-chat-product-card-price-block--inline .gengage-chat-product-card-current-price {\n font-size: 16px;\n font-weight: 800;\n color: var(--gengage-discounted-price-color, var(--text-primary));\n}\n\n/* Price row + compact rating (★ 4.5) */\n.gengage-chat-product-card-meta-row {\n display: flex;\n flex-direction: row;\n align-items: flex-start;\n justify-content: space-between;\n gap: 8px;\n width: 100%;\n min-height: 34px;\n}\n\n.gengage-chat-product-card-meta-row--empty {\n visibility: hidden;\n}\n\n.gengage-chat-product-card-rating-compact {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n flex-shrink: 0;\n padding: 3px 8px;\n border: 1px solid var(--border-subtle);\n border-radius: 999px;\n background: var(--surface-card-muted);\n font-size: 11px;\n font-weight: 700;\n color: var(--text-secondary);\n line-height: 1.2;\n}\n\n.gengage-chat-product-card-rating-compact-star {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: color-mix(in srgb, var(--warning) 60%, var(--text-primary));\n line-height: 0;\n}\n\n.gengage-chat-product-card-rating-compact-value {\n font-variant-numeric: tabular-nums;\n color: var(--text-primary);\n}\n\n/* Image corner actions (fav + find similar) */\n.gengage-chat-product-card-img-actions {\n position: absolute;\n top: 8px;\n right: 8px;\n z-index: 4;\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 6px;\n pointer-events: none;\n}\n\n.gengage-chat-product-card-img-actions > * {\n pointer-events: auto;\n}\n\n.gengage-chat-product-card-img-actions .gengage-chat-favorite-btn {\n position: static;\n}\n\n.gengage-chat-product-card-img-actions .gengage-chat-find-similar-pill {\n position: static;\n}\n\n/* Async pricing skeleton */\n.gengage-chat-price-skeleton {\n display: inline-block;\n width: 80px;\n height: 16px;\n background: linear-gradient(90deg, var(--ds-neutral-200) 25%, var(--ds-neutral-100) 50%, var(--ds-neutral-200) 75%);\n background-size: 200% 100%;\n animation: gengage-skeleton-price-pulse 1.5s ease-in-out infinite;\n border-radius: 4px;\n vertical-align: middle;\n}\n\n@keyframes gengage-skeleton-price-pulse {\n 0% {\n background-position: 200% 0;\n }\n 100% {\n background-position: -200% 0;\n }\n}\n\n.gengage-chat-product-card-cta {\n display: block;\n margin-top: auto;\n padding: 8px 10px;\n text-align: center;\n background: transparent;\n color: var(--client-primary);\n text-decoration: none;\n font-size: 12px;\n font-weight: 700;\n letter-spacing: 0.01em;\n border-top: 1px solid var(--border-default);\n cursor: pointer;\n transition:\n color 0.15s ease,\n background 0.15s ease;\n}\n\n.gengage-chat-product-card-cta:hover {\n color: var(--client-primary-hover);\n background: var(--surface-card-soft);\n}\n\n.gengage-chat-product-card-cta:active {\n background: var(--surface-card-soft);\n}\n\nbutton.gengage-chat-product-card-cta {\n appearance: none;\n -webkit-appearance: none;\n font: inherit;\n margin: 0;\n width: 100%;\n box-sizing: border-box;\n border-left: none;\n border-right: none;\n border-bottom: none;\n}\n\n/*\n * ProductSummaryCard — compact horizontal card for inline chat-pane rendering.\n * Mirrors production's LaunchSingleProduct: image left, info right.\n */\n.gengage-chat-product-summary {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 12px;\n padding: 10px 12px;\n border: 1px solid var(--border-default);\n border-radius: var(--radius-control);\n background: var(--surface-card);\n cursor: pointer;\n transition:\n background 0.15s,\n box-shadow 0.15s,\n border-color 0.15s;\n margin: 4px 0;\n}\n.gengage-chat-product-summary:hover {\n background: var(--surface-card-soft);\n border-color: color-mix(in srgb, var(--client-primary) 18%, var(--border-default));\n box-shadow: var(--shadow-1);\n}\n\n.gengage-chat-product-summary__image {\n flex-shrink: 0;\n width: 64px;\n height: 64px;\n border-radius: var(--radius-control);\n overflow: hidden;\n border: 1px solid var(--border-subtle);\n background: var(--surface-card-soft);\n}\n.gengage-chat-product-summary__image img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n}\n\n.gengage-chat-product-summary__content {\n flex: 1 1 180px;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.gengage-chat-product-summary__name {\n font-size: 13px;\n font-weight: 500;\n line-height: 1.3;\n color: var(--text-primary);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.gengage-chat-product-summary__rating {\n font-size: 11px;\n color: var(--_gengage-rating-color);\n line-height: 1;\n}\n.gengage-chat-product-summary__review-count {\n color: var(--text-secondary);\n font-size: 11px;\n}\n\n.gengage-chat-product-summary__price {\n font-size: 13px;\n font-weight: 600;\n color: var(--text-primary);\n line-height: 1;\n margin-top: 2px;\n}\n.gengage-chat-product-summary__price-original {\n text-decoration: line-through;\n color: var(--_gengage-text-secondary);\n font-weight: 400;\n font-size: 11px;\n}\n.gengage-chat-product-summary__price-current {\n color: var(--gengage-discounted-price-color, var(--text-primary));\n}\n\n.gengage-chat-product-summary__price-stack {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 2px;\n margin-top: 2px;\n}\n\n.gengage-chat-product-summary__price-stack .gengage-chat-campaign-reason {\n font-size: 11px;\n font-weight: 700;\n}\n\n.gengage-chat-product-summary__price--inline {\n display: flex;\n flex-direction: row;\n align-items: baseline;\n flex-wrap: wrap;\n gap: 0 6px;\n}\n\n.gengage-chat-product-summary__price-sep {\n display: inline-block;\n width: 1px;\n height: 0.9em;\n margin: 0 1px;\n background: var(--border-subtle);\n align-self: center;\n flex-shrink: 0;\n}\n\n.gengage-chat-product-summary__price--inline .gengage-chat-product-summary__price-original {\n text-decoration: none;\n font-size: 13px;\n font-weight: 500;\n color: var(--gengage-inline-original-price-color, var(--text-secondary));\n}\n\n.gengage-chat-product-summary__price--inline .gengage-chat-product-summary__price-current {\n font-size: 13px;\n font-weight: 700;\n color: var(--gengage-discounted-price-color, var(--text-primary));\n}\n\n.gengage-chat-product-summary__cta {\n flex-shrink: 0;\n margin-left: auto;\n font-size: 12px;\n font-weight: 600;\n color: var(--client-primary);\n text-decoration: none;\n padding: 4px 10px;\n border-radius: var(--radius-pill);\n background: var(--client-primary-subtle);\n transition:\n background 0.15s,\n color 0.15s;\n white-space: nowrap;\n}\n.gengage-chat-product-summary__cta:hover {\n background: var(--client-primary-soft);\n}\n.gengage-chat-product-summary__cta:focus-visible {\n outline: 2px solid var(--client-focus-ring);\n outline-offset: 2px;\n}\n\n.gengage-chat-product-grid {\n display: flex;\n gap: 12px;\n overflow-x: auto;\n /* Right padding hints at horizontal scrollability (scroll affordance) */\n padding: 8px 16px 8px 0;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: none;\n scroll-snap-type: x proximity;\n min-height: 180px;\n}\n\n.gengage-chat-product-grid > * {\n scroll-snap-align: start;\n flex: 0 0 auto;\n}\n\n.gengage-chat-product-grid::-webkit-scrollbar {\n display: none;\n}\n\n.gengage-chat-review-highlights {\n padding: 4px 0 8px;\n}\n\n.gengage-chat-review-empty {\n font-size: 12px;\n color: var(--_gengage-text-secondary);\n}\n\n.gengage-chat-review-subjects-heading {\n font-size: 14px;\n font-weight: 700;\n color: var(--text-primary);\n margin-bottom: 10px;\n}\n\n.gengage-chat-review-subjects {\n margin-bottom: 12px;\n}\n\n.gengage-chat-review-subject {\n gap: 6px;\n padding: 7px 11px;\n font-size: 13px;\n font-weight: 600;\n line-height: 1.2;\n color: var(--text-primary);\n box-shadow: none;\n}\n\n.gengage-chat-review-subject--active {\n border-color: var(--ds-chip-border-active);\n background: var(--ds-chip-bg-active);\n color: var(--ds-chip-fg-active);\n}\n\n.gengage-chat-review-subject-icon {\n width: 14px;\n height: 14px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.gengage-chat-review-subject-icon svg {\n width: 14px;\n height: 14px;\n display: block;\n}\n\n.gengage-chat-review-subject[data-tone='positive'] .gengage-chat-review-subject-icon {\n color: color-mix(in srgb, var(--success) 82%, var(--text-primary));\n}\n\n.gengage-chat-review-subject[data-tone='negative'] .gengage-chat-review-subject-icon {\n color: color-mix(in srgb, var(--error) 84%, var(--text-primary));\n}\n\n.gengage-chat-review-subject[data-tone='neutral'] .gengage-chat-review-subject-icon {\n color: var(--text-secondary);\n}\n\n.gengage-chat-review-subject-label {\n color: inherit;\n}\n\n.gengage-chat-review-subject-count {\n color: var(--text-secondary);\n font-size: 12px;\n font-weight: 700;\n}\n\n.gengage-chat-review-detail {\n padding: 12px;\n}\n\n.gengage-chat-review-detail-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 10px 16px;\n align-items: baseline;\n margin-bottom: 10px;\n}\n\n.gengage-chat-review-detail-mentions {\n font-size: 13px;\n font-weight: 700;\n color: var(--_gengage-text-primary);\n}\n\n.gengage-chat-review-detail-positive {\n font-size: 13px;\n font-weight: 700;\n color: var(--_gengage-success-color);\n}\n\n.gengage-chat-review-detail-negative {\n font-size: 13px;\n font-weight: 700;\n color: var(--error);\n}\n\n.gengage-chat-review-snippets {\n display: grid;\n gap: 10px;\n}\n\n.gengage-chat-review-snippet {\n border: 1px solid var(--border-subtle);\n border-left: 3px solid var(--border-default);\n border-radius: var(--radius-control);\n background: var(--surface-card-soft);\n padding: 8px 10px;\n}\n\n.gengage-chat-review-snippet[data-tone='positive'] {\n border-left-color: color-mix(in srgb, var(--success) 40%, var(--border-default));\n}\n\n.gengage-chat-review-snippet[data-tone='negative'] {\n border-left-color: color-mix(in srgb, var(--error) 40%, var(--border-default));\n}\n\n.gengage-chat-review-snippet-text {\n font-size: 13px;\n line-height: 1.4;\n color: var(--_gengage-text-primary);\n}\n\n.gengage-chat-review-snippet-rating {\n margin-top: 4px;\n font-size: 12px;\n font-weight: 700;\n color: var(--text-secondary);\n}\n\n/* -- Favorites Page -------------------------------------------------------- */\n\n.gengage-chat-favorites-page {\n padding: 12px;\n}\n\n.gengage-chat-favorites-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 48px 24px;\n text-align: center;\n color: var(--_gengage-text-secondary);\n}\n\n.gengage-chat-favorites-empty-icon {\n opacity: 0.25;\n color: var(--error);\n}\n\n.gengage-chat-favorites-empty p {\n font-size: 13px;\n margin: 0;\n line-height: 1.5;\n}\n\n.gengage-chat-favorites-list {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.gengage-chat-favorites-card {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 12px;\n border: 1px solid var(--border-default);\n border-radius: var(--radius-control);\n background: var(--surface-card);\n position: relative;\n box-shadow: var(--shadow-1);\n transition:\n opacity 0.2s,\n transform 0.2s,\n box-shadow 0.2s,\n border-color 0.2s;\n}\n\n.gengage-chat-favorites-card:hover {\n border-color: color-mix(in srgb, var(--client-primary) 18%, var(--border-default));\n box-shadow: var(--shadow-1);\n}\n\n.gengage-chat-favorites-card--removing {\n opacity: 0;\n transform: translateX(8px);\n}\n\n.gengage-chat-favorites-card__image {\n width: 56px;\n height: 56px;\n flex-shrink: 0;\n border-radius: var(--radius-control);\n overflow: hidden;\n background: var(--surface-card-muted);\n}\n\n.gengage-chat-favorites-card__image img {\n width: 100%;\n height: 100%;\n object-fit: contain;\n}\n\n.gengage-chat-favorites-card__info {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.gengage-chat-favorites-card__name {\n font-size: 13px;\n font-weight: 500;\n color: var(--text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.gengage-chat-favorites-card__price {\n font-size: 13px;\n font-weight: 700;\n color: var(--client-primary);\n}\n\n.gengage-chat-favorites-remove {\n flex-shrink: 0;\n width: 28px;\n height: 28px;\n border-radius: var(--radius-pill);\n background: transparent;\n border: none;\n color: var(--error);\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n opacity: 0.7;\n transition:\n opacity 0.15s,\n background 0.15s;\n}\n\n.gengage-chat-favorites-remove:hover {\n opacity: 1;\n background: color-mix(in srgb, var(--error) 10%, white);\n}\n\n/* -- Divider --------------------------------------------------------------- */\n\n.gengage-chat-divider {\n border: none;\n border-top: 1px solid var(--_gengage-border-color);\n margin: 8px 0;\n}\n\n.gengage-chat-divider-wrapper {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 8px 0;\n}\n\n.gengage-chat-divider-wrapper hr {\n flex: 1;\n border: none;\n border-top: 1px solid var(--_gengage-border-color);\n margin: 0;\n}\n\n.gengage-chat-divider-label {\n font-size: 11px;\n color: var(--text-muted);\n white-space: nowrap;\n}\n\n/* Overlay variant — full-screen modal with backdrop */\n.gengage-chat--overlay .gengage-chat-drawer {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n width: 100%;\n max-width: 480px;\n max-height: 100%;\n margin: 0 auto;\n border-radius: 0;\n animation: gengage-chat-overlay-in 0.24s cubic-bezier(0.2, 0.72, 0.2, 1);\n}\n\n@keyframes gengage-chat-overlay-in {\n from {\n opacity: 0;\n transform: translateY(12px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* Panel loading skeleton */\n.gengage-chat-panel-skeleton {\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding: 6px 0;\n min-width: 0;\n max-width: 100%;\n box-sizing: border-box;\n}\n\n.gengage-chat-panel-skeleton-block {\n height: 110px;\n border-radius: 10px;\n background: linear-gradient(90deg, var(--ds-neutral-200) 25%, var(--ds-neutral-100) 50%, var(--ds-neutral-200) 75%);\n background-size: 200% 100%;\n animation: gengage-panel-shimmer 1.4s infinite;\n}\n\n@keyframes gengage-panel-shimmer {\n 0% {\n background-position: 200% 0;\n }\n 100% {\n background-position: -200% 0;\n }\n}\n\n/* Skeleton variant: product details */\n.gengage-chat-panel-skeleton-block--image {\n height: 200px;\n border-radius: 8px;\n margin-bottom: 12px;\n}\n\n.gengage-chat-panel-skeleton-block--text {\n height: 16px;\n border-radius: 4px;\n margin-bottom: 8px;\n}\n\n.gengage-chat-panel-skeleton-block--text:nth-child(3) {\n width: 80%;\n}\n\n.gengage-chat-panel-skeleton-block--text:nth-child(4) {\n width: 60%;\n}\n\n/* Skeleton variant: product grid */\n.gengage-chat-panel-skeleton-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 8px;\n}\n\n.gengage-chat-panel-skeleton-block--card {\n height: 120px;\n border-radius: 8px;\n}\n\n/* Skeleton variant: comparison — panel wrapper (matches .gengage-chat-comparison dimensions) */\n.gengage-chat-panel-skeleton--comparison {\n padding: 0;\n gap: 0;\n}\n\n.gengage-chat-panel-skeleton--comparison .gengage-chat-panel-loading-status {\n margin: 0;\n padding: 16px 16px 0;\n}\n\n.gengage-chat-comparison--skeleton {\n pointer-events: none;\n width: 100%;\n box-sizing: border-box;\n}\n\n/* Shimmer — same animation as .gengage-chat-panel-skeleton-block */\n.gengage-chat-comparison-skeleton-shimmer {\n background: linear-gradient(90deg, var(--ds-neutral-200) 25%, var(--ds-neutral-100) 50%, var(--ds-neutral-200) 75%);\n background-size: 200% 100%;\n animation: gengage-panel-shimmer 1.4s infinite;\n}\n\n.gengage-chat-comparison-skeleton-rec-label {\n height: 12px;\n width: 132px;\n border-radius: 4px;\n margin-bottom: 12px;\n}\n\n.gengage-chat-comparison-skeleton-rec-img {\n width: 80px;\n height: 80px;\n flex-shrink: 0;\n border-radius: 8px;\n}\n\n.gengage-chat-comparison-skeleton-rec-title {\n height: 14px;\n border-radius: 4px;\n margin-bottom: 6px;\n max-width: 100%;\n}\n\n.gengage-chat-comparison-skeleton-rec-title--short {\n width: 72%;\n margin-bottom: 8px;\n}\n\n.gengage-chat-comparison-skeleton-rec-price {\n height: 18px;\n width: 112px;\n border-radius: 4px;\n}\n\n.gengage-chat-comparison-skeleton-hl-label {\n height: 12px;\n width: 96px;\n border-radius: 4px;\n margin-bottom: 6px;\n}\n\n.gengage-chat-comparison-skeleton-hl-list {\n margin: 0;\n padding-left: 18px;\n list-style: none;\n}\n\n.gengage-chat-comparison-skeleton-hl-list li {\n margin-bottom: 8px;\n}\n\n.gengage-chat-comparison-skeleton-hl-list li:last-child {\n margin-bottom: 0;\n}\n\n.gengage-chat-comparison-skeleton-hl-line {\n height: 13px;\n border-radius: 4px;\n width: 100%;\n}\n\n.gengage-chat-comparison-skeleton-hl-line--medium {\n width: 92%;\n}\n\n.gengage-chat-comparison-skeleton-hl-line--short {\n width: 78%;\n}\n\n.gengage-chat-comparison-skeleton-kd-heading {\n height: 14px;\n width: 42%;\n border-radius: 4px;\n margin: 0 0 8px;\n}\n\n.gengage-chat-comparison-skeleton-kd-line {\n height: 13px;\n border-radius: 4px;\n margin-bottom: 8px;\n max-width: 100%;\n}\n\n.gengage-chat-comparison-skeleton-kd-line:last-child {\n margin-bottom: 0;\n}\n\n.gengage-chat-comparison-skeleton-kd-line:nth-child(odd) {\n width: 100%;\n}\n\n.gengage-chat-comparison-skeleton-kd-line:nth-child(even) {\n width: 90%;\n}\n\n.gengage-chat-comparison-special--skeleton {\n display: flex;\n align-items: center;\n min-height: 44px;\n cursor: default;\n}\n\n.gengage-chat-comparison-skeleton-special-line {\n height: 14px;\n width: 56%;\n max-width: 280px;\n border-radius: 4px;\n}\n\n.gengage-chat-comparison-skeleton-table-wrap {\n margin-top: 12px;\n border-radius: 8px;\n border: 1px solid var(--border-default);\n background: var(--surface-card);\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n}\n\n.gengage-chat-comparison-skeleton-table-head,\n.gengage-chat-comparison-skeleton-table-row {\n display: grid;\n grid-template-columns: 72px repeat(3, minmax(0, 1fr));\n gap: 8px;\n align-items: start;\n padding: 10px 12px;\n}\n\n.gengage-chat-comparison-skeleton-table-head {\n border-bottom: 1px solid var(--_gengage-surface-muted);\n background: var(--_gengage-surface-muted);\n}\n\n.gengage-chat-comparison-skeleton-table-corner {\n min-height: 92px;\n}\n\n.gengage-chat-comparison-skeleton-table-col {\n display: flex;\n flex-direction: column;\n align-items: center;\n text-align: center;\n gap: 6px;\n min-width: 0;\n}\n\n.gengage-chat-comparison-skeleton-table-th-img {\n width: 60px;\n height: 60px;\n border-radius: 6px;\n flex-shrink: 0;\n}\n\n.gengage-chat-comparison-skeleton-table-th-name {\n height: 11px;\n width: 100%;\n max-width: 100%;\n border-radius: 3px;\n}\n\n.gengage-chat-comparison-skeleton-table-th-price {\n height: 13px;\n width: 64%;\n border-radius: 3px;\n}\n\n.gengage-chat-comparison-skeleton-table-row {\n border-bottom: 1px solid var(--_gengage-surface-muted);\n}\n\n.gengage-chat-comparison-skeleton-table-row:last-child {\n border-bottom: none;\n}\n\n.gengage-chat-comparison-skeleton-table-label {\n height: 12px;\n width: 100%;\n max-width: 56px;\n border-radius: 3px;\n align-self: center;\n}\n\n.gengage-chat-comparison-skeleton-table-cell {\n height: 14px;\n width: 100%;\n border-radius: 3px;\n}\n\n.gengage-chat-panel-skeleton-block--row {\n height: 32px;\n border-radius: 4px;\n margin-bottom: 4px;\n}\n\n/* Panel content: retail-style product cards (e-commerce reference layout) */\n.gengage-chat-panel .gengage-chat-product-card {\n width: 100%;\n max-width: 100%;\n min-width: 0;\n display: flex;\n flex-direction: column;\n border-radius: var(--radius-card);\n border: 1px solid var(--border-default);\n box-shadow: var(--shadow-1);\n overflow: hidden;\n padding: 0;\n background: var(--surface-card);\n}\n\n.gengage-chat-panel .gengage-chat-product-card:hover {\n transform: translateY(-2px);\n box-shadow: var(--shadow-2);\n border-color: var(--border-strong);\n}\n\n.gengage-chat-panel .gengage-chat-product-card-img-actions {\n top: 10px;\n right: 10px;\n gap: 6px;\n}\n\n.gengage-chat-panel .gengage-chat-product-card-img-actions .gengage-chat-favorite-btn,\n.gengage-chat-panel .gengage-chat-product-card-img-actions .gengage-chat-find-similar-pill {\n width: 28px;\n height: 28px;\n border-radius: 8px;\n}\n\n.gengage-chat-panel .gengage-chat-product-card-discount-badge {\n top: 10px;\n left: 10px;\n min-width: 36px;\n height: 36px;\n padding: 0 5px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--radius-pill);\n font-size: 11px;\n font-weight: 700;\n background: var(--client-primary);\n color: var(--client-on-primary);\n box-shadow: var(--shadow-1);\n}\n\n.gengage-chat-panel .gengage-chat-product-card-brand {\n display: none;\n}\n\n.gengage-chat-panel .gengage-chat-product-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(176px, 1fr));\n overflow-x: visible;\n gap: 12px;\n min-height: auto;\n padding: 4px 0;\n}\n\n/*\n * Mobile panel search results: panel grid with `minmax(176px)` collapsed to single column on narrow screens.\n * `--mobile` forces two equal columns; no horizontal scroll, vertical list.\n */\n.gengage-chat-root--mobile .gengage-chat-panel .gengage-chat-product-grid.gengage-chat-product-grid--mobile {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n overflow-x: visible;\n gap: 8px;\n padding: 6px 0 10px;\n min-height: auto;\n}\n\n.gengage-chat-root--mobile\n .gengage-chat-panel\n .gengage-chat-product-grid.gengage-chat-product-grid--mobile\n .gengage-chat-product-card {\n width: 100%;\n min-width: 0;\n max-width: none;\n box-sizing: border-box;\n}\n\n.gengage-chat-panel .gengage-chat-product-card-img-wrapper {\n position: relative;\n width: 100%;\n aspect-ratio: 1;\n flex-shrink: 0;\n background: var(--surface-card-muted);\n border-radius: var(--radius-card) var(--radius-card) 0 0;\n overflow: hidden;\n}\n\n.gengage-chat-panel .gengage-chat-product-card-img {\n display: block;\n width: 100%;\n height: 100%;\n min-height: 0;\n border-radius: inherit;\n object-fit: cover;\n object-position: center;\n background: transparent;\n padding: 0;\n box-sizing: border-box;\n}\n\n.gengage-chat-panel .gengage-chat-product-card-body {\n padding: 12px 14px 10px;\n text-align: left;\n gap: 8px;\n}\n\n.gengage-chat-panel .gengage-chat-product-card-meta-row {\n align-items: center;\n margin-bottom: 0;\n min-height: 38px;\n}\n\n.gengage-chat-panel .gengage-chat-product-card-price,\n.gengage-chat-panel .gengage-chat-product-card-price-block {\n justify-content: flex-start;\n font-size: 15px;\n font-weight: 600;\n color: var(--text-primary);\n}\n\n.gengage-chat-panel .gengage-chat-product-card-current-price {\n color: var(--gengage-discounted-price-color, var(--text-primary));\n font-weight: 600;\n font-size: 15px;\n}\n\n.gengage-chat-panel .gengage-chat-product-card-original-price {\n font-size: 12px;\n font-weight: 400;\n color: var(--text-muted);\n}\n\n.gengage-chat-panel .gengage-chat-product-card-rating-compact {\n font-size: 11px;\n font-weight: 700;\n}\n\n.gengage-chat-panel .gengage-chat-product-card-rating-compact-star {\n color: color-mix(in srgb, var(--warning) 58%, var(--text-primary));\n}\n\n.gengage-chat-panel .gengage-chat-product-card-name {\n min-height: 0;\n margin: 0 0 2px;\n font-size: 14px;\n font-weight: 600;\n line-height: 1.35;\n color: var(--text-primary);\n -webkit-line-clamp: 2;\n line-clamp: 2;\n}\n\n/* Primary listing CTA lives in the card footer; action-only cards still use outline CTA */\n.gengage-chat-panel .gengage-chat-product-card-buy-footer {\n position: relative;\n margin-top: auto;\n padding: 0 14px 16px;\n flex-shrink: 0;\n display: flex;\n min-width: 0;\n}\n\n.gengage-chat-panel .gengage-chat-product-card-buy-trigger {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n min-width: 0;\n margin: 0;\n min-height: 42px;\n padding: 9px 14px;\n box-sizing: border-box;\n border: 1.5px solid var(--client-primary);\n border-radius: 12px;\n background: var(--surface-card);\n color: var(--client-primary);\n font-weight: 600;\n font-size: 13px;\n font-family: inherit;\n line-height: 1.2;\n white-space: nowrap;\n cursor: pointer;\n transition:\n background 0.15s ease,\n box-shadow 0.15s ease;\n}\n\n.gengage-chat-panel .gengage-chat-product-card-buy-trigger:hover {\n background: color-mix(in srgb, var(--client-primary) 6%, white);\n box-shadow: var(--shadow-1);\n}\n\n/* Panel: action-only cards still use full-width outline CTA */\n.gengage-chat-panel .gengage-chat-product-card .gengage-chat-product-card-cta {\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 14px 16px;\n padding: 9px 14px;\n width: calc(100% - 28px);\n min-width: 0;\n border: 1.5px solid var(--client-primary);\n border-radius: 12px;\n background: var(--surface-card);\n color: var(--client-primary);\n font-weight: 600;\n font-size: 13px;\n min-height: 42px;\n box-sizing: border-box;\n}\n\n.gengage-chat-panel .gengage-chat-product-card .gengage-chat-product-card-cta:hover {\n background: color-mix(in srgb, var(--client-primary) 6%, white);\n}\n\n/* Selected card highlight state */\n.gengage-chat-product-card--selected {\n border-color: var(--client-primary);\n background: color-mix(in srgb, var(--client-primary) 8%, transparent);\n box-shadow: 0 0 0 1px var(--client-primary);\n}\n\n/* Panel content layout — prevent empty space below grid */\n.gengage-chat-panel--visible {\n min-height: 0;\n}\n\n.gengage-chat-panel--visible > * {\n flex-shrink: 0;\n}\n\n/* \"Load more\" button in product grids */\n.gengage-chat-load-more-btn {\n display: block;\n margin: 12px auto;\n padding: 8px 24px;\n border: 1px solid var(--border-default);\n border-radius: 8px;\n background: transparent;\n color: var(--client-primary);\n cursor: pointer;\n font-size: 14px;\n}\n\n.gengage-chat-load-more-btn:hover {\n background: var(--surface-card-muted);\n}\n\n.gengage-chat-product-details-panel {\n display: flex;\n flex-direction: column;\n gap: 12px;\n width: 100%;\n background: var(--surface-card);\n border: 1px solid var(--border-default);\n border-radius: 16px;\n padding: 14px;\n box-sizing: border-box;\n}\n\n.gengage-chat-product-details-media {\n border: 1px solid var(--border-subtle);\n border-radius: 12px;\n background: var(--surface-card-muted);\n padding: 12px;\n display: flex;\n flex-direction: column;\n align-items: center;\n}\n\n.gengage-chat-product-details-img {\n width: 100%;\n height: auto;\n max-height: 300px;\n object-fit: contain;\n border-radius: 8px;\n}\n\n.gengage-chat-product-details-content {\n display: flex;\n flex-direction: column;\n gap: 8px;\n min-width: 0;\n}\n\n.gengage-chat-product-details-title {\n margin: 0;\n font-size: 17px;\n font-weight: 700;\n line-height: 1.3;\n color: var(--text-primary);\n}\n\n.gengage-chat-product-details-rating {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n width: fit-content;\n padding: 3px 8px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--warning) 10%, white);\n color: color-mix(in srgb, var(--warning) 72%, var(--text-primary));\n font-size: 13px;\n font-weight: 700;\n}\n\n.gengage-chat-product-details-review-count {\n color: color-mix(in srgb, var(--warning) 84%, var(--text-primary));\n font-weight: 600;\n}\n\n.gengage-chat-product-details-price {\n display: flex;\n align-items: baseline;\n gap: 6px;\n}\n\n.gengage-chat-product-details-original-price {\n font-size: 14px;\n color: var(--text-secondary);\n text-decoration: line-through;\n}\n\n.gengage-chat-product-details-price-stack {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 2px;\n width: 100%;\n}\n\n.gengage-chat-product-details-price-stack .gengage-chat-campaign-reason {\n font-size: 13px;\n font-weight: 600;\n margin-bottom: 2px;\n}\n\n.gengage-chat-product-details-price--inline {\n flex-wrap: nowrap;\n align-items: baseline;\n}\n\n.gengage-chat-product-details-price--inline .gengage-chat-product-details-price-sep {\n display: inline-block;\n width: 1px;\n height: 1em;\n margin: 0 4px;\n background: var(--border-subtle);\n align-self: center;\n flex-shrink: 0;\n}\n\n.gengage-chat-product-details-price--inline .gengage-chat-product-details-original-price {\n text-decoration: none;\n font-size: 22px;\n font-weight: 500;\n color: var(--text-secondary);\n order: 0;\n}\n\n.gengage-chat-product-details-price--inline .gengage-chat-product-details-current-price {\n font-size: 22px;\n font-weight: 800;\n order: 0;\n color: var(--gengage-discounted-price-color, var(--text-primary));\n}\n\n.gengage-chat-product-details-current-price {\n font-size: 22px;\n font-weight: 800;\n line-height: 1.1;\n color: var(--gengage-discounted-price-color, var(--text-primary));\n}\n\n.gengage-chat-product-details-stock {\n width: fit-content;\n padding: 4px 10px;\n border-radius: 999px;\n font-size: 12px;\n font-weight: 700;\n}\n\n.gengage-chat-product-details-stock.is-in-stock {\n background: color-mix(in srgb, var(--success) 12%, white);\n color: color-mix(in srgb, var(--success) 82%, var(--text-primary));\n}\n\n.gengage-chat-product-details-stock.is-out-of-stock {\n background: color-mix(in srgb, var(--error) 10%, white);\n color: color-mix(in srgb, var(--error) 82%, var(--text-primary));\n}\n\n.gengage-chat-product-details-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n margin-top: 4px;\n}\n\n.gengage-chat-product-details-cta {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-height: 40px;\n padding: 0 20px;\n border-radius: var(--radius-control, 12px);\n border: 1px solid var(--ds-button-primary-border);\n text-decoration: none;\n background: var(--ds-button-primary-bg);\n color: var(--ds-button-primary-fg);\n font-size: 14px;\n font-weight: 700;\n cursor: pointer;\n transition:\n filter 0.14s ease,\n transform 0.12s ease;\n}\n\n.gengage-chat-product-details-cta:hover {\n background: var(--ds-button-primary-bg-hover);\n}\n\n.gengage-chat-product-details-cta:active {\n background: var(--ds-button-primary-bg-active);\n}\n\n.gengage-chat-product-details-atc {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: 10px 20px;\n border: none;\n border-radius: var(--radius-control);\n background: var(--_gengage-success-color);\n color: var(--client-on-primary);\n font-size: 14px;\n font-weight: 700;\n font-family: inherit;\n cursor: pointer;\n transition:\n filter 0.12s ease,\n transform 0.12s ease;\n}\n\n.gengage-chat-product-details-atc:hover {\n filter: brightness(1.08);\n transform: translateY(-1px);\n}\n\n.gengage-chat-product-details-atc:active {\n transform: translateY(1px);\n}\n\n/* (Find Similar is now a hover pill on the image — see .gengage-chat-find-similar-pill) */\n\n/* Product detail tabs (Product Info / Tech Specs) */\n.gengage-chat-product-detail-tabs {\n margin-top: 4px;\n border-top: 1px solid var(--border-default);\n padding-top: 12px;\n}\n\n.gengage-chat-product-detail-tab-bar {\n display: flex;\n gap: 6px;\n border-bottom: 1px solid var(--border-default);\n margin-bottom: 12px;\n padding-bottom: 8px;\n}\n\n.gengage-chat-product-detail-tab {\n flex: 1;\n padding: 8px 12px;\n border: 1px solid var(--border-default);\n background: var(--surface-card);\n color: var(--text-secondary);\n font-size: 13px;\n font-weight: 600;\n font-family: inherit;\n cursor: pointer;\n transition:\n color 0.15s,\n border-color 0.15s,\n background 0.15s;\n}\n\n.gengage-chat-product-detail-tab:hover {\n color: var(--text-primary);\n background: color-mix(in srgb, var(--surface-card-soft) 82%, transparent);\n}\n\n.gengage-chat-product-detail-tab--active {\n color: var(--client-primary);\n border-color: color-mix(in srgb, var(--client-primary) 26%, var(--border-default));\n background: color-mix(in srgb, var(--client-primary) 8%, white);\n}\n\n.gengage-chat-product-detail-tab-panel {\n font-size: 13px;\n line-height: 1.6;\n color: var(--text-secondary);\n}\n\n.gengage-chat-product-description {\n display: flex;\n flex-direction: column;\n gap: 10px;\n color: var(--text-primary);\n}\n\n.gengage-chat-product-description p,\n.gengage-chat-product-description h2,\n.gengage-chat-product-description h3,\n.gengage-chat-product-description h4,\n.gengage-chat-product-description ul,\n.gengage-chat-product-description ol {\n margin: 0;\n}\n\n.gengage-chat-product-description h2,\n.gengage-chat-product-description h3,\n.gengage-chat-product-description h4 {\n color: var(--text-primary);\n font-size: 15px;\n font-weight: 800;\n line-height: 1.35;\n}\n\n.gengage-chat-product-description ul,\n.gengage-chat-product-description ol {\n padding-left: 20px;\n}\n\n.gengage-chat-product-description li + li {\n margin-top: 6px;\n}\n\n.gengage-chat-product-description strong,\n.gengage-chat-product-description b {\n font-weight: 800;\n}\n\n.gengage-chat-product-specs-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n}\n\n.gengage-chat-product-specs-table tr:nth-child(even) {\n background: var(--surface-card-muted);\n}\n\n.gengage-chat-product-specs-key {\n padding: 6px 10px;\n font-weight: 600;\n color: var(--text-secondary);\n white-space: nowrap;\n border-bottom: 1px solid var(--border-subtle);\n width: 40%;\n}\n\n.gengage-chat-product-specs-value {\n padding: 6px 10px;\n color: var(--text-primary);\n border-bottom: 1px solid var(--border-subtle);\n}\n\n/* Product card ATC stepper */\n.gengage-chat-product-card-atc {\n position: absolute;\n bottom: 42px;\n right: 6px;\n opacity: 0;\n transition: opacity 0.15s ease;\n z-index: 1;\n}\n\n.gengage-chat-product-card:hover .gengage-chat-product-card-atc {\n opacity: 1;\n}\n\n/* Touch devices: always show ATC stepper since :hover doesn't fire */\n@media (hover: none), (pointer: coarse) {\n /* Edge-to-edge strip at card bottom */\n .gengage-chat-product-card-atc {\n opacity: 1;\n position: relative;\n bottom: auto;\n right: auto;\n margin: 0;\n display: flex;\n width: 100%;\n }\n}\n\n/* Hidden state — slide out to right (desktop) or down (mobile) */\n.gengage-chat-drawer.gengage-chat-drawer--hidden {\n transform: translateX(100%);\n opacity: 0;\n pointer-events: none;\n box-shadow: none;\n visibility: hidden;\n}\n\n/* Mobile hidden: slide down */\n.gengage-chat-root--mobile .gengage-chat-drawer.gengage-chat-drawer--hidden {\n transform: translateY(100%);\n opacity: 0;\n pointer-events: none;\n box-shadow: none;\n visibility: hidden;\n}\n\n/* Mobile and medium breakpoints */\n@media (max-width: 900px) {\n .gengage-chat-drawer--with-panel .gengage-chat-panel--visible {\n width: clamp(280px, calc(100vw - var(--_gengage-chat-conversation-width)), 560px);\n max-width: 560px;\n }\n}\n\n/* Attachment preview strip */\n.gengage-chat-attachment-preview {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 10px;\n background: var(--surface-card-muted);\n border: 1px solid var(--border-default);\n border-radius: 8px;\n width: 100%;\n box-sizing: border-box;\n}\n\n.gengage-chat-attachment-preview--hidden {\n display: none;\n}\n\n.gengage-chat-attachment-preview-thumb {\n width: 40px;\n height: 40px;\n border-radius: 4px;\n object-fit: cover;\n flex-shrink: 0;\n}\n\n.gengage-chat-attachment-name {\n flex: 1;\n font-size: 12px;\n color: var(--text-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.gengage-chat-attachment-remove {\n background: none;\n border: none;\n cursor: pointer;\n font-size: 18px;\n color: var(--text-secondary);\n padding: 0 4px;\n line-height: 1;\n flex-shrink: 0;\n}\n\n.gengage-chat-attachment-remove:hover {\n color: var(--error);\n}\n\n/* Attach image button */\n.gengage-chat-attach-btn {\n background: var(--surface-card-soft);\n border: 1px solid var(--border-default);\n cursor: pointer;\n width: 40px;\n height: 40px;\n padding: 0;\n line-height: 1;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 12px;\n color: var(--text-primary);\n transition:\n color 0.15s,\n background 0.15s,\n border-color 0.15s;\n}\n\n.gengage-chat-attach-btn:hover {\n color: var(--client-primary);\n background: var(--client-primary-subtle);\n border-color: color-mix(in srgb, var(--client-primary) 18%, white);\n}\n\n.gengage-chat-attach-btn svg {\n width: 20px;\n height: 20px;\n}\n\n.gengage-chat-attach-wrap {\n position: relative;\n display: flex;\n align-items: center;\n flex-shrink: 0;\n}\n\n.gengage-chat-attach-menu {\n position: absolute;\n bottom: calc(100% + 8px);\n left: 0;\n z-index: 50;\n min-width: 196px;\n padding: 4px 0;\n margin: 0;\n list-style: none;\n background: var(--surface-elevated);\n border: 1px solid var(--border-default);\n border-radius: var(--radius-card);\n box-shadow: var(--shadow-3);\n}\n\n.gengage-chat-attach-menu[hidden] {\n display: none !important;\n}\n\n.gengage-chat-attach-menu-item {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n padding: 10px 14px;\n border: none;\n background: transparent;\n cursor: pointer;\n font-size: 14px;\n font-weight: 500;\n color: var(--text-primary);\n font-family: inherit;\n text-align: left;\n transition: background 0.12s ease;\n}\n\n.gengage-chat-attach-menu-item:hover {\n background: var(--surface-card-muted);\n}\n\n.gengage-chat-attach-menu-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--text-secondary);\n flex-shrink: 0;\n}\n\n.gengage-chat-attach-menu-label {\n flex: 1;\n min-width: 0;\n}\n\n.gengage-chat-attach-menu-sep {\n height: 1px;\n margin: 4px 10px;\n background: var(--border-default);\n}\n\n/* Voice input mic button */\n.gengage-chat-mic-btn {\n background: transparent;\n border: 1px solid transparent;\n cursor: pointer;\n width: 32px;\n height: 32px;\n padding: 0;\n line-height: 1;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--radius-control);\n color: var(--text-secondary);\n transition:\n color 0.15s,\n background 0.15s;\n}\n\n.gengage-chat-mic-btn:hover {\n color: var(--text-primary);\n background: var(--surface-card-muted);\n border-color: var(--border-default);\n}\n\n.gengage-chat-mic-btn svg {\n width: 20px;\n height: 20px;\n}\n\n.gengage-chat-mic-btn--active {\n color: var(--error);\n animation: gengage-mic-pulse 1.2s ease-in-out infinite;\n}\n\n.gengage-chat-mic-btn--active:hover {\n color: color-mix(in srgb, var(--error) 84%, black);\n}\n\n@keyframes gengage-mic-pulse {\n 0%,\n 100% {\n transform: scale(1);\n opacity: 1;\n }\n 50% {\n transform: scale(1.15);\n opacity: 0.8;\n }\n}\n\n/* Drag-over highlight on input area */\n.gengage-chat-input-area--dragover {\n outline: 2px dashed var(--client-primary);\n outline-offset: -2px;\n background: color-mix(in srgb, var(--client-primary) 8%, white);\n}\n\n/* Thumbnail in user bubble */\n.gengage-chat-attachment-thumb {\n width: 120px;\n max-width: 100%;\n height: auto;\n border-radius: 8px;\n margin-bottom: 6px;\n display: block;\n}\n\n/* Mobile layout — driven by JS .gengage-chat-root--mobile class so the breakpoint\n is configurable via config.mobileBreakpoint (default 768px). */\n\n.gengage-chat-root--mobile .gengage-chat-header-btn,\n.gengage-chat-root--mobile .gengage-chat-close {\n width: 44px;\n height: 44px;\n border-radius: 12px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-attach-btn,\n.gengage-chat-root--mobile .gengage-chat-mic-btn,\n.gengage-chat-root--mobile .gengage-chat-send {\n width: 44px;\n height: 44px;\n min-width: 44px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-attach-btn svg,\n.gengage-chat-root--mobile .gengage-chat-mic-btn svg {\n width: 22px;\n height: 22px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-panel-divider {\n display: flex;\n width: 100%;\n height: 20px;\n align-items: center;\n justify-content: center;\n cursor: ns-resize;\n background: transparent;\n touch-action: pan-y;\n position: relative;\n overflow: visible;\n}\n\n.gengage-chat-root--mobile .gengage-chat-panel-divider--hidden {\n display: none;\n}\n\n.gengage-chat-root--mobile .gengage-chat-drawer--with-panel {\n left: 0;\n right: 0;\n width: 100%;\n border-radius: 0;\n}\n\n/* Panel becomes a full-screen side overlay (slides in from the right) */\n.gengage-chat-root--mobile .gengage-chat-panel {\n display: flex;\n flex-direction: column;\n position: absolute;\n inset: 0;\n z-index: 10;\n width: 100%;\n min-width: 0;\n max-width: none;\n max-height: none;\n border: none;\n box-shadow: none;\n border-radius: 0;\n background: var(--gengage-chat-panel-bg, var(--surface-card));\n overflow-y: auto;\n /* overflow-x intentionally not set: inner elements (comparison table) must be able to scroll horizontally */\n transform: translateX(100%);\n transition: transform 0.32s cubic-bezier(0.4, 0, 0.2, 1);\n pointer-events: none;\n padding: 0 12px 16px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-panel--visible {\n transform: translateX(0);\n pointer-events: auto;\n}\n\n/* Divider is not needed for overlay panel on mobile */\n.gengage-chat-root--mobile .gengage-chat-panel-divider,\n.gengage-chat-root--mobile .gengage-chat-drawer--with-panel .gengage-chat-panel-divider {\n display: none;\n}\n\n/* Drawer layout stays unaffected by panel on mobile */\n.gengage-chat-root--mobile .gengage-chat-drawer--with-panel .gengage-chat-body {\n flex-direction: row;\n}\n\n/* Conversation always takes full width on mobile */\n.gengage-chat-root--mobile .gengage-chat-drawer--with-panel .gengage-chat-conversation {\n width: 100%;\n flex: 1;\n border-left: none;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-details-share {\n width: 44px;\n height: 44px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-panel .gengage-chat-product-card-buy-footer {\n width: 100%;\n max-width: 100%;\n box-sizing: border-box;\n}\n\n.gengage-chat-root--mobile .gengage-chat-panel .gengage-chat-product-card-cta {\n width: 100%;\n max-width: 100%;\n box-sizing: border-box;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-details-panel {\n padding: 10px;\n gap: 8px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-details-media {\n min-height: 0;\n padding: 8px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-details-content {\n gap: 6px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-details-title {\n font-size: 14px;\n line-height: 1.25;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-details-rating {\n padding: 2px 6px;\n font-size: 12px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-details-original-price {\n font-size: 12px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-details-current-price {\n font-size: 18px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-details-price--inline .gengage-chat-product-details-original-price,\n.gengage-chat-root--mobile .gengage-chat-product-details-price--inline .gengage-chat-product-details-current-price {\n font-size: 18px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-details-actions {\n margin-top: 2px;\n gap: 6px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-details-cta {\n min-height: 38px;\n padding: 0 14px;\n font-size: 13px;\n}\n\n/* Mobile product grid: 2 columns (--mobile) or horizontal strip (legacy, when --mobile absent) */\n.gengage-chat-root--mobile .gengage-chat-product-grid:not(.gengage-chat-product-grid--mobile) {\n scroll-snap-type: x mandatory;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-grid.gengage-chat-product-grid--mobile {\n scroll-snap-type: none;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-card {\n width: 280px;\n min-width: 280px;\n max-width: 100%;\n box-sizing: border-box;\n scroll-snap-align: center;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-grid--mobile .gengage-chat-product-card {\n width: 100%;\n min-width: 0;\n max-width: none;\n scroll-snap-align: start;\n}\n\n/* Horizontal product strip + any mobile card: vertically center image in its frame */\n.gengage-chat-root--mobile .gengage-chat-product-card-img-wrapper {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 0;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-card-img {\n width: auto;\n max-width: 100%;\n height: auto;\n max-height: 180px;\n min-height: 0;\n object-fit: contain;\n object-position: center;\n display: block;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-card-discount-badge {\n padding: 2px 6px;\n font-size: 10px;\n font-weight: 700;\n line-height: 1.2;\n}\n\n.gengage-chat-root--mobile .gengage-chat-drawer {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n width: 100%;\n max-height: 100%;\n border-radius: 0;\n}\n\n.gengage-chat-root--mobile .gengage-chat-bubble {\n max-width: 100%;\n}\n\n/* Mobile panel cards: center product image vertically in the media band (avoid “stuck to top”) */\n.gengage-chat-root--mobile .gengage-chat-panel .gengage-chat-product-card-img-wrapper {\n display: flex;\n align-items: center;\n justify-content: center;\n aspect-ratio: 1;\n min-height: 0;\n}\n\n.gengage-chat-root--mobile .gengage-chat-panel .gengage-chat-product-card-img {\n width: auto;\n max-width: 100%;\n height: auto;\n max-height: min(200px, 72%);\n min-height: 0;\n object-fit: contain;\n object-position: center;\n}\n\n/* Product card overlay: keep fav + find-similar square (global mobile rule sets fav to 44px for header/details) */\n.gengage-chat-root--mobile .gengage-chat-product-card-img-actions .gengage-chat-favorite-btn,\n.gengage-chat-root--mobile .gengage-chat-product-card-img-actions .gengage-chat-find-similar-pill {\n width: 28px;\n height: 28px;\n min-width: 28px;\n min-height: 28px;\n flex-shrink: 0;\n}\n\n.gengage-chat-root--mobile .gengage-chat-panel .gengage-chat-product-card-discount-badge {\n min-width: 28px;\n height: 28px;\n padding: 0 6px;\n font-size: 10px;\n font-weight: 700;\n}\n\n.gengage-chat-root--mobile .gengage-chat-panel .gengage-chat-product-card-buy-footer {\n padding: 0 12px 14px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-panel .gengage-chat-product-card-buy-trigger {\n max-width: 100%;\n min-width: 0;\n box-sizing: border-box;\n white-space: normal;\n text-align: center;\n line-height: 1.25;\n hyphens: auto;\n}\n\n.gengage-chat-root--mobile .gengage-chat-panel .gengage-chat-product-card .gengage-chat-product-card-cta {\n margin-left: 12px;\n margin-right: 12px;\n width: calc(100% - 24px);\n max-width: calc(100% - 24px);\n min-width: 0;\n box-sizing: border-box;\n white-space: normal;\n}\n\n.gengage-chat-root--mobile .gengage-chat-panel .gengage-chat-product-card-rating-compact {\n padding: 2px 6px;\n font-size: 10px;\n gap: 3px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-panel .gengage-chat-product-card-stock {\n font-size: 10px;\n padding: 2px 5px;\n}\n\n/* GAP-012: Hide native scrollbar on mobile */\n.gengage-chat-root--mobile .gengage-chat-messages {\n scrollbar-width: none;\n /* GAP-013: Improve mobile chat padding */\n padding: 12px 16px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-messages::-webkit-scrollbar {\n display: none;\n}\n\n/* GAP-011: Fix drawer header white sliver on mobile */\n.gengage-chat-root--mobile .gengage-chat-drawer {\n border-top: none;\n}\n\n.gengage-chat-root--mobile .gengage-chat-header {\n border-radius: 16px 16px 0 0;\n /* Extra top padding to give the drag handle pill room at the very top */\n padding-top: 22px;\n padding-bottom: 8px;\n min-height: 72px;\n}\n\n.gengage-chat-root--mobile.gengage-chat-root--mobile-full .gengage-chat-header {\n border-radius: 0;\n}\n\n/* GAP-036: Fix chat content cut off at panel boundary */\n.gengage-chat-root--mobile .gengage-chat-conversation {\n overflow-y: auto;\n -webkit-overflow-scrolling: touch;\n min-height: 0;\n}\n\n/* GAP-047: Mobile product image — compact frame, still readable */\n.gengage-chat-root--mobile .gengage-chat-product-details-img {\n min-height: 0;\n max-height: 200px;\n object-fit: contain;\n}\n\n/* GAP-063: Fix panel action buttons crowding */\n.gengage-chat-root--mobile .gengage-chat-product-details-actions {\n flex-wrap: wrap;\n gap: 8px;\n}\n\n/* GAP-065: Fix badge overlap on mobile */\n.gengage-chat-root--mobile .gengage-chat-product-details-promos {\n flex-wrap: wrap;\n gap: 4px;\n}\n\n/* GAP-086: Fix placeholder text too long on mobile */\n.gengage-chat-root--mobile .gengage-chat-input::placeholder {\n font-size: 13px;\n}\n\n/* KVKK disclaimer banner */\n.gengage-chat-kvkk-banner {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n padding: 10px 14px;\n background: color-mix(in srgb, var(--warning) 10%, white);\n border-bottom: 1px solid color-mix(in srgb, var(--warning) 24%, var(--border-default));\n font-size: 12px;\n line-height: 1.5;\n color: color-mix(in srgb, var(--warning) 72%, var(--text-primary));\n}\n\n.gengage-chat-kvkk-content {\n flex: 1;\n min-width: 0;\n}\n\n.gengage-chat-kvkk-content a {\n color: color-mix(in srgb, var(--warning) 82%, var(--text-primary));\n text-decoration: underline;\n}\n\n.gengage-chat-kvkk-dismiss {\n flex-shrink: 0;\n width: 20px;\n height: 20px;\n border: none;\n background: transparent;\n color: color-mix(in srgb, var(--warning) 84%, var(--text-primary));\n font-size: 18px;\n cursor: pointer;\n padding: 0;\n line-height: 1;\n}\n\n/* Comparison table */\n.gengage-chat-comparison {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 16px;\n box-sizing: border-box;\n}\n\n.gengage-chat-comparison-heading {\n font-size: 14px;\n font-weight: 700;\n letter-spacing: 0.05em;\n color: var(--text-primary);\n margin: 0 0 16px;\n}\n\n.gengage-chat-comparison-recommended {\n border: 1px solid color-mix(in srgb, var(--client-primary) 18%, var(--border-default));\n border-radius: var(--radius-card);\n padding: 16px;\n background: var(--surface-card);\n box-shadow: var(--shadow-1);\n}\n\n.gengage-chat-comparison-recommended-label {\n font-size: 12px;\n font-weight: 700;\n color: var(--client-primary);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n margin-bottom: 12px;\n}\n\n.gengage-chat-comparison-recommended-body {\n display: grid;\n grid-template-columns: minmax(108px, 148px) minmax(0, 1fr);\n gap: 16px;\n align-items: flex-start;\n}\n\n.gengage-chat-comparison-recommended-media {\n display: flex;\n align-items: center;\n justify-content: center;\n aspect-ratio: 1 / 1;\n border-radius: 14px;\n border: 1px solid var(--border-subtle);\n background: var(--surface-card);\n overflow: hidden;\n}\n\n.gengage-chat-comparison-recommended-body img,\n.gengage-chat-comparison-recommended-placeholder {\n width: 100%;\n height: 100%;\n object-fit: contain;\n padding: 8px;\n box-sizing: border-box;\n}\n\n.gengage-chat-comparison-recommended-placeholder {\n background: var(--surface-card-muted);\n}\n\n.gengage-chat-comparison-recommended-info {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.gengage-chat-comparison-recommended-title {\n font-size: 24px;\n line-height: 1.2;\n font-weight: 800;\n color: var(--text-primary);\n margin: 0;\n}\n\n.gengage-chat-comparison-recommended-meta {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-wrap: wrap;\n}\n\n.gengage-chat-comparison-recommended-rating {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n border-radius: 999px;\n border: 1px solid var(--border-subtle);\n background: var(--surface-card-muted);\n color: color-mix(in srgb, var(--warning) 78%, var(--text-primary));\n font-size: 13px;\n font-weight: 700;\n}\n\n.gengage-chat-comparison-recommended-rating-value {\n color: var(--text-primary);\n}\n\n.gengage-chat-comparison-recommended-price {\n font-size: 20px;\n font-weight: 700;\n color: var(--text-primary);\n}\n\n.gengage-chat-comparison-highlights {\n padding-top: 12px;\n border-top: 1px solid var(--border-subtle);\n}\n\n.gengage-chat-comparison-highlights-label {\n font-size: 12px;\n font-weight: 600;\n color: var(--text-secondary);\n margin-bottom: 6px;\n}\n\n.gengage-chat-comparison-highlights ul {\n margin: 0;\n padding-left: 18px;\n font-size: 14px;\n color: var(--text-primary);\n line-height: 1.55;\n}\n\n.gengage-chat-comparison-special {\n background: color-mix(in srgb, var(--warning) 10%, white);\n border: 1px solid color-mix(in srgb, var(--warning) 24%, var(--border-default));\n border-radius: var(--radius-card);\n padding: 12px 16px;\n margin-bottom: 12px;\n font-size: 13px;\n color: color-mix(in srgb, var(--warning) 72%, var(--text-primary));\n}\n\n.gengage-chat-comparison-special summary {\n cursor: pointer;\n font-weight: 600;\n}\n\n.gengage-chat-comparison-special ul {\n margin: 8px 0 0;\n padding-left: 18px;\n line-height: 1.6;\n}\n\n.gengage-chat-comparison-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n background: var(--surface-card);\n}\n\n.gengage-chat-comparison-table th,\n.gengage-chat-comparison-table td {\n padding: 10px 12px;\n text-align: center;\n border-bottom: 1px solid var(--border-subtle);\n vertical-align: top;\n}\n\n.gengage-chat-comparison-table th {\n font-weight: 600;\n font-size: 12px;\n color: var(--text-secondary);\n}\n\n/*\n * display:grid must not be used on th — table-cell breaks and columns stack vertically.\n * Grid is only on inner wrappers; prices stay horizontally aligned across the row.\n */\n.gengage-chat-comparison-table thead th:not(:first-child) {\n vertical-align: top;\n}\n\n.gengage-chat-comparison-table-header-cell {\n display: grid;\n grid-template-rows: auto 1fr auto;\n align-items: start;\n justify-items: center;\n gap: 6px;\n min-height: 0;\n height: 100%;\n box-sizing: border-box;\n}\n\n.gengage-chat-comparison-table-header-cell--clickable {\n cursor: pointer;\n border-radius: var(--radius-control);\n padding: 6px;\n transition:\n background 0.16s ease,\n box-shadow 0.16s ease,\n transform 0.16s ease;\n}\n\n.gengage-chat-comparison-table-header-cell--clickable:hover {\n background: var(--surface-card-soft);\n box-shadow: var(--shadow-1);\n transform: translateY(-1px);\n}\n\n.gengage-chat-comparison-table-header-cell--clickable:focus-visible {\n outline: 2px solid var(--client-focus-ring);\n outline-offset: 2px;\n}\n\n.gengage-chat-comparison-table-header-cell img {\n width: 60px;\n height: 60px;\n object-fit: contain;\n border-radius: calc(var(--radius-control) - 4px);\n background: var(--surface-card-muted);\n}\n\n.gengage-chat-comparison-table-header-img-placeholder {\n width: 60px;\n height: 60px;\n border-radius: calc(var(--radius-control) - 4px);\n background: var(--surface-card-muted);\n}\n\n.gengage-chat-comparison-table-product-name {\n width: 100%;\n min-width: 0;\n font-weight: 600;\n font-size: 12px;\n color: var(--text-secondary);\n line-height: 1.35;\n text-align: center;\n align-self: start;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.gengage-chat-comparison-table-price {\n font-weight: 700;\n color: var(--text-primary);\n width: 100%;\n text-align: center;\n}\n\n.gengage-chat-comparison-label {\n text-align: left;\n font-weight: 600;\n color: var(--text-secondary);\n white-space: nowrap;\n}\n\n.gengage-chat-comparison-selected {\n background: color-mix(in srgb, var(--client-primary) 6%, white);\n}\n\n/* Comparison Table Enhancements */\n.gengage-chat-comparison-recommended-text {\n margin: 0;\n font-size: 14px;\n line-height: 1.6;\n color: var(--text-secondary);\n}\n\n.gengage-chat-comparison-key-differences {\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-card);\n background: var(--surface-card-muted);\n}\n\n.gengage-chat-comparison-key-differences-summary {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 14px 16px;\n cursor: pointer;\n list-style: none;\n font-weight: 700;\n}\n\n.gengage-chat-comparison-key-differences-summary::-webkit-details-marker {\n display: none;\n}\n\n.gengage-chat-comparison-key-differences-summary-label {\n color: var(--text-primary);\n}\n\n.gengage-chat-comparison-key-differences-summary-meta {\n color: var(--client-primary);\n font-size: 13px;\n font-weight: 700;\n}\n\n.gengage-chat-comparison-key-differences[open] .gengage-chat-comparison-key-differences-summary-meta {\n opacity: 0;\n}\n\n.gengage-chat-comparison-key-differences-content {\n padding: 0 16px 16px;\n font-size: 14px;\n line-height: 1.7;\n color: var(--text-primary);\n}\n\n.gengage-chat-comparison-key-differences-content ul,\n.gengage-chat-comparison-key-differences-content ol {\n margin: 0;\n padding-left: 20px;\n}\n\n.gengage-chat-comparison-key-differences-content li + li {\n margin-top: 8px;\n}\n\n@media (max-width: 960px) {\n .gengage-chat-comparison-recommended-body {\n grid-template-columns: 1fr;\n }\n\n .gengage-chat-comparison-recommended-title {\n font-size: 20px;\n }\n}\n\n/* Suggestion pills row */\n.gengage-chat-pills {\n position: relative;\n padding: 8px 12px 6px;\n border-top: 1px solid var(--border-subtle);\n background: var(--surface-card);\n}\n\n.gengage-chat-pills-scroll {\n display: flex;\n gap: 8px;\n overflow-x: auto;\n scrollbar-width: none;\n -webkit-overflow-scrolling: touch;\n padding-right: 28px;\n}\n\n.gengage-chat-pills-scroll::-webkit-scrollbar {\n display: none;\n}\n\n.gengage-chat-root--mobile .gengage-chat-pills-scroll {\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-pill {\n white-space: normal;\n max-width: 100%;\n}\n\n.gengage-chat-pill {\n flex: 0 0 auto;\n padding: 10px 14px;\n border: 1px solid var(--ds-chip-border-active);\n border-radius: 999px;\n background: var(--ds-chip-bg-active);\n color: var(--ds-chip-fg-active);\n font-size: 13px;\n font-weight: 700;\n font-family: inherit;\n white-space: nowrap;\n cursor: pointer;\n transition:\n background 0.15s ease,\n border-color 0.15s ease,\n color 0.15s ease;\n}\n\n.gengage-chat-pill:hover {\n background: color-mix(in srgb, var(--client-primary) 12%, white);\n border-color: color-mix(in srgb, var(--client-primary) 36%, white);\n}\n\n.gengage-chat-pill:active {\n transform: none;\n}\n\n/* Rich pills (with image/description, e.g. AI groupings) */\n.gengage-chat-pill--rich {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 14px 8px 8px;\n border-radius: 18px;\n}\n\n.gengage-chat-pill-img {\n width: 32px;\n height: 32px;\n border-radius: 12px;\n object-fit: cover;\n flex-shrink: 0;\n background: var(--surface-card);\n}\n\n.gengage-chat-pill-text {\n font-size: 13px;\n font-weight: 600;\n line-height: 1.2;\n}\n\n.gengage-chat-pill-desc {\n display: none;\n}\n\n.gengage-chat-pill--rich .gengage-chat-pill-desc {\n display: block;\n font-size: 11px;\n font-weight: 500;\n color: var(--text-muted);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 160px;\n}\n\n.gengage-chat-pills-arrow {\n position: absolute;\n right: 8px;\n top: 50%;\n transform: translateY(-50%);\n width: 36px;\n height: 36px;\n border-radius: 12px;\n border: 1px solid var(--border-default);\n background: var(--surface-card);\n color: var(--text-secondary);\n font-size: 16px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: var(--shadow-1);\n z-index: 1;\n}\n\n.gengage-chat-pills-arrow:hover {\n background: var(--surface-card-soft);\n color: var(--client-primary);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .gengage-chat-uispec > *,\n .gengage-chat-action-btn,\n .gengage-chat-product-card,\n .gengage-chat-product-card-cta,\n .gengage-chat-send,\n .gengage-chat-launcher,\n .gengage-chat-pill,\n .gengage-chat-drawer,\n .gengage-chat-typing-dots span,\n .gengage-chat-thinking-step-marker--active,\n .gengage-chat-bubble,\n .gengage-chat-panel-skeleton-block,\n .gengage-chat-ai-toppick-card,\n .gengage-chat-mic-btn--active,\n .gengage-chat-ai-toppick-spinner::after,\n .gengage-skeleton-card,\n .gengage-skeleton-line,\n .gengage-skeleton-img,\n .gengage-skeleton-text,\n .gengage-skeleton-price,\n .gengage-chat-cart-toast,\n .gengage-chat-header-btn--cart-flash {\n animation: none !important;\n transition: none !important;\n transform: none !important;\n }\n}\n\n/* ---------------------------------------------------------------------------\n * Rollback button (on user message bubbles)\n * ---------------------------------------------------------------------------*/\n\n.gengage-chat-bubble--user {\n position: relative;\n}\n\n.gengage-chat-rollback-btn {\n position: absolute;\n left: -28px;\n top: 50%;\n transform: translateY(-50%);\n width: 24px;\n height: 24px;\n border: none;\n border-radius: var(--radius-pill);\n background: var(--surface-card-muted);\n color: var(--text-secondary);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n opacity: 0;\n transition: opacity 0.15s ease;\n padding: 0;\n}\n\n.gengage-chat-bubble--user:hover .gengage-chat-rollback-btn {\n opacity: 1;\n}\n\n.gengage-chat-rollback-btn:hover {\n background: var(--client-primary);\n color: var(--client-on-primary);\n}\n\n/* Hidden state for rolled-back messages */\n.gengage-chat-bubble--hidden {\n display: none;\n}\n\n/* ---------------------------------------------------------------------------\n * AI Top Picks\n * ---------------------------------------------------------------------------*/\n\n.gengage-chat-ai-top-picks {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 8px 0;\n /* GAP-078: Keep AI cards visible below sticky header when scroll-linked */\n scroll-margin-top: 60px;\n}\n\n.gengage-chat-ai-top-picks-title {\n font-size: 15px;\n font-weight: 600;\n color: var(--text-primary);\n margin: 0 0 4px;\n}\n\n.gengage-chat-ai-top-picks-scroll {\n display: flex;\n flex-direction: column;\n gap: 10px;\n min-width: 0;\n}\n\n/* 2+ picks: wraps non-hero cards. Desktop/panel: transparent to layout (grid still sees cards). */\n.gengage-chat-ai-top-picks-rest {\n display: contents;\n}\n\n/* Top row: image + (name / rating / price / chips). Transparent on desktop — children merge into card flow. */\n.gengage-chat-ai-toppick-top-row {\n display: contents;\n}\n\n.gengage-chat-ai-toppick-detail {\n width: 100%;\n min-width: 0;\n}\n\n/* Panel AI zone: title row + horizontal scroll rail (parity with grouping cards / legacy Top Picks) */\n.gengage-chat-panel-ai-zone .gengage-chat-ai-top-picks {\n gap: 0;\n padding-top: 4px;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-ai-top-picks-title {\n flex-shrink: 0;\n width: 100%;\n margin: 0 0 10px;\n font-size: 18px;\n line-height: 1.2;\n font-weight: 700;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-ai-top-picks-scroll {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n grid-auto-rows: minmax(0, auto);\n align-items: stretch;\n gap: 12px; /* gap-3 */\n overflow: visible;\n /* Top padding for the border badge */\n padding: 12px 0 12px;\n margin: 0;\n scrollbar-width: none;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-ai-toppick-card--winner,\n.gengage-chat-panel-ai-zone .gengage-chat-ai-toppick-card--compact {\n width: 100%;\n min-width: 0;\n max-width: none;\n flex: initial;\n scroll-snap-align: none;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-ai-top-picks-scroll > :first-child {\n grid-column: 1 / -1;\n display: grid;\n grid-template-columns: minmax(260px, 340px) minmax(0, 1fr);\n grid-template-rows: auto auto;\n align-items: start;\n column-gap: 18px;\n row-gap: 12px;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-ai-top-picks-scroll > :first-child:has(.gengage-chat-ai-toppick-detail) {\n grid-template-rows: auto auto auto;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-ai-toppick-card--compact {\n flex-direction: column;\n align-items: stretch;\n}\n\n/* Panel: visual max height (narrow column) — maintain lean aspect-square */\n.gengage-chat-panel-ai-zone .gengage-chat-ai-toppick-card--compact .gengage-chat-ai-toppick-media,\n.gengage-chat-panel-ai-zone .gengage-chat-ai-toppick-card--winner .gengage-chat-ai-toppick-media {\n max-height: 140px;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-ai-top-picks-scroll > :first-child .gengage-chat-ai-toppick-media {\n grid-column: 1;\n grid-row: 1 / span 2;\n max-height: 260px;\n aspect-ratio: 4 / 3;\n}\n\n.gengage-chat-panel-ai-zone\n .gengage-chat-ai-top-picks-scroll\n > :first-child:has(.gengage-chat-ai-toppick-detail)\n .gengage-chat-ai-toppick-media {\n grid-row: 1 / span 3;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-ai-top-picks-scroll > :first-child .gengage-chat-ai-toppick-detail {\n grid-column: 2;\n grid-row: 2;\n min-width: 0;\n padding-top: 0;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-ai-toppick-card--compact .gengage-chat-ai-toppick-img,\n.gengage-chat-panel-ai-zone .gengage-chat-ai-toppick-card--winner .gengage-chat-ai-toppick-img {\n width: 100%;\n height: 100%;\n max-height: 140px;\n object-fit: contain;\n flex-shrink: 0;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-ai-top-picks-scroll > :first-child .gengage-chat-ai-toppick-img {\n max-height: 260px;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-ai-toppick-card--winner .gengage-chat-ai-toppick-body,\n.gengage-chat-panel-ai-zone .gengage-chat-ai-toppick-card--compact .gengage-chat-ai-toppick-body {\n padding: 0;\n flex: 1;\n min-height: 0;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-ai-top-picks-scroll > :first-child .gengage-chat-ai-toppick-body {\n grid-column: 2;\n grid-row: 1;\n padding-top: 6px;\n}\n\n.gengage-chat-panel-ai-zone\n .gengage-chat-ai-top-picks-scroll\n > :first-child:has(.gengage-chat-ai-toppick-detail)\n .gengage-chat-ai-toppick-cta {\n grid-row: 3;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-ai-toppick-card--compact .gengage-chat-ai-toppick-cta {\n width: calc(100% - 24px);\n margin: 14px 12px 12px;\n align-self: stretch;\n border: 1px solid var(--ds-button-secondary-border);\n background: var(--ds-button-secondary-bg);\n color: var(--ds-button-secondary-fg);\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-ai-toppick-card--winner .gengage-chat-ai-toppick-cta {\n grid-column: 2;\n grid-row: 2;\n width: auto;\n min-width: 180px;\n margin: 0;\n justify-self: start;\n align-self: end;\n border: 1px solid var(--ds-button-secondary-border);\n background: var(--ds-button-secondary-bg);\n color: var(--ds-button-secondary-fg);\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-ai-toppick-card--winner .gengage-chat-ai-toppick-cta:hover,\n.gengage-chat-panel-ai-zone .gengage-chat-ai-toppick-card--compact .gengage-chat-ai-toppick-cta:hover {\n background: var(--ds-button-secondary-bg-hover);\n filter: none;\n}\n\n.gengage-chat-panel-ai-zone .gengage-chat-ai-toppick-card--winner {\n border-color: color-mix(in srgb, var(--client-primary) 26%, var(--border-default));\n box-shadow: var(--shadow-2);\n}\n\n.gengage-chat-ai-toppick-rating {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n align-self: flex-start;\n padding: 3px 8px;\n margin-top: 4px;\n border: 1px solid var(--border-subtle);\n border-radius: 999px;\n background: var(--surface-card-muted);\n font-size: 11px;\n font-weight: 700;\n color: var(--text-secondary);\n}\n\n.gengage-chat-ai-toppick-rating-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: color-mix(in srgb, var(--warning) 60%, var(--text-primary));\n line-height: 0;\n}\n\n.gengage-chat-ai-toppick-rating-value {\n color: var(--text-primary);\n}\n\n/* Discount: lean top-1 left-1, text-[9px] font-extrabold */\n.gengage-chat-ai-toppick-media .gengage-chat-ai-toppick-discount-badge {\n position: absolute;\n top: 4px;\n left: 4px;\n right: auto;\n background: var(--client-primary);\n color: var(--client-on-primary);\n font-size: 9px;\n font-weight: 800;\n padding: 2px 6px;\n border-radius: 999px;\n z-index: 1;\n line-height: 1.2;\n}\n\n/*\n * AI Top Picks cards — same layout as robot-engine-lean TopPicksResults:\n * relative p-3 rounded-xl border | badge absolute -top-2.5 left-3 |\n * media: aspect-square rounded-lg border-gray-100 mt-1 mb-2\n */\n.gengage-chat-ai-toppick-card--winner,\n.gengage-chat-ai-toppick-card--compact {\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: stretch;\n box-sizing: border-box;\n padding: 12px;\n border-radius: var(--radius-card);\n background: var(--surface-card);\n overflow: visible;\n transition:\n box-shadow 0.2s ease,\n background 0.15s ease,\n transform 0.15s ease;\n}\n\n.gengage-chat-ai-toppick-card--winner {\n border: 1.5px solid color-mix(in srgb, var(--client-primary) 24%, var(--border-default));\n}\n\n.gengage-chat-ai-toppick-card--winner:hover {\n background: var(--surface-card-muted);\n box-shadow: var(--shadow-2);\n}\n\n.gengage-chat-ai-toppick-card--compact {\n border: 1px solid var(--border-default);\n}\n\n.gengage-chat-ai-toppick-card--compact:hover {\n background: var(--surface-card-muted);\n box-shadow: var(--shadow-1);\n transform: translateY(-1px);\n}\n\n/* Role badge: absolute -top-2.5 left-3 (no translateY — prevent overlap) */\n.gengage-chat-ai-toppick-badge {\n position: absolute;\n top: -10px;\n left: 12px;\n z-index: 2;\n max-width: calc(100% - 24px);\n box-sizing: border-box;\n font-size: 10px;\n font-weight: 700;\n line-height: 1.25;\n padding: 2px 10px;\n border-radius: 999px;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n pointer-events: none;\n white-space: normal;\n}\n\n.gengage-chat-ai-toppick-card--winner .gengage-chat-ai-toppick-badge {\n background: var(--client-primary);\n color: var(--client-on-primary);\n}\n\n.gengage-chat-ai-toppick-card--compact .gengage-chat-ai-toppick-badge {\n background: var(--surface-card-muted);\n color: var(--text-secondary);\n}\n\n.gengage-chat-ai-toppick-media {\n position: relative;\n width: 100%;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-top: 4px;\n margin-bottom: 8px;\n aspect-ratio: 1 / 1;\n border-radius: 8px;\n border: 1px solid var(--border-subtle);\n overflow: hidden;\n background: var(--surface-card);\n}\n\n.gengage-chat-ai-toppick-img {\n max-width: 100%;\n max-height: 100%;\n width: auto;\n height: auto;\n object-fit: contain;\n padding: 4px;\n box-sizing: border-box;\n}\n\n/* Body — padding inside card (p-3); no extra wrapper in lean mode */\n.gengage-chat-ai-toppick-body {\n display: flex;\n flex-direction: column;\n gap: 0;\n padding: 0;\n flex: 1;\n min-width: 0;\n}\n\n.gengage-chat-ai-toppick-name {\n font-size: 14px;\n font-weight: 700;\n line-height: 1.375;\n color: var(--text-primary);\n margin-bottom: 4px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.gengage-chat-ai-toppick-role {\n font-size: 11px;\n font-weight: 600;\n color: var(--client-primary);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n/* Sentiment chips */\n.gengage-chat-ai-toppick-labels {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin-top: 10px;\n}\n\n.gengage-chat-ai-toppick-label {\n font-size: 10px;\n padding: 4px 8px;\n border-radius: 999px;\n font-weight: 700;\n}\n\n.gengage-chat-ai-toppick-label[data-sentiment='positive'] {\n background: color-mix(in srgb, var(--success) 12%, white);\n color: color-mix(in srgb, var(--success) 82%, var(--text-primary));\n}\n\n.gengage-chat-ai-toppick-label[data-sentiment='negative'] {\n background: color-mix(in srgb, var(--error) 10%, white);\n color: color-mix(in srgb, var(--error) 82%, var(--text-primary));\n}\n\n.gengage-chat-ai-toppick-label[data-sentiment='neutral'] {\n background: var(--surface-card-muted);\n color: var(--text-secondary);\n}\n\n.gengage-chat-ai-toppick-reason {\n margin: 10px 0 0;\n font-size: 13px;\n line-height: 1.5;\n color: var(--text-secondary);\n}\n\n.gengage-chat-ai-toppick-review {\n margin-top: 10px;\n padding: 10px 12px;\n border: 1px solid var(--border-subtle);\n border-radius: 14px;\n background: var(--surface-card-muted);\n font-size: 12px;\n line-height: 1.45;\n color: var(--text-primary);\n}\n\n.gengage-chat-ai-toppick-snippet {\n margin: 8px 0 0;\n font-size: 12px;\n line-height: 1.45;\n color: var(--text-secondary);\n}\n\n/* Price */\n.gengage-chat-ai-toppick-price {\n font-size: 14px;\n font-weight: 700;\n color: var(--text-primary);\n margin-top: 4px;\n}\n\n.gengage-chat-ai-toppick-price-current {\n color: var(--gengage-discounted-price-color, var(--text-primary));\n}\n\n.gengage-chat-ai-toppick-original-price {\n text-decoration: line-through;\n color: var(--text-secondary);\n font-weight: 400;\n font-size: 12px;\n}\n\n.gengage-chat-ai-toppick-price-stack {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 2px;\n margin-top: 4px;\n}\n\n.gengage-chat-ai-toppick-price-stack .gengage-chat-campaign-reason {\n font-size: 11px;\n font-weight: 700;\n}\n\n/* Campaign badge: logo | reason + sale — campaign-gradient border only; fill is white */\n.gengage-chat-campaign-price-badge {\n display: inline-flex;\n flex-direction: row;\n align-items: center;\n gap: 10px;\n box-sizing: border-box;\n max-width: 100%;\n padding: 10px 12px;\n border-radius: var(--radius-control, 10px);\n border: 1px solid transparent;\n background-image:\n linear-gradient(var(--gengage-campaign-price-badge-fill, #ffffff) 0 0),\n linear-gradient(135deg, var(--_gengage-campaign-reason-start), var(--_gengage-campaign-reason-end));\n background-origin: border-box;\n background-clip: padding-box, border-box;\n}\n\n.gengage-chat-campaign-price-badge__logo {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.gengage-chat-campaign-price-badge__logo img {\n display: block;\n max-height: 32px;\n max-width: 72px;\n width: auto;\n height: auto;\n object-fit: contain;\n}\n\n.gengage-chat-campaign-price-badge__body {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n justify-content: center;\n gap: 2px;\n min-width: 0;\n}\n\n.gengage-chat-campaign-price-badge__reason {\n max-width: 100%;\n font-size: 12px;\n font-weight: 700;\n line-height: 1.25;\n margin: 0;\n letter-spacing: 0.02em;\n background: linear-gradient(90deg, var(--_gengage-campaign-reason-start), var(--_gengage-campaign-reason-end));\n -webkit-background-clip: text;\n background-clip: text;\n color: transparent;\n}\n\n.gengage-chat-campaign-price-badge__sale {\n font-size: 17px;\n font-weight: 800;\n line-height: 1.15;\n color: var(--gengage-discounted-price-color, var(--text-primary));\n}\n\n.gengage-chat-product-card-price-row .gengage-chat-campaign-price-badge__sale {\n font-size: 16px;\n}\n\n.gengage-chat-product-summary__price .gengage-chat-campaign-price-badge__sale {\n font-size: 15px;\n}\n\n.gengage-chat-product-details-price .gengage-chat-campaign-price-badge {\n padding: 12px 14px;\n border-radius: var(--radius-control, 12px);\n}\n\n.gengage-chat-product-details-price .gengage-chat-campaign-price-badge__reason {\n font-size: 13px;\n}\n\n.gengage-chat-product-details-price .gengage-chat-campaign-price-badge__sale {\n font-size: 22px;\n}\n\n/* Inline row with campaign badge: align last baselines so list price lines up with sale price inside badge */\n.gengage-chat-ai-toppick-price--inline:has(.gengage-chat-campaign-price-badge),\n.gengage-chat-product-card-price-row:has(.gengage-chat-campaign-price-badge),\n.gengage-chat-product-summary__price--inline:has(.gengage-chat-campaign-price-badge),\n.gengage-chat-product-details-price--inline:has(.gengage-chat-campaign-price-badge) {\n align-items: last baseline;\n}\n\n.gengage-chat-ai-toppick-price--inline:has(.gengage-chat-campaign-price-badge) .gengage-chat-ai-toppick-price-sep,\n.gengage-chat-product-card-price-row:has(.gengage-chat-campaign-price-badge) .gengage-chat-product-card-price-sep,\n.gengage-chat-product-summary__price--inline:has(.gengage-chat-campaign-price-badge)\n .gengage-chat-product-summary__price-sep,\n.gengage-chat-product-details-price--inline:has(.gengage-chat-campaign-price-badge)\n .gengage-chat-product-details-price-sep {\n align-self: last baseline;\n}\n\n.gengage-chat-ai-toppick-price--inline {\n display: flex;\n flex-direction: row;\n align-items: baseline;\n flex-wrap: wrap;\n gap: 0 6px;\n}\n\n.gengage-chat-ai-toppick-price-sep {\n display: inline-block;\n width: 1px;\n height: 0.85em;\n margin: 0 1px;\n background: var(--border-subtle);\n align-self: center;\n flex-shrink: 0;\n}\n\n.gengage-chat-ai-toppick-price--inline .gengage-chat-ai-toppick-original-price {\n text-decoration: none;\n font-size: 14px;\n font-weight: 500;\n}\n\n.gengage-chat-ai-toppick-price--inline .gengage-chat-ai-toppick-price-current {\n font-size: 14px;\n font-weight: 800;\n color: var(--gengage-discounted-price-color, var(--text-primary));\n}\n\n/* CTA — clear margin from the content above (View Details) */\n.gengage-chat-ai-toppick-cta {\n display: block;\n width: calc(100% - 24px);\n margin: 14px 12px 12px;\n padding: 8px 0;\n border: 1px solid var(--ds-button-primary-border);\n border-radius: var(--radius-control, 12px);\n background: var(--ds-button-primary-bg);\n color: var(--ds-button-primary-fg);\n font-size: 13px;\n font-weight: 700;\n cursor: pointer;\n text-align: center;\n transition:\n opacity 0.18s ease,\n transform 0.18s ease,\n background 0.15s ease;\n flex-shrink: 0;\n align-self: stretch;\n box-sizing: border-box;\n opacity: 0;\n transform: translateY(6px);\n pointer-events: none;\n}\n\n.gengage-chat-ai-toppick-card--winner:hover .gengage-chat-ai-toppick-cta,\n.gengage-chat-ai-toppick-card--compact:hover .gengage-chat-ai-toppick-cta,\n.gengage-chat-ai-toppick-card--winner:focus-within .gengage-chat-ai-toppick-cta,\n.gengage-chat-ai-toppick-card--compact:focus-within .gengage-chat-ai-toppick-cta {\n opacity: 1;\n transform: translateY(0);\n pointer-events: auto;\n}\n\n.gengage-chat-ai-toppick-cta:hover {\n background: var(--ds-button-primary-bg-hover);\n}\n\n/* Per-card loading spinner overlay */\n.gengage-chat-ai-toppick-spinner {\n position: absolute;\n inset: 0;\n background: color-mix(in srgb, var(--surface-card) 78%, transparent);\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 12px;\n z-index: 6;\n}\n\n.gengage-chat-ai-toppick-spinner::after {\n content: '';\n width: 24px;\n height: 24px;\n border: 3px solid var(--border-default);\n border-top-color: var(--ai-accent-start);\n border-radius: 50%;\n animation: gengage-spin 0.7s linear infinite;\n}\n\n@keyframes gengage-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n/* Mobile: hero pick full width, next two half-width (1 + 2 column row) */\n.gengage-chat-root--mobile .gengage-chat-ai-top-picks {\n gap: 8px;\n padding: 4px 0;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-top-picks-title {\n font-size: 14px;\n margin: 0 0 2px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-top-picks-scroll {\n display: flex;\n flex-direction: column;\n gap: 8px;\n overflow: visible;\n padding: 6px 0 2px;\n min-width: 0;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-top-picks-scroll > :first-child {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n width: 100%;\n min-width: 0;\n}\n\n/* Mobile: top row (image + text) — winner gets detail + CTA below */\n.gengage-chat-root--mobile .gengage-chat-ai-top-picks-scroll > :first-child.gengage-chat-ai-toppick-card--winner {\n flex-direction: column;\n}\n\n/* Non-hero picks: horizontally scrollable strip (mobile only) */\n.gengage-chat-root--mobile .gengage-chat-ai-top-picks-rest {\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n gap: 8px;\n overflow-x: auto;\n overflow-y: visible;\n padding: 2px 2px 8px;\n margin: 0 -2px;\n -webkit-overflow-scrolling: touch;\n scroll-snap-type: x proximity;\n scrollbar-width: none;\n -ms-overflow-style: none;\n min-width: 0;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-top-picks-rest::-webkit-scrollbar {\n display: none;\n width: 0;\n height: 0;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-top-picks-rest .gengage-chat-ai-toppick-card--compact {\n flex: 0 0 min(82vw, 272px);\n width: min(82vw, 272px);\n max-width: 272px;\n min-width: 0;\n scroll-snap-align: start;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--winner {\n width: 100%;\n min-width: 0;\n max-width: none;\n flex-shrink: 1;\n scroll-snap-align: none;\n padding: 10px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--compact {\n width: 100%;\n min-width: 0;\n max-width: none;\n flex-shrink: 1;\n scroll-snap-align: none;\n padding: 6px 8px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--winner {\n margin-bottom: 8px;\n border: 1.5px solid color-mix(in srgb, var(--client-primary) 28%, var(--border-default));\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--compact {\n flex-direction: column;\n align-items: stretch;\n border: 1px solid var(--border-default);\n box-shadow: none;\n}\n\n/* Winner top row: square image on the left, title / rating / price on the right */\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--winner .gengage-chat-ai-toppick-top-row {\n display: flex;\n flex-direction: row;\n align-items: flex-start;\n gap: 10px;\n width: 100%;\n min-width: 0;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--winner .gengage-chat-ai-toppick-media {\n flex: 0 0 clamp(96px, 30vw, 124px);\n width: clamp(96px, 30vw, 124px);\n height: clamp(96px, 30vw, 124px);\n max-height: none;\n margin: 0;\n aspect-ratio: 1 / 1;\n border-radius: 10px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--winner .gengage-chat-ai-toppick-img {\n max-height: 100%;\n width: 100%;\n height: 100%;\n object-fit: contain;\n padding: 4px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--winner .gengage-chat-ai-toppick-body {\n flex: 1;\n min-width: 0;\n padding-top: 2px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--winner .gengage-chat-ai-toppick-detail {\n width: 100%;\n margin-top: 10px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--winner .gengage-chat-ai-toppick-reason {\n display: block !important;\n margin: 0;\n font-size: 12px;\n line-height: 1.45;\n color: var(--text-primary);\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--winner .gengage-chat-ai-toppick-review {\n display: block !important;\n margin-top: 8px;\n padding: 8px 10px;\n font-size: 12px;\n line-height: 1.4;\n font-style: italic;\n color: var(--text-secondary);\n border: 1px solid var(--border-subtle);\n border-radius: 12px;\n background: var(--surface-card-muted);\n}\n\n/* Compact cards (horizontal strip): small square on left, text on right; snippet below */\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--compact .gengage-chat-ai-toppick-top-row {\n display: flex;\n flex-direction: row;\n align-items: flex-start;\n gap: 6px;\n width: 100%;\n min-width: 0;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--compact .gengage-chat-ai-toppick-media {\n flex: 0 0 64px;\n width: 64px;\n height: 64px;\n max-height: 64px;\n margin: 0;\n aspect-ratio: 1 / 1;\n border-radius: 8px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--compact .gengage-chat-ai-toppick-img {\n max-height: 100%;\n width: 100%;\n object-fit: contain;\n padding: 2px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--compact .gengage-chat-ai-toppick-body {\n flex: 1;\n min-width: 0;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--compact .gengage-chat-ai-toppick-rating {\n display: none;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--compact .gengage-chat-ai-toppick-detail {\n margin-top: 4px;\n width: 100%;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--compact .gengage-chat-ai-toppick-snippet {\n margin: 0;\n font-size: 10px;\n line-height: 1.3;\n color: var(--text-secondary);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--compact .gengage-chat-ai-toppick-role-line {\n font-size: 9px;\n font-weight: 600;\n line-height: 1.15;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--text-muted);\n margin: 0 0 2px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-snippet {\n margin: 0;\n font-size: 11px;\n line-height: 1.35;\n color: var(--text-secondary);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* Compact card fav + similar actions — top-right corner of image */\n.gengage-chat-root--mobile\n .gengage-chat-ai-toppick-card--compact\n .gengage-chat-ai-toppick-media\n .gengage-chat-product-card-img-actions {\n top: 3px;\n right: 5px;\n gap: 5px;\n}\n\n.gengage-chat-root--mobile\n .gengage-chat-ai-toppick-card--winner\n .gengage-chat-ai-toppick-media\n .gengage-chat-product-card-img-actions {\n top: 5px;\n right: 6px;\n gap: 5px;\n}\n\n/* Winner badge: solid client color + white text; smaller padding — font size unchanged */\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--winner .gengage-chat-ai-toppick-badge {\n top: -14px;\n left: 8px;\n max-width: calc(100% - 18px);\n font-size: 8px;\n font-weight: 700;\n line-height: 1.15;\n padding: 2px 6px;\n letter-spacing: 0.04em;\n background: var(--client-primary);\n color: var(--client-on-primary);\n border: none;\n box-shadow: none;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--compact .gengage-chat-ai-toppick-name {\n font-size: 12px;\n line-height: 1.22;\n margin-bottom: 1px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-name {\n font-size: 13px;\n line-height: 1.28;\n margin-bottom: 2px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-rating {\n margin-top: 2px;\n padding: 2px 6px;\n gap: 3px;\n font-size: 10px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--compact .gengage-chat-ai-toppick-price {\n font-size: 13px;\n margin-top: 1px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-price {\n font-size: 13px;\n margin-top: 2px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-original-price {\n font-size: 11px;\n}\n\n/* Mobile: hide sentiment chips; reason/review shown inside winner detail instead */\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-labels {\n display: none !important;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--compact .gengage-chat-ai-toppick-cta {\n display: none !important;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--winner .gengage-chat-ai-toppick-cta {\n width: calc(100% - 16px);\n margin: 8px 8px 6px;\n padding: 6px 0;\n font-size: 12px;\n font-weight: 600;\n align-self: stretch;\n opacity: 1;\n transform: none;\n pointer-events: auto;\n border: 1px solid var(--client-primary);\n background: var(--surface-card);\n color: var(--client-primary);\n box-shadow: none;\n filter: none;\n}\n\n.gengage-chat-root--mobile .gengage-chat-ai-toppick-card--winner .gengage-chat-ai-toppick-cta:hover {\n background: color-mix(in srgb, var(--client-primary) 10%, var(--surface-card));\n color: var(--client-primary);\n border-color: var(--client-primary);\n filter: none;\n}\n\n.gengage-chat-root--mobile .gengage-chat-rollback-btn {\n left: -24px;\n}\n\n/* ---------------------------------------------------------------------------\n * GroundingReviewCard\n * ---------------------------------------------------------------------------*/\n.gengage-chat-grounding-review {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n transition:\n background 0.15s ease,\n border-color 0.15s ease,\n box-shadow 0.15s ease;\n}\n\n.gengage-chat-grounding-review:hover {\n background: var(--ds-card-bg-hover);\n border-color: var(--ds-card-border-hover);\n box-shadow: var(--ds-card-shadow-hover);\n}\n\n.gengage-chat-grounding-review-icon {\n width: 32px;\n height: 32px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 999px;\n background: color-mix(in srgb, var(--client-primary) 8%, var(--surface-card));\n color: var(--client-primary);\n flex-shrink: 0;\n}\n\n.gengage-chat-grounding-review-icon svg {\n width: 18px;\n height: 18px;\n display: block;\n}\n\n.gengage-chat-grounding-review-body {\n flex: 1;\n min-width: 0;\n}\n\n.gengage-chat-grounding-review-title {\n font-weight: 600;\n font-size: 13px;\n color: var(--text-primary);\n}\n\n.gengage-chat-grounding-review-subtitle {\n font-size: 12px;\n color: var(--text-muted);\n margin-top: 2px;\n}\n\n.gengage-chat-grounding-review-cta {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 13px;\n font-weight: 600;\n color: var(--client-primary);\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.gengage-chat-grounding-review-cta-icon {\n display: inline-flex;\n align-items: center;\n}\n\n.gengage-chat-grounding-review-cta-icon svg {\n width: 14px;\n height: 14px;\n display: block;\n}\n\n/* ---------------------------------------------------------------------------\n * AIGroupingCards\n * ---------------------------------------------------------------------------*/\n.gengage-chat-grouping-cards {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n/* Card list: chat = vertical stack; panel AI zone overrides row + overflow-x */\n.gengage-chat-grouping-cards-scroll {\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 0;\n}\n\n.gengage-chat-grouping-card {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 12px;\n background: var(--surface-card-soft);\n border: 1px solid var(--border-default);\n border-radius: var(--radius-control);\n transition:\n background 0.15s ease,\n border-color 0.15s ease;\n}\n\n.gengage-chat-grouping-card:hover {\n background: color-mix(in srgb, var(--client-primary) 6%, white);\n border-color: color-mix(in srgb, var(--client-primary) 24%, var(--border-default));\n}\n\n.gengage-chat-grouping-card-arrow {\n display: none;\n font-size: 14px;\n color: var(--client-primary);\n flex-shrink: 0;\n}\n\n.gengage-chat-grouping-card-img {\n width: 48px;\n height: 48px;\n min-width: 48px;\n min-height: 48px;\n border-radius: calc(var(--radius-control) - 4px);\n object-fit: cover;\n flex-shrink: 0;\n}\n\n.gengage-chat-grouping-card-body {\n flex: 1;\n min-width: 0;\n display: flex;\n /* Always stack name + description vertically for consistent card height */\n flex-direction: column;\n align-items: flex-start;\n gap: 2px;\n}\n\n.gengage-chat-grouping-card-name {\n font-weight: 600;\n font-size: 13px;\n color: var(--text-primary);\n}\n\n.gengage-chat-grouping-card-desc {\n font-size: 12px;\n color: var(--text-muted);\n}\n\n.gengage-chat-grouping-card-labels {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin-top: 4px;\n}\n\n.gengage-chat-grouping-card-label {\n font-size: 10px;\n font-weight: 700;\n line-height: 1.2;\n padding: 4px 8px;\n color: var(--text-secondary);\n background: var(--surface-card-muted);\n border: 1px solid var(--border-subtle);\n border-radius: 999px;\n}\n\n/* Mobile: hide image, show arrow prefix */\n.gengage-chat-root--mobile .gengage-chat-grouping-card-img {\n display: none;\n}\n.gengage-chat-root--mobile .gengage-chat-grouping-card-arrow {\n display: inline;\n}\n\n/* ---------------------------------------------------------------------------\n * AISuggestedSearchCards\n * ---------------------------------------------------------------------------*/\n.gengage-chat-suggested-search-cards {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.gengage-chat-suggested-search-card {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 14px 16px;\n background: var(--surface-card);\n border: 1px solid var(--border-default);\n border-radius: var(--radius-card);\n box-shadow: var(--shadow-1);\n transition:\n background 0.15s ease,\n border-color 0.15s ease,\n box-shadow 0.15s ease,\n transform 0.15s ease;\n}\n\n.gengage-chat-suggested-search-card:hover {\n background: var(--surface-card);\n border-color: color-mix(in srgb, var(--client-primary) 24%, var(--border-default));\n box-shadow: var(--shadow-2);\n transform: translateY(-1px);\n}\n\n.gengage-chat-suggested-search-card-img {\n width: 64px;\n height: 64px;\n border-radius: 14px;\n object-fit: contain;\n padding: 4px;\n background: var(--surface-card-muted);\n border: 1px solid var(--border-subtle);\n flex-shrink: 0;\n}\n\n.gengage-chat-suggested-search-card-body {\n flex: 1;\n min-width: 0;\n}\n\n.gengage-chat-suggested-search-card-name {\n font-weight: 700;\n font-size: 15px;\n line-height: 1.35;\n color: var(--text-primary);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* Product Gallery — hero image + thumbnail strip below */\n.gengage-chat-product-details-gallery {\n position: relative;\n}\n\n.gengage-chat-product-details-gallery .gengage-chat-product-details-img {\n touch-action: pan-y;\n}\n\n.gengage-chat-product-gallery-nav {\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n z-index: 3;\n width: 36px;\n height: 36px;\n min-width: 36px;\n min-height: 36px;\n border: 1px solid var(--border-default);\n border-radius: var(--radius-pill);\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n background: color-mix(in srgb, var(--surface-card) 92%, transparent);\n color: var(--text-primary);\n padding: 0;\n transition:\n background 0.15s ease,\n opacity 0.15s ease;\n box-shadow: var(--shadow-1);\n}\n\n.gengage-chat-product-gallery-nav:hover:not(:disabled) {\n background: var(--surface-card);\n color: var(--client-primary);\n}\n\n.gengage-chat-product-gallery-nav:focus-visible {\n outline: 2px solid var(--client-focus-ring);\n outline-offset: 2px;\n}\n\n.gengage-chat-product-gallery-nav:disabled {\n opacity: 0.35;\n cursor: not-allowed;\n}\n\n.gengage-chat-product-gallery-nav--prev {\n left: 8px;\n}\n\n.gengage-chat-product-gallery-nav--next {\n right: 8px;\n}\n\n.gengage-chat-product-gallery-thumbs {\n display: flex;\n gap: 8px;\n padding: 10px 0 4px;\n overflow-x: auto;\n scrollbar-width: thin;\n justify-content: center;\n width: 100%;\n}\n\n.gengage-chat-product-gallery-thumb {\n width: 56px;\n height: 56px;\n object-fit: cover;\n border-radius: 8px;\n border: 2px solid transparent;\n cursor: pointer;\n flex-shrink: 0;\n transition: border-color 0.15s ease;\n background: var(--surface-card);\n}\n\n.gengage-chat-product-gallery-thumb:hover {\n border-color: var(--border-strong);\n}\n\n.gengage-chat-product-gallery-thumb--active {\n border-color: var(--client-primary);\n}\n\n.gengage-chat-product-gallery-thumb-more {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 56px;\n height: 56px;\n border-radius: 8px;\n background: var(--surface-card-muted);\n color: var(--text-secondary);\n font-size: 13px;\n font-weight: 600;\n flex-shrink: 0;\n}\n\n/* Variant Selector */\n.gengage-chat-product-variants {\n margin-top: 12px;\n}\n\n.gengage-chat-product-variants-label {\n font-size: 12px;\n font-weight: 600;\n color: var(--text-muted);\n margin-bottom: 6px;\n text-transform: none;\n letter-spacing: 0.5px;\n}\n\n.gengage-chat-product-variants-list {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.gengage-chat-product-variant-btn {\n padding: 6px 12px;\n border: 1px solid var(--border-default);\n border-radius: var(--radius-pill);\n background: var(--surface-card);\n color: var(--text-secondary);\n font-size: 12px;\n cursor: pointer;\n transition:\n background 0.15s ease,\n border-color 0.15s ease;\n}\n\n.gengage-chat-product-variant-btn:hover {\n background: var(--surface-card-muted);\n border-color: color-mix(in srgb, var(--client-primary) 20%, var(--border-default));\n color: var(--client-primary);\n}\n\n/* Product details PDP redesign */\n.gengage-chat-product-details-panel {\n gap: 16px;\n padding: 16px;\n border-radius: var(--radius-card);\n background: var(--surface-card);\n box-shadow: var(--shadow-1);\n}\n\n.gengage-chat-product-details-media {\n min-height: 320px;\n justify-content: center;\n border-color: var(--border-default);\n border-radius: var(--radius-card);\n background: var(--surface-card-soft);\n}\n\n.gengage-chat-product-details-img {\n max-height: 420px;\n}\n\n.gengage-chat-product-details-content {\n gap: 12px;\n}\n\n.gengage-chat-product-details-brand {\n color: var(--client-primary);\n font-size: 12px;\n font-weight: 800;\n line-height: 1.2;\n}\n\n.gengage-chat-product-details-title {\n font-size: 22px;\n line-height: 1.2;\n}\n\n.gengage-chat-product-details-rating {\n gap: 6px;\n padding: 6px 10px;\n border: 1px solid color-mix(in srgb, var(--warning) 24%, var(--border-default));\n background: color-mix(in srgb, var(--warning) 10%, var(--surface-card));\n color: color-mix(in srgb, var(--warning) 72%, var(--text-primary));\n font-family: inherit;\n}\n\nbutton.gengage-chat-product-details-rating {\n border: 1px solid color-mix(in srgb, var(--warning) 24%, var(--border-default));\n cursor: pointer;\n}\n\n.gengage-chat-product-details-rating--clickable:hover {\n border-color: color-mix(in srgb, var(--warning) 42%, var(--border-default));\n background: color-mix(in srgb, var(--warning) 14%, var(--surface-card));\n}\n\n.gengage-chat-product-details-rating .gengage-star-rating {\n line-height: 1;\n}\n\n.gengage-chat-product-details-rating-value {\n color: var(--text-primary);\n}\n\n.gengage-chat-product-details-price {\n gap: 10px;\n flex-wrap: wrap;\n}\n\n.gengage-chat-product-details-original-price {\n order: 2;\n}\n\n.gengage-chat-product-details-current-price {\n order: 1;\n font-size: 30px;\n}\n\n.gengage-chat-product-details-discount-badge {\n order: 3;\n display: inline-flex;\n align-items: center;\n min-height: 24px;\n padding: 2px 8px;\n border-radius: var(--radius-pill);\n background: color-mix(in srgb, var(--error) 12%, white);\n color: color-mix(in srgb, var(--error) 84%, var(--text-primary));\n font-size: 12px;\n font-weight: 800;\n}\n\n.gengage-chat-product-details-price--inline .gengage-chat-product-details-original-price,\n.gengage-chat-product-details-price--inline .gengage-chat-product-details-current-price {\n order: 0;\n font-size: 30px;\n}\n\n.gengage-chat-product-details-price--inline .gengage-chat-product-details-original-price {\n font-weight: 500;\n}\n\n.gengage-chat-product-details-facts {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 8px;\n margin: 0;\n}\n\n.gengage-chat-product-details-fact {\n min-width: 0;\n padding: 8px 10px;\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-control);\n background: var(--surface-card-soft);\n}\n\n.gengage-chat-product-details-fact dt,\n.gengage-chat-product-details-fact dd {\n margin: 0;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.gengage-chat-product-details-fact dt {\n color: var(--text-muted);\n font-size: 11px;\n font-weight: 700;\n}\n\n.gengage-chat-product-details-fact dd {\n margin-top: 2px;\n color: var(--text-primary);\n font-size: 13px;\n font-weight: 700;\n}\n\n.gengage-chat-product-variants {\n margin-top: 4px;\n}\n\n.gengage-chat-product-variants-label {\n margin-bottom: 8px;\n color: var(--text-primary);\n font-size: 14px;\n font-weight: 800;\n letter-spacing: normal;\n}\n\n.gengage-chat-product-variants-list {\n gap: 8px;\n}\n\n.gengage-chat-product-variant-btn {\n min-height: 44px;\n gap: 8px;\n padding: 7px 10px;\n border-radius: var(--radius-control);\n color: var(--text-primary);\n font-size: 13px;\n font-weight: 700;\n}\n\n.gengage-chat-product-variant-btn--active {\n border-color: var(--client-primary);\n background: color-mix(in srgb, var(--client-primary) 8%, var(--surface-card));\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--client-primary) 36%, transparent);\n color: var(--client-primary);\n}\n\n.gengage-chat-product-variant-btn--out {\n opacity: 0.48;\n cursor: not-allowed;\n}\n\n.gengage-chat-product-variant-swatch {\n width: 22px;\n height: 22px;\n flex: 0 0 22px;\n border: 1px solid var(--border-default);\n border-radius: 50%;\n background: var(--surface-card-muted);\n box-shadow: inset 0 0 0 2px var(--surface-card);\n}\n\n.gengage-chat-product-variant-swatch--image {\n object-fit: cover;\n box-shadow: none;\n}\n\n.gengage-chat-product-variant-label,\n.gengage-chat-product-variant-price {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.gengage-chat-product-variant-price {\n color: var(--text-secondary);\n font-size: 12px;\n font-weight: 700;\n}\n\n.gengage-chat-product-details-actions {\n gap: 10px;\n margin-top: 6px;\n}\n\n.gengage-chat-product-details-atc,\n.gengage-chat-product-details-cta {\n min-height: 44px;\n border-radius: var(--radius-control);\n}\n\n.gengage-chat-product-details-atc {\n border: 1px solid var(--ds-button-primary-border);\n background: var(--ds-button-primary-bg);\n color: var(--ds-button-primary-fg);\n}\n\n.gengage-chat-product-details-atc:hover {\n background: var(--ds-button-primary-bg-hover);\n filter: none;\n}\n\n.gengage-chat-product-details-cta.gds-btn-secondary {\n border-color: var(--ds-button-secondary-border);\n background: var(--ds-button-secondary-bg);\n color: var(--ds-button-secondary-fg);\n}\n\n.gengage-chat-product-details-cta.gds-btn-secondary:hover {\n background: var(--ds-button-secondary-bg-hover);\n}\n\n@media (min-width: 960px) {\n .gengage-chat-product-details-panel {\n display: grid;\n grid-template-columns: minmax(0, 1.15fr) minmax(300px, 0.85fr);\n align-items: start;\n }\n\n .gengage-chat-product-details-media {\n position: sticky;\n top: 0;\n }\n\n .gengage-chat-product-detail-tabs {\n grid-column: 1 / -1;\n }\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-details-panel {\n padding: 10px;\n gap: 10px;\n box-shadow: none;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-details-media {\n min-height: 220px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-details-title {\n font-size: 18px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-details-current-price {\n font-size: 24px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-details-facts {\n grid-template-columns: 1fr;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-details-actions {\n display: grid;\n grid-template-columns: 1fr 1fr 44px;\n align-items: stretch;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-details-actions > .gengage-chat-product-details-share {\n width: 44px;\n min-width: 44px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-details-atc,\n.gengage-chat-root--mobile .gengage-chat-product-details-cta {\n width: 100%;\n padding-inline: 10px;\n}\n\n/* Product grid: optional heading row + sort/compare (no outer box — deneysel) */\n.gengage-chat-product-grid-wrapper {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.gengage-chat-product-grid-head {\n display: flex;\n align-items: center;\n gap: 8px;\n justify-content: space-between;\n min-width: 0;\n margin-bottom: 2px;\n}\n\n.gengage-chat-product-grid-head-title {\n flex: 1 1 auto;\n min-width: 0;\n font-size: 18px;\n font-weight: 700;\n line-height: 1.2;\n color: var(--text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.gengage-chat-product-grid-head-actions {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-shrink: 0;\n max-width: min(100%, 52%);\n}\n\n.gengage-chat-product-sort-toolbar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px 8px;\n padding: 0;\n margin: 0;\n background: transparent;\n border: none;\n border-radius: 0;\n box-shadow: none;\n}\n\n.gengage-chat-product-sort-toolbar--inline {\n flex-wrap: nowrap;\n justify-content: flex-end;\n}\n\n/* Sort dropdown */\n.gengage-chat-product-sort-dropdown {\n position: relative;\n flex: 0 1 auto;\n min-width: 0;\n max-width: 200px;\n width: auto;\n z-index: 5;\n}\n\n.gengage-chat-product-sort-toolbar:not(.gengage-chat-product-sort-toolbar--inline) .gengage-chat-product-sort-dropdown {\n flex: 1 1 auto;\n max-width: min(200px, 100%);\n}\n\n.gengage-chat-product-sort-dropdown--open {\n z-index: 25;\n}\n\n.gengage-chat-product-sort-trigger {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n min-height: 32px;\n padding: 5px 8px;\n border: 1px solid var(--border-default);\n border-radius: var(--radius-control);\n background: var(--surface-card);\n color: var(--text-primary);\n font-size: 12px;\n font-weight: 600;\n line-height: 1.2;\n cursor: pointer;\n text-align: left;\n transition:\n border-color 0.15s ease,\n box-shadow 0.15s ease,\n background 0.15s ease;\n}\n\n.gengage-chat-product-sort-trigger:hover {\n border-color: color-mix(in srgb, var(--client-primary) 22%, var(--border-default));\n background: var(--surface-card-soft);\n}\n\n.gengage-chat-product-sort-trigger:focus-visible {\n outline: 2px solid var(--client-focus-ring);\n outline-offset: 2px;\n}\n\n.gengage-chat-product-sort-dropdown--open .gengage-chat-product-sort-trigger {\n border-color: color-mix(in srgb, var(--client-primary) 32%, var(--border-default));\n box-shadow: var(--shadow-2);\n}\n\n.gengage-chat-product-sort-trigger-icon {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--text-secondary);\n}\n\n.gengage-chat-product-sort-dropdown[data-sort-icon='related'] .gengage-chat-product-sort-trigger-icon {\n color: var(--client-primary);\n}\n\n.gengage-chat-product-sort-trigger-icon svg {\n display: block;\n}\n\n.gengage-chat-product-sort-trigger-label {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.gengage-chat-product-sort-trigger-chevron {\n flex-shrink: 0;\n display: flex;\n color: var(--text-muted);\n transition: transform 0.2s ease;\n}\n\n.gengage-chat-product-sort-dropdown--open .gengage-chat-product-sort-trigger-chevron {\n transform: rotate(180deg);\n}\n\n.gengage-chat-product-sort-menu {\n position: absolute;\n left: 0;\n right: auto;\n top: calc(100% + 4px);\n min-width: 100%;\n width: max-content;\n max-width: min(260px, calc(100vw - 48px));\n margin: 0;\n list-style: none;\n}\n\n.gengage-chat-product-sort-option {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n padding: 8px 10px;\n border: none;\n border-radius: calc(var(--radius-control) - 4px);\n background: transparent;\n color: var(--text-primary);\n font-size: 12px;\n font-weight: 500;\n line-height: 1.25;\n cursor: pointer;\n text-align: left;\n transition: background 0.12s ease;\n}\n\n.gengage-chat-product-sort-option:hover {\n background: var(--surface-card-muted);\n}\n\n.gengage-chat-product-sort-option--active {\n color: var(--client-primary);\n font-weight: 600;\n background: var(--client-primary-subtle);\n}\n\n.gengage-chat-product-sort-option-icon {\n flex-shrink: 0;\n display: flex;\n color: var(--text-secondary);\n}\n\n.gengage-chat-product-sort-option--active .gengage-chat-product-sort-option-icon {\n color: var(--client-primary);\n}\n\n.gengage-chat-product-sort-option--active[data-sort-key='related'] .gengage-chat-product-sort-option-icon {\n color: var(--client-primary);\n}\n\n.gengage-chat-product-sort-option-icon svg {\n display: block;\n}\n\n.gengage-chat-product-sort-option-label {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.gengage-chat-product-sort-option-check {\n flex-shrink: 0;\n display: flex;\n width: 22px;\n justify-content: flex-end;\n color: var(--client-primary);\n}\n\n.gengage-chat-product-sort-option-check--hidden {\n visibility: hidden;\n}\n\n/* Sort toolbar separator (legacy — no longer rendered; kept for older DOM snapshots) */\n.gengage-chat-product-sort-separator {\n width: 1px;\n background: var(--border-default);\n margin: 2px 2px;\n}\n\n/* Comparison toggle — icon + label, matches sort trigger weight */\n.gengage-chat-comparison-toggle-btn {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n flex: none;\n min-height: 32px;\n padding: 5px 8px;\n border: 1px solid var(--border-default);\n border-radius: var(--radius-control, 12px);\n background: var(--surface-card);\n color: var(--text-primary);\n font-size: 12px;\n font-weight: 700;\n cursor: pointer;\n transition:\n background 0.15s ease,\n border-color 0.15s ease,\n color 0.15s ease;\n white-space: nowrap;\n}\n\n.gengage-chat-comparison-toggle-icon {\n flex-shrink: 0;\n display: flex;\n color: var(--text-secondary);\n}\n\n.gengage-chat-comparison-toggle-icon svg {\n display: block;\n}\n\n.gengage-chat-comparison-toggle-btn:hover {\n background: var(--surface-card-soft);\n border-color: color-mix(in srgb, var(--client-primary) 18%, white);\n color: var(--client-primary);\n}\n\n.gengage-chat-comparison-toggle-btn--active {\n border-color: color-mix(in srgb, var(--client-primary) 26%, white);\n color: var(--client-primary);\n background: color-mix(in srgb, var(--client-primary) 8%, white);\n}\n\n.gengage-chat-comparison-toggle-btn--active .gengage-chat-comparison-toggle-icon {\n color: var(--client-primary);\n}\n\n.gengage-chat-comparison-toggle-btn--active:hover {\n background: var(--client-primary-hover);\n color: var(--client-on-primary);\n border-color: var(--client-primary-hover);\n}\n\n.gengage-chat-comparison-toggle-btn--active:hover .gengage-chat-comparison-toggle-icon {\n color: var(--client-on-primary);\n}\n\n/* Comparison select wrapper (checkbox overlay on product cards) */\n.gengage-chat-comparison-select-wrapper {\n position: relative;\n cursor: pointer;\n border-radius: var(--radius-control);\n transition:\n box-shadow 0.2s ease,\n transform 0.2s ease;\n}\n\n.gengage-chat-comparison-select-wrapper:hover {\n box-shadow: 0 0 0 2px var(--client-focus-ring);\n}\n\n.gengage-chat-comparison-select-wrapper--selected {\n box-shadow: none;\n}\n\n.gengage-chat-comparison-select-wrapper--selected:hover {\n box-shadow: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .gengage-chat-comparison-select-wrapper {\n transition: none;\n }\n}\n\n/* Pass clicks to wrapper so CTA / favorite / find-similar / stepper do not activate */\n.gengage-chat-comparison-select-wrapper > .gengage-chat-product-card {\n pointer-events: none;\n transition:\n opacity 0.2s ease,\n transform 0.2s ease,\n filter 0.2s ease;\n}\n\n.gengage-chat-comparison-select-wrapper > .gengage-chat-product-card .gengage-chat-product-card-img-actions,\n.gengage-chat-comparison-select-wrapper > .gengage-chat-product-card .gengage-chat-product-card-discount-badge,\n.gengage-chat-comparison-select-wrapper > .gengage-chat-product-card .gengage-chat-product-card-buy-footer,\n.gengage-chat-comparison-select-wrapper > .gengage-chat-product-card .gengage-chat-product-card-cta {\n opacity: 0;\n}\n\n.gengage-chat-comparison-select-wrapper > .gengage-chat-product-card .gengage-chat-product-card-body {\n opacity: 0.82;\n}\n\n.gengage-chat-comparison-select-wrapper:hover > .gengage-chat-product-card,\n.gengage-chat-comparison-select-wrapper--selected > .gengage-chat-product-card {\n opacity: 1;\n transform: translateY(-1px);\n}\n\n.gengage-chat-comparison-select-wrapper--selected > .gengage-chat-product-card {\n border-color: transparent;\n outline: none;\n box-shadow:\n inset 0 0 0 2px color-mix(in srgb, var(--client-primary) 26%, white),\n var(--shadow-1);\n filter: saturate(1.02);\n}\n\n.gengage-chat-comparison-select-wrapper--selected > .gengage-chat-product-card .gengage-chat-product-card-body {\n opacity: 1;\n}\n\n.gengage-chat-comparison-card-hint {\n display: none;\n}\n\n.gengage-chat-comparison-checkbox {\n position: absolute;\n left: 16px;\n right: 16px;\n bottom: 16px;\n z-index: 4;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n min-height: 40px;\n padding: 0 14px;\n cursor: pointer;\n pointer-events: auto;\n border: 1px solid var(--border-default);\n background: color-mix(in srgb, var(--surface-card) 94%, white);\n color: var(--text-primary);\n border-radius: 999px;\n box-shadow: var(--shadow-2);\n}\n\n.gengage-chat-comparison-checkbox[data-selected='true'] {\n background: color-mix(in srgb, var(--client-primary) 10%, white);\n border-color: color-mix(in srgb, var(--client-primary) 22%, var(--border-default));\n color: var(--client-primary);\n}\n\n.gengage-chat-comparison-checkbox-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n border-radius: 999px;\n background: var(--surface-card-soft);\n color: currentColor;\n flex-shrink: 0;\n}\n\n.gengage-chat-comparison-checkbox[data-selected='true'] .gengage-chat-comparison-checkbox-icon {\n background: var(--client-primary);\n color: var(--client-on-primary);\n}\n\n.gengage-chat-comparison-checkbox-dot {\n width: 8px;\n height: 8px;\n border-radius: 999px;\n background: color-mix(in srgb, currentColor 75%, white);\n}\n\n.gengage-chat-comparison-checkbox-label {\n font-size: 12px;\n font-weight: 700;\n line-height: 1.2;\n}\n\n/* Hidden state for comparison toggle during streaming — revealed after stream end */\n.gengage-chat-comparison-toggle-btn--hidden {\n opacity: 0;\n pointer-events: none;\n}\n\n@keyframes gengage-comparison-fade-in {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n.gengage-chat-comparison-toggle-btn--reveal {\n animation: gengage-comparison-fade-in 0.5s ease forwards;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .gengage-chat-comparison-toggle-btn--reveal {\n animation: none;\n opacity: 1;\n }\n}\n\n/* Floating comparison button — sticky at bottom of product grid */\n.gengage-chat-comparison-floating-btn {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 14px;\n width: min(100%, 520px);\n margin: 16px auto 0;\n padding: 12px 14px;\n border: 1px solid var(--border-default);\n border-radius: 18px;\n background: color-mix(in srgb, var(--surface-card) 92%, white);\n color: var(--text-primary);\n position: sticky;\n bottom: 12px;\n z-index: 5;\n box-shadow: var(--shadow-2);\n backdrop-filter: blur(10px);\n}\n\n/* Mobile: dock mounts here (body child) so it stacks above the transformed panel overlay */\n.gengage-chat-comparison-dock-slot {\n display: none;\n}\n\n.gengage-chat-root--mobile .gengage-chat-comparison-dock-slot:not(:empty) {\n display: block;\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 30;\n padding: 0 10px max(6px, env(safe-area-inset-bottom, 0px));\n max-width: 100%;\n box-sizing: border-box;\n pointer-events: none;\n}\n\n.gengage-chat-root--mobile .gengage-chat-comparison-dock-slot > * {\n pointer-events: auto;\n}\n\n.gengage-chat-comparison-floating-summary {\n display: flex;\n align-items: center;\n gap: 12px;\n min-width: 0;\n flex: 1 1 auto;\n}\n\n.gengage-chat-comparison-floating-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 32px;\n height: 32px;\n padding: 0 10px;\n border-radius: 999px;\n background: var(--client-primary-subtle);\n color: var(--client-primary);\n font-size: 13px;\n font-weight: 800;\n}\n\n.gengage-chat-comparison-floating-copy {\n min-width: 0;\n}\n\n.gengage-chat-comparison-floating-title {\n font-size: 14px;\n font-weight: 700;\n color: var(--text-primary);\n line-height: 1.2;\n}\n\n.gengage-chat-comparison-floating-warning {\n margin-top: 4px;\n font-size: 12px;\n color: var(--error);\n line-height: 1.35;\n}\n\n.gengage-chat-comparison-floating-subtitle {\n margin-top: 2px;\n font-size: 12px;\n color: var(--text-secondary);\n line-height: 1.35;\n}\n\n.gengage-chat-comparison-floating-action {\n flex: 0 0 auto;\n min-width: 132px;\n}\n\n.gengage-chat-comparison-floating-close {\n flex: 0 0 auto;\n width: 40px;\n height: 40px;\n padding: 0;\n border-radius: 14px;\n}\n\n.gengage-chat-comparison-floating-action--disabled {\n opacity: 0.6;\n}\n\n/* Mobile: bar lives in .gengage-chat-comparison-dock-slot (above panel z-index, not inside scroll) */\n.gengage-chat-root--mobile .gengage-chat-comparison-dock-slot .gengage-chat-comparison-floating-btn {\n position: relative;\n bottom: auto;\n margin: 0;\n width: 100%;\n max-width: 100%;\n min-width: 0;\n box-sizing: border-box;\n border-radius: 14px;\n flex-direction: column;\n align-items: stretch;\n justify-content: flex-start;\n gap: 10px;\n padding: 10px 10px 10px;\n box-shadow: var(--shadow-3);\n}\n\n.gengage-chat-root--mobile .gengage-chat-comparison-dock-slot .gengage-chat-comparison-floating-summary {\n display: flex;\n gap: 8px;\n padding-right: 34px; /* clear close (top-right) */\n align-items: center; /* badge and text on the same vertical axis */\n min-height: 26px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-comparison-dock-slot .gengage-chat-comparison-floating-count {\n min-width: 26px;\n height: 26px;\n padding: 0 7px;\n font-size: 12px;\n font-weight: 800;\n line-height: 1;\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n}\n\n.gengage-chat-root--mobile .gengage-chat-comparison-dock-slot .gengage-chat-comparison-floating-copy {\n display: flex;\n flex-direction: column;\n justify-content: center;\n min-width: 0;\n}\n\n.gengage-chat-root--mobile .gengage-chat-comparison-dock-slot .gengage-chat-comparison-floating-title {\n font-size: 13px;\n line-height: 1.25;\n margin: 0;\n}\n\n.gengage-chat-root--mobile .gengage-chat-comparison-dock-slot .gengage-chat-comparison-floating-warning {\n margin-top: 2px;\n font-size: 11px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-comparison-dock-slot .gengage-chat-comparison-floating-action {\n width: 100%;\n min-width: 0;\n max-width: 100%;\n box-sizing: border-box;\n min-height: 42px;\n padding: 0 12px;\n font-size: 13px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n/* Close (✕) — top-right, fixed square; overrides gds-btn min-height and gds-icon-btn sizing */\n.gengage-chat-root--mobile .gengage-chat-comparison-dock-slot .gengage-chat-comparison-floating-close {\n position: absolute;\n top: 9px;\n right: 9px;\n box-sizing: border-box;\n width: 26px;\n height: 26px;\n min-width: 26px;\n min-height: 26px;\n max-width: 26px;\n max-height: 26px;\n padding: 0;\n border-radius: 8px;\n flex: none;\n align-self: unset;\n z-index: 10;\n line-height: 0;\n touch-action: manipulation;\n}\n\n.gengage-chat-root--mobile .gengage-chat-comparison-dock-slot .gengage-chat-comparison-floating-close svg {\n width: 11px;\n height: 11px;\n display: block;\n}\n\n/* Panel TopBar — navigation arrows + title */\n.gengage-chat-panel-topbar {\n display: flex;\n align-items: center;\n flex-wrap: nowrap;\n gap: 12px;\n box-sizing: border-box;\n height: var(--_gengage-chat-header-height);\n padding: 14px 16px;\n border-bottom: none;\n background: var(--surface-card);\n flex-shrink: 0;\n position: sticky;\n top: 0;\n z-index: 3;\n}\n\n.gengage-chat-panel-topbar-title-wrap {\n display: flex;\n align-items: center;\n min-width: 0;\n flex: 1 1 0;\n}\n\n.gengage-chat-panel-topbar-back,\n.gengage-chat-panel-topbar-forward {\n width: 42px;\n height: 42px;\n min-width: 42px;\n min-height: 42px;\n padding: 0;\n border: 1px solid var(--border-default);\n border-radius: var(--radius-control);\n background: var(--surface-card);\n color: var(--text-primary);\n font-size: 0;\n font-family: inherit;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition:\n background 0.15s ease,\n border-color 0.15s ease,\n opacity 0.15s ease;\n}\n\n.gengage-chat-panel-topbar-back[hidden],\n.gengage-chat-panel-topbar-forward[hidden] {\n display: none;\n}\n\n.gengage-chat-panel-topbar-back:hover:not(:disabled),\n.gengage-chat-panel-topbar-forward:hover:not(:disabled) {\n background: var(--surface-card-soft);\n border-color: color-mix(in srgb, var(--client-primary) 18%, var(--border-default));\n}\n\n.gengage-chat-panel-topbar-back:disabled,\n.gengage-chat-panel-topbar-forward:disabled {\n opacity: 0.3;\n cursor: default;\n}\n\n.gengage-chat-panel-topbar-title {\n display: block;\n width: 100%;\n font-size: 17px;\n font-weight: 700;\n line-height: 1.2;\n color: var(--text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n text-align: left;\n letter-spacing: -0.01em;\n}\n\n.gengage-chat-panel-topbar-actions {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n min-width: 0;\n flex: 0 0 auto;\n}\n\n.gengage-chat-panel-topbar-actions:empty {\n display: none;\n}\n\n.gengage-chat-panel-topbar-toolbar-host {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n width: 100%;\n}\n\n.gengage-chat-panel-topbar-toolbar-host .gengage-chat-product-sort-toolbar {\n flex-wrap: nowrap;\n justify-content: flex-end;\n gap: 8px;\n}\n\n/* Close button — hidden on desktop, shown on mobile as an escape hatch to\n dismiss all panel layers at once without tapping Back repeatedly. */\n.gengage-chat-panel-topbar-close {\n display: none;\n}\n\n/* ── Typewriter block reveal animation ── */\n@keyframes gengage-typewriter-fade-in {\n from {\n opacity: 0;\n transform: translateY(4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n.gengage-chat-typewriter-block {\n display: block;\n animation: gengage-typewriter-fade-in 0.15s ease both;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .gengage-chat-typewriter-block {\n animation: none;\n }\n}\n\n/* ── Product mention links in bot text ── */\n.gengage-product-mention {\n color: var(--client-primary);\n text-decoration: none;\n cursor: pointer;\n font-weight: 500;\n border-bottom: 1px dashed var(--client-primary);\n transition: border-color 0.15s;\n}\n\n.gengage-product-mention:hover {\n border-bottom-style: solid;\n}\n\n/* ── Panel skeleton pulse (analyze animation) ── */\n@keyframes gengage-skeleton-pulse {\n 0%,\n 100% {\n opacity: 0.4;\n }\n 50% {\n opacity: 0.8;\n }\n}\n\n.gengage-chat-panel-skeleton-block {\n animation: gengage-skeleton-pulse 1.5s ease-in-out infinite;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .gengage-chat-panel-skeleton-block {\n animation: none;\n }\n}\n\n/* ── Input-area chips (compact shortcuts above input) ── */\n.gengage-chat-input-chips {\n display: flex;\n gap: 6px;\n padding: 4px 12px 2px;\n flex-wrap: nowrap;\n overflow-x: auto;\n scrollbar-width: none;\n -ms-overflow-style: none;\n}\n\n.gengage-chat-input-chips::-webkit-scrollbar {\n display: none;\n}\n\n.gengage-chat-input-chip {\n display: inline-flex;\n align-items: center;\n padding: 8px 12px;\n border-radius: var(--radius-pill);\n border: 1px solid var(--border-default);\n background: var(--surface-card);\n color: var(--text-secondary);\n font-size: 12px;\n font-weight: 700;\n font-family: inherit;\n cursor: pointer;\n transition:\n background 0.12s,\n border-color 0.12s,\n color 0.12s;\n white-space: nowrap;\n}\n\n.gengage-chat-input-chip:hover {\n background: var(--client-primary-subtle);\n border-color: color-mix(in srgb, var(--client-primary) 24%, var(--border-default));\n color: var(--client-primary);\n}\n\n/* Suggested action icons */\n.gengage-chat-icon {\n width: 14px;\n height: 14px;\n flex-shrink: 0;\n}\n\n.gengage-chat-input-chip-icon {\n display: inline-flex;\n align-items: center;\n margin-right: 4px;\n}\n\n.gengage-chat-pill-icon {\n display: inline-flex;\n align-items: center;\n margin-right: 4px;\n}\n\n/* ── Product card/details image wrapper (positioned container for overlays) ── */\n.gengage-chat-product-card-img-wrapper,\n.gengage-chat-product-details-img-wrap {\n position: relative;\n overflow: hidden;\n}\n\n/* ── Product card enrichment: discount badge ── */\n.gengage-chat-product-card-discount-badge {\n position: absolute;\n top: 8px;\n left: 8px;\n padding: 3px 8px;\n border-radius: var(--radius-pill);\n background: var(--_gengage-discount-color);\n color: var(--text-inverse);\n font-size: 11px;\n font-weight: 700;\n line-height: 1.3;\n z-index: 1;\n}\n\n/* ── Product card enrichment: stock indicator ── */\n.gengage-chat-product-card-stock {\n font-size: 11px;\n font-weight: 600;\n margin-top: 2px;\n padding: 2px 6px;\n border-radius: var(--radius-pill);\n width: fit-content;\n}\n\n.gengage-chat-product-card-stock.is-in-stock {\n background: color-mix(in srgb, var(--success) 12%, white);\n color: color-mix(in srgb, var(--success) 82%, var(--text-primary));\n}\n\n.gengage-chat-product-card-stock.is-out-of-stock {\n background: color-mix(in srgb, var(--error) 10%, white);\n color: var(--error);\n}\n\n/* ── Find Similar (icon in action stack; details panel keeps hover pill) ── */\n.gengage-chat-find-similar-pill {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 0;\n font-size: 11px;\n font-weight: 500;\n font-family: inherit;\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-control);\n background: color-mix(in srgb, var(--surface-elevated) 96%, transparent);\n color: var(--text-primary);\n box-shadow: var(--shadow-1);\n cursor: pointer;\n z-index: 2;\n transition:\n opacity 0.2s ease,\n box-shadow 0.15s ease;\n}\n\n.gengage-chat-find-similar-pill:hover {\n box-shadow: var(--shadow-2);\n border-color: color-mix(in srgb, var(--client-primary) 18%, var(--border-default));\n}\n\n@media (hover: hover) and (pointer: fine) {\n .gengage-chat-find-similar-pill::after {\n content: attr(data-tooltip);\n position: absolute;\n right: calc(100% + 8px);\n top: 50%;\n transform: translateY(-50%);\n padding: 6px 10px;\n border-radius: 10px;\n background: color-mix(in srgb, var(--surface-shell) 98%, black);\n border: 1px solid color-mix(in srgb, var(--surface-shell) 60%, white);\n box-shadow: var(--shadow-2);\n color: var(--text-inverse);\n font-size: 11px;\n font-weight: 600;\n line-height: 1.2;\n white-space: nowrap;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.14s ease;\n z-index: 12;\n }\n\n .gengage-chat-find-similar-pill:hover::after,\n .gengage-chat-find-similar-pill:focus-visible::after {\n opacity: 1;\n }\n}\n\n.gengage-chat-find-similar-pill-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.gengage-chat-find-similar-pill-icon svg {\n display: block;\n}\n\n/* Screen-reader label; visible text mode only in wide / panel contexts */\n.gengage-chat-find-similar-pill-text {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n\n/* Fine pointer: tuck actions until hover on narrow cards; touch always shows */\n.gengage-chat-product-card-img-wrapper .gengage-chat-product-card-img-actions,\n.gengage-chat-ai-toppick-media .gengage-chat-product-card-img-actions {\n transition: opacity 0.2s ease;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .gengage-chat-product-card-img-wrapper:not(:hover) .gengage-chat-product-card-img-actions,\n .gengage-chat-ai-toppick-media:not(:hover) .gengage-chat-product-card-img-actions {\n opacity: 0;\n pointer-events: none;\n }\n\n .gengage-chat-product-card-img-wrapper:hover .gengage-chat-product-card-img-actions,\n .gengage-chat-ai-toppick-media:hover .gengage-chat-product-card-img-actions {\n opacity: 1;\n pointer-events: auto;\n }\n}\n\n@media (hover: none), (pointer: coarse) {\n .gengage-chat-product-card-img-wrapper .gengage-chat-product-card-img-actions,\n .gengage-chat-ai-toppick-media .gengage-chat-product-card-img-actions {\n opacity: 1;\n pointer-events: auto;\n }\n}\n\n/* Product details: legacy dark hover pill (not using img-actions stack) */\n.gengage-chat-product-details-img-wrap .gengage-chat-find-similar-pill {\n position: absolute;\n top: 40px;\n right: 8px;\n width: auto;\n height: auto;\n padding: 4px 10px;\n border-radius: var(--radius-pill);\n background: color-mix(in srgb, var(--surface-shell) 88%, transparent);\n color: var(--text-inverse);\n box-shadow: none;\n opacity: 0;\n pointer-events: none;\n}\n\n.gengage-chat-product-details-img-wrap .gengage-chat-find-similar-pill-icon {\n display: none;\n}\n\n.gengage-chat-product-details-img-wrap .gengage-chat-find-similar-pill-text {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n white-space: nowrap;\n}\n\n.gengage-chat-product-card-img-wrapper:hover .gengage-chat-find-similar-pill,\n.gengage-chat-ai-toppick-media:hover .gengage-chat-find-similar-pill,\n.gengage-chat-product-details-img-wrap:hover .gengage-chat-find-similar-pill {\n opacity: 1;\n pointer-events: auto;\n}\n\n/* ── Favorite (stacked with find-similar on product cards) ── */\n.gengage-chat-favorite-btn {\n position: absolute;\n top: 8px;\n right: 8px;\n background: color-mix(in srgb, var(--surface-elevated) 96%, transparent);\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-control);\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n color: var(--text-secondary);\n box-shadow: var(--shadow-1);\n z-index: 3;\n transition:\n color 0.2s,\n box-shadow 0.15s ease;\n padding: 0;\n}\n\n.gengage-chat-favorite-btn:hover {\n box-shadow: var(--shadow-2);\n color: var(--error);\n border-color: color-mix(in srgb, var(--error) 20%, var(--border-default));\n}\n\n.gengage-chat-favorite-btn--active {\n color: var(--error);\n}\n\n/* ── Product-detail promotion badges ── */\n.gengage-chat-product-details-promos {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n margin-top: 4px;\n}\n\n.gengage-chat-product-details-promo-badge {\n display: inline-block;\n padding: 2px 6px;\n font-size: 10px;\n font-weight: 500;\n border-radius: var(--radius-pill);\n background: var(--gengage-chat-promo-bg, color-mix(in srgb, var(--success) 12%, white));\n color: var(--gengage-chat-promo-text, color-mix(in srgb, var(--success) 82%, var(--text-primary)));\n white-space: nowrap;\n min-height: 20px;\n box-sizing: border-box;\n}\n\n/* ── ThumbnailsColumn ──\n * Logic/DOM preserved; strip not shown in product panel (legacy parity). */\n.gengage-chat-thumbnails-column {\n display: none !important;\n pointer-events: none;\n}\n\n.gengage-chat-thumbnail-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n padding: 0;\n border: 2px solid var(--border-default);\n border-radius: calc(var(--radius-control) - 4px);\n background: var(--surface-card);\n cursor: pointer;\n overflow: hidden;\n transition: border-color 0.15s ease;\n}\n\n.gengage-chat-thumbnail-btn:hover {\n border-color: var(--client-primary);\n}\n\n.gengage-chat-thumbnail-img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n/* ── Panel floating overlay: sticky zero-height anchor at the bottom of the scroll area.\n Children (e.g. ChoicePrompter) use position:absolute and stay fixed to the visible area.\n margin-top:auto: when the grid is shorter than the panel (few products), consume flex free\n space so the anchor sits at the bottom of the pane; sticky+bottom keeps it there on scroll. ── */\n.gengage-chat-panel-float {\n position: sticky;\n bottom: 0;\n align-self: stretch;\n width: 100%;\n box-sizing: border-box;\n margin-top: auto;\n flex-shrink: 0;\n height: 0;\n overflow: visible;\n pointer-events: none;\n z-index: 10;\n}\n\n/* ── ChoicePrompter ── */\n.gengage-chat-choice-prompter {\n position: absolute;\n left: 16px;\n right: 16px;\n bottom: 12px;\n pointer-events: none;\n width: auto;\n max-width: none;\n margin-left: 0;\n margin-right: 0;\n padding: 12px 14px;\n background: var(--surface-card);\n border: 1px solid var(--border-default);\n border-radius: 16px;\n box-shadow: var(--shadow-2);\n z-index: 5;\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto auto;\n gap: 12px;\n align-items: center;\n opacity: 0;\n transform: translateY(12px);\n transition:\n opacity 0.2s ease,\n transform 0.2s ease;\n}\n\n.gengage-chat-panel--scrolled .gengage-chat-choice-prompter {\n pointer-events: auto;\n opacity: 1;\n transform: translateY(0);\n}\n\n.gengage-chat-choice-prompter-heading {\n font-size: 12px;\n font-weight: 700;\n color: var(--text-primary);\n margin-bottom: 2px;\n}\n\n.gengage-chat-choice-prompter-suggestion {\n font-size: 12px;\n color: var(--text-secondary);\n margin-bottom: 0;\n line-height: 1.35;\n}\n\n.gengage-chat-choice-prompter-copy {\n min-width: 0;\n}\n\n.gengage-chat-choice-prompter-cta {\n display: inline-flex;\n width: auto;\n min-width: 180px;\n min-height: 40px;\n padding: 0 18px;\n border: 1px solid var(--ds-button-primary-border);\n border-radius: var(--radius-control);\n background: var(--ds-button-primary-bg);\n color: var(--ds-button-primary-fg);\n font-size: 13px;\n font-weight: 700;\n font-family: inherit;\n cursor: pointer;\n text-align: center;\n transition: background 0.15s ease;\n}\n\n.gengage-chat-choice-prompter-cta:hover {\n background: var(--ds-button-primary-bg-hover);\n}\n\n.gengage-chat-choice-prompter-dismiss {\n position: absolute;\n top: 10px;\n right: 10px;\n padding: 0;\n min-width: 32px;\n min-height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 1px solid var(--border-default);\n border-radius: 12px;\n background: var(--surface-card-soft);\n color: var(--text-muted);\n font-size: 16px;\n cursor: pointer;\n line-height: 1;\n}\n\n.gengage-chat-choice-prompter-dismiss:hover {\n color: var(--client-primary);\n border-color: color-mix(in srgb, var(--client-primary) 18%, white);\n background: var(--client-primary-subtle);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .gengage-chat-choice-prompter {\n animation: none;\n }\n}\n\n/* Pros & Cons */\n.gengage-chat-pros-cons {\n padding: 12px 14px;\n background: var(--surface-card-soft);\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-control);\n margin: 8px 0;\n}\n\n.gengage-chat-pros-cons-heading {\n margin: 0 0 10px;\n font-size: 14px;\n font-weight: 600;\n color: var(--text-primary);\n}\n\n.gengage-chat-pros-cons-list {\n list-style: none;\n margin: 0 0 8px;\n padding: 0;\n}\n\n.gengage-chat-pros-cons-list:last-child {\n margin-bottom: 0;\n}\n\n.gengage-chat-pros-cons-item {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n padding: 4px 0;\n font-size: 13px;\n line-height: 1.5;\n color: var(--text-secondary);\n}\n\n.gengage-chat-pros-cons-icon {\n flex-shrink: 0;\n width: 18px;\n height: 18px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: 700;\n border-radius: 50%;\n}\n\n.gengage-chat-pros-cons-icon--pro {\n color: var(--success);\n background: color-mix(in srgb, var(--success) 15%, white);\n}\n\n.gengage-chat-pros-cons-icon--con {\n color: var(--error);\n background: color-mix(in srgb, var(--error) 12%, white);\n}\n\n/* ===== CategoriesContainer ===== */\n.gengage-chat-categories {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.gengage-chat-categories-tabs {\n display: flex;\n gap: 4px;\n overflow-x: auto;\n padding: 4px 0;\n -webkit-overflow-scrolling: touch;\n}\n\n.gengage-chat-categories-tab {\n padding: 6px 14px;\n border: 1px solid var(--border-default);\n border-radius: var(--radius-pill);\n background: var(--surface-card);\n color: var(--text-secondary);\n cursor: pointer;\n white-space: nowrap;\n font-size: 13px;\n transition:\n background 0.15s,\n color 0.15s,\n border-color 0.15s;\n}\n\n.gengage-chat-categories-tab:hover {\n background: var(--surface-card-soft);\n border-color: var(--border-strong);\n}\n\n.gengage-chat-categories-tab--active {\n background: var(--client-primary-subtle);\n color: var(--client-primary);\n border-color: color-mix(in srgb, var(--client-primary) 26%, var(--border-default));\n}\n\n.gengage-chat-categories-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));\n gap: 8px;\n}\n\n.gengage-chat-categories-filter-tags {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n margin-top: 4px;\n}\n\n.gengage-chat-categories-filter-tag {\n padding: 4px 10px;\n border: 1px solid var(--border-default);\n border-radius: var(--radius-pill);\n background: var(--surface-card);\n color: var(--text-secondary);\n font-size: 12px;\n cursor: pointer;\n transition:\n background 0.15s,\n border-color 0.15s,\n color 0.15s;\n}\n\n.gengage-chat-categories-filter-tag:hover {\n background: var(--client-primary-subtle);\n border-color: color-mix(in srgb, var(--client-primary) 18%, var(--border-default));\n color: var(--client-primary);\n}\n\n/* ===== View More button ===== */\n.gengage-chat-product-grid-view-more {\n display: block;\n width: 100%;\n padding: 10px;\n margin-top: 8px;\n border: 1px solid var(--ds-button-secondary-border);\n border-radius: var(--radius-control);\n background: var(--ds-button-secondary-bg);\n color: var(--ds-button-secondary-fg);\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n text-align: center;\n transition: background 0.15s;\n grid-column: 1 / -1;\n}\n\n.gengage-chat-product-grid-view-more:hover {\n background: var(--ds-button-secondary-bg-hover);\n}\n\n/* ===== Mobile product grid (narrow screen: 2 products per row, no horizontal scroll) ===== */\n.gengage-chat-product-grid--mobile {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 8px;\n padding: 8px 0;\n overflow-x: visible;\n}\n\n.gengage-chat-product-grid--mobile .gengage-chat-product-card {\n width: 100%;\n min-width: 0;\n max-width: none;\n}\n\n/* ===== Mobile comparison ===== */\n/* The table-wrapper scrolls horizontally; the outer container clips without scrolling. */\n.gengage-chat-comparison--mobile {\n overflow-x: clip; /* prevent the container from overflowing the page */\n position: relative;\n}\n\n/* Scrollable wrapper around the table only */\n.gengage-chat-comparison--mobile .gengage-chat-comparison-table-wrapper {\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n touch-action: pan-x pan-y;\n /* Right-edge fade signals horizontal scrollability */\n -webkit-mask-image: linear-gradient(to right, black 88%, transparent);\n mask-image: linear-gradient(to right, black 88%, transparent);\n}\n\n.gengage-chat-comparison--mobile .gengage-chat-comparison-table {\n min-width: 480px;\n table-layout: fixed;\n}\n\n/* Sticky first column: label column stays fixed while products scroll */\n.gengage-chat-comparison--mobile .gengage-chat-comparison-table th:first-child,\n.gengage-chat-comparison--mobile .gengage-chat-comparison-table .gengage-chat-comparison-label {\n position: sticky;\n left: 0;\n background: var(--surface-card);\n z-index: 1;\n /* Shadow instead of border to avoid border-collapse artefacts */\n box-shadow: 2px 0 4px color-mix(in srgb, var(--surface-shell) 8%, transparent);\n}\n\n/* First column: left-aligned and narrow — long criterion names truncate with ellipsis */\n.gengage-chat-comparison--mobile .gengage-chat-comparison-table thead th:first-child {\n width: 22ch;\n max-width: 22ch;\n min-width: 0;\n padding: 8px 6px;\n box-sizing: border-box;\n}\n\n.gengage-chat-comparison--mobile .gengage-chat-comparison-table td.gengage-chat-comparison-label {\n text-align: left;\n width: 22ch;\n max-width: 22ch;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n box-sizing: border-box;\n vertical-align: middle;\n}\n\n/* ===== Similar Products heading in panel ===== */\n.gengage-chat-product-details-similars-heading {\n margin: 16px 0 8px 0;\n font-size: 18px;\n font-weight: 700;\n line-height: 1.2;\n color: var(--text-primary);\n}\n\n.gengage-chat-product-details-similars {\n margin-top: 8px;\n}\n\n/* Handoff Notice */\n.gengage-chat-handoff-notice {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n padding: 16px;\n margin: 8px 0;\n background: color-mix(in srgb, var(--warning) 10%, white);\n border: 1px solid color-mix(in srgb, var(--warning) 28%, var(--border-default));\n border-radius: var(--radius-control);\n text-align: center;\n}\n\n.gengage-chat-handoff-notice-icon {\n font-size: 24px;\n}\n\n.gengage-chat-handoff-notice-heading {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: color-mix(in srgb, var(--warning) 72%, var(--text-primary));\n}\n\n.gengage-chat-handoff-notice-summary {\n margin: 0;\n font-size: 13px;\n line-height: 1.5;\n color: color-mix(in srgb, var(--warning) 84%, var(--text-primary));\n}\n\n/* ---- Share button ---- */\n.gengage-chat-product-details-share {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n border: 1px solid var(--border-default);\n border-radius: var(--radius-control);\n background: var(--surface-card);\n color: var(--text-muted);\n cursor: pointer;\n transition:\n color 0.12s,\n border-color 0.12s,\n background-color 0.12s;\n position: relative;\n flex-shrink: 0;\n}\n\n.gengage-chat-product-details-share:hover {\n color: var(--client-primary);\n border-color: var(--client-primary);\n background: var(--client-primary-subtle);\n}\n\n/* CSS-only tooltip for share button */\n.gengage-chat-product-details-share::before {\n content: attr(aria-label);\n position: absolute;\n bottom: calc(100% + 6px);\n left: 50%;\n transform: translateX(-50%);\n padding: 4px 8px;\n border-radius: calc(var(--radius-control) - 4px);\n background: var(--surface-shell);\n color: var(--text-inverse);\n font-size: 11px;\n font-weight: 500;\n white-space: nowrap;\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.15s ease;\n z-index: 2;\n}\n\n.gengage-chat-product-details-share:hover::before {\n opacity: 1;\n}\n\n.gengage-chat-product-details-share--copied::after {\n content: '\\2713';\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--_gengage-success-color);\n color: var(--client-on-primary);\n border-radius: var(--radius-control);\n font-size: 16px;\n font-weight: 700;\n}\n\n/* ── Share button SVG sizing ── */\n.gengage-chat-product-details-share svg {\n width: 20px;\n height: 20px;\n}\n\n/* ── Review / grounding link styling ── */\n.gengage-chat-review-link,\n.gengage-chat-grounding-link {\n text-decoration: underline;\n color: var(--client-primary);\n}\n\n/* ── Product-detail promotion badge mobile clamp ── */\n@media (max-width: 768px) {\n .gengage-chat-product-details-promo-badge {\n -webkit-line-clamp: 2;\n line-clamp: 2;\n display: -webkit-box;\n -webkit-box-orient: vertical;\n overflow: hidden;\n white-space: normal;\n }\n}\n\n/* ---------------------------------------------------------------------------\n * Focus-visible outlines (WCAG 2.4.7)\n * ---------------------------------------------------------------------------*/\n.gengage-chat-launcher:focus-visible,\n.gengage-chat-close:focus-visible,\n.gengage-chat-header-btn:focus-visible,\n.gengage-chat-header-powered:focus-visible,\n.gengage-chat-send:focus-visible,\n.gengage-chat-action-btn:focus-visible,\n.gengage-chat-pill:focus-visible,\n.gengage-chat-input-chip:focus-visible,\n.gengage-chat-pills-arrow:focus-visible,\n.gengage-chat-error-retry:focus-visible,\n.gengage-chat-product-card-cta:focus-visible,\n.gengage-chat-product-details-cta:focus-visible,\n.gengage-chat-product-details-atc:focus-visible,\n.gengage-chat-product-detail-tab:focus-visible,\n.gengage-chat-panel-divider-toggle:focus-visible,\n.gengage-chat-panel-topbar-back:focus-visible,\n.gengage-chat-panel-topbar-forward:focus-visible,\n.gengage-chat-rollback-btn:focus-visible,\n.gengage-chat-ai-toppick-cta:focus-visible,\n.gengage-chat-comparison-toggle-btn:focus-visible,\n.gengage-chat-comparison-floating-btn:focus-visible,\n.gengage-chat-comparison-checkbox:focus-visible,\n.gengage-chat-product-sort-trigger:focus-visible,\n.gengage-chat-product-sort-option:focus-visible,\n.gengage-chat-product-variant-btn:focus-visible,\n.gengage-chat-product-gallery-thumb:focus-visible,\n.gengage-chat-thumbnail-btn:focus-visible,\n.gengage-chat-find-similar-pill:focus-visible,\n.gengage-chat-favorite-btn:focus-visible,\n.gengage-chat-attach-btn:focus-visible,\n.gengage-chat-attach-menu-item:focus-visible,\n.gengage-chat-mic-btn:focus-visible,\n.gengage-chat-attachment-remove:focus-visible,\n.gengage-chat-kvkk-dismiss:focus-visible,\n.gengage-chat-choice-prompter-cta:focus-visible,\n.gengage-chat-choice-prompter-dismiss:focus-visible,\n.gengage-chat-categories-tab:focus-visible,\n.gengage-chat-categories-filter-tag:focus-visible,\n.gengage-chat-product-grid-view-more:focus-visible,\n.gengage-chat-product-details-share:focus-visible {\n outline: 2px solid var(--client-focus-ring);\n outline-offset: 2px;\n}\n\n/* Mobile touch targets — WCAG 2.5.5 (44px minimum) */\n.gengage-chat-root--mobile .gengage-chat-choice-prompter {\n left: 12px;\n right: 12px;\n width: auto;\n max-width: none;\n bottom: 12px;\n max-height: 40vh;\n overflow-y: auto;\n grid-template-columns: 1fr;\n}\n\n.gengage-chat-root--mobile .gengage-chat-choice-prompter-cta {\n width: 100%;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-sort-trigger,\n.gengage-chat-root--mobile .gengage-chat-product-sort-option,\n.gengage-chat-root--mobile .gengage-chat-comparison-toggle-btn {\n min-height: 40px;\n}\n\n/* Panel (side sheet): sort + compare — icon only on mobile */\n.gengage-chat-root--mobile .gengage-chat-panel .gengage-chat-product-sort-trigger-label,\n.gengage-chat-root--mobile .gengage-chat-panel .gengage-chat-product-sort-trigger-chevron,\n.gengage-chat-root--mobile .gengage-chat-panel .gengage-chat-comparison-toggle-label {\n display: none;\n}\n\n.gengage-chat-root--mobile .gengage-chat-panel .gengage-chat-product-sort-dropdown {\n flex: 0 0 auto;\n max-width: none;\n width: auto;\n}\n\n.gengage-chat-root--mobile .gengage-chat-panel .gengage-chat-product-sort-trigger {\n width: auto;\n min-width: 44px;\n justify-content: center;\n padding: 0 10px;\n gap: 0;\n}\n\n.gengage-chat-root--mobile .gengage-chat-panel .gengage-chat-comparison-toggle-btn {\n min-width: 44px;\n width: 44px;\n padding: 0;\n justify-content: center;\n gap: 0;\n}\n\n.gengage-chat-root--mobile .gengage-chat-input-chip {\n min-height: 40px;\n padding: 8px 12px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-choice-prompter-dismiss {\n width: 32px;\n height: 32px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-panel-divider-toggle {\n min-height: 44px;\n}\n\n.gengage-chat-root--mobile .gengage-chat-product-card-cta,\n.gengage-chat-root--mobile .gengage-chat-choice-prompter-cta,\n.gengage-chat-root--mobile .gengage-chat-product-grid-view-more {\n min-height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.gengage-chat-root--mobile .gengage-chat-panel-topbar-back,\n.gengage-chat-root--mobile .gengage-chat-panel-topbar-forward,\n.gengage-chat-root--mobile .gengage-chat-favorite-btn,\n.gengage-chat-root--mobile .gengage-chat-product-details-share {\n width: 44px;\n height: 44px;\n min-width: 44px;\n}\n\n/* Panel close (✕) — hidden on mobile; use header “reopen panel” / back stack instead */\n.gengage-chat-root--mobile .gengage-chat-panel-topbar-close {\n display: none;\n}\n\n/* ---------------------------------------------------------------------------\n * Shared loading skeleton (src/common/skeleton.ts)\n * ---------------------------------------------------------------------------*/\n.gengage-skeleton {\n display: flex;\n gap: 12px;\n padding: 12px;\n}\n\n.gengage-skeleton--message {\n flex-direction: column;\n gap: 8px;\n padding: 16px;\n}\n\n.gengage-skeleton-card {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.gengage-skeleton-img {\n height: 120px;\n border-radius: var(--radius-control);\n}\n\n.gengage-skeleton-text {\n height: 14px;\n border-radius: 4px;\n}\n\n.gengage-skeleton-price {\n height: 14px;\n width: 60%;\n border-radius: 4px;\n}\n\n.gengage-skeleton-line {\n height: 12px;\n border-radius: 4px;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n .gengage-skeleton-card,\n .gengage-skeleton-line,\n .gengage-skeleton-img,\n .gengage-skeleton-text,\n .gengage-skeleton-price {\n background: linear-gradient(90deg, var(--ds-neutral-100) 25%, var(--ds-neutral-200) 50%, var(--ds-neutral-100) 75%);\n background-size: 200% 100%;\n animation: gengage-shimmer 1.5s infinite;\n }\n\n @keyframes gengage-shimmer {\n 0% {\n background-position: 200% 0;\n }\n 100% {\n background-position: -200% 0;\n }\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .gengage-skeleton-card,\n .gengage-skeleton-line,\n .gengage-skeleton-img,\n .gengage-skeleton-text,\n .gengage-skeleton-price {\n background: var(--ds-neutral-100);\n }\n}\n\n/* ---------------------------------------------------------------------------\n * Panel scroll affordance (bottom fade gradient)\n * ---------------------------------------------------------------------------*/\n.gengage-chat-panel--has-scroll::after {\n content: '';\n position: sticky;\n bottom: 0;\n display: block;\n height: 40px;\n background: linear-gradient(transparent, var(--surface-card-soft));\n pointer-events: none;\n}\n\n/* ---------------------------------------------------------------------------\n * Panel content crossfade transition\n * ---------------------------------------------------------------------------*/\n@media (prefers-reduced-motion: no-preference) {\n .gengage-chat-panel--transitioning {\n opacity: 0;\n transition: opacity 150ms ease;\n }\n}\n\n/* ---------------------------------------------------------------------------\n * Cart success toast\n * ---------------------------------------------------------------------------*/\n.gengage-chat-cart-toast {\n position: absolute;\n bottom: 72px;\n left: 50%;\n transform: translateX(-50%) translateY(8px);\n background: var(--success);\n color: var(--text-inverse);\n font-size: 13px;\n font-weight: 600;\n padding: 8px 18px;\n border-radius: var(--radius-pill);\n box-shadow: var(--shadow-3);\n pointer-events: none;\n opacity: 0;\n transition:\n opacity 0.2s ease,\n transform 0.2s ease;\n z-index: 10;\n white-space: nowrap;\n}\n\n.gengage-chat-cart-toast--visible {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n}\n\n/* Cart button flash animation */\n@keyframes gengage-cart-flash {\n 0% {\n transform: scale(1);\n }\n 30% {\n transform: scale(1.25);\n }\n 60% {\n transform: scale(0.95);\n }\n 100% {\n transform: scale(1);\n }\n}\n\n.gengage-chat-header-btn--cart-flash {\n animation: gengage-cart-flash 0.5s ease;\n}\n","/**\n * Google Analytics dataLayer integration.\n *\n * Pushes chat activity events to `window.dataLayer` when GA is available.\n * Falls back to `console.debug` when GA is not detected (useful for debugging).\n *\n * Event naming follows the GA4 recommended event pattern:\n * - All lowercase, hyphen-separated\n * - Prefixed with `gengage-` for easy filtering in GA dashboards\n *\n * Clients can build custom chat funnels in GA using these events.\n */\n\n// ---------------------------------------------------------------------------\n// GA dataLayer type augmentation\n// ---------------------------------------------------------------------------\n\ninterface DataLayerEvent {\n event: string;\n [key: string]: unknown;\n}\n\ndeclare global {\n interface Window {\n dataLayer?: DataLayerEvent[];\n }\n}\n\n// ---------------------------------------------------------------------------\n// GA detection\n// ---------------------------------------------------------------------------\n\nfunction isGAAvailable(): boolean {\n return typeof window !== 'undefined' && Array.isArray(window.dataLayer);\n}\n\n// ---------------------------------------------------------------------------\n// Core push function\n// ---------------------------------------------------------------------------\n\nfunction pushEvent(eventName: string, params?: Record<string, unknown>): void {\n const payload: DataLayerEvent = {\n event: eventName,\n ...params,\n };\n\n if (isGAAvailable()) {\n window.dataLayer!.push(payload);\n }\n // No fallback log — GA events are silently dropped when dataLayer is absent.\n}\n\n// ---------------------------------------------------------------------------\n// Typed event emitters\n// ---------------------------------------------------------------------------\n\n/** Widget icon/avatar displayed on page. */\nexport function trackInit(widget: string): void {\n pushEvent('gengage-on-init', { gengage_widget: widget });\n}\n\n/** Widget opened / shown to user. */\nexport function trackShow(widget: string): void {\n pushEvent('gengage-show', { gengage_widget: widget });\n}\n\n/** Widget closed / hidden. */\nexport function trackHide(widget: string): void {\n pushEvent('gengage-hide', { gengage_widget: widget });\n}\n\n/** User clicked a suggested question / action button. */\nexport function trackSuggestedQuestion(title: string, type: string): void {\n pushEvent('gengage-suggested-question', {\n gengage_question_title: title,\n gengage_action_type: type,\n });\n}\n\n/** User clicked \"Find Similar\" for a product. */\nexport function trackFindSimilars(sku: string): void {\n pushEvent('gengage-find-similars', { gengage_sku: sku });\n}\n\n/** User pre-selected a product for comparison. */\nexport function trackComparePreselection(sku: string): void {\n pushEvent('gengage-compare-preselection', { gengage_sku: sku });\n}\n\n/** User submitted the comparison (clicked \"Compare Selected\"). */\nexport function trackCompareSelected(skus: string[]): void {\n pushEvent('gengage-compare-selected', {\n gengage_skus: skus,\n gengage_product_count: skus.length,\n });\n}\n\n/** User cleared the comparison selection. */\nexport function trackCompareClear(): void {\n pushEvent('gengage-compare-clear');\n}\n\n/** Comparison results received and rendered. */\nexport function trackCompareReceived(productCount: number): void {\n pushEvent('gengage-compare-received', {\n gengage_product_count: productCount,\n });\n}\n\n/** User liked / favorited a product. */\nexport function trackLikeProduct(sku: string): void {\n pushEvent('gengage-like-product', { gengage_sku: sku });\n}\n\n/** User clicked the favorites/likes list button. */\nexport function trackLikeList(): void {\n pushEvent('gengage-like-list');\n}\n\n/** Product list / search results displayed. */\nexport function trackSearch(query?: string, resultCount?: number): void {\n pushEvent('gengage-search', {\n gengage_search_query: query,\n gengage_result_count: resultCount,\n });\n}\n\n/** User clicked on a product to view details. */\nexport function trackProductDetail(sku: string, name?: string): void {\n pushEvent('gengage-product-detail', {\n gengage_sku: sku,\n gengage_product_name: name,\n });\n}\n\n/** User added a product to cart from the widget. */\nexport function trackCartAdd(sku: string, quantity: number): void {\n pushEvent('gengage-cart-add', {\n gengage_sku: sku,\n gengage_quantity: quantity,\n });\n}\n\n/** User sent a chat message. */\nexport function trackMessageSent(): void {\n pushEvent('gengage-message-sent');\n}\n\n/** Assistant responded with text. */\nexport function trackMessageReceived(): void {\n pushEvent('gengage-message-received');\n}\n\n/** User started a new conversation. */\nexport function trackConversationStart(): void {\n pushEvent('gengage-conversation-start');\n}\n\n/** User used voice input. */\nexport function trackVoiceInput(): void {\n pushEvent('gengage-voice-input');\n}\n\n/** Widget or stream error occurred. */\nexport function trackError(widget: string, error: string): void {\n pushEvent('gengage-error', { gengage_widget: widget, gengage_error: error });\n}\n\n// ---------------------------------------------------------------------------\n// Batch wire-up: connect to the Gengage event bus\n// ---------------------------------------------------------------------------\n\n/**\n * Wire GA dataLayer tracking to the Gengage event bus.\n * Call once after widgets are initialized.\n *\n * @returns unsubscribe function that removes all listeners.\n */\nlet _gaWiredUnsub: (() => void) | null = null;\n\nexport function wireGADataLayer(): () => void {\n if (typeof window === 'undefined') return () => {};\n // Idempotency guard: return existing unsubscribe if already wired\n if (_gaWiredUnsub) return _gaWiredUnsub;\n\n const listeners: Array<() => void> = [];\n\n function on<T>(eventName: string, handler: (detail: T) => void): void {\n const listener = (e: Event) => handler((e as CustomEvent<T>).detail);\n window.addEventListener(eventName, listener);\n listeners.push(() => window.removeEventListener(eventName, listener));\n }\n\n // Chat lifecycle\n on<{ state?: string }>('gengage:chat:open', () => trackShow('chat'));\n on<Record<string, never>>('gengage:chat:close', () => trackHide('chat'));\n on<Record<string, never>>('gengage:chat:ready', () => trackInit('chat'));\n\n // Add to cart (from similar products widget — chat handles GA4 directly)\n on<{ sku: string; quantity: number; cartCode: string }>('gengage:similar:add-to-cart', ({ sku, quantity }) => {\n trackCartAdd(sku, quantity);\n });\n\n // Product click from similar products\n on<{ sku: string; url: string }>('gengage:similar:product-click', ({ sku }) => {\n trackProductDetail(sku);\n });\n\n // QNA action (suggested question click)\n on<{ title: string; type: string }>('gengage:qna:action', ({ title, type }) => {\n trackSuggestedQuestion(title, type);\n });\n\n // Voice input\n on<{ payload: unknown }>('gengage:chat:voice', () => trackVoiceInput());\n\n // QNA open chat\n on<Record<string, never>>('gengage:qna:open-chat', () => trackShow('chat'));\n\n // Error tracking\n on<{ source: string; message: string }>('gengage:global:error', ({ source, message }) => {\n trackError(source, message);\n });\n\n _gaWiredUnsub = () => {\n for (const unsub of listeners) unsub();\n listeners.length = 0;\n _gaWiredUnsub = null;\n };\n return _gaWiredUnsub;\n}\n","/** A special constant with type `never` */\nexport const NEVER = Object.freeze({\n status: \"aborted\",\n});\nexport /*@__NO_SIDE_EFFECTS__*/ function $constructor(name, initializer, params) {\n function init(inst, def) {\n if (!inst._zod) {\n Object.defineProperty(inst, \"_zod\", {\n value: {\n def,\n constr: _,\n traits: new Set(),\n },\n enumerable: false,\n });\n }\n if (inst._zod.traits.has(name)) {\n return;\n }\n inst._zod.traits.add(name);\n initializer(inst, def);\n // support prototype modifications\n const proto = _.prototype;\n const keys = Object.keys(proto);\n for (let i = 0; i < keys.length; i++) {\n const k = keys[i];\n if (!(k in inst)) {\n inst[k] = proto[k].bind(inst);\n }\n }\n }\n // doesn't work if Parent has a constructor with arguments\n const Parent = params?.Parent ?? Object;\n class Definition extends Parent {\n }\n Object.defineProperty(Definition, \"name\", { value: name });\n function _(def) {\n var _a;\n const inst = params?.Parent ? new Definition() : this;\n init(inst, def);\n (_a = inst._zod).deferred ?? (_a.deferred = []);\n for (const fn of inst._zod.deferred) {\n fn();\n }\n return inst;\n }\n Object.defineProperty(_, \"init\", { value: init });\n Object.defineProperty(_, Symbol.hasInstance, {\n value: (inst) => {\n if (params?.Parent && inst instanceof params.Parent)\n return true;\n return inst?._zod?.traits?.has(name);\n },\n });\n Object.defineProperty(_, \"name\", { value: name });\n return _;\n}\n////////////////////////////// UTILITIES ///////////////////////////////////////\nexport const $brand = Symbol(\"zod_brand\");\nexport class $ZodAsyncError extends Error {\n constructor() {\n super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`);\n }\n}\nexport class $ZodEncodeError extends Error {\n constructor(name) {\n super(`Encountered unidirectional transform during encode: ${name}`);\n this.name = \"ZodEncodeError\";\n }\n}\nexport const globalConfig = {};\nexport function config(newConfig) {\n if (newConfig)\n Object.assign(globalConfig, newConfig);\n return globalConfig;\n}\n","// functions\nexport function assertEqual(val) {\n return val;\n}\nexport function assertNotEqual(val) {\n return val;\n}\nexport function assertIs(_arg) { }\nexport function assertNever(_x) {\n throw new Error(\"Unexpected value in exhaustive check\");\n}\nexport function assert(_) { }\nexport function getEnumValues(entries) {\n const numericValues = Object.values(entries).filter((v) => typeof v === \"number\");\n const values = Object.entries(entries)\n .filter(([k, _]) => numericValues.indexOf(+k) === -1)\n .map(([_, v]) => v);\n return values;\n}\nexport function joinValues(array, separator = \"|\") {\n return array.map((val) => stringifyPrimitive(val)).join(separator);\n}\nexport function jsonStringifyReplacer(_, value) {\n if (typeof value === \"bigint\")\n return value.toString();\n return value;\n}\nexport function cached(getter) {\n const set = false;\n return {\n get value() {\n if (!set) {\n const value = getter();\n Object.defineProperty(this, \"value\", { value });\n return value;\n }\n throw new Error(\"cached value already set\");\n },\n };\n}\nexport function nullish(input) {\n return input === null || input === undefined;\n}\nexport function cleanRegex(source) {\n const start = source.startsWith(\"^\") ? 1 : 0;\n const end = source.endsWith(\"$\") ? source.length - 1 : source.length;\n return source.slice(start, end);\n}\nexport function floatSafeRemainder(val, step) {\n const valDecCount = (val.toString().split(\".\")[1] || \"\").length;\n const stepString = step.toString();\n let stepDecCount = (stepString.split(\".\")[1] || \"\").length;\n if (stepDecCount === 0 && /\\d?e-\\d?/.test(stepString)) {\n const match = stepString.match(/\\d?e-(\\d?)/);\n if (match?.[1]) {\n stepDecCount = Number.parseInt(match[1]);\n }\n }\n const decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount;\n const valInt = Number.parseInt(val.toFixed(decCount).replace(\".\", \"\"));\n const stepInt = Number.parseInt(step.toFixed(decCount).replace(\".\", \"\"));\n return (valInt % stepInt) / 10 ** decCount;\n}\nconst EVALUATING = Symbol(\"evaluating\");\nexport function defineLazy(object, key, getter) {\n let value = undefined;\n Object.defineProperty(object, key, {\n get() {\n if (value === EVALUATING) {\n // Circular reference detected, return undefined to break the cycle\n return undefined;\n }\n if (value === undefined) {\n value = EVALUATING;\n value = getter();\n }\n return value;\n },\n set(v) {\n Object.defineProperty(object, key, {\n value: v,\n // configurable: true,\n });\n // object[key] = v;\n },\n configurable: true,\n });\n}\nexport function objectClone(obj) {\n return Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));\n}\nexport function assignProp(target, prop, value) {\n Object.defineProperty(target, prop, {\n value,\n writable: true,\n enumerable: true,\n configurable: true,\n });\n}\nexport function mergeDefs(...defs) {\n const mergedDescriptors = {};\n for (const def of defs) {\n const descriptors = Object.getOwnPropertyDescriptors(def);\n Object.assign(mergedDescriptors, descriptors);\n }\n return Object.defineProperties({}, mergedDescriptors);\n}\nexport function cloneDef(schema) {\n return mergeDefs(schema._zod.def);\n}\nexport function getElementAtPath(obj, path) {\n if (!path)\n return obj;\n return path.reduce((acc, key) => acc?.[key], obj);\n}\nexport function promiseAllObject(promisesObj) {\n const keys = Object.keys(promisesObj);\n const promises = keys.map((key) => promisesObj[key]);\n return Promise.all(promises).then((results) => {\n const resolvedObj = {};\n for (let i = 0; i < keys.length; i++) {\n resolvedObj[keys[i]] = results[i];\n }\n return resolvedObj;\n });\n}\nexport function randomString(length = 10) {\n const chars = \"abcdefghijklmnopqrstuvwxyz\";\n let str = \"\";\n for (let i = 0; i < length; i++) {\n str += chars[Math.floor(Math.random() * chars.length)];\n }\n return str;\n}\nexport function esc(str) {\n return JSON.stringify(str);\n}\nexport function slugify(input) {\n return input\n .toLowerCase()\n .trim()\n .replace(/[^\\w\\s-]/g, \"\")\n .replace(/[\\s_-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\nexport const captureStackTrace = (\"captureStackTrace\" in Error ? Error.captureStackTrace : (..._args) => { });\nexport function isObject(data) {\n return typeof data === \"object\" && data !== null && !Array.isArray(data);\n}\nexport const allowsEval = cached(() => {\n // @ts-ignore\n if (typeof navigator !== \"undefined\" && navigator?.userAgent?.includes(\"Cloudflare\")) {\n return false;\n }\n try {\n const F = Function;\n new F(\"\");\n return true;\n }\n catch (_) {\n return false;\n }\n});\nexport function isPlainObject(o) {\n if (isObject(o) === false)\n return false;\n // modified constructor\n const ctor = o.constructor;\n if (ctor === undefined)\n return true;\n if (typeof ctor !== \"function\")\n return true;\n // modified prototype\n const prot = ctor.prototype;\n if (isObject(prot) === false)\n return false;\n // ctor doesn't have static `isPrototypeOf`\n if (Object.prototype.hasOwnProperty.call(prot, \"isPrototypeOf\") === false) {\n return false;\n }\n return true;\n}\nexport function shallowClone(o) {\n if (isPlainObject(o))\n return { ...o };\n if (Array.isArray(o))\n return [...o];\n return o;\n}\nexport function numKeys(data) {\n let keyCount = 0;\n for (const key in data) {\n if (Object.prototype.hasOwnProperty.call(data, key)) {\n keyCount++;\n }\n }\n return keyCount;\n}\nexport const getParsedType = (data) => {\n const t = typeof data;\n switch (t) {\n case \"undefined\":\n return \"undefined\";\n case \"string\":\n return \"string\";\n case \"number\":\n return Number.isNaN(data) ? \"nan\" : \"number\";\n case \"boolean\":\n return \"boolean\";\n case \"function\":\n return \"function\";\n case \"bigint\":\n return \"bigint\";\n case \"symbol\":\n return \"symbol\";\n case \"object\":\n if (Array.isArray(data)) {\n return \"array\";\n }\n if (data === null) {\n return \"null\";\n }\n if (data.then && typeof data.then === \"function\" && data.catch && typeof data.catch === \"function\") {\n return \"promise\";\n }\n if (typeof Map !== \"undefined\" && data instanceof Map) {\n return \"map\";\n }\n if (typeof Set !== \"undefined\" && data instanceof Set) {\n return \"set\";\n }\n if (typeof Date !== \"undefined\" && data instanceof Date) {\n return \"date\";\n }\n // @ts-ignore\n if (typeof File !== \"undefined\" && data instanceof File) {\n return \"file\";\n }\n return \"object\";\n default:\n throw new Error(`Unknown data type: ${t}`);\n }\n};\nexport const propertyKeyTypes = new Set([\"string\", \"number\", \"symbol\"]);\nexport const primitiveTypes = new Set([\"string\", \"number\", \"bigint\", \"boolean\", \"symbol\", \"undefined\"]);\nexport function escapeRegex(str) {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n// zod-specific utils\nexport function clone(inst, def, params) {\n const cl = new inst._zod.constr(def ?? inst._zod.def);\n if (!def || params?.parent)\n cl._zod.parent = inst;\n return cl;\n}\nexport function normalizeParams(_params) {\n const params = _params;\n if (!params)\n return {};\n if (typeof params === \"string\")\n return { error: () => params };\n if (params?.message !== undefined) {\n if (params?.error !== undefined)\n throw new Error(\"Cannot specify both `message` and `error` params\");\n params.error = params.message;\n }\n delete params.message;\n if (typeof params.error === \"string\")\n return { ...params, error: () => params.error };\n return params;\n}\nexport function createTransparentProxy(getter) {\n let target;\n return new Proxy({}, {\n get(_, prop, receiver) {\n target ?? (target = getter());\n return Reflect.get(target, prop, receiver);\n },\n set(_, prop, value, receiver) {\n target ?? (target = getter());\n return Reflect.set(target, prop, value, receiver);\n },\n has(_, prop) {\n target ?? (target = getter());\n return Reflect.has(target, prop);\n },\n deleteProperty(_, prop) {\n target ?? (target = getter());\n return Reflect.deleteProperty(target, prop);\n },\n ownKeys(_) {\n target ?? (target = getter());\n return Reflect.ownKeys(target);\n },\n getOwnPropertyDescriptor(_, prop) {\n target ?? (target = getter());\n return Reflect.getOwnPropertyDescriptor(target, prop);\n },\n defineProperty(_, prop, descriptor) {\n target ?? (target = getter());\n return Reflect.defineProperty(target, prop, descriptor);\n },\n });\n}\nexport function stringifyPrimitive(value) {\n if (typeof value === \"bigint\")\n return value.toString() + \"n\";\n if (typeof value === \"string\")\n return `\"${value}\"`;\n return `${value}`;\n}\nexport function optionalKeys(shape) {\n return Object.keys(shape).filter((k) => {\n return shape[k]._zod.optin === \"optional\" && shape[k]._zod.optout === \"optional\";\n });\n}\nexport const NUMBER_FORMAT_RANGES = {\n safeint: [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER],\n int32: [-2147483648, 2147483647],\n uint32: [0, 4294967295],\n float32: [-3.4028234663852886e38, 3.4028234663852886e38],\n float64: [-Number.MAX_VALUE, Number.MAX_VALUE],\n};\nexport const BIGINT_FORMAT_RANGES = {\n int64: [/* @__PURE__*/ BigInt(\"-9223372036854775808\"), /* @__PURE__*/ BigInt(\"9223372036854775807\")],\n uint64: [/* @__PURE__*/ BigInt(0), /* @__PURE__*/ BigInt(\"18446744073709551615\")],\n};\nexport function pick(schema, mask) {\n const currDef = schema._zod.def;\n const checks = currDef.checks;\n const hasChecks = checks && checks.length > 0;\n if (hasChecks) {\n throw new Error(\".pick() cannot be used on object schemas containing refinements\");\n }\n const def = mergeDefs(schema._zod.def, {\n get shape() {\n const newShape = {};\n for (const key in mask) {\n if (!(key in currDef.shape)) {\n throw new Error(`Unrecognized key: \"${key}\"`);\n }\n if (!mask[key])\n continue;\n newShape[key] = currDef.shape[key];\n }\n assignProp(this, \"shape\", newShape); // self-caching\n return newShape;\n },\n checks: [],\n });\n return clone(schema, def);\n}\nexport function omit(schema, mask) {\n const currDef = schema._zod.def;\n const checks = currDef.checks;\n const hasChecks = checks && checks.length > 0;\n if (hasChecks) {\n throw new Error(\".omit() cannot be used on object schemas containing refinements\");\n }\n const def = mergeDefs(schema._zod.def, {\n get shape() {\n const newShape = { ...schema._zod.def.shape };\n for (const key in mask) {\n if (!(key in currDef.shape)) {\n throw new Error(`Unrecognized key: \"${key}\"`);\n }\n if (!mask[key])\n continue;\n delete newShape[key];\n }\n assignProp(this, \"shape\", newShape); // self-caching\n return newShape;\n },\n checks: [],\n });\n return clone(schema, def);\n}\nexport function extend(schema, shape) {\n if (!isPlainObject(shape)) {\n throw new Error(\"Invalid input to extend: expected a plain object\");\n }\n const checks = schema._zod.def.checks;\n const hasChecks = checks && checks.length > 0;\n if (hasChecks) {\n // Only throw if new shape overlaps with existing shape\n // Use getOwnPropertyDescriptor to check key existence without accessing values\n const existingShape = schema._zod.def.shape;\n for (const key in shape) {\n if (Object.getOwnPropertyDescriptor(existingShape, key) !== undefined) {\n throw new Error(\"Cannot overwrite keys on object schemas containing refinements. Use `.safeExtend()` instead.\");\n }\n }\n }\n const def = mergeDefs(schema._zod.def, {\n get shape() {\n const _shape = { ...schema._zod.def.shape, ...shape };\n assignProp(this, \"shape\", _shape); // self-caching\n return _shape;\n },\n });\n return clone(schema, def);\n}\nexport function safeExtend(schema, shape) {\n if (!isPlainObject(shape)) {\n throw new Error(\"Invalid input to safeExtend: expected a plain object\");\n }\n const def = mergeDefs(schema._zod.def, {\n get shape() {\n const _shape = { ...schema._zod.def.shape, ...shape };\n assignProp(this, \"shape\", _shape); // self-caching\n return _shape;\n },\n });\n return clone(schema, def);\n}\nexport function merge(a, b) {\n const def = mergeDefs(a._zod.def, {\n get shape() {\n const _shape = { ...a._zod.def.shape, ...b._zod.def.shape };\n assignProp(this, \"shape\", _shape); // self-caching\n return _shape;\n },\n get catchall() {\n return b._zod.def.catchall;\n },\n checks: [], // delete existing checks\n });\n return clone(a, def);\n}\nexport function partial(Class, schema, mask) {\n const currDef = schema._zod.def;\n const checks = currDef.checks;\n const hasChecks = checks && checks.length > 0;\n if (hasChecks) {\n throw new Error(\".partial() cannot be used on object schemas containing refinements\");\n }\n const def = mergeDefs(schema._zod.def, {\n get shape() {\n const oldShape = schema._zod.def.shape;\n const shape = { ...oldShape };\n if (mask) {\n for (const key in mask) {\n if (!(key in oldShape)) {\n throw new Error(`Unrecognized key: \"${key}\"`);\n }\n if (!mask[key])\n continue;\n // if (oldShape[key]!._zod.optin === \"optional\") continue;\n shape[key] = Class\n ? new Class({\n type: \"optional\",\n innerType: oldShape[key],\n })\n : oldShape[key];\n }\n }\n else {\n for (const key in oldShape) {\n // if (oldShape[key]!._zod.optin === \"optional\") continue;\n shape[key] = Class\n ? new Class({\n type: \"optional\",\n innerType: oldShape[key],\n })\n : oldShape[key];\n }\n }\n assignProp(this, \"shape\", shape); // self-caching\n return shape;\n },\n checks: [],\n });\n return clone(schema, def);\n}\nexport function required(Class, schema, mask) {\n const def = mergeDefs(schema._zod.def, {\n get shape() {\n const oldShape = schema._zod.def.shape;\n const shape = { ...oldShape };\n if (mask) {\n for (const key in mask) {\n if (!(key in shape)) {\n throw new Error(`Unrecognized key: \"${key}\"`);\n }\n if (!mask[key])\n continue;\n // overwrite with non-optional\n shape[key] = new Class({\n type: \"nonoptional\",\n innerType: oldShape[key],\n });\n }\n }\n else {\n for (const key in oldShape) {\n // overwrite with non-optional\n shape[key] = new Class({\n type: \"nonoptional\",\n innerType: oldShape[key],\n });\n }\n }\n assignProp(this, \"shape\", shape); // self-caching\n return shape;\n },\n });\n return clone(schema, def);\n}\n// invalid_type | too_big | too_small | invalid_format | not_multiple_of | unrecognized_keys | invalid_union | invalid_key | invalid_element | invalid_value | custom\nexport function aborted(x, startIndex = 0) {\n if (x.aborted === true)\n return true;\n for (let i = startIndex; i < x.issues.length; i++) {\n if (x.issues[i]?.continue !== true) {\n return true;\n }\n }\n return false;\n}\nexport function prefixIssues(path, issues) {\n return issues.map((iss) => {\n var _a;\n (_a = iss).path ?? (_a.path = []);\n iss.path.unshift(path);\n return iss;\n });\n}\nexport function unwrapMessage(message) {\n return typeof message === \"string\" ? message : message?.message;\n}\nexport function finalizeIssue(iss, ctx, config) {\n const full = { ...iss, path: iss.path ?? [] };\n // for backwards compatibility\n if (!iss.message) {\n const message = unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ??\n unwrapMessage(ctx?.error?.(iss)) ??\n unwrapMessage(config.customError?.(iss)) ??\n unwrapMessage(config.localeError?.(iss)) ??\n \"Invalid input\";\n full.message = message;\n }\n // delete (full as any).def;\n delete full.inst;\n delete full.continue;\n if (!ctx?.reportInput) {\n delete full.input;\n }\n return full;\n}\nexport function getSizableOrigin(input) {\n if (input instanceof Set)\n return \"set\";\n if (input instanceof Map)\n return \"map\";\n // @ts-ignore\n if (input instanceof File)\n return \"file\";\n return \"unknown\";\n}\nexport function getLengthableOrigin(input) {\n if (Array.isArray(input))\n return \"array\";\n if (typeof input === \"string\")\n return \"string\";\n return \"unknown\";\n}\nexport function parsedType(data) {\n const t = typeof data;\n switch (t) {\n case \"number\": {\n return Number.isNaN(data) ? \"nan\" : \"number\";\n }\n case \"object\": {\n if (data === null) {\n return \"null\";\n }\n if (Array.isArray(data)) {\n return \"array\";\n }\n const obj = data;\n if (obj && Object.getPrototypeOf(obj) !== Object.prototype && \"constructor\" in obj && obj.constructor) {\n return obj.constructor.name;\n }\n }\n }\n return t;\n}\nexport function issue(...args) {\n const [iss, input, inst] = args;\n if (typeof iss === \"string\") {\n return {\n message: iss,\n code: \"custom\",\n input,\n inst,\n };\n }\n return { ...iss };\n}\nexport function cleanEnum(obj) {\n return Object.entries(obj)\n .filter(([k, _]) => {\n // return true if NaN, meaning it's not a number, thus a string key\n return Number.isNaN(Number.parseInt(k, 10));\n })\n .map((el) => el[1]);\n}\n// Codec utility functions\nexport function base64ToUint8Array(base64) {\n const binaryString = atob(base64);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return bytes;\n}\nexport function uint8ArrayToBase64(bytes) {\n let binaryString = \"\";\n for (let i = 0; i < bytes.length; i++) {\n binaryString += String.fromCharCode(bytes[i]);\n }\n return btoa(binaryString);\n}\nexport function base64urlToUint8Array(base64url) {\n const base64 = base64url.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padding = \"=\".repeat((4 - (base64.length % 4)) % 4);\n return base64ToUint8Array(base64 + padding);\n}\nexport function uint8ArrayToBase64url(bytes) {\n return uint8ArrayToBase64(bytes).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=/g, \"\");\n}\nexport function hexToUint8Array(hex) {\n const cleanHex = hex.replace(/^0x/, \"\");\n if (cleanHex.length % 2 !== 0) {\n throw new Error(\"Invalid hex string length\");\n }\n const bytes = new Uint8Array(cleanHex.length / 2);\n for (let i = 0; i < cleanHex.length; i += 2) {\n bytes[i / 2] = Number.parseInt(cleanHex.slice(i, i + 2), 16);\n }\n return bytes;\n}\nexport function uint8ArrayToHex(bytes) {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n// instanceof\nexport class Class {\n constructor(..._args) { }\n}\n","import { $constructor } from \"./core.js\";\nimport * as util from \"./util.js\";\nconst initializer = (inst, def) => {\n inst.name = \"$ZodError\";\n Object.defineProperty(inst, \"_zod\", {\n value: inst._zod,\n enumerable: false,\n });\n Object.defineProperty(inst, \"issues\", {\n value: def,\n enumerable: false,\n });\n inst.message = JSON.stringify(def, util.jsonStringifyReplacer, 2);\n Object.defineProperty(inst, \"toString\", {\n value: () => inst.message,\n enumerable: false,\n });\n};\nexport const $ZodError = $constructor(\"$ZodError\", initializer);\nexport const $ZodRealError = $constructor(\"$ZodError\", initializer, { Parent: Error });\nexport function flattenError(error, mapper = (issue) => issue.message) {\n const fieldErrors = {};\n const formErrors = [];\n for (const sub of error.issues) {\n if (sub.path.length > 0) {\n fieldErrors[sub.path[0]] = fieldErrors[sub.path[0]] || [];\n fieldErrors[sub.path[0]].push(mapper(sub));\n }\n else {\n formErrors.push(mapper(sub));\n }\n }\n return { formErrors, fieldErrors };\n}\nexport function formatError(error, mapper = (issue) => issue.message) {\n const fieldErrors = { _errors: [] };\n const processError = (error) => {\n for (const issue of error.issues) {\n if (issue.code === \"invalid_union\" && issue.errors.length) {\n issue.errors.map((issues) => processError({ issues }));\n }\n else if (issue.code === \"invalid_key\") {\n processError({ issues: issue.issues });\n }\n else if (issue.code === \"invalid_element\") {\n processError({ issues: issue.issues });\n }\n else if (issue.path.length === 0) {\n fieldErrors._errors.push(mapper(issue));\n }\n else {\n let curr = fieldErrors;\n let i = 0;\n while (i < issue.path.length) {\n const el = issue.path[i];\n const terminal = i === issue.path.length - 1;\n if (!terminal) {\n curr[el] = curr[el] || { _errors: [] };\n }\n else {\n curr[el] = curr[el] || { _errors: [] };\n curr[el]._errors.push(mapper(issue));\n }\n curr = curr[el];\n i++;\n }\n }\n }\n };\n processError(error);\n return fieldErrors;\n}\nexport function treeifyError(error, mapper = (issue) => issue.message) {\n const result = { errors: [] };\n const processError = (error, path = []) => {\n var _a, _b;\n for (const issue of error.issues) {\n if (issue.code === \"invalid_union\" && issue.errors.length) {\n // regular union error\n issue.errors.map((issues) => processError({ issues }, issue.path));\n }\n else if (issue.code === \"invalid_key\") {\n processError({ issues: issue.issues }, issue.path);\n }\n else if (issue.code === \"invalid_element\") {\n processError({ issues: issue.issues }, issue.path);\n }\n else {\n const fullpath = [...path, ...issue.path];\n if (fullpath.length === 0) {\n result.errors.push(mapper(issue));\n continue;\n }\n let curr = result;\n let i = 0;\n while (i < fullpath.length) {\n const el = fullpath[i];\n const terminal = i === fullpath.length - 1;\n if (typeof el === \"string\") {\n curr.properties ?? (curr.properties = {});\n (_a = curr.properties)[el] ?? (_a[el] = { errors: [] });\n curr = curr.properties[el];\n }\n else {\n curr.items ?? (curr.items = []);\n (_b = curr.items)[el] ?? (_b[el] = { errors: [] });\n curr = curr.items[el];\n }\n if (terminal) {\n curr.errors.push(mapper(issue));\n }\n i++;\n }\n }\n }\n };\n processError(error);\n return result;\n}\n/** Format a ZodError as a human-readable string in the following form.\n *\n * From\n *\n * ```ts\n * ZodError {\n * issues: [\n * {\n * expected: 'string',\n * code: 'invalid_type',\n * path: [ 'username' ],\n * message: 'Invalid input: expected string'\n * },\n * {\n * expected: 'number',\n * code: 'invalid_type',\n * path: [ 'favoriteNumbers', 1 ],\n * message: 'Invalid input: expected number'\n * }\n * ];\n * }\n * ```\n *\n * to\n *\n * ```\n * username\n * ✖ Expected number, received string at \"username\n * favoriteNumbers[0]\n * ✖ Invalid input: expected number\n * ```\n */\nexport function toDotPath(_path) {\n const segs = [];\n const path = _path.map((seg) => (typeof seg === \"object\" ? seg.key : seg));\n for (const seg of path) {\n if (typeof seg === \"number\")\n segs.push(`[${seg}]`);\n else if (typeof seg === \"symbol\")\n segs.push(`[${JSON.stringify(String(seg))}]`);\n else if (/[^\\w$]/.test(seg))\n segs.push(`[${JSON.stringify(seg)}]`);\n else {\n if (segs.length)\n segs.push(\".\");\n segs.push(seg);\n }\n }\n return segs.join(\"\");\n}\nexport function prettifyError(error) {\n const lines = [];\n // sort by path length\n const issues = [...error.issues].sort((a, b) => (a.path ?? []).length - (b.path ?? []).length);\n // Process each issue\n for (const issue of issues) {\n lines.push(`✖ ${issue.message}`);\n if (issue.path?.length)\n lines.push(` → at ${toDotPath(issue.path)}`);\n }\n // Convert Map to formatted string\n return lines.join(\"\\n\");\n}\n","import * as core from \"./core.js\";\nimport * as errors from \"./errors.js\";\nimport * as util from \"./util.js\";\nexport const _parse = (_Err) => (schema, value, _ctx, _params) => {\n const ctx = _ctx ? Object.assign(_ctx, { async: false }) : { async: false };\n const result = schema._zod.run({ value, issues: [] }, ctx);\n if (result instanceof Promise) {\n throw new core.$ZodAsyncError();\n }\n if (result.issues.length) {\n const e = new (_params?.Err ?? _Err)(result.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config())));\n util.captureStackTrace(e, _params?.callee);\n throw e;\n }\n return result.value;\n};\nexport const parse = /* @__PURE__*/ _parse(errors.$ZodRealError);\nexport const _parseAsync = (_Err) => async (schema, value, _ctx, params) => {\n const ctx = _ctx ? Object.assign(_ctx, { async: true }) : { async: true };\n let result = schema._zod.run({ value, issues: [] }, ctx);\n if (result instanceof Promise)\n result = await result;\n if (result.issues.length) {\n const e = new (params?.Err ?? _Err)(result.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config())));\n util.captureStackTrace(e, params?.callee);\n throw e;\n }\n return result.value;\n};\nexport const parseAsync = /* @__PURE__*/ _parseAsync(errors.$ZodRealError);\nexport const _safeParse = (_Err) => (schema, value, _ctx) => {\n const ctx = _ctx ? { ..._ctx, async: false } : { async: false };\n const result = schema._zod.run({ value, issues: [] }, ctx);\n if (result instanceof Promise) {\n throw new core.$ZodAsyncError();\n }\n return result.issues.length\n ? {\n success: false,\n error: new (_Err ?? errors.$ZodError)(result.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config()))),\n }\n : { success: true, data: result.value };\n};\nexport const safeParse = /* @__PURE__*/ _safeParse(errors.$ZodRealError);\nexport const _safeParseAsync = (_Err) => async (schema, value, _ctx) => {\n const ctx = _ctx ? Object.assign(_ctx, { async: true }) : { async: true };\n let result = schema._zod.run({ value, issues: [] }, ctx);\n if (result instanceof Promise)\n result = await result;\n return result.issues.length\n ? {\n success: false,\n error: new _Err(result.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config()))),\n }\n : { success: true, data: result.value };\n};\nexport const safeParseAsync = /* @__PURE__*/ _safeParseAsync(errors.$ZodRealError);\nexport const _encode = (_Err) => (schema, value, _ctx) => {\n const ctx = _ctx ? Object.assign(_ctx, { direction: \"backward\" }) : { direction: \"backward\" };\n return _parse(_Err)(schema, value, ctx);\n};\nexport const encode = /* @__PURE__*/ _encode(errors.$ZodRealError);\nexport const _decode = (_Err) => (schema, value, _ctx) => {\n return _parse(_Err)(schema, value, _ctx);\n};\nexport const decode = /* @__PURE__*/ _decode(errors.$ZodRealError);\nexport const _encodeAsync = (_Err) => async (schema, value, _ctx) => {\n const ctx = _ctx ? Object.assign(_ctx, { direction: \"backward\" }) : { direction: \"backward\" };\n return _parseAsync(_Err)(schema, value, ctx);\n};\nexport const encodeAsync = /* @__PURE__*/ _encodeAsync(errors.$ZodRealError);\nexport const _decodeAsync = (_Err) => async (schema, value, _ctx) => {\n return _parseAsync(_Err)(schema, value, _ctx);\n};\nexport const decodeAsync = /* @__PURE__*/ _decodeAsync(errors.$ZodRealError);\nexport const _safeEncode = (_Err) => (schema, value, _ctx) => {\n const ctx = _ctx ? Object.assign(_ctx, { direction: \"backward\" }) : { direction: \"backward\" };\n return _safeParse(_Err)(schema, value, ctx);\n};\nexport const safeEncode = /* @__PURE__*/ _safeEncode(errors.$ZodRealError);\nexport const _safeDecode = (_Err) => (schema, value, _ctx) => {\n return _safeParse(_Err)(schema, value, _ctx);\n};\nexport const safeDecode = /* @__PURE__*/ _safeDecode(errors.$ZodRealError);\nexport const _safeEncodeAsync = (_Err) => async (schema, value, _ctx) => {\n const ctx = _ctx ? Object.assign(_ctx, { direction: \"backward\" }) : { direction: \"backward\" };\n return _safeParseAsync(_Err)(schema, value, ctx);\n};\nexport const safeEncodeAsync = /* @__PURE__*/ _safeEncodeAsync(errors.$ZodRealError);\nexport const _safeDecodeAsync = (_Err) => async (schema, value, _ctx) => {\n return _safeParseAsync(_Err)(schema, value, _ctx);\n};\nexport const safeDecodeAsync = /* @__PURE__*/ _safeDecodeAsync(errors.$ZodRealError);\n","import * as util from \"./util.js\";\nexport const cuid = /^[cC][^\\s-]{8,}$/;\nexport const cuid2 = /^[0-9a-z]+$/;\nexport const ulid = /^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/;\nexport const xid = /^[0-9a-vA-V]{20}$/;\nexport const ksuid = /^[A-Za-z0-9]{27}$/;\nexport const nanoid = /^[a-zA-Z0-9_-]{21}$/;\n/** ISO 8601-1 duration regex. Does not support the 8601-2 extensions like negative durations or fractional/negative components. */\nexport const duration = /^P(?:(\\d+W)|(?!.*W)(?=\\d|T\\d)(\\d+Y)?(\\d+M)?(\\d+D)?(T(?=\\d)(\\d+H)?(\\d+M)?(\\d+([.,]\\d+)?S)?)?)$/;\n/** Implements ISO 8601-2 extensions like explicit +- prefixes, mixing weeks with other units, and fractional/negative components. */\nexport const extendedDuration = /^[-+]?P(?!$)(?:(?:[-+]?\\d+Y)|(?:[-+]?\\d+[.,]\\d+Y$))?(?:(?:[-+]?\\d+M)|(?:[-+]?\\d+[.,]\\d+M$))?(?:(?:[-+]?\\d+W)|(?:[-+]?\\d+[.,]\\d+W$))?(?:(?:[-+]?\\d+D)|(?:[-+]?\\d+[.,]\\d+D$))?(?:T(?=[\\d+-])(?:(?:[-+]?\\d+H)|(?:[-+]?\\d+[.,]\\d+H$))?(?:(?:[-+]?\\d+M)|(?:[-+]?\\d+[.,]\\d+M$))?(?:[-+]?\\d+(?:[.,]\\d+)?S)?)??$/;\n/** A regex for any UUID-like identifier: 8-4-4-4-12 hex pattern */\nexport const guid = /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/;\n/** Returns a regex for validating an RFC 9562/4122 UUID.\n *\n * @param version Optionally specify a version 1-8. If no version is specified, all versions are supported. */\nexport const uuid = (version) => {\n if (!version)\n return /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/;\n return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`);\n};\nexport const uuid4 = /*@__PURE__*/ uuid(4);\nexport const uuid6 = /*@__PURE__*/ uuid(6);\nexport const uuid7 = /*@__PURE__*/ uuid(7);\n/** Practical email validation */\nexport const email = /^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$/;\n/** Equivalent to the HTML5 input[type=email] validation implemented by browsers. Source: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/email */\nexport const html5Email = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;\n/** The classic emailregex.com regex for RFC 5322-compliant emails */\nexport const rfc5322Email = /^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/;\n/** A loose regex that allows Unicode characters, enforces length limits, and that's about it. */\nexport const unicodeEmail = /^[^\\s@\"]{1,64}@[^\\s@]{1,255}$/u;\nexport const idnEmail = unicodeEmail;\nexport const browserEmail = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;\n// from https://thekevinscott.com/emojis-in-javascript/#writing-a-regular-expression\nconst _emoji = `^(\\\\p{Extended_Pictographic}|\\\\p{Emoji_Component})+$`;\nexport function emoji() {\n return new RegExp(_emoji, \"u\");\n}\nexport const ipv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;\nexport const ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;\nexport const mac = (delimiter) => {\n const escapedDelim = util.escapeRegex(delimiter ?? \":\");\n return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);\n};\nexport const cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\/([0-9]|[1-2][0-9]|3[0-2])$/;\nexport const cidrv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/;\n// https://stackoverflow.com/questions/7860392/determine-if-string-is-in-base64-using-javascript\nexport const base64 = /^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/;\nexport const base64url = /^[A-Za-z0-9_-]*$/;\n// based on https://stackoverflow.com/questions/106179/regular-expression-to-match-dns-hostname-or-ip-address\n// export const hostname: RegExp = /^([a-zA-Z0-9-]+\\.)*[a-zA-Z0-9-]+$/;\nexport const hostname = /^(?=.{1,253}\\.?$)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[-0-9a-zA-Z]{0,61}[0-9a-zA-Z])?)*\\.?$/;\nexport const domain = /^([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}$/;\n// https://blog.stevenlevithan.com/archives/validate-phone-number#r4-3 (regex sans spaces)\n// E.164: leading digit must be 1-9; total digits (excluding '+') between 7-15\nexport const e164 = /^\\+[1-9]\\d{6,14}$/;\n// const dateSource = `((\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\\\d|30)|(02)-(0[1-9]|1\\\\d|2[0-8])))`;\nconst dateSource = `(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))`;\nexport const date = /*@__PURE__*/ new RegExp(`^${dateSource}$`);\nfunction timeSource(args) {\n const hhmm = `(?:[01]\\\\d|2[0-3]):[0-5]\\\\d`;\n const regex = typeof args.precision === \"number\"\n ? args.precision === -1\n ? `${hhmm}`\n : args.precision === 0\n ? `${hhmm}:[0-5]\\\\d`\n : `${hhmm}:[0-5]\\\\d\\\\.\\\\d{${args.precision}}`\n : `${hhmm}(?::[0-5]\\\\d(?:\\\\.\\\\d+)?)?`;\n return regex;\n}\nexport function time(args) {\n return new RegExp(`^${timeSource(args)}$`);\n}\n// Adapted from https://stackoverflow.com/a/3143231\nexport function datetime(args) {\n const time = timeSource({ precision: args.precision });\n const opts = [\"Z\"];\n if (args.local)\n opts.push(\"\");\n // if (args.offset) opts.push(`([+-]\\\\d{2}:\\\\d{2})`);\n if (args.offset)\n opts.push(`([+-](?:[01]\\\\d|2[0-3]):[0-5]\\\\d)`);\n const timeRegex = `${time}(?:${opts.join(\"|\")})`;\n return new RegExp(`^${dateSource}T(?:${timeRegex})$`);\n}\nexport const string = (params) => {\n const regex = params ? `[\\\\s\\\\S]{${params?.minimum ?? 0},${params?.maximum ?? \"\"}}` : `[\\\\s\\\\S]*`;\n return new RegExp(`^${regex}$`);\n};\nexport const bigint = /^-?\\d+n?$/;\nexport const integer = /^-?\\d+$/;\nexport const number = /^-?\\d+(?:\\.\\d+)?$/;\nexport const boolean = /^(?:true|false)$/i;\nconst _null = /^null$/i;\nexport { _null as null };\nconst _undefined = /^undefined$/i;\nexport { _undefined as undefined };\n// regex for string with no uppercase letters\nexport const lowercase = /^[^A-Z]*$/;\n// regex for string with no lowercase letters\nexport const uppercase = /^[^a-z]*$/;\n// regex for hexadecimal strings (any length)\nexport const hex = /^[0-9a-fA-F]*$/;\n// Hash regexes for different algorithms and encodings\n// Helper function to create base64 regex with exact length and padding\nfunction fixedBase64(bodyLength, padding) {\n return new RegExp(`^[A-Za-z0-9+/]{${bodyLength}}${padding}$`);\n}\n// Helper function to create base64url regex with exact length (no padding)\nfunction fixedBase64url(length) {\n return new RegExp(`^[A-Za-z0-9_-]{${length}}$`);\n}\n// MD5 (16 bytes): base64 = 24 chars total (22 + \"==\")\nexport const md5_hex = /^[0-9a-fA-F]{32}$/;\nexport const md5_base64 = /*@__PURE__*/ fixedBase64(22, \"==\");\nexport const md5_base64url = /*@__PURE__*/ fixedBase64url(22);\n// SHA1 (20 bytes): base64 = 28 chars total (27 + \"=\")\nexport const sha1_hex = /^[0-9a-fA-F]{40}$/;\nexport const sha1_base64 = /*@__PURE__*/ fixedBase64(27, \"=\");\nexport const sha1_base64url = /*@__PURE__*/ fixedBase64url(27);\n// SHA256 (32 bytes): base64 = 44 chars total (43 + \"=\")\nexport const sha256_hex = /^[0-9a-fA-F]{64}$/;\nexport const sha256_base64 = /*@__PURE__*/ fixedBase64(43, \"=\");\nexport const sha256_base64url = /*@__PURE__*/ fixedBase64url(43);\n// SHA384 (48 bytes): base64 = 64 chars total (no padding)\nexport const sha384_hex = /^[0-9a-fA-F]{96}$/;\nexport const sha384_base64 = /*@__PURE__*/ fixedBase64(64, \"\");\nexport const sha384_base64url = /*@__PURE__*/ fixedBase64url(64);\n// SHA512 (64 bytes): base64 = 88 chars total (86 + \"==\")\nexport const sha512_hex = /^[0-9a-fA-F]{128}$/;\nexport const sha512_base64 = /*@__PURE__*/ fixedBase64(86, \"==\");\nexport const sha512_base64url = /*@__PURE__*/ fixedBase64url(86);\n","// import { $ZodType } from \"./schemas.js\";\nimport * as core from \"./core.js\";\nimport * as regexes from \"./regexes.js\";\nimport * as util from \"./util.js\";\nexport const $ZodCheck = /*@__PURE__*/ core.$constructor(\"$ZodCheck\", (inst, def) => {\n var _a;\n inst._zod ?? (inst._zod = {});\n inst._zod.def = def;\n (_a = inst._zod).onattach ?? (_a.onattach = []);\n});\nconst numericOriginMap = {\n number: \"number\",\n bigint: \"bigint\",\n object: \"date\",\n};\nexport const $ZodCheckLessThan = /*@__PURE__*/ core.$constructor(\"$ZodCheckLessThan\", (inst, def) => {\n $ZodCheck.init(inst, def);\n const origin = numericOriginMap[typeof def.value];\n inst._zod.onattach.push((inst) => {\n const bag = inst._zod.bag;\n const curr = (def.inclusive ? bag.maximum : bag.exclusiveMaximum) ?? Number.POSITIVE_INFINITY;\n if (def.value < curr) {\n if (def.inclusive)\n bag.maximum = def.value;\n else\n bag.exclusiveMaximum = def.value;\n }\n });\n inst._zod.check = (payload) => {\n if (def.inclusive ? payload.value <= def.value : payload.value < def.value) {\n return;\n }\n payload.issues.push({\n origin,\n code: \"too_big\",\n maximum: typeof def.value === \"object\" ? def.value.getTime() : def.value,\n input: payload.value,\n inclusive: def.inclusive,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckGreaterThan = /*@__PURE__*/ core.$constructor(\"$ZodCheckGreaterThan\", (inst, def) => {\n $ZodCheck.init(inst, def);\n const origin = numericOriginMap[typeof def.value];\n inst._zod.onattach.push((inst) => {\n const bag = inst._zod.bag;\n const curr = (def.inclusive ? bag.minimum : bag.exclusiveMinimum) ?? Number.NEGATIVE_INFINITY;\n if (def.value > curr) {\n if (def.inclusive)\n bag.minimum = def.value;\n else\n bag.exclusiveMinimum = def.value;\n }\n });\n inst._zod.check = (payload) => {\n if (def.inclusive ? payload.value >= def.value : payload.value > def.value) {\n return;\n }\n payload.issues.push({\n origin,\n code: \"too_small\",\n minimum: typeof def.value === \"object\" ? def.value.getTime() : def.value,\n input: payload.value,\n inclusive: def.inclusive,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckMultipleOf = \n/*@__PURE__*/ core.$constructor(\"$ZodCheckMultipleOf\", (inst, def) => {\n $ZodCheck.init(inst, def);\n inst._zod.onattach.push((inst) => {\n var _a;\n (_a = inst._zod.bag).multipleOf ?? (_a.multipleOf = def.value);\n });\n inst._zod.check = (payload) => {\n if (typeof payload.value !== typeof def.value)\n throw new Error(\"Cannot mix number and bigint in multiple_of check.\");\n const isMultiple = typeof payload.value === \"bigint\"\n ? payload.value % def.value === BigInt(0)\n : util.floatSafeRemainder(payload.value, def.value) === 0;\n if (isMultiple)\n return;\n payload.issues.push({\n origin: typeof payload.value,\n code: \"not_multiple_of\",\n divisor: def.value,\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckNumberFormat = /*@__PURE__*/ core.$constructor(\"$ZodCheckNumberFormat\", (inst, def) => {\n $ZodCheck.init(inst, def); // no format checks\n def.format = def.format || \"float64\";\n const isInt = def.format?.includes(\"int\");\n const origin = isInt ? \"int\" : \"number\";\n const [minimum, maximum] = util.NUMBER_FORMAT_RANGES[def.format];\n inst._zod.onattach.push((inst) => {\n const bag = inst._zod.bag;\n bag.format = def.format;\n bag.minimum = minimum;\n bag.maximum = maximum;\n if (isInt)\n bag.pattern = regexes.integer;\n });\n inst._zod.check = (payload) => {\n const input = payload.value;\n if (isInt) {\n if (!Number.isInteger(input)) {\n // invalid_format issue\n // payload.issues.push({\n // expected: def.format,\n // format: def.format,\n // code: \"invalid_format\",\n // input,\n // inst,\n // });\n // invalid_type issue\n payload.issues.push({\n expected: origin,\n format: def.format,\n code: \"invalid_type\",\n continue: false,\n input,\n inst,\n });\n return;\n // not_multiple_of issue\n // payload.issues.push({\n // code: \"not_multiple_of\",\n // origin: \"number\",\n // input,\n // inst,\n // divisor: 1,\n // });\n }\n if (!Number.isSafeInteger(input)) {\n if (input > 0) {\n // too_big\n payload.issues.push({\n input,\n code: \"too_big\",\n maximum: Number.MAX_SAFE_INTEGER,\n note: \"Integers must be within the safe integer range.\",\n inst,\n origin,\n inclusive: true,\n continue: !def.abort,\n });\n }\n else {\n // too_small\n payload.issues.push({\n input,\n code: \"too_small\",\n minimum: Number.MIN_SAFE_INTEGER,\n note: \"Integers must be within the safe integer range.\",\n inst,\n origin,\n inclusive: true,\n continue: !def.abort,\n });\n }\n return;\n }\n }\n if (input < minimum) {\n payload.issues.push({\n origin: \"number\",\n input,\n code: \"too_small\",\n minimum,\n inclusive: true,\n inst,\n continue: !def.abort,\n });\n }\n if (input > maximum) {\n payload.issues.push({\n origin: \"number\",\n input,\n code: \"too_big\",\n maximum,\n inclusive: true,\n inst,\n continue: !def.abort,\n });\n }\n };\n});\nexport const $ZodCheckBigIntFormat = /*@__PURE__*/ core.$constructor(\"$ZodCheckBigIntFormat\", (inst, def) => {\n $ZodCheck.init(inst, def); // no format checks\n const [minimum, maximum] = util.BIGINT_FORMAT_RANGES[def.format];\n inst._zod.onattach.push((inst) => {\n const bag = inst._zod.bag;\n bag.format = def.format;\n bag.minimum = minimum;\n bag.maximum = maximum;\n });\n inst._zod.check = (payload) => {\n const input = payload.value;\n if (input < minimum) {\n payload.issues.push({\n origin: \"bigint\",\n input,\n code: \"too_small\",\n minimum: minimum,\n inclusive: true,\n inst,\n continue: !def.abort,\n });\n }\n if (input > maximum) {\n payload.issues.push({\n origin: \"bigint\",\n input,\n code: \"too_big\",\n maximum,\n inclusive: true,\n inst,\n continue: !def.abort,\n });\n }\n };\n});\nexport const $ZodCheckMaxSize = /*@__PURE__*/ core.$constructor(\"$ZodCheckMaxSize\", (inst, def) => {\n var _a;\n $ZodCheck.init(inst, def);\n (_a = inst._zod.def).when ?? (_a.when = (payload) => {\n const val = payload.value;\n return !util.nullish(val) && val.size !== undefined;\n });\n inst._zod.onattach.push((inst) => {\n const curr = (inst._zod.bag.maximum ?? Number.POSITIVE_INFINITY);\n if (def.maximum < curr)\n inst._zod.bag.maximum = def.maximum;\n });\n inst._zod.check = (payload) => {\n const input = payload.value;\n const size = input.size;\n if (size <= def.maximum)\n return;\n payload.issues.push({\n origin: util.getSizableOrigin(input),\n code: \"too_big\",\n maximum: def.maximum,\n inclusive: true,\n input,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckMinSize = /*@__PURE__*/ core.$constructor(\"$ZodCheckMinSize\", (inst, def) => {\n var _a;\n $ZodCheck.init(inst, def);\n (_a = inst._zod.def).when ?? (_a.when = (payload) => {\n const val = payload.value;\n return !util.nullish(val) && val.size !== undefined;\n });\n inst._zod.onattach.push((inst) => {\n const curr = (inst._zod.bag.minimum ?? Number.NEGATIVE_INFINITY);\n if (def.minimum > curr)\n inst._zod.bag.minimum = def.minimum;\n });\n inst._zod.check = (payload) => {\n const input = payload.value;\n const size = input.size;\n if (size >= def.minimum)\n return;\n payload.issues.push({\n origin: util.getSizableOrigin(input),\n code: \"too_small\",\n minimum: def.minimum,\n inclusive: true,\n input,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckSizeEquals = /*@__PURE__*/ core.$constructor(\"$ZodCheckSizeEquals\", (inst, def) => {\n var _a;\n $ZodCheck.init(inst, def);\n (_a = inst._zod.def).when ?? (_a.when = (payload) => {\n const val = payload.value;\n return !util.nullish(val) && val.size !== undefined;\n });\n inst._zod.onattach.push((inst) => {\n const bag = inst._zod.bag;\n bag.minimum = def.size;\n bag.maximum = def.size;\n bag.size = def.size;\n });\n inst._zod.check = (payload) => {\n const input = payload.value;\n const size = input.size;\n if (size === def.size)\n return;\n const tooBig = size > def.size;\n payload.issues.push({\n origin: util.getSizableOrigin(input),\n ...(tooBig ? { code: \"too_big\", maximum: def.size } : { code: \"too_small\", minimum: def.size }),\n inclusive: true,\n exact: true,\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckMaxLength = /*@__PURE__*/ core.$constructor(\"$ZodCheckMaxLength\", (inst, def) => {\n var _a;\n $ZodCheck.init(inst, def);\n (_a = inst._zod.def).when ?? (_a.when = (payload) => {\n const val = payload.value;\n return !util.nullish(val) && val.length !== undefined;\n });\n inst._zod.onattach.push((inst) => {\n const curr = (inst._zod.bag.maximum ?? Number.POSITIVE_INFINITY);\n if (def.maximum < curr)\n inst._zod.bag.maximum = def.maximum;\n });\n inst._zod.check = (payload) => {\n const input = payload.value;\n const length = input.length;\n if (length <= def.maximum)\n return;\n const origin = util.getLengthableOrigin(input);\n payload.issues.push({\n origin,\n code: \"too_big\",\n maximum: def.maximum,\n inclusive: true,\n input,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckMinLength = /*@__PURE__*/ core.$constructor(\"$ZodCheckMinLength\", (inst, def) => {\n var _a;\n $ZodCheck.init(inst, def);\n (_a = inst._zod.def).when ?? (_a.when = (payload) => {\n const val = payload.value;\n return !util.nullish(val) && val.length !== undefined;\n });\n inst._zod.onattach.push((inst) => {\n const curr = (inst._zod.bag.minimum ?? Number.NEGATIVE_INFINITY);\n if (def.minimum > curr)\n inst._zod.bag.minimum = def.minimum;\n });\n inst._zod.check = (payload) => {\n const input = payload.value;\n const length = input.length;\n if (length >= def.minimum)\n return;\n const origin = util.getLengthableOrigin(input);\n payload.issues.push({\n origin,\n code: \"too_small\",\n minimum: def.minimum,\n inclusive: true,\n input,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckLengthEquals = /*@__PURE__*/ core.$constructor(\"$ZodCheckLengthEquals\", (inst, def) => {\n var _a;\n $ZodCheck.init(inst, def);\n (_a = inst._zod.def).when ?? (_a.when = (payload) => {\n const val = payload.value;\n return !util.nullish(val) && val.length !== undefined;\n });\n inst._zod.onattach.push((inst) => {\n const bag = inst._zod.bag;\n bag.minimum = def.length;\n bag.maximum = def.length;\n bag.length = def.length;\n });\n inst._zod.check = (payload) => {\n const input = payload.value;\n const length = input.length;\n if (length === def.length)\n return;\n const origin = util.getLengthableOrigin(input);\n const tooBig = length > def.length;\n payload.issues.push({\n origin,\n ...(tooBig ? { code: \"too_big\", maximum: def.length } : { code: \"too_small\", minimum: def.length }),\n inclusive: true,\n exact: true,\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckStringFormat = /*@__PURE__*/ core.$constructor(\"$ZodCheckStringFormat\", (inst, def) => {\n var _a, _b;\n $ZodCheck.init(inst, def);\n inst._zod.onattach.push((inst) => {\n const bag = inst._zod.bag;\n bag.format = def.format;\n if (def.pattern) {\n bag.patterns ?? (bag.patterns = new Set());\n bag.patterns.add(def.pattern);\n }\n });\n if (def.pattern)\n (_a = inst._zod).check ?? (_a.check = (payload) => {\n def.pattern.lastIndex = 0;\n if (def.pattern.test(payload.value))\n return;\n payload.issues.push({\n origin: \"string\",\n code: \"invalid_format\",\n format: def.format,\n input: payload.value,\n ...(def.pattern ? { pattern: def.pattern.toString() } : {}),\n inst,\n continue: !def.abort,\n });\n });\n else\n (_b = inst._zod).check ?? (_b.check = () => { });\n});\nexport const $ZodCheckRegex = /*@__PURE__*/ core.$constructor(\"$ZodCheckRegex\", (inst, def) => {\n $ZodCheckStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n def.pattern.lastIndex = 0;\n if (def.pattern.test(payload.value))\n return;\n payload.issues.push({\n origin: \"string\",\n code: \"invalid_format\",\n format: \"regex\",\n input: payload.value,\n pattern: def.pattern.toString(),\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckLowerCase = /*@__PURE__*/ core.$constructor(\"$ZodCheckLowerCase\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.lowercase);\n $ZodCheckStringFormat.init(inst, def);\n});\nexport const $ZodCheckUpperCase = /*@__PURE__*/ core.$constructor(\"$ZodCheckUpperCase\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.uppercase);\n $ZodCheckStringFormat.init(inst, def);\n});\nexport const $ZodCheckIncludes = /*@__PURE__*/ core.$constructor(\"$ZodCheckIncludes\", (inst, def) => {\n $ZodCheck.init(inst, def);\n const escapedRegex = util.escapeRegex(def.includes);\n const pattern = new RegExp(typeof def.position === \"number\" ? `^.{${def.position}}${escapedRegex}` : escapedRegex);\n def.pattern = pattern;\n inst._zod.onattach.push((inst) => {\n const bag = inst._zod.bag;\n bag.patterns ?? (bag.patterns = new Set());\n bag.patterns.add(pattern);\n });\n inst._zod.check = (payload) => {\n if (payload.value.includes(def.includes, def.position))\n return;\n payload.issues.push({\n origin: \"string\",\n code: \"invalid_format\",\n format: \"includes\",\n includes: def.includes,\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckStartsWith = /*@__PURE__*/ core.$constructor(\"$ZodCheckStartsWith\", (inst, def) => {\n $ZodCheck.init(inst, def);\n const pattern = new RegExp(`^${util.escapeRegex(def.prefix)}.*`);\n def.pattern ?? (def.pattern = pattern);\n inst._zod.onattach.push((inst) => {\n const bag = inst._zod.bag;\n bag.patterns ?? (bag.patterns = new Set());\n bag.patterns.add(pattern);\n });\n inst._zod.check = (payload) => {\n if (payload.value.startsWith(def.prefix))\n return;\n payload.issues.push({\n origin: \"string\",\n code: \"invalid_format\",\n format: \"starts_with\",\n prefix: def.prefix,\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckEndsWith = /*@__PURE__*/ core.$constructor(\"$ZodCheckEndsWith\", (inst, def) => {\n $ZodCheck.init(inst, def);\n const pattern = new RegExp(`.*${util.escapeRegex(def.suffix)}$`);\n def.pattern ?? (def.pattern = pattern);\n inst._zod.onattach.push((inst) => {\n const bag = inst._zod.bag;\n bag.patterns ?? (bag.patterns = new Set());\n bag.patterns.add(pattern);\n });\n inst._zod.check = (payload) => {\n if (payload.value.endsWith(def.suffix))\n return;\n payload.issues.push({\n origin: \"string\",\n code: \"invalid_format\",\n format: \"ends_with\",\n suffix: def.suffix,\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n };\n});\n///////////////////////////////////\n///// $ZodCheckProperty /////\n///////////////////////////////////\nfunction handleCheckPropertyResult(result, payload, property) {\n if (result.issues.length) {\n payload.issues.push(...util.prefixIssues(property, result.issues));\n }\n}\nexport const $ZodCheckProperty = /*@__PURE__*/ core.$constructor(\"$ZodCheckProperty\", (inst, def) => {\n $ZodCheck.init(inst, def);\n inst._zod.check = (payload) => {\n const result = def.schema._zod.run({\n value: payload.value[def.property],\n issues: [],\n }, {});\n if (result instanceof Promise) {\n return result.then((result) => handleCheckPropertyResult(result, payload, def.property));\n }\n handleCheckPropertyResult(result, payload, def.property);\n return;\n };\n});\nexport const $ZodCheckMimeType = /*@__PURE__*/ core.$constructor(\"$ZodCheckMimeType\", (inst, def) => {\n $ZodCheck.init(inst, def);\n const mimeSet = new Set(def.mime);\n inst._zod.onattach.push((inst) => {\n inst._zod.bag.mime = def.mime;\n });\n inst._zod.check = (payload) => {\n if (mimeSet.has(payload.value.type))\n return;\n payload.issues.push({\n code: \"invalid_value\",\n values: def.mime,\n input: payload.value.type,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCheckOverwrite = /*@__PURE__*/ core.$constructor(\"$ZodCheckOverwrite\", (inst, def) => {\n $ZodCheck.init(inst, def);\n inst._zod.check = (payload) => {\n payload.value = def.tx(payload.value);\n };\n});\n","export class Doc {\n constructor(args = []) {\n this.content = [];\n this.indent = 0;\n if (this)\n this.args = args;\n }\n indented(fn) {\n this.indent += 1;\n fn(this);\n this.indent -= 1;\n }\n write(arg) {\n if (typeof arg === \"function\") {\n arg(this, { execution: \"sync\" });\n arg(this, { execution: \"async\" });\n return;\n }\n const content = arg;\n const lines = content.split(\"\\n\").filter((x) => x);\n const minIndent = Math.min(...lines.map((x) => x.length - x.trimStart().length));\n const dedented = lines.map((x) => x.slice(minIndent)).map((x) => \" \".repeat(this.indent * 2) + x);\n for (const line of dedented) {\n this.content.push(line);\n }\n }\n compile() {\n const F = Function;\n const args = this?.args;\n const content = this?.content ?? [``];\n const lines = [...content.map((x) => ` ${x}`)];\n // console.log(lines.join(\"\\n\"));\n return new F(...args, lines.join(\"\\n\"));\n }\n}\n","export const version = {\n major: 4,\n minor: 3,\n patch: 6,\n};\n","import * as checks from \"./checks.js\";\nimport * as core from \"./core.js\";\nimport { Doc } from \"./doc.js\";\nimport { parse, parseAsync, safeParse, safeParseAsync } from \"./parse.js\";\nimport * as regexes from \"./regexes.js\";\nimport * as util from \"./util.js\";\nimport { version } from \"./versions.js\";\nexport const $ZodType = /*@__PURE__*/ core.$constructor(\"$ZodType\", (inst, def) => {\n var _a;\n inst ?? (inst = {});\n inst._zod.def = def; // set _def property\n inst._zod.bag = inst._zod.bag || {}; // initialize _bag object\n inst._zod.version = version;\n const checks = [...(inst._zod.def.checks ?? [])];\n // if inst is itself a checks.$ZodCheck, run it as a check\n if (inst._zod.traits.has(\"$ZodCheck\")) {\n checks.unshift(inst);\n }\n for (const ch of checks) {\n for (const fn of ch._zod.onattach) {\n fn(inst);\n }\n }\n if (checks.length === 0) {\n // deferred initializer\n // inst._zod.parse is not yet defined\n (_a = inst._zod).deferred ?? (_a.deferred = []);\n inst._zod.deferred?.push(() => {\n inst._zod.run = inst._zod.parse;\n });\n }\n else {\n const runChecks = (payload, checks, ctx) => {\n let isAborted = util.aborted(payload);\n let asyncResult;\n for (const ch of checks) {\n if (ch._zod.def.when) {\n const shouldRun = ch._zod.def.when(payload);\n if (!shouldRun)\n continue;\n }\n else if (isAborted) {\n continue;\n }\n const currLen = payload.issues.length;\n const _ = ch._zod.check(payload);\n if (_ instanceof Promise && ctx?.async === false) {\n throw new core.$ZodAsyncError();\n }\n if (asyncResult || _ instanceof Promise) {\n asyncResult = (asyncResult ?? Promise.resolve()).then(async () => {\n await _;\n const nextLen = payload.issues.length;\n if (nextLen === currLen)\n return;\n if (!isAborted)\n isAborted = util.aborted(payload, currLen);\n });\n }\n else {\n const nextLen = payload.issues.length;\n if (nextLen === currLen)\n continue;\n if (!isAborted)\n isAborted = util.aborted(payload, currLen);\n }\n }\n if (asyncResult) {\n return asyncResult.then(() => {\n return payload;\n });\n }\n return payload;\n };\n const handleCanaryResult = (canary, payload, ctx) => {\n // abort if the canary is aborted\n if (util.aborted(canary)) {\n canary.aborted = true;\n return canary;\n }\n // run checks first, then\n const checkResult = runChecks(payload, checks, ctx);\n if (checkResult instanceof Promise) {\n if (ctx.async === false)\n throw new core.$ZodAsyncError();\n return checkResult.then((checkResult) => inst._zod.parse(checkResult, ctx));\n }\n return inst._zod.parse(checkResult, ctx);\n };\n inst._zod.run = (payload, ctx) => {\n if (ctx.skipChecks) {\n return inst._zod.parse(payload, ctx);\n }\n if (ctx.direction === \"backward\") {\n // run canary\n // initial pass (no checks)\n const canary = inst._zod.parse({ value: payload.value, issues: [] }, { ...ctx, skipChecks: true });\n if (canary instanceof Promise) {\n return canary.then((canary) => {\n return handleCanaryResult(canary, payload, ctx);\n });\n }\n return handleCanaryResult(canary, payload, ctx);\n }\n // forward\n const result = inst._zod.parse(payload, ctx);\n if (result instanceof Promise) {\n if (ctx.async === false)\n throw new core.$ZodAsyncError();\n return result.then((result) => runChecks(result, checks, ctx));\n }\n return runChecks(result, checks, ctx);\n };\n }\n // Lazy initialize ~standard to avoid creating objects for every schema\n util.defineLazy(inst, \"~standard\", () => ({\n validate: (value) => {\n try {\n const r = safeParse(inst, value);\n return r.success ? { value: r.data } : { issues: r.error?.issues };\n }\n catch (_) {\n return safeParseAsync(inst, value).then((r) => (r.success ? { value: r.data } : { issues: r.error?.issues }));\n }\n },\n vendor: \"zod\",\n version: 1,\n }));\n});\nexport { clone } from \"./util.js\";\nexport const $ZodString = /*@__PURE__*/ core.$constructor(\"$ZodString\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.pattern = [...(inst?._zod.bag?.patterns ?? [])].pop() ?? regexes.string(inst._zod.bag);\n inst._zod.parse = (payload, _) => {\n if (def.coerce)\n try {\n payload.value = String(payload.value);\n }\n catch (_) { }\n if (typeof payload.value === \"string\")\n return payload;\n payload.issues.push({\n expected: \"string\",\n code: \"invalid_type\",\n input: payload.value,\n inst,\n });\n return payload;\n };\n});\nexport const $ZodStringFormat = /*@__PURE__*/ core.$constructor(\"$ZodStringFormat\", (inst, def) => {\n // check initialization must come first\n checks.$ZodCheckStringFormat.init(inst, def);\n $ZodString.init(inst, def);\n});\nexport const $ZodGUID = /*@__PURE__*/ core.$constructor(\"$ZodGUID\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.guid);\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodUUID = /*@__PURE__*/ core.$constructor(\"$ZodUUID\", (inst, def) => {\n if (def.version) {\n const versionMap = {\n v1: 1,\n v2: 2,\n v3: 3,\n v4: 4,\n v5: 5,\n v6: 6,\n v7: 7,\n v8: 8,\n };\n const v = versionMap[def.version];\n if (v === undefined)\n throw new Error(`Invalid UUID version: \"${def.version}\"`);\n def.pattern ?? (def.pattern = regexes.uuid(v));\n }\n else\n def.pattern ?? (def.pattern = regexes.uuid());\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodEmail = /*@__PURE__*/ core.$constructor(\"$ZodEmail\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.email);\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodURL = /*@__PURE__*/ core.$constructor(\"$ZodURL\", (inst, def) => {\n $ZodStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n try {\n // Trim whitespace from input\n const trimmed = payload.value.trim();\n // @ts-ignore\n const url = new URL(trimmed);\n if (def.hostname) {\n def.hostname.lastIndex = 0;\n if (!def.hostname.test(url.hostname)) {\n payload.issues.push({\n code: \"invalid_format\",\n format: \"url\",\n note: \"Invalid hostname\",\n pattern: def.hostname.source,\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n }\n }\n if (def.protocol) {\n def.protocol.lastIndex = 0;\n if (!def.protocol.test(url.protocol.endsWith(\":\") ? url.protocol.slice(0, -1) : url.protocol)) {\n payload.issues.push({\n code: \"invalid_format\",\n format: \"url\",\n note: \"Invalid protocol\",\n pattern: def.protocol.source,\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n }\n }\n // Set the output value based on normalize flag\n if (def.normalize) {\n // Use normalized URL\n payload.value = url.href;\n }\n else {\n // Preserve the original input (trimmed)\n payload.value = trimmed;\n }\n return;\n }\n catch (_) {\n payload.issues.push({\n code: \"invalid_format\",\n format: \"url\",\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n }\n };\n});\nexport const $ZodEmoji = /*@__PURE__*/ core.$constructor(\"$ZodEmoji\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.emoji());\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodNanoID = /*@__PURE__*/ core.$constructor(\"$ZodNanoID\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.nanoid);\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodCUID = /*@__PURE__*/ core.$constructor(\"$ZodCUID\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.cuid);\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodCUID2 = /*@__PURE__*/ core.$constructor(\"$ZodCUID2\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.cuid2);\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodULID = /*@__PURE__*/ core.$constructor(\"$ZodULID\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.ulid);\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodXID = /*@__PURE__*/ core.$constructor(\"$ZodXID\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.xid);\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodKSUID = /*@__PURE__*/ core.$constructor(\"$ZodKSUID\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.ksuid);\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodISODateTime = /*@__PURE__*/ core.$constructor(\"$ZodISODateTime\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.datetime(def));\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodISODate = /*@__PURE__*/ core.$constructor(\"$ZodISODate\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.date);\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodISOTime = /*@__PURE__*/ core.$constructor(\"$ZodISOTime\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.time(def));\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodISODuration = /*@__PURE__*/ core.$constructor(\"$ZodISODuration\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.duration);\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodIPv4 = /*@__PURE__*/ core.$constructor(\"$ZodIPv4\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.ipv4);\n $ZodStringFormat.init(inst, def);\n inst._zod.bag.format = `ipv4`;\n});\nexport const $ZodIPv6 = /*@__PURE__*/ core.$constructor(\"$ZodIPv6\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.ipv6);\n $ZodStringFormat.init(inst, def);\n inst._zod.bag.format = `ipv6`;\n inst._zod.check = (payload) => {\n try {\n // @ts-ignore\n new URL(`http://[${payload.value}]`);\n // return;\n }\n catch {\n payload.issues.push({\n code: \"invalid_format\",\n format: \"ipv6\",\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n }\n };\n});\nexport const $ZodMAC = /*@__PURE__*/ core.$constructor(\"$ZodMAC\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.mac(def.delimiter));\n $ZodStringFormat.init(inst, def);\n inst._zod.bag.format = `mac`;\n});\nexport const $ZodCIDRv4 = /*@__PURE__*/ core.$constructor(\"$ZodCIDRv4\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.cidrv4);\n $ZodStringFormat.init(inst, def);\n});\nexport const $ZodCIDRv6 = /*@__PURE__*/ core.$constructor(\"$ZodCIDRv6\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.cidrv6); // not used for validation\n $ZodStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n const parts = payload.value.split(\"/\");\n try {\n if (parts.length !== 2)\n throw new Error();\n const [address, prefix] = parts;\n if (!prefix)\n throw new Error();\n const prefixNum = Number(prefix);\n if (`${prefixNum}` !== prefix)\n throw new Error();\n if (prefixNum < 0 || prefixNum > 128)\n throw new Error();\n // @ts-ignore\n new URL(`http://[${address}]`);\n }\n catch {\n payload.issues.push({\n code: \"invalid_format\",\n format: \"cidrv6\",\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n }\n };\n});\n////////////////////////////// ZodBase64 //////////////////////////////\nexport function isValidBase64(data) {\n if (data === \"\")\n return true;\n if (data.length % 4 !== 0)\n return false;\n try {\n // @ts-ignore\n atob(data);\n return true;\n }\n catch {\n return false;\n }\n}\nexport const $ZodBase64 = /*@__PURE__*/ core.$constructor(\"$ZodBase64\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.base64);\n $ZodStringFormat.init(inst, def);\n inst._zod.bag.contentEncoding = \"base64\";\n inst._zod.check = (payload) => {\n if (isValidBase64(payload.value))\n return;\n payload.issues.push({\n code: \"invalid_format\",\n format: \"base64\",\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n };\n});\n////////////////////////////// ZodBase64 //////////////////////////////\nexport function isValidBase64URL(data) {\n if (!regexes.base64url.test(data))\n return false;\n const base64 = data.replace(/[-_]/g, (c) => (c === \"-\" ? \"+\" : \"/\"));\n const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, \"=\");\n return isValidBase64(padded);\n}\nexport const $ZodBase64URL = /*@__PURE__*/ core.$constructor(\"$ZodBase64URL\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.base64url);\n $ZodStringFormat.init(inst, def);\n inst._zod.bag.contentEncoding = \"base64url\";\n inst._zod.check = (payload) => {\n if (isValidBase64URL(payload.value))\n return;\n payload.issues.push({\n code: \"invalid_format\",\n format: \"base64url\",\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodE164 = /*@__PURE__*/ core.$constructor(\"$ZodE164\", (inst, def) => {\n def.pattern ?? (def.pattern = regexes.e164);\n $ZodStringFormat.init(inst, def);\n});\n////////////////////////////// ZodJWT //////////////////////////////\nexport function isValidJWT(token, algorithm = null) {\n try {\n const tokensParts = token.split(\".\");\n if (tokensParts.length !== 3)\n return false;\n const [header] = tokensParts;\n if (!header)\n return false;\n // @ts-ignore\n const parsedHeader = JSON.parse(atob(header));\n if (\"typ\" in parsedHeader && parsedHeader?.typ !== \"JWT\")\n return false;\n if (!parsedHeader.alg)\n return false;\n if (algorithm && (!(\"alg\" in parsedHeader) || parsedHeader.alg !== algorithm))\n return false;\n return true;\n }\n catch {\n return false;\n }\n}\nexport const $ZodJWT = /*@__PURE__*/ core.$constructor(\"$ZodJWT\", (inst, def) => {\n $ZodStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n if (isValidJWT(payload.value, def.alg))\n return;\n payload.issues.push({\n code: \"invalid_format\",\n format: \"jwt\",\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodCustomStringFormat = /*@__PURE__*/ core.$constructor(\"$ZodCustomStringFormat\", (inst, def) => {\n $ZodStringFormat.init(inst, def);\n inst._zod.check = (payload) => {\n if (def.fn(payload.value))\n return;\n payload.issues.push({\n code: \"invalid_format\",\n format: def.format,\n input: payload.value,\n inst,\n continue: !def.abort,\n });\n };\n});\nexport const $ZodNumber = /*@__PURE__*/ core.$constructor(\"$ZodNumber\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.pattern = inst._zod.bag.pattern ?? regexes.number;\n inst._zod.parse = (payload, _ctx) => {\n if (def.coerce)\n try {\n payload.value = Number(payload.value);\n }\n catch (_) { }\n const input = payload.value;\n if (typeof input === \"number\" && !Number.isNaN(input) && Number.isFinite(input)) {\n return payload;\n }\n const received = typeof input === \"number\"\n ? Number.isNaN(input)\n ? \"NaN\"\n : !Number.isFinite(input)\n ? \"Infinity\"\n : undefined\n : undefined;\n payload.issues.push({\n expected: \"number\",\n code: \"invalid_type\",\n input,\n inst,\n ...(received ? { received } : {}),\n });\n return payload;\n };\n});\nexport const $ZodNumberFormat = /*@__PURE__*/ core.$constructor(\"$ZodNumberFormat\", (inst, def) => {\n checks.$ZodCheckNumberFormat.init(inst, def);\n $ZodNumber.init(inst, def); // no format checks\n});\nexport const $ZodBoolean = /*@__PURE__*/ core.$constructor(\"$ZodBoolean\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.pattern = regexes.boolean;\n inst._zod.parse = (payload, _ctx) => {\n if (def.coerce)\n try {\n payload.value = Boolean(payload.value);\n }\n catch (_) { }\n const input = payload.value;\n if (typeof input === \"boolean\")\n return payload;\n payload.issues.push({\n expected: \"boolean\",\n code: \"invalid_type\",\n input,\n inst,\n });\n return payload;\n };\n});\nexport const $ZodBigInt = /*@__PURE__*/ core.$constructor(\"$ZodBigInt\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.pattern = regexes.bigint;\n inst._zod.parse = (payload, _ctx) => {\n if (def.coerce)\n try {\n payload.value = BigInt(payload.value);\n }\n catch (_) { }\n if (typeof payload.value === \"bigint\")\n return payload;\n payload.issues.push({\n expected: \"bigint\",\n code: \"invalid_type\",\n input: payload.value,\n inst,\n });\n return payload;\n };\n});\nexport const $ZodBigIntFormat = /*@__PURE__*/ core.$constructor(\"$ZodBigIntFormat\", (inst, def) => {\n checks.$ZodCheckBigIntFormat.init(inst, def);\n $ZodBigInt.init(inst, def); // no format checks\n});\nexport const $ZodSymbol = /*@__PURE__*/ core.$constructor(\"$ZodSymbol\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, _ctx) => {\n const input = payload.value;\n if (typeof input === \"symbol\")\n return payload;\n payload.issues.push({\n expected: \"symbol\",\n code: \"invalid_type\",\n input,\n inst,\n });\n return payload;\n };\n});\nexport const $ZodUndefined = /*@__PURE__*/ core.$constructor(\"$ZodUndefined\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.pattern = regexes.undefined;\n inst._zod.values = new Set([undefined]);\n inst._zod.optin = \"optional\";\n inst._zod.optout = \"optional\";\n inst._zod.parse = (payload, _ctx) => {\n const input = payload.value;\n if (typeof input === \"undefined\")\n return payload;\n payload.issues.push({\n expected: \"undefined\",\n code: \"invalid_type\",\n input,\n inst,\n });\n return payload;\n };\n});\nexport const $ZodNull = /*@__PURE__*/ core.$constructor(\"$ZodNull\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.pattern = regexes.null;\n inst._zod.values = new Set([null]);\n inst._zod.parse = (payload, _ctx) => {\n const input = payload.value;\n if (input === null)\n return payload;\n payload.issues.push({\n expected: \"null\",\n code: \"invalid_type\",\n input,\n inst,\n });\n return payload;\n };\n});\nexport const $ZodAny = /*@__PURE__*/ core.$constructor(\"$ZodAny\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload) => payload;\n});\nexport const $ZodUnknown = /*@__PURE__*/ core.$constructor(\"$ZodUnknown\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload) => payload;\n});\nexport const $ZodNever = /*@__PURE__*/ core.$constructor(\"$ZodNever\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, _ctx) => {\n payload.issues.push({\n expected: \"never\",\n code: \"invalid_type\",\n input: payload.value,\n inst,\n });\n return payload;\n };\n});\nexport const $ZodVoid = /*@__PURE__*/ core.$constructor(\"$ZodVoid\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, _ctx) => {\n const input = payload.value;\n if (typeof input === \"undefined\")\n return payload;\n payload.issues.push({\n expected: \"void\",\n code: \"invalid_type\",\n input,\n inst,\n });\n return payload;\n };\n});\nexport const $ZodDate = /*@__PURE__*/ core.$constructor(\"$ZodDate\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, _ctx) => {\n if (def.coerce) {\n try {\n payload.value = new Date(payload.value);\n }\n catch (_err) { }\n }\n const input = payload.value;\n const isDate = input instanceof Date;\n const isValidDate = isDate && !Number.isNaN(input.getTime());\n if (isValidDate)\n return payload;\n payload.issues.push({\n expected: \"date\",\n code: \"invalid_type\",\n input,\n ...(isDate ? { received: \"Invalid Date\" } : {}),\n inst,\n });\n return payload;\n };\n});\nfunction handleArrayResult(result, final, index) {\n if (result.issues.length) {\n final.issues.push(...util.prefixIssues(index, result.issues));\n }\n final.value[index] = result.value;\n}\nexport const $ZodArray = /*@__PURE__*/ core.$constructor(\"$ZodArray\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, ctx) => {\n const input = payload.value;\n if (!Array.isArray(input)) {\n payload.issues.push({\n expected: \"array\",\n code: \"invalid_type\",\n input,\n inst,\n });\n return payload;\n }\n payload.value = Array(input.length);\n const proms = [];\n for (let i = 0; i < input.length; i++) {\n const item = input[i];\n const result = def.element._zod.run({\n value: item,\n issues: [],\n }, ctx);\n if (result instanceof Promise) {\n proms.push(result.then((result) => handleArrayResult(result, payload, i)));\n }\n else {\n handleArrayResult(result, payload, i);\n }\n }\n if (proms.length) {\n return Promise.all(proms).then(() => payload);\n }\n return payload; //handleArrayResultsAsync(parseResults, final);\n };\n});\nfunction handlePropertyResult(result, final, key, input, isOptionalOut) {\n if (result.issues.length) {\n // For optional-out schemas, ignore errors on absent keys\n if (isOptionalOut && !(key in input)) {\n return;\n }\n final.issues.push(...util.prefixIssues(key, result.issues));\n }\n if (result.value === undefined) {\n if (key in input) {\n final.value[key] = undefined;\n }\n }\n else {\n final.value[key] = result.value;\n }\n}\nfunction normalizeDef(def) {\n const keys = Object.keys(def.shape);\n for (const k of keys) {\n if (!def.shape?.[k]?._zod?.traits?.has(\"$ZodType\")) {\n throw new Error(`Invalid element at key \"${k}\": expected a Zod schema`);\n }\n }\n const okeys = util.optionalKeys(def.shape);\n return {\n ...def,\n keys,\n keySet: new Set(keys),\n numKeys: keys.length,\n optionalKeys: new Set(okeys),\n };\n}\nfunction handleCatchall(proms, input, payload, ctx, def, inst) {\n const unrecognized = [];\n // iterate over input keys\n const keySet = def.keySet;\n const _catchall = def.catchall._zod;\n const t = _catchall.def.type;\n const isOptionalOut = _catchall.optout === \"optional\";\n for (const key in input) {\n if (keySet.has(key))\n continue;\n if (t === \"never\") {\n unrecognized.push(key);\n continue;\n }\n const r = _catchall.run({ value: input[key], issues: [] }, ctx);\n if (r instanceof Promise) {\n proms.push(r.then((r) => handlePropertyResult(r, payload, key, input, isOptionalOut)));\n }\n else {\n handlePropertyResult(r, payload, key, input, isOptionalOut);\n }\n }\n if (unrecognized.length) {\n payload.issues.push({\n code: \"unrecognized_keys\",\n keys: unrecognized,\n input,\n inst,\n });\n }\n if (!proms.length)\n return payload;\n return Promise.all(proms).then(() => {\n return payload;\n });\n}\nexport const $ZodObject = /*@__PURE__*/ core.$constructor(\"$ZodObject\", (inst, def) => {\n // requires cast because technically $ZodObject doesn't extend\n $ZodType.init(inst, def);\n // const sh = def.shape;\n const desc = Object.getOwnPropertyDescriptor(def, \"shape\");\n if (!desc?.get) {\n const sh = def.shape;\n Object.defineProperty(def, \"shape\", {\n get: () => {\n const newSh = { ...sh };\n Object.defineProperty(def, \"shape\", {\n value: newSh,\n });\n return newSh;\n },\n });\n }\n const _normalized = util.cached(() => normalizeDef(def));\n util.defineLazy(inst._zod, \"propValues\", () => {\n const shape = def.shape;\n const propValues = {};\n for (const key in shape) {\n const field = shape[key]._zod;\n if (field.values) {\n propValues[key] ?? (propValues[key] = new Set());\n for (const v of field.values)\n propValues[key].add(v);\n }\n }\n return propValues;\n });\n const isObject = util.isObject;\n const catchall = def.catchall;\n let value;\n inst._zod.parse = (payload, ctx) => {\n value ?? (value = _normalized.value);\n const input = payload.value;\n if (!isObject(input)) {\n payload.issues.push({\n expected: \"object\",\n code: \"invalid_type\",\n input,\n inst,\n });\n return payload;\n }\n payload.value = {};\n const proms = [];\n const shape = value.shape;\n for (const key of value.keys) {\n const el = shape[key];\n const isOptionalOut = el._zod.optout === \"optional\";\n const r = el._zod.run({ value: input[key], issues: [] }, ctx);\n if (r instanceof Promise) {\n proms.push(r.then((r) => handlePropertyResult(r, payload, key, input, isOptionalOut)));\n }\n else {\n handlePropertyResult(r, payload, key, input, isOptionalOut);\n }\n }\n if (!catchall) {\n return proms.length ? Promise.all(proms).then(() => payload) : payload;\n }\n return handleCatchall(proms, input, payload, ctx, _normalized.value, inst);\n };\n});\nexport const $ZodObjectJIT = /*@__PURE__*/ core.$constructor(\"$ZodObjectJIT\", (inst, def) => {\n // requires cast because technically $ZodObject doesn't extend\n $ZodObject.init(inst, def);\n const superParse = inst._zod.parse;\n const _normalized = util.cached(() => normalizeDef(def));\n const generateFastpass = (shape) => {\n const doc = new Doc([\"shape\", \"payload\", \"ctx\"]);\n const normalized = _normalized.value;\n const parseStr = (key) => {\n const k = util.esc(key);\n return `shape[${k}]._zod.run({ value: input[${k}], issues: [] }, ctx)`;\n };\n doc.write(`const input = payload.value;`);\n const ids = Object.create(null);\n let counter = 0;\n for (const key of normalized.keys) {\n ids[key] = `key_${counter++}`;\n }\n // A: preserve key order {\n doc.write(`const newResult = {};`);\n for (const key of normalized.keys) {\n const id = ids[key];\n const k = util.esc(key);\n const schema = shape[key];\n const isOptionalOut = schema?._zod?.optout === \"optional\";\n doc.write(`const ${id} = ${parseStr(key)};`);\n if (isOptionalOut) {\n // For optional-out schemas, ignore errors on absent keys\n doc.write(`\n if (${id}.issues.length) {\n if (${k} in input) {\n payload.issues = payload.issues.concat(${id}.issues.map(iss => ({\n ...iss,\n path: iss.path ? [${k}, ...iss.path] : [${k}]\n })));\n }\n }\n \n if (${id}.value === undefined) {\n if (${k} in input) {\n newResult[${k}] = undefined;\n }\n } else {\n newResult[${k}] = ${id}.value;\n }\n \n `);\n }\n else {\n doc.write(`\n if (${id}.issues.length) {\n payload.issues = payload.issues.concat(${id}.issues.map(iss => ({\n ...iss,\n path: iss.path ? [${k}, ...iss.path] : [${k}]\n })));\n }\n \n if (${id}.value === undefined) {\n if (${k} in input) {\n newResult[${k}] = undefined;\n }\n } else {\n newResult[${k}] = ${id}.value;\n }\n \n `);\n }\n }\n doc.write(`payload.value = newResult;`);\n doc.write(`return payload;`);\n const fn = doc.compile();\n return (payload, ctx) => fn(shape, payload, ctx);\n };\n let fastpass;\n const isObject = util.isObject;\n const jit = !core.globalConfig.jitless;\n const allowsEval = util.allowsEval;\n const fastEnabled = jit && allowsEval.value; // && !def.catchall;\n const catchall = def.catchall;\n let value;\n inst._zod.parse = (payload, ctx) => {\n value ?? (value = _normalized.value);\n const input = payload.value;\n if (!isObject(input)) {\n payload.issues.push({\n expected: \"object\",\n code: \"invalid_type\",\n input,\n inst,\n });\n return payload;\n }\n if (jit && fastEnabled && ctx?.async === false && ctx.jitless !== true) {\n // always synchronous\n if (!fastpass)\n fastpass = generateFastpass(def.shape);\n payload = fastpass(payload, ctx);\n if (!catchall)\n return payload;\n return handleCatchall([], input, payload, ctx, value, inst);\n }\n return superParse(payload, ctx);\n };\n});\nfunction handleUnionResults(results, final, inst, ctx) {\n for (const result of results) {\n if (result.issues.length === 0) {\n final.value = result.value;\n return final;\n }\n }\n const nonaborted = results.filter((r) => !util.aborted(r));\n if (nonaborted.length === 1) {\n final.value = nonaborted[0].value;\n return nonaborted[0];\n }\n final.issues.push({\n code: \"invalid_union\",\n input: final.value,\n inst,\n errors: results.map((result) => result.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config()))),\n });\n return final;\n}\nexport const $ZodUnion = /*@__PURE__*/ core.$constructor(\"$ZodUnion\", (inst, def) => {\n $ZodType.init(inst, def);\n util.defineLazy(inst._zod, \"optin\", () => def.options.some((o) => o._zod.optin === \"optional\") ? \"optional\" : undefined);\n util.defineLazy(inst._zod, \"optout\", () => def.options.some((o) => o._zod.optout === \"optional\") ? \"optional\" : undefined);\n util.defineLazy(inst._zod, \"values\", () => {\n if (def.options.every((o) => o._zod.values)) {\n return new Set(def.options.flatMap((option) => Array.from(option._zod.values)));\n }\n return undefined;\n });\n util.defineLazy(inst._zod, \"pattern\", () => {\n if (def.options.every((o) => o._zod.pattern)) {\n const patterns = def.options.map((o) => o._zod.pattern);\n return new RegExp(`^(${patterns.map((p) => util.cleanRegex(p.source)).join(\"|\")})$`);\n }\n return undefined;\n });\n const single = def.options.length === 1;\n const first = def.options[0]._zod.run;\n inst._zod.parse = (payload, ctx) => {\n if (single) {\n return first(payload, ctx);\n }\n let async = false;\n const results = [];\n for (const option of def.options) {\n const result = option._zod.run({\n value: payload.value,\n issues: [],\n }, ctx);\n if (result instanceof Promise) {\n results.push(result);\n async = true;\n }\n else {\n if (result.issues.length === 0)\n return result;\n results.push(result);\n }\n }\n if (!async)\n return handleUnionResults(results, payload, inst, ctx);\n return Promise.all(results).then((results) => {\n return handleUnionResults(results, payload, inst, ctx);\n });\n };\n});\nfunction handleExclusiveUnionResults(results, final, inst, ctx) {\n const successes = results.filter((r) => r.issues.length === 0);\n if (successes.length === 1) {\n final.value = successes[0].value;\n return final;\n }\n if (successes.length === 0) {\n // No matches - same as regular union\n final.issues.push({\n code: \"invalid_union\",\n input: final.value,\n inst,\n errors: results.map((result) => result.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config()))),\n });\n }\n else {\n // Multiple matches - exclusive union failure\n final.issues.push({\n code: \"invalid_union\",\n input: final.value,\n inst,\n errors: [],\n inclusive: false,\n });\n }\n return final;\n}\nexport const $ZodXor = /*@__PURE__*/ core.$constructor(\"$ZodXor\", (inst, def) => {\n $ZodUnion.init(inst, def);\n def.inclusive = false;\n const single = def.options.length === 1;\n const first = def.options[0]._zod.run;\n inst._zod.parse = (payload, ctx) => {\n if (single) {\n return first(payload, ctx);\n }\n let async = false;\n const results = [];\n for (const option of def.options) {\n const result = option._zod.run({\n value: payload.value,\n issues: [],\n }, ctx);\n if (result instanceof Promise) {\n results.push(result);\n async = true;\n }\n else {\n results.push(result);\n }\n }\n if (!async)\n return handleExclusiveUnionResults(results, payload, inst, ctx);\n return Promise.all(results).then((results) => {\n return handleExclusiveUnionResults(results, payload, inst, ctx);\n });\n };\n});\nexport const $ZodDiscriminatedUnion = \n/*@__PURE__*/\ncore.$constructor(\"$ZodDiscriminatedUnion\", (inst, def) => {\n def.inclusive = false;\n $ZodUnion.init(inst, def);\n const _super = inst._zod.parse;\n util.defineLazy(inst._zod, \"propValues\", () => {\n const propValues = {};\n for (const option of def.options) {\n const pv = option._zod.propValues;\n if (!pv || Object.keys(pv).length === 0)\n throw new Error(`Invalid discriminated union option at index \"${def.options.indexOf(option)}\"`);\n for (const [k, v] of Object.entries(pv)) {\n if (!propValues[k])\n propValues[k] = new Set();\n for (const val of v) {\n propValues[k].add(val);\n }\n }\n }\n return propValues;\n });\n const disc = util.cached(() => {\n const opts = def.options;\n const map = new Map();\n for (const o of opts) {\n const values = o._zod.propValues?.[def.discriminator];\n if (!values || values.size === 0)\n throw new Error(`Invalid discriminated union option at index \"${def.options.indexOf(o)}\"`);\n for (const v of values) {\n if (map.has(v)) {\n throw new Error(`Duplicate discriminator value \"${String(v)}\"`);\n }\n map.set(v, o);\n }\n }\n return map;\n });\n inst._zod.parse = (payload, ctx) => {\n const input = payload.value;\n if (!util.isObject(input)) {\n payload.issues.push({\n code: \"invalid_type\",\n expected: \"object\",\n input,\n inst,\n });\n return payload;\n }\n const opt = disc.value.get(input?.[def.discriminator]);\n if (opt) {\n return opt._zod.run(payload, ctx);\n }\n if (def.unionFallback) {\n return _super(payload, ctx);\n }\n // no matching discriminator\n payload.issues.push({\n code: \"invalid_union\",\n errors: [],\n note: \"No matching discriminator\",\n discriminator: def.discriminator,\n input,\n path: [def.discriminator],\n inst,\n });\n return payload;\n };\n});\nexport const $ZodIntersection = /*@__PURE__*/ core.$constructor(\"$ZodIntersection\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, ctx) => {\n const input = payload.value;\n const left = def.left._zod.run({ value: input, issues: [] }, ctx);\n const right = def.right._zod.run({ value: input, issues: [] }, ctx);\n const async = left instanceof Promise || right instanceof Promise;\n if (async) {\n return Promise.all([left, right]).then(([left, right]) => {\n return handleIntersectionResults(payload, left, right);\n });\n }\n return handleIntersectionResults(payload, left, right);\n };\n});\nfunction mergeValues(a, b) {\n // const aType = parse.t(a);\n // const bType = parse.t(b);\n if (a === b) {\n return { valid: true, data: a };\n }\n if (a instanceof Date && b instanceof Date && +a === +b) {\n return { valid: true, data: a };\n }\n if (util.isPlainObject(a) && util.isPlainObject(b)) {\n const bKeys = Object.keys(b);\n const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);\n const newObj = { ...a, ...b };\n for (const key of sharedKeys) {\n const sharedValue = mergeValues(a[key], b[key]);\n if (!sharedValue.valid) {\n return {\n valid: false,\n mergeErrorPath: [key, ...sharedValue.mergeErrorPath],\n };\n }\n newObj[key] = sharedValue.data;\n }\n return { valid: true, data: newObj };\n }\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) {\n return { valid: false, mergeErrorPath: [] };\n }\n const newArray = [];\n for (let index = 0; index < a.length; index++) {\n const itemA = a[index];\n const itemB = b[index];\n const sharedValue = mergeValues(itemA, itemB);\n if (!sharedValue.valid) {\n return {\n valid: false,\n mergeErrorPath: [index, ...sharedValue.mergeErrorPath],\n };\n }\n newArray.push(sharedValue.data);\n }\n return { valid: true, data: newArray };\n }\n return { valid: false, mergeErrorPath: [] };\n}\nfunction handleIntersectionResults(result, left, right) {\n // Track which side(s) report each key as unrecognized\n const unrecKeys = new Map();\n let unrecIssue;\n for (const iss of left.issues) {\n if (iss.code === \"unrecognized_keys\") {\n unrecIssue ?? (unrecIssue = iss);\n for (const k of iss.keys) {\n if (!unrecKeys.has(k))\n unrecKeys.set(k, {});\n unrecKeys.get(k).l = true;\n }\n }\n else {\n result.issues.push(iss);\n }\n }\n for (const iss of right.issues) {\n if (iss.code === \"unrecognized_keys\") {\n for (const k of iss.keys) {\n if (!unrecKeys.has(k))\n unrecKeys.set(k, {});\n unrecKeys.get(k).r = true;\n }\n }\n else {\n result.issues.push(iss);\n }\n }\n // Report only keys unrecognized by BOTH sides\n const bothKeys = [...unrecKeys].filter(([, f]) => f.l && f.r).map(([k]) => k);\n if (bothKeys.length && unrecIssue) {\n result.issues.push({ ...unrecIssue, keys: bothKeys });\n }\n if (util.aborted(result))\n return result;\n const merged = mergeValues(left.value, right.value);\n if (!merged.valid) {\n throw new Error(`Unmergable intersection. Error path: ` + `${JSON.stringify(merged.mergeErrorPath)}`);\n }\n result.value = merged.data;\n return result;\n}\nexport const $ZodTuple = /*@__PURE__*/ core.$constructor(\"$ZodTuple\", (inst, def) => {\n $ZodType.init(inst, def);\n const items = def.items;\n inst._zod.parse = (payload, ctx) => {\n const input = payload.value;\n if (!Array.isArray(input)) {\n payload.issues.push({\n input,\n inst,\n expected: \"tuple\",\n code: \"invalid_type\",\n });\n return payload;\n }\n payload.value = [];\n const proms = [];\n const reversedIndex = [...items].reverse().findIndex((item) => item._zod.optin !== \"optional\");\n const optStart = reversedIndex === -1 ? 0 : items.length - reversedIndex;\n if (!def.rest) {\n const tooBig = input.length > items.length;\n const tooSmall = input.length < optStart - 1;\n if (tooBig || tooSmall) {\n payload.issues.push({\n ...(tooBig\n ? { code: \"too_big\", maximum: items.length, inclusive: true }\n : { code: \"too_small\", minimum: items.length }),\n input,\n inst,\n origin: \"array\",\n });\n return payload;\n }\n }\n let i = -1;\n for (const item of items) {\n i++;\n if (i >= input.length)\n if (i >= optStart)\n continue;\n const result = item._zod.run({\n value: input[i],\n issues: [],\n }, ctx);\n if (result instanceof Promise) {\n proms.push(result.then((result) => handleTupleResult(result, payload, i)));\n }\n else {\n handleTupleResult(result, payload, i);\n }\n }\n if (def.rest) {\n const rest = input.slice(items.length);\n for (const el of rest) {\n i++;\n const result = def.rest._zod.run({\n value: el,\n issues: [],\n }, ctx);\n if (result instanceof Promise) {\n proms.push(result.then((result) => handleTupleResult(result, payload, i)));\n }\n else {\n handleTupleResult(result, payload, i);\n }\n }\n }\n if (proms.length)\n return Promise.all(proms).then(() => payload);\n return payload;\n };\n});\nfunction handleTupleResult(result, final, index) {\n if (result.issues.length) {\n final.issues.push(...util.prefixIssues(index, result.issues));\n }\n final.value[index] = result.value;\n}\nexport const $ZodRecord = /*@__PURE__*/ core.$constructor(\"$ZodRecord\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, ctx) => {\n const input = payload.value;\n if (!util.isPlainObject(input)) {\n payload.issues.push({\n expected: \"record\",\n code: \"invalid_type\",\n input,\n inst,\n });\n return payload;\n }\n const proms = [];\n const values = def.keyType._zod.values;\n if (values) {\n payload.value = {};\n const recordKeys = new Set();\n for (const key of values) {\n if (typeof key === \"string\" || typeof key === \"number\" || typeof key === \"symbol\") {\n recordKeys.add(typeof key === \"number\" ? key.toString() : key);\n const result = def.valueType._zod.run({ value: input[key], issues: [] }, ctx);\n if (result instanceof Promise) {\n proms.push(result.then((result) => {\n if (result.issues.length) {\n payload.issues.push(...util.prefixIssues(key, result.issues));\n }\n payload.value[key] = result.value;\n }));\n }\n else {\n if (result.issues.length) {\n payload.issues.push(...util.prefixIssues(key, result.issues));\n }\n payload.value[key] = result.value;\n }\n }\n }\n let unrecognized;\n for (const key in input) {\n if (!recordKeys.has(key)) {\n unrecognized = unrecognized ?? [];\n unrecognized.push(key);\n }\n }\n if (unrecognized && unrecognized.length > 0) {\n payload.issues.push({\n code: \"unrecognized_keys\",\n input,\n inst,\n keys: unrecognized,\n });\n }\n }\n else {\n payload.value = {};\n for (const key of Reflect.ownKeys(input)) {\n if (key === \"__proto__\")\n continue;\n let keyResult = def.keyType._zod.run({ value: key, issues: [] }, ctx);\n if (keyResult instanceof Promise) {\n throw new Error(\"Async schemas not supported in object keys currently\");\n }\n // Numeric string fallback: if key is a numeric string and failed, retry with Number(key)\n // This handles z.number(), z.literal([1, 2, 3]), and unions containing numeric literals\n const checkNumericKey = typeof key === \"string\" && regexes.number.test(key) && keyResult.issues.length;\n if (checkNumericKey) {\n const retryResult = def.keyType._zod.run({ value: Number(key), issues: [] }, ctx);\n if (retryResult instanceof Promise) {\n throw new Error(\"Async schemas not supported in object keys currently\");\n }\n if (retryResult.issues.length === 0) {\n keyResult = retryResult;\n }\n }\n if (keyResult.issues.length) {\n if (def.mode === \"loose\") {\n // Pass through unchanged\n payload.value[key] = input[key];\n }\n else {\n // Default \"strict\" behavior: error on invalid key\n payload.issues.push({\n code: \"invalid_key\",\n origin: \"record\",\n issues: keyResult.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config())),\n input: key,\n path: [key],\n inst,\n });\n }\n continue;\n }\n const result = def.valueType._zod.run({ value: input[key], issues: [] }, ctx);\n if (result instanceof Promise) {\n proms.push(result.then((result) => {\n if (result.issues.length) {\n payload.issues.push(...util.prefixIssues(key, result.issues));\n }\n payload.value[keyResult.value] = result.value;\n }));\n }\n else {\n if (result.issues.length) {\n payload.issues.push(...util.prefixIssues(key, result.issues));\n }\n payload.value[keyResult.value] = result.value;\n }\n }\n }\n if (proms.length) {\n return Promise.all(proms).then(() => payload);\n }\n return payload;\n };\n});\nexport const $ZodMap = /*@__PURE__*/ core.$constructor(\"$ZodMap\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, ctx) => {\n const input = payload.value;\n if (!(input instanceof Map)) {\n payload.issues.push({\n expected: \"map\",\n code: \"invalid_type\",\n input,\n inst,\n });\n return payload;\n }\n const proms = [];\n payload.value = new Map();\n for (const [key, value] of input) {\n const keyResult = def.keyType._zod.run({ value: key, issues: [] }, ctx);\n const valueResult = def.valueType._zod.run({ value: value, issues: [] }, ctx);\n if (keyResult instanceof Promise || valueResult instanceof Promise) {\n proms.push(Promise.all([keyResult, valueResult]).then(([keyResult, valueResult]) => {\n handleMapResult(keyResult, valueResult, payload, key, input, inst, ctx);\n }));\n }\n else {\n handleMapResult(keyResult, valueResult, payload, key, input, inst, ctx);\n }\n }\n if (proms.length)\n return Promise.all(proms).then(() => payload);\n return payload;\n };\n});\nfunction handleMapResult(keyResult, valueResult, final, key, input, inst, ctx) {\n if (keyResult.issues.length) {\n if (util.propertyKeyTypes.has(typeof key)) {\n final.issues.push(...util.prefixIssues(key, keyResult.issues));\n }\n else {\n final.issues.push({\n code: \"invalid_key\",\n origin: \"map\",\n input,\n inst,\n issues: keyResult.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config())),\n });\n }\n }\n if (valueResult.issues.length) {\n if (util.propertyKeyTypes.has(typeof key)) {\n final.issues.push(...util.prefixIssues(key, valueResult.issues));\n }\n else {\n final.issues.push({\n origin: \"map\",\n code: \"invalid_element\",\n input,\n inst,\n key: key,\n issues: valueResult.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config())),\n });\n }\n }\n final.value.set(keyResult.value, valueResult.value);\n}\nexport const $ZodSet = /*@__PURE__*/ core.$constructor(\"$ZodSet\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, ctx) => {\n const input = payload.value;\n if (!(input instanceof Set)) {\n payload.issues.push({\n input,\n inst,\n expected: \"set\",\n code: \"invalid_type\",\n });\n return payload;\n }\n const proms = [];\n payload.value = new Set();\n for (const item of input) {\n const result = def.valueType._zod.run({ value: item, issues: [] }, ctx);\n if (result instanceof Promise) {\n proms.push(result.then((result) => handleSetResult(result, payload)));\n }\n else\n handleSetResult(result, payload);\n }\n if (proms.length)\n return Promise.all(proms).then(() => payload);\n return payload;\n };\n});\nfunction handleSetResult(result, final) {\n if (result.issues.length) {\n final.issues.push(...result.issues);\n }\n final.value.add(result.value);\n}\nexport const $ZodEnum = /*@__PURE__*/ core.$constructor(\"$ZodEnum\", (inst, def) => {\n $ZodType.init(inst, def);\n const values = util.getEnumValues(def.entries);\n const valuesSet = new Set(values);\n inst._zod.values = valuesSet;\n inst._zod.pattern = new RegExp(`^(${values\n .filter((k) => util.propertyKeyTypes.has(typeof k))\n .map((o) => (typeof o === \"string\" ? util.escapeRegex(o) : o.toString()))\n .join(\"|\")})$`);\n inst._zod.parse = (payload, _ctx) => {\n const input = payload.value;\n if (valuesSet.has(input)) {\n return payload;\n }\n payload.issues.push({\n code: \"invalid_value\",\n values,\n input,\n inst,\n });\n return payload;\n };\n});\nexport const $ZodLiteral = /*@__PURE__*/ core.$constructor(\"$ZodLiteral\", (inst, def) => {\n $ZodType.init(inst, def);\n if (def.values.length === 0) {\n throw new Error(\"Cannot create literal schema with no valid values\");\n }\n const values = new Set(def.values);\n inst._zod.values = values;\n inst._zod.pattern = new RegExp(`^(${def.values\n .map((o) => (typeof o === \"string\" ? util.escapeRegex(o) : o ? util.escapeRegex(o.toString()) : String(o)))\n .join(\"|\")})$`);\n inst._zod.parse = (payload, _ctx) => {\n const input = payload.value;\n if (values.has(input)) {\n return payload;\n }\n payload.issues.push({\n code: \"invalid_value\",\n values: def.values,\n input,\n inst,\n });\n return payload;\n };\n});\nexport const $ZodFile = /*@__PURE__*/ core.$constructor(\"$ZodFile\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, _ctx) => {\n const input = payload.value;\n // @ts-ignore\n if (input instanceof File)\n return payload;\n payload.issues.push({\n expected: \"file\",\n code: \"invalid_type\",\n input,\n inst,\n });\n return payload;\n };\n});\nexport const $ZodTransform = /*@__PURE__*/ core.$constructor(\"$ZodTransform\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") {\n throw new core.$ZodEncodeError(inst.constructor.name);\n }\n const _out = def.transform(payload.value, payload);\n if (ctx.async) {\n const output = _out instanceof Promise ? _out : Promise.resolve(_out);\n return output.then((output) => {\n payload.value = output;\n return payload;\n });\n }\n if (_out instanceof Promise) {\n throw new core.$ZodAsyncError();\n }\n payload.value = _out;\n return payload;\n };\n});\nfunction handleOptionalResult(result, input) {\n if (result.issues.length && input === undefined) {\n return { issues: [], value: undefined };\n }\n return result;\n}\nexport const $ZodOptional = /*@__PURE__*/ core.$constructor(\"$ZodOptional\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.optin = \"optional\";\n inst._zod.optout = \"optional\";\n util.defineLazy(inst._zod, \"values\", () => {\n return def.innerType._zod.values ? new Set([...def.innerType._zod.values, undefined]) : undefined;\n });\n util.defineLazy(inst._zod, \"pattern\", () => {\n const pattern = def.innerType._zod.pattern;\n return pattern ? new RegExp(`^(${util.cleanRegex(pattern.source)})?$`) : undefined;\n });\n inst._zod.parse = (payload, ctx) => {\n if (def.innerType._zod.optin === \"optional\") {\n const result = def.innerType._zod.run(payload, ctx);\n if (result instanceof Promise)\n return result.then((r) => handleOptionalResult(r, payload.value));\n return handleOptionalResult(result, payload.value);\n }\n if (payload.value === undefined) {\n return payload;\n }\n return def.innerType._zod.run(payload, ctx);\n };\n});\nexport const $ZodExactOptional = /*@__PURE__*/ core.$constructor(\"$ZodExactOptional\", (inst, def) => {\n // Call parent init - inherits optin/optout = \"optional\"\n $ZodOptional.init(inst, def);\n // Override values/pattern to NOT add undefined\n util.defineLazy(inst._zod, \"values\", () => def.innerType._zod.values);\n util.defineLazy(inst._zod, \"pattern\", () => def.innerType._zod.pattern);\n // Override parse to just delegate (no undefined handling)\n inst._zod.parse = (payload, ctx) => {\n return def.innerType._zod.run(payload, ctx);\n };\n});\nexport const $ZodNullable = /*@__PURE__*/ core.$constructor(\"$ZodNullable\", (inst, def) => {\n $ZodType.init(inst, def);\n util.defineLazy(inst._zod, \"optin\", () => def.innerType._zod.optin);\n util.defineLazy(inst._zod, \"optout\", () => def.innerType._zod.optout);\n util.defineLazy(inst._zod, \"pattern\", () => {\n const pattern = def.innerType._zod.pattern;\n return pattern ? new RegExp(`^(${util.cleanRegex(pattern.source)}|null)$`) : undefined;\n });\n util.defineLazy(inst._zod, \"values\", () => {\n return def.innerType._zod.values ? new Set([...def.innerType._zod.values, null]) : undefined;\n });\n inst._zod.parse = (payload, ctx) => {\n // Forward direction (decode): allow null to pass through\n if (payload.value === null)\n return payload;\n return def.innerType._zod.run(payload, ctx);\n };\n});\nexport const $ZodDefault = /*@__PURE__*/ core.$constructor(\"$ZodDefault\", (inst, def) => {\n $ZodType.init(inst, def);\n // inst._zod.qin = \"true\";\n inst._zod.optin = \"optional\";\n util.defineLazy(inst._zod, \"values\", () => def.innerType._zod.values);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") {\n return def.innerType._zod.run(payload, ctx);\n }\n // Forward direction (decode): apply defaults for undefined input\n if (payload.value === undefined) {\n payload.value = def.defaultValue;\n /**\n * $ZodDefault returns the default value immediately in forward direction.\n * It doesn't pass the default value into the validator (\"prefault\"). There's no reason to pass the default value through validation. The validity of the default is enforced by TypeScript statically. Otherwise, it's the responsibility of the user to ensure the default is valid. In the case of pipes with divergent in/out types, you can specify the default on the `in` schema of your ZodPipe to set a \"prefault\" for the pipe. */\n return payload;\n }\n // Forward direction: continue with default handling\n const result = def.innerType._zod.run(payload, ctx);\n if (result instanceof Promise) {\n return result.then((result) => handleDefaultResult(result, def));\n }\n return handleDefaultResult(result, def);\n };\n});\nfunction handleDefaultResult(payload, def) {\n if (payload.value === undefined) {\n payload.value = def.defaultValue;\n }\n return payload;\n}\nexport const $ZodPrefault = /*@__PURE__*/ core.$constructor(\"$ZodPrefault\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.optin = \"optional\";\n util.defineLazy(inst._zod, \"values\", () => def.innerType._zod.values);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") {\n return def.innerType._zod.run(payload, ctx);\n }\n // Forward direction (decode): apply prefault for undefined input\n if (payload.value === undefined) {\n payload.value = def.defaultValue;\n }\n return def.innerType._zod.run(payload, ctx);\n };\n});\nexport const $ZodNonOptional = /*@__PURE__*/ core.$constructor(\"$ZodNonOptional\", (inst, def) => {\n $ZodType.init(inst, def);\n util.defineLazy(inst._zod, \"values\", () => {\n const v = def.innerType._zod.values;\n return v ? new Set([...v].filter((x) => x !== undefined)) : undefined;\n });\n inst._zod.parse = (payload, ctx) => {\n const result = def.innerType._zod.run(payload, ctx);\n if (result instanceof Promise) {\n return result.then((result) => handleNonOptionalResult(result, inst));\n }\n return handleNonOptionalResult(result, inst);\n };\n});\nfunction handleNonOptionalResult(payload, inst) {\n if (!payload.issues.length && payload.value === undefined) {\n payload.issues.push({\n code: \"invalid_type\",\n expected: \"nonoptional\",\n input: payload.value,\n inst,\n });\n }\n return payload;\n}\nexport const $ZodSuccess = /*@__PURE__*/ core.$constructor(\"$ZodSuccess\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") {\n throw new core.$ZodEncodeError(\"ZodSuccess\");\n }\n const result = def.innerType._zod.run(payload, ctx);\n if (result instanceof Promise) {\n return result.then((result) => {\n payload.value = result.issues.length === 0;\n return payload;\n });\n }\n payload.value = result.issues.length === 0;\n return payload;\n };\n});\nexport const $ZodCatch = /*@__PURE__*/ core.$constructor(\"$ZodCatch\", (inst, def) => {\n $ZodType.init(inst, def);\n util.defineLazy(inst._zod, \"optin\", () => def.innerType._zod.optin);\n util.defineLazy(inst._zod, \"optout\", () => def.innerType._zod.optout);\n util.defineLazy(inst._zod, \"values\", () => def.innerType._zod.values);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") {\n return def.innerType._zod.run(payload, ctx);\n }\n // Forward direction (decode): apply catch logic\n const result = def.innerType._zod.run(payload, ctx);\n if (result instanceof Promise) {\n return result.then((result) => {\n payload.value = result.value;\n if (result.issues.length) {\n payload.value = def.catchValue({\n ...payload,\n error: {\n issues: result.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config())),\n },\n input: payload.value,\n });\n payload.issues = [];\n }\n return payload;\n });\n }\n payload.value = result.value;\n if (result.issues.length) {\n payload.value = def.catchValue({\n ...payload,\n error: {\n issues: result.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config())),\n },\n input: payload.value,\n });\n payload.issues = [];\n }\n return payload;\n };\n});\nexport const $ZodNaN = /*@__PURE__*/ core.$constructor(\"$ZodNaN\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, _ctx) => {\n if (typeof payload.value !== \"number\" || !Number.isNaN(payload.value)) {\n payload.issues.push({\n input: payload.value,\n inst,\n expected: \"nan\",\n code: \"invalid_type\",\n });\n return payload;\n }\n return payload;\n };\n});\nexport const $ZodPipe = /*@__PURE__*/ core.$constructor(\"$ZodPipe\", (inst, def) => {\n $ZodType.init(inst, def);\n util.defineLazy(inst._zod, \"values\", () => def.in._zod.values);\n util.defineLazy(inst._zod, \"optin\", () => def.in._zod.optin);\n util.defineLazy(inst._zod, \"optout\", () => def.out._zod.optout);\n util.defineLazy(inst._zod, \"propValues\", () => def.in._zod.propValues);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") {\n const right = def.out._zod.run(payload, ctx);\n if (right instanceof Promise) {\n return right.then((right) => handlePipeResult(right, def.in, ctx));\n }\n return handlePipeResult(right, def.in, ctx);\n }\n const left = def.in._zod.run(payload, ctx);\n if (left instanceof Promise) {\n return left.then((left) => handlePipeResult(left, def.out, ctx));\n }\n return handlePipeResult(left, def.out, ctx);\n };\n});\nfunction handlePipeResult(left, next, ctx) {\n if (left.issues.length) {\n // prevent further checks\n left.aborted = true;\n return left;\n }\n return next._zod.run({ value: left.value, issues: left.issues }, ctx);\n}\nexport const $ZodCodec = /*@__PURE__*/ core.$constructor(\"$ZodCodec\", (inst, def) => {\n $ZodType.init(inst, def);\n util.defineLazy(inst._zod, \"values\", () => def.in._zod.values);\n util.defineLazy(inst._zod, \"optin\", () => def.in._zod.optin);\n util.defineLazy(inst._zod, \"optout\", () => def.out._zod.optout);\n util.defineLazy(inst._zod, \"propValues\", () => def.in._zod.propValues);\n inst._zod.parse = (payload, ctx) => {\n const direction = ctx.direction || \"forward\";\n if (direction === \"forward\") {\n const left = def.in._zod.run(payload, ctx);\n if (left instanceof Promise) {\n return left.then((left) => handleCodecAResult(left, def, ctx));\n }\n return handleCodecAResult(left, def, ctx);\n }\n else {\n const right = def.out._zod.run(payload, ctx);\n if (right instanceof Promise) {\n return right.then((right) => handleCodecAResult(right, def, ctx));\n }\n return handleCodecAResult(right, def, ctx);\n }\n };\n});\nfunction handleCodecAResult(result, def, ctx) {\n if (result.issues.length) {\n // prevent further checks\n result.aborted = true;\n return result;\n }\n const direction = ctx.direction || \"forward\";\n if (direction === \"forward\") {\n const transformed = def.transform(result.value, result);\n if (transformed instanceof Promise) {\n return transformed.then((value) => handleCodecTxResult(result, value, def.out, ctx));\n }\n return handleCodecTxResult(result, transformed, def.out, ctx);\n }\n else {\n const transformed = def.reverseTransform(result.value, result);\n if (transformed instanceof Promise) {\n return transformed.then((value) => handleCodecTxResult(result, value, def.in, ctx));\n }\n return handleCodecTxResult(result, transformed, def.in, ctx);\n }\n}\nfunction handleCodecTxResult(left, value, nextSchema, ctx) {\n // Check if transform added any issues\n if (left.issues.length) {\n left.aborted = true;\n return left;\n }\n return nextSchema._zod.run({ value, issues: left.issues }, ctx);\n}\nexport const $ZodReadonly = /*@__PURE__*/ core.$constructor(\"$ZodReadonly\", (inst, def) => {\n $ZodType.init(inst, def);\n util.defineLazy(inst._zod, \"propValues\", () => def.innerType._zod.propValues);\n util.defineLazy(inst._zod, \"values\", () => def.innerType._zod.values);\n util.defineLazy(inst._zod, \"optin\", () => def.innerType?._zod?.optin);\n util.defineLazy(inst._zod, \"optout\", () => def.innerType?._zod?.optout);\n inst._zod.parse = (payload, ctx) => {\n if (ctx.direction === \"backward\") {\n return def.innerType._zod.run(payload, ctx);\n }\n const result = def.innerType._zod.run(payload, ctx);\n if (result instanceof Promise) {\n return result.then(handleReadonlyResult);\n }\n return handleReadonlyResult(result);\n };\n});\nfunction handleReadonlyResult(payload) {\n payload.value = Object.freeze(payload.value);\n return payload;\n}\nexport const $ZodTemplateLiteral = /*@__PURE__*/ core.$constructor(\"$ZodTemplateLiteral\", (inst, def) => {\n $ZodType.init(inst, def);\n const regexParts = [];\n for (const part of def.parts) {\n if (typeof part === \"object\" && part !== null) {\n // is Zod schema\n if (!part._zod.pattern) {\n // if (!source)\n throw new Error(`Invalid template literal part, no pattern found: ${[...part._zod.traits].shift()}`);\n }\n const source = part._zod.pattern instanceof RegExp ? part._zod.pattern.source : part._zod.pattern;\n if (!source)\n throw new Error(`Invalid template literal part: ${part._zod.traits}`);\n const start = source.startsWith(\"^\") ? 1 : 0;\n const end = source.endsWith(\"$\") ? source.length - 1 : source.length;\n regexParts.push(source.slice(start, end));\n }\n else if (part === null || util.primitiveTypes.has(typeof part)) {\n regexParts.push(util.escapeRegex(`${part}`));\n }\n else {\n throw new Error(`Invalid template literal part: ${part}`);\n }\n }\n inst._zod.pattern = new RegExp(`^${regexParts.join(\"\")}$`);\n inst._zod.parse = (payload, _ctx) => {\n if (typeof payload.value !== \"string\") {\n payload.issues.push({\n input: payload.value,\n inst,\n expected: \"string\",\n code: \"invalid_type\",\n });\n return payload;\n }\n inst._zod.pattern.lastIndex = 0;\n if (!inst._zod.pattern.test(payload.value)) {\n payload.issues.push({\n input: payload.value,\n inst,\n code: \"invalid_format\",\n format: def.format ?? \"template_literal\",\n pattern: inst._zod.pattern.source,\n });\n return payload;\n }\n return payload;\n };\n});\nexport const $ZodFunction = /*@__PURE__*/ core.$constructor(\"$ZodFunction\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._def = def;\n inst._zod.def = def;\n inst.implement = (func) => {\n if (typeof func !== \"function\") {\n throw new Error(\"implement() must be called with a function\");\n }\n return function (...args) {\n const parsedArgs = inst._def.input ? parse(inst._def.input, args) : args;\n const result = Reflect.apply(func, this, parsedArgs);\n if (inst._def.output) {\n return parse(inst._def.output, result);\n }\n return result;\n };\n };\n inst.implementAsync = (func) => {\n if (typeof func !== \"function\") {\n throw new Error(\"implementAsync() must be called with a function\");\n }\n return async function (...args) {\n const parsedArgs = inst._def.input ? await parseAsync(inst._def.input, args) : args;\n const result = await Reflect.apply(func, this, parsedArgs);\n if (inst._def.output) {\n return await parseAsync(inst._def.output, result);\n }\n return result;\n };\n };\n inst._zod.parse = (payload, _ctx) => {\n if (typeof payload.value !== \"function\") {\n payload.issues.push({\n code: \"invalid_type\",\n expected: \"function\",\n input: payload.value,\n inst,\n });\n return payload;\n }\n // Check if output is a promise type to determine if we should use async implementation\n const hasPromiseOutput = inst._def.output && inst._def.output._zod.def.type === \"promise\";\n if (hasPromiseOutput) {\n payload.value = inst.implementAsync(payload.value);\n }\n else {\n payload.value = inst.implement(payload.value);\n }\n return payload;\n };\n inst.input = (...args) => {\n const F = inst.constructor;\n if (Array.isArray(args[0])) {\n return new F({\n type: \"function\",\n input: new $ZodTuple({\n type: \"tuple\",\n items: args[0],\n rest: args[1],\n }),\n output: inst._def.output,\n });\n }\n return new F({\n type: \"function\",\n input: args[0],\n output: inst._def.output,\n });\n };\n inst.output = (output) => {\n const F = inst.constructor;\n return new F({\n type: \"function\",\n input: inst._def.input,\n output,\n });\n };\n return inst;\n});\nexport const $ZodPromise = /*@__PURE__*/ core.$constructor(\"$ZodPromise\", (inst, def) => {\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, ctx) => {\n return Promise.resolve(payload.value).then((inner) => def.innerType._zod.run({ value: inner, issues: [] }, ctx));\n };\n});\nexport const $ZodLazy = /*@__PURE__*/ core.$constructor(\"$ZodLazy\", (inst, def) => {\n $ZodType.init(inst, def);\n // let _innerType!: any;\n // util.defineLazy(def, \"getter\", () => {\n // if (!_innerType) {\n // _innerType = def.getter();\n // }\n // return () => _innerType;\n // });\n util.defineLazy(inst._zod, \"innerType\", () => def.getter());\n util.defineLazy(inst._zod, \"pattern\", () => inst._zod.innerType?._zod?.pattern);\n util.defineLazy(inst._zod, \"propValues\", () => inst._zod.innerType?._zod?.propValues);\n util.defineLazy(inst._zod, \"optin\", () => inst._zod.innerType?._zod?.optin ?? undefined);\n util.defineLazy(inst._zod, \"optout\", () => inst._zod.innerType?._zod?.optout ?? undefined);\n inst._zod.parse = (payload, ctx) => {\n const inner = inst._zod.innerType;\n return inner._zod.run(payload, ctx);\n };\n});\nexport const $ZodCustom = /*@__PURE__*/ core.$constructor(\"$ZodCustom\", (inst, def) => {\n checks.$ZodCheck.init(inst, def);\n $ZodType.init(inst, def);\n inst._zod.parse = (payload, _) => {\n return payload;\n };\n inst._zod.check = (payload) => {\n const input = payload.value;\n const r = def.fn(input);\n if (r instanceof Promise) {\n return r.then((r) => handleRefineResult(r, payload, input, inst));\n }\n handleRefineResult(r, payload, input, inst);\n return;\n };\n});\nfunction handleRefineResult(result, payload, input, inst) {\n if (!result) {\n const _iss = {\n code: \"custom\",\n input,\n inst, // incorporates params.error into issue reporting\n path: [...(inst._zod.def.path ?? [])], // incorporates params.error into issue reporting\n continue: !inst._zod.def.abort,\n // params: inst._zod.def.params,\n };\n if (inst._zod.def.params)\n _iss.params = inst._zod.def.params;\n payload.issues.push(util.issue(_iss));\n }\n}\n","var _a;\nexport const $output = Symbol(\"ZodOutput\");\nexport const $input = Symbol(\"ZodInput\");\nexport class $ZodRegistry {\n constructor() {\n this._map = new WeakMap();\n this._idmap = new Map();\n }\n add(schema, ..._meta) {\n const meta = _meta[0];\n this._map.set(schema, meta);\n if (meta && typeof meta === \"object\" && \"id\" in meta) {\n this._idmap.set(meta.id, schema);\n }\n return this;\n }\n clear() {\n this._map = new WeakMap();\n this._idmap = new Map();\n return this;\n }\n remove(schema) {\n const meta = this._map.get(schema);\n if (meta && typeof meta === \"object\" && \"id\" in meta) {\n this._idmap.delete(meta.id);\n }\n this._map.delete(schema);\n return this;\n }\n get(schema) {\n // return this._map.get(schema) as any;\n // inherit metadata\n const p = schema._zod.parent;\n if (p) {\n const pm = { ...(this.get(p) ?? {}) };\n delete pm.id; // do not inherit id\n const f = { ...pm, ...this._map.get(schema) };\n return Object.keys(f).length ? f : undefined;\n }\n return this._map.get(schema);\n }\n has(schema) {\n return this._map.has(schema);\n }\n}\n// registries\nexport function registry() {\n return new $ZodRegistry();\n}\n(_a = globalThis).__zod_globalRegistry ?? (_a.__zod_globalRegistry = registry());\nexport const globalRegistry = globalThis.__zod_globalRegistry;\n","import * as checks from \"./checks.js\";\nimport * as registries from \"./registries.js\";\nimport * as schemas from \"./schemas.js\";\nimport * as util from \"./util.js\";\n// @__NO_SIDE_EFFECTS__\nexport function _string(Class, params) {\n return new Class({\n type: \"string\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _coercedString(Class, params) {\n return new Class({\n type: \"string\",\n coerce: true,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _email(Class, params) {\n return new Class({\n type: \"string\",\n format: \"email\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _guid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"guid\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _uuid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"uuid\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _uuidv4(Class, params) {\n return new Class({\n type: \"string\",\n format: \"uuid\",\n check: \"string_format\",\n abort: false,\n version: \"v4\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _uuidv6(Class, params) {\n return new Class({\n type: \"string\",\n format: \"uuid\",\n check: \"string_format\",\n abort: false,\n version: \"v6\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _uuidv7(Class, params) {\n return new Class({\n type: \"string\",\n format: \"uuid\",\n check: \"string_format\",\n abort: false,\n version: \"v7\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _url(Class, params) {\n return new Class({\n type: \"string\",\n format: \"url\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _emoji(Class, params) {\n return new Class({\n type: \"string\",\n format: \"emoji\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _nanoid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"nanoid\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _cuid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"cuid\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _cuid2(Class, params) {\n return new Class({\n type: \"string\",\n format: \"cuid2\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _ulid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"ulid\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _xid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"xid\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _ksuid(Class, params) {\n return new Class({\n type: \"string\",\n format: \"ksuid\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _ipv4(Class, params) {\n return new Class({\n type: \"string\",\n format: \"ipv4\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _ipv6(Class, params) {\n return new Class({\n type: \"string\",\n format: \"ipv6\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _mac(Class, params) {\n return new Class({\n type: \"string\",\n format: \"mac\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _cidrv4(Class, params) {\n return new Class({\n type: \"string\",\n format: \"cidrv4\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _cidrv6(Class, params) {\n return new Class({\n type: \"string\",\n format: \"cidrv6\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _base64(Class, params) {\n return new Class({\n type: \"string\",\n format: \"base64\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _base64url(Class, params) {\n return new Class({\n type: \"string\",\n format: \"base64url\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _e164(Class, params) {\n return new Class({\n type: \"string\",\n format: \"e164\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _jwt(Class, params) {\n return new Class({\n type: \"string\",\n format: \"jwt\",\n check: \"string_format\",\n abort: false,\n ...util.normalizeParams(params),\n });\n}\nexport const TimePrecision = {\n Any: null,\n Minute: -1,\n Second: 0,\n Millisecond: 3,\n Microsecond: 6,\n};\n// @__NO_SIDE_EFFECTS__\nexport function _isoDateTime(Class, params) {\n return new Class({\n type: \"string\",\n format: \"datetime\",\n check: \"string_format\",\n offset: false,\n local: false,\n precision: null,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _isoDate(Class, params) {\n return new Class({\n type: \"string\",\n format: \"date\",\n check: \"string_format\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _isoTime(Class, params) {\n return new Class({\n type: \"string\",\n format: \"time\",\n check: \"string_format\",\n precision: null,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _isoDuration(Class, params) {\n return new Class({\n type: \"string\",\n format: \"duration\",\n check: \"string_format\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _number(Class, params) {\n return new Class({\n type: \"number\",\n checks: [],\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _coercedNumber(Class, params) {\n return new Class({\n type: \"number\",\n coerce: true,\n checks: [],\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _int(Class, params) {\n return new Class({\n type: \"number\",\n check: \"number_format\",\n abort: false,\n format: \"safeint\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _float32(Class, params) {\n return new Class({\n type: \"number\",\n check: \"number_format\",\n abort: false,\n format: \"float32\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _float64(Class, params) {\n return new Class({\n type: \"number\",\n check: \"number_format\",\n abort: false,\n format: \"float64\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _int32(Class, params) {\n return new Class({\n type: \"number\",\n check: \"number_format\",\n abort: false,\n format: \"int32\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _uint32(Class, params) {\n return new Class({\n type: \"number\",\n check: \"number_format\",\n abort: false,\n format: \"uint32\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _boolean(Class, params) {\n return new Class({\n type: \"boolean\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _coercedBoolean(Class, params) {\n return new Class({\n type: \"boolean\",\n coerce: true,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _bigint(Class, params) {\n return new Class({\n type: \"bigint\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _coercedBigint(Class, params) {\n return new Class({\n type: \"bigint\",\n coerce: true,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _int64(Class, params) {\n return new Class({\n type: \"bigint\",\n check: \"bigint_format\",\n abort: false,\n format: \"int64\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _uint64(Class, params) {\n return new Class({\n type: \"bigint\",\n check: \"bigint_format\",\n abort: false,\n format: \"uint64\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _symbol(Class, params) {\n return new Class({\n type: \"symbol\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _undefined(Class, params) {\n return new Class({\n type: \"undefined\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _null(Class, params) {\n return new Class({\n type: \"null\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _any(Class) {\n return new Class({\n type: \"any\",\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _unknown(Class) {\n return new Class({\n type: \"unknown\",\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _never(Class, params) {\n return new Class({\n type: \"never\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _void(Class, params) {\n return new Class({\n type: \"void\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _date(Class, params) {\n return new Class({\n type: \"date\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _coercedDate(Class, params) {\n return new Class({\n type: \"date\",\n coerce: true,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _nan(Class, params) {\n return new Class({\n type: \"nan\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _lt(value, params) {\n return new checks.$ZodCheckLessThan({\n check: \"less_than\",\n ...util.normalizeParams(params),\n value,\n inclusive: false,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _lte(value, params) {\n return new checks.$ZodCheckLessThan({\n check: \"less_than\",\n ...util.normalizeParams(params),\n value,\n inclusive: true,\n });\n}\nexport { \n/** @deprecated Use `z.lte()` instead. */\n_lte as _max, };\n// @__NO_SIDE_EFFECTS__\nexport function _gt(value, params) {\n return new checks.$ZodCheckGreaterThan({\n check: \"greater_than\",\n ...util.normalizeParams(params),\n value,\n inclusive: false,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _gte(value, params) {\n return new checks.$ZodCheckGreaterThan({\n check: \"greater_than\",\n ...util.normalizeParams(params),\n value,\n inclusive: true,\n });\n}\nexport { \n/** @deprecated Use `z.gte()` instead. */\n_gte as _min, };\n// @__NO_SIDE_EFFECTS__\nexport function _positive(params) {\n return _gt(0, params);\n}\n// negative\n// @__NO_SIDE_EFFECTS__\nexport function _negative(params) {\n return _lt(0, params);\n}\n// nonpositive\n// @__NO_SIDE_EFFECTS__\nexport function _nonpositive(params) {\n return _lte(0, params);\n}\n// nonnegative\n// @__NO_SIDE_EFFECTS__\nexport function _nonnegative(params) {\n return _gte(0, params);\n}\n// @__NO_SIDE_EFFECTS__\nexport function _multipleOf(value, params) {\n return new checks.$ZodCheckMultipleOf({\n check: \"multiple_of\",\n ...util.normalizeParams(params),\n value,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _maxSize(maximum, params) {\n return new checks.$ZodCheckMaxSize({\n check: \"max_size\",\n ...util.normalizeParams(params),\n maximum,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _minSize(minimum, params) {\n return new checks.$ZodCheckMinSize({\n check: \"min_size\",\n ...util.normalizeParams(params),\n minimum,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _size(size, params) {\n return new checks.$ZodCheckSizeEquals({\n check: \"size_equals\",\n ...util.normalizeParams(params),\n size,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _maxLength(maximum, params) {\n const ch = new checks.$ZodCheckMaxLength({\n check: \"max_length\",\n ...util.normalizeParams(params),\n maximum,\n });\n return ch;\n}\n// @__NO_SIDE_EFFECTS__\nexport function _minLength(minimum, params) {\n return new checks.$ZodCheckMinLength({\n check: \"min_length\",\n ...util.normalizeParams(params),\n minimum,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _length(length, params) {\n return new checks.$ZodCheckLengthEquals({\n check: \"length_equals\",\n ...util.normalizeParams(params),\n length,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _regex(pattern, params) {\n return new checks.$ZodCheckRegex({\n check: \"string_format\",\n format: \"regex\",\n ...util.normalizeParams(params),\n pattern,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _lowercase(params) {\n return new checks.$ZodCheckLowerCase({\n check: \"string_format\",\n format: \"lowercase\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _uppercase(params) {\n return new checks.$ZodCheckUpperCase({\n check: \"string_format\",\n format: \"uppercase\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _includes(includes, params) {\n return new checks.$ZodCheckIncludes({\n check: \"string_format\",\n format: \"includes\",\n ...util.normalizeParams(params),\n includes,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _startsWith(prefix, params) {\n return new checks.$ZodCheckStartsWith({\n check: \"string_format\",\n format: \"starts_with\",\n ...util.normalizeParams(params),\n prefix,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _endsWith(suffix, params) {\n return new checks.$ZodCheckEndsWith({\n check: \"string_format\",\n format: \"ends_with\",\n ...util.normalizeParams(params),\n suffix,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _property(property, schema, params) {\n return new checks.$ZodCheckProperty({\n check: \"property\",\n property,\n schema,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _mime(types, params) {\n return new checks.$ZodCheckMimeType({\n check: \"mime_type\",\n mime: types,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _overwrite(tx) {\n return new checks.$ZodCheckOverwrite({\n check: \"overwrite\",\n tx,\n });\n}\n// normalize\n// @__NO_SIDE_EFFECTS__\nexport function _normalize(form) {\n return _overwrite((input) => input.normalize(form));\n}\n// trim\n// @__NO_SIDE_EFFECTS__\nexport function _trim() {\n return _overwrite((input) => input.trim());\n}\n// toLowerCase\n// @__NO_SIDE_EFFECTS__\nexport function _toLowerCase() {\n return _overwrite((input) => input.toLowerCase());\n}\n// toUpperCase\n// @__NO_SIDE_EFFECTS__\nexport function _toUpperCase() {\n return _overwrite((input) => input.toUpperCase());\n}\n// slugify\n// @__NO_SIDE_EFFECTS__\nexport function _slugify() {\n return _overwrite((input) => util.slugify(input));\n}\n// @__NO_SIDE_EFFECTS__\nexport function _array(Class, element, params) {\n return new Class({\n type: \"array\",\n element,\n // get element() {\n // return element;\n // },\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _union(Class, options, params) {\n return new Class({\n type: \"union\",\n options,\n ...util.normalizeParams(params),\n });\n}\nexport function _xor(Class, options, params) {\n return new Class({\n type: \"union\",\n options,\n inclusive: false,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _discriminatedUnion(Class, discriminator, options, params) {\n return new Class({\n type: \"union\",\n options,\n discriminator,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _intersection(Class, left, right) {\n return new Class({\n type: \"intersection\",\n left,\n right,\n });\n}\n// export function _tuple(\n// Class: util.SchemaClass<schemas.$ZodTuple>,\n// items: [],\n// params?: string | $ZodTupleParams\n// ): schemas.$ZodTuple<[], null>;\n// @__NO_SIDE_EFFECTS__\nexport function _tuple(Class, items, _paramsOrRest, _params) {\n const hasRest = _paramsOrRest instanceof schemas.$ZodType;\n const params = hasRest ? _params : _paramsOrRest;\n const rest = hasRest ? _paramsOrRest : null;\n return new Class({\n type: \"tuple\",\n items,\n rest,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _record(Class, keyType, valueType, params) {\n return new Class({\n type: \"record\",\n keyType,\n valueType,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _map(Class, keyType, valueType, params) {\n return new Class({\n type: \"map\",\n keyType,\n valueType,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _set(Class, valueType, params) {\n return new Class({\n type: \"set\",\n valueType,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _enum(Class, values, params) {\n const entries = Array.isArray(values) ? Object.fromEntries(values.map((v) => [v, v])) : values;\n // if (Array.isArray(values)) {\n // for (const value of values) {\n // entries[value] = value;\n // }\n // } else {\n // Object.assign(entries, values);\n // }\n // const entries: util.EnumLike = {};\n // for (const val of values) {\n // entries[val] = val;\n // }\n return new Class({\n type: \"enum\",\n entries,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\n/** @deprecated This API has been merged into `z.enum()`. Use `z.enum()` instead.\n *\n * ```ts\n * enum Colors { red, green, blue }\n * z.enum(Colors);\n * ```\n */\nexport function _nativeEnum(Class, entries, params) {\n return new Class({\n type: \"enum\",\n entries,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _literal(Class, value, params) {\n return new Class({\n type: \"literal\",\n values: Array.isArray(value) ? value : [value],\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _file(Class, params) {\n return new Class({\n type: \"file\",\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _transform(Class, fn) {\n return new Class({\n type: \"transform\",\n transform: fn,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _optional(Class, innerType) {\n return new Class({\n type: \"optional\",\n innerType,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _nullable(Class, innerType) {\n return new Class({\n type: \"nullable\",\n innerType,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _default(Class, innerType, defaultValue) {\n return new Class({\n type: \"default\",\n innerType,\n get defaultValue() {\n return typeof defaultValue === \"function\" ? defaultValue() : util.shallowClone(defaultValue);\n },\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _nonoptional(Class, innerType, params) {\n return new Class({\n type: \"nonoptional\",\n innerType,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _success(Class, innerType) {\n return new Class({\n type: \"success\",\n innerType,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _catch(Class, innerType, catchValue) {\n return new Class({\n type: \"catch\",\n innerType,\n catchValue: (typeof catchValue === \"function\" ? catchValue : () => catchValue),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _pipe(Class, in_, out) {\n return new Class({\n type: \"pipe\",\n in: in_,\n out,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _readonly(Class, innerType) {\n return new Class({\n type: \"readonly\",\n innerType,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _templateLiteral(Class, parts, params) {\n return new Class({\n type: \"template_literal\",\n parts,\n ...util.normalizeParams(params),\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _lazy(Class, getter) {\n return new Class({\n type: \"lazy\",\n getter,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _promise(Class, innerType) {\n return new Class({\n type: \"promise\",\n innerType,\n });\n}\n// @__NO_SIDE_EFFECTS__\nexport function _custom(Class, fn, _params) {\n const norm = util.normalizeParams(_params);\n norm.abort ?? (norm.abort = true); // default to abort:false\n const schema = new Class({\n type: \"custom\",\n check: \"custom\",\n fn: fn,\n ...norm,\n });\n return schema;\n}\n// same as _custom but defaults to abort:false\n// @__NO_SIDE_EFFECTS__\nexport function _refine(Class, fn, _params) {\n const schema = new Class({\n type: \"custom\",\n check: \"custom\",\n fn: fn,\n ...util.normalizeParams(_params),\n });\n return schema;\n}\n// @__NO_SIDE_EFFECTS__\nexport function _superRefine(fn) {\n const ch = _check((payload) => {\n payload.addIssue = (issue) => {\n if (typeof issue === \"string\") {\n payload.issues.push(util.issue(issue, payload.value, ch._zod.def));\n }\n else {\n // for Zod 3 backwards compatibility\n const _issue = issue;\n if (_issue.fatal)\n _issue.continue = false;\n _issue.code ?? (_issue.code = \"custom\");\n _issue.input ?? (_issue.input = payload.value);\n _issue.inst ?? (_issue.inst = ch);\n _issue.continue ?? (_issue.continue = !ch._zod.def.abort); // abort is always undefined, so this is always true...\n payload.issues.push(util.issue(_issue));\n }\n };\n return fn(payload.value, payload);\n });\n return ch;\n}\n// @__NO_SIDE_EFFECTS__\nexport function _check(fn, params) {\n const ch = new checks.$ZodCheck({\n check: \"custom\",\n ...util.normalizeParams(params),\n });\n ch._zod.check = fn;\n return ch;\n}\n// @__NO_SIDE_EFFECTS__\nexport function describe(description) {\n const ch = new checks.$ZodCheck({ check: \"describe\" });\n ch._zod.onattach = [\n (inst) => {\n const existing = registries.globalRegistry.get(inst) ?? {};\n registries.globalRegistry.add(inst, { ...existing, description });\n },\n ];\n ch._zod.check = () => { }; // no-op check\n return ch;\n}\n// @__NO_SIDE_EFFECTS__\nexport function meta(metadata) {\n const ch = new checks.$ZodCheck({ check: \"meta\" });\n ch._zod.onattach = [\n (inst) => {\n const existing = registries.globalRegistry.get(inst) ?? {};\n registries.globalRegistry.add(inst, { ...existing, ...metadata });\n },\n ];\n ch._zod.check = () => { }; // no-op check\n return ch;\n}\n// @__NO_SIDE_EFFECTS__\nexport function _stringbool(Classes, _params) {\n const params = util.normalizeParams(_params);\n let truthyArray = params.truthy ?? [\"true\", \"1\", \"yes\", \"on\", \"y\", \"enabled\"];\n let falsyArray = params.falsy ?? [\"false\", \"0\", \"no\", \"off\", \"n\", \"disabled\"];\n if (params.case !== \"sensitive\") {\n truthyArray = truthyArray.map((v) => (typeof v === \"string\" ? v.toLowerCase() : v));\n falsyArray = falsyArray.map((v) => (typeof v === \"string\" ? v.toLowerCase() : v));\n }\n const truthySet = new Set(truthyArray);\n const falsySet = new Set(falsyArray);\n const _Codec = Classes.Codec ?? schemas.$ZodCodec;\n const _Boolean = Classes.Boolean ?? schemas.$ZodBoolean;\n const _String = Classes.String ?? schemas.$ZodString;\n const stringSchema = new _String({ type: \"string\", error: params.error });\n const booleanSchema = new _Boolean({ type: \"boolean\", error: params.error });\n const codec = new _Codec({\n type: \"pipe\",\n in: stringSchema,\n out: booleanSchema,\n transform: ((input, payload) => {\n let data = input;\n if (params.case !== \"sensitive\")\n data = data.toLowerCase();\n if (truthySet.has(data)) {\n return true;\n }\n else if (falsySet.has(data)) {\n return false;\n }\n else {\n payload.issues.push({\n code: \"invalid_value\",\n expected: \"stringbool\",\n values: [...truthySet, ...falsySet],\n input: payload.value,\n inst: codec,\n continue: false,\n });\n return {};\n }\n }),\n reverseTransform: ((input, _payload) => {\n if (input === true) {\n return truthyArray[0] || \"true\";\n }\n else {\n return falsyArray[0] || \"false\";\n }\n }),\n error: params.error,\n });\n return codec;\n}\n// @__NO_SIDE_EFFECTS__\nexport function _stringFormat(Class, format, fnOrRegex, _params = {}) {\n const params = util.normalizeParams(_params);\n const def = {\n ...util.normalizeParams(_params),\n check: \"string_format\",\n type: \"string\",\n format,\n fn: typeof fnOrRegex === \"function\" ? fnOrRegex : (val) => fnOrRegex.test(val),\n ...params,\n };\n if (fnOrRegex instanceof RegExp) {\n def.pattern = fnOrRegex;\n }\n const inst = new Class(def);\n return inst;\n}\n","import { globalRegistry } from \"./registries.js\";\n// function initializeContext<T extends schemas.$ZodType>(inputs: JSONSchemaGeneratorParams<T>): ToJSONSchemaContext<T> {\n// return {\n// processor: inputs.processor,\n// metadataRegistry: inputs.metadata ?? globalRegistry,\n// target: inputs.target ?? \"draft-2020-12\",\n// unrepresentable: inputs.unrepresentable ?? \"throw\",\n// };\n// }\nexport function initializeContext(params) {\n // Normalize target: convert old non-hyphenated versions to hyphenated versions\n let target = params?.target ?? \"draft-2020-12\";\n if (target === \"draft-4\")\n target = \"draft-04\";\n if (target === \"draft-7\")\n target = \"draft-07\";\n return {\n processors: params.processors ?? {},\n metadataRegistry: params?.metadata ?? globalRegistry,\n target,\n unrepresentable: params?.unrepresentable ?? \"throw\",\n override: params?.override ?? (() => { }),\n io: params?.io ?? \"output\",\n counter: 0,\n seen: new Map(),\n cycles: params?.cycles ?? \"ref\",\n reused: params?.reused ?? \"inline\",\n external: params?.external ?? undefined,\n };\n}\nexport function process(schema, ctx, _params = { path: [], schemaPath: [] }) {\n var _a;\n const def = schema._zod.def;\n // check for schema in seens\n const seen = ctx.seen.get(schema);\n if (seen) {\n seen.count++;\n // check if cycle\n const isCycle = _params.schemaPath.includes(schema);\n if (isCycle) {\n seen.cycle = _params.path;\n }\n return seen.schema;\n }\n // initialize\n const result = { schema: {}, count: 1, cycle: undefined, path: _params.path };\n ctx.seen.set(schema, result);\n // custom method overrides default behavior\n const overrideSchema = schema._zod.toJSONSchema?.();\n if (overrideSchema) {\n result.schema = overrideSchema;\n }\n else {\n const params = {\n ..._params,\n schemaPath: [..._params.schemaPath, schema],\n path: _params.path,\n };\n if (schema._zod.processJSONSchema) {\n schema._zod.processJSONSchema(ctx, result.schema, params);\n }\n else {\n const _json = result.schema;\n const processor = ctx.processors[def.type];\n if (!processor) {\n throw new Error(`[toJSONSchema]: Non-representable type encountered: ${def.type}`);\n }\n processor(schema, ctx, _json, params);\n }\n const parent = schema._zod.parent;\n if (parent) {\n // Also set ref if processor didn't (for inheritance)\n if (!result.ref)\n result.ref = parent;\n process(parent, ctx, params);\n ctx.seen.get(parent).isParent = true;\n }\n }\n // metadata\n const meta = ctx.metadataRegistry.get(schema);\n if (meta)\n Object.assign(result.schema, meta);\n if (ctx.io === \"input\" && isTransforming(schema)) {\n // examples/defaults only apply to output type of pipe\n delete result.schema.examples;\n delete result.schema.default;\n }\n // set prefault as default\n if (ctx.io === \"input\" && result.schema._prefault)\n (_a = result.schema).default ?? (_a.default = result.schema._prefault);\n delete result.schema._prefault;\n // pulling fresh from ctx.seen in case it was overwritten\n const _result = ctx.seen.get(schema);\n return _result.schema;\n}\nexport function extractDefs(ctx, schema\n// params: EmitParams\n) {\n // iterate over seen map;\n const root = ctx.seen.get(schema);\n if (!root)\n throw new Error(\"Unprocessed schema. This is a bug in Zod.\");\n // Track ids to detect duplicates across different schemas\n const idToSchema = new Map();\n for (const entry of ctx.seen.entries()) {\n const id = ctx.metadataRegistry.get(entry[0])?.id;\n if (id) {\n const existing = idToSchema.get(id);\n if (existing && existing !== entry[0]) {\n throw new Error(`Duplicate schema id \"${id}\" detected during JSON Schema conversion. Two different schemas cannot share the same id when converted together.`);\n }\n idToSchema.set(id, entry[0]);\n }\n }\n // returns a ref to the schema\n // defId will be empty if the ref points to an external schema (or #)\n const makeURI = (entry) => {\n // comparing the seen objects because sometimes\n // multiple schemas map to the same seen object.\n // e.g. lazy\n // external is configured\n const defsSegment = ctx.target === \"draft-2020-12\" ? \"$defs\" : \"definitions\";\n if (ctx.external) {\n const externalId = ctx.external.registry.get(entry[0])?.id; // ?? \"__shared\";// `__schema${ctx.counter++}`;\n // check if schema is in the external registry\n const uriGenerator = ctx.external.uri ?? ((id) => id);\n if (externalId) {\n return { ref: uriGenerator(externalId) };\n }\n // otherwise, add to __shared\n const id = entry[1].defId ?? entry[1].schema.id ?? `schema${ctx.counter++}`;\n entry[1].defId = id; // set defId so it will be reused if needed\n return { defId: id, ref: `${uriGenerator(\"__shared\")}#/${defsSegment}/${id}` };\n }\n if (entry[1] === root) {\n return { ref: \"#\" };\n }\n // self-contained schema\n const uriPrefix = `#`;\n const defUriPrefix = `${uriPrefix}/${defsSegment}/`;\n const defId = entry[1].schema.id ?? `__schema${ctx.counter++}`;\n return { defId, ref: defUriPrefix + defId };\n };\n // stored cached version in `def` property\n // remove all properties, set $ref\n const extractToDef = (entry) => {\n // if the schema is already a reference, do not extract it\n if (entry[1].schema.$ref) {\n return;\n }\n const seen = entry[1];\n const { ref, defId } = makeURI(entry);\n seen.def = { ...seen.schema };\n // defId won't be set if the schema is a reference to an external schema\n // or if the schema is the root schema\n if (defId)\n seen.defId = defId;\n // wipe away all properties except $ref\n const schema = seen.schema;\n for (const key in schema) {\n delete schema[key];\n }\n schema.$ref = ref;\n };\n // throw on cycles\n // break cycles\n if (ctx.cycles === \"throw\") {\n for (const entry of ctx.seen.entries()) {\n const seen = entry[1];\n if (seen.cycle) {\n throw new Error(\"Cycle detected: \" +\n `#/${seen.cycle?.join(\"/\")}/<root>` +\n '\\n\\nSet the `cycles` parameter to `\"ref\"` to resolve cyclical schemas with defs.');\n }\n }\n }\n // extract schemas into $defs\n for (const entry of ctx.seen.entries()) {\n const seen = entry[1];\n // convert root schema to # $ref\n if (schema === entry[0]) {\n extractToDef(entry); // this has special handling for the root schema\n continue;\n }\n // extract schemas that are in the external registry\n if (ctx.external) {\n const ext = ctx.external.registry.get(entry[0])?.id;\n if (schema !== entry[0] && ext) {\n extractToDef(entry);\n continue;\n }\n }\n // extract schemas with `id` meta\n const id = ctx.metadataRegistry.get(entry[0])?.id;\n if (id) {\n extractToDef(entry);\n continue;\n }\n // break cycles\n if (seen.cycle) {\n // any\n extractToDef(entry);\n continue;\n }\n // extract reused schemas\n if (seen.count > 1) {\n if (ctx.reused === \"ref\") {\n extractToDef(entry);\n // biome-ignore lint:\n continue;\n }\n }\n }\n}\nexport function finalize(ctx, schema) {\n const root = ctx.seen.get(schema);\n if (!root)\n throw new Error(\"Unprocessed schema. This is a bug in Zod.\");\n // flatten refs - inherit properties from parent schemas\n const flattenRef = (zodSchema) => {\n const seen = ctx.seen.get(zodSchema);\n // already processed\n if (seen.ref === null)\n return;\n const schema = seen.def ?? seen.schema;\n const _cached = { ...schema };\n const ref = seen.ref;\n seen.ref = null; // prevent infinite recursion\n if (ref) {\n flattenRef(ref);\n const refSeen = ctx.seen.get(ref);\n const refSchema = refSeen.schema;\n // merge referenced schema into current\n if (refSchema.$ref && (ctx.target === \"draft-07\" || ctx.target === \"draft-04\" || ctx.target === \"openapi-3.0\")) {\n // older drafts can't combine $ref with other properties\n schema.allOf = schema.allOf ?? [];\n schema.allOf.push(refSchema);\n }\n else {\n Object.assign(schema, refSchema);\n }\n // restore child's own properties (child wins)\n Object.assign(schema, _cached);\n const isParentRef = zodSchema._zod.parent === ref;\n // For parent chain, child is a refinement - remove parent-only properties\n if (isParentRef) {\n for (const key in schema) {\n if (key === \"$ref\" || key === \"allOf\")\n continue;\n if (!(key in _cached)) {\n delete schema[key];\n }\n }\n }\n // When ref was extracted to $defs, remove properties that match the definition\n if (refSchema.$ref && refSeen.def) {\n for (const key in schema) {\n if (key === \"$ref\" || key === \"allOf\")\n continue;\n if (key in refSeen.def && JSON.stringify(schema[key]) === JSON.stringify(refSeen.def[key])) {\n delete schema[key];\n }\n }\n }\n }\n // If parent was extracted (has $ref), propagate $ref to this schema\n // This handles cases like: readonly().meta({id}).describe()\n // where processor sets ref to innerType but parent should be referenced\n const parent = zodSchema._zod.parent;\n if (parent && parent !== ref) {\n // Ensure parent is processed first so its def has inherited properties\n flattenRef(parent);\n const parentSeen = ctx.seen.get(parent);\n if (parentSeen?.schema.$ref) {\n schema.$ref = parentSeen.schema.$ref;\n // De-duplicate with parent's definition\n if (parentSeen.def) {\n for (const key in schema) {\n if (key === \"$ref\" || key === \"allOf\")\n continue;\n if (key in parentSeen.def && JSON.stringify(schema[key]) === JSON.stringify(parentSeen.def[key])) {\n delete schema[key];\n }\n }\n }\n }\n }\n // execute overrides\n ctx.override({\n zodSchema: zodSchema,\n jsonSchema: schema,\n path: seen.path ?? [],\n });\n };\n for (const entry of [...ctx.seen.entries()].reverse()) {\n flattenRef(entry[0]);\n }\n const result = {};\n if (ctx.target === \"draft-2020-12\") {\n result.$schema = \"https://json-schema.org/draft/2020-12/schema\";\n }\n else if (ctx.target === \"draft-07\") {\n result.$schema = \"http://json-schema.org/draft-07/schema#\";\n }\n else if (ctx.target === \"draft-04\") {\n result.$schema = \"http://json-schema.org/draft-04/schema#\";\n }\n else if (ctx.target === \"openapi-3.0\") {\n // OpenAPI 3.0 schema objects should not include a $schema property\n }\n else {\n // Arbitrary string values are allowed but won't have a $schema property set\n }\n if (ctx.external?.uri) {\n const id = ctx.external.registry.get(schema)?.id;\n if (!id)\n throw new Error(\"Schema is missing an `id` property\");\n result.$id = ctx.external.uri(id);\n }\n Object.assign(result, root.def ?? root.schema);\n // build defs object\n const defs = ctx.external?.defs ?? {};\n for (const entry of ctx.seen.entries()) {\n const seen = entry[1];\n if (seen.def && seen.defId) {\n defs[seen.defId] = seen.def;\n }\n }\n // set definitions in result\n if (ctx.external) {\n }\n else {\n if (Object.keys(defs).length > 0) {\n if (ctx.target === \"draft-2020-12\") {\n result.$defs = defs;\n }\n else {\n result.definitions = defs;\n }\n }\n }\n try {\n // this \"finalizes\" this schema and ensures all cycles are removed\n // each call to finalize() is functionally independent\n // though the seen map is shared\n const finalized = JSON.parse(JSON.stringify(result));\n Object.defineProperty(finalized, \"~standard\", {\n value: {\n ...schema[\"~standard\"],\n jsonSchema: {\n input: createStandardJSONSchemaMethod(schema, \"input\", ctx.processors),\n output: createStandardJSONSchemaMethod(schema, \"output\", ctx.processors),\n },\n },\n enumerable: false,\n writable: false,\n });\n return finalized;\n }\n catch (_err) {\n throw new Error(\"Error converting schema to JSON.\");\n }\n}\nfunction isTransforming(_schema, _ctx) {\n const ctx = _ctx ?? { seen: new Set() };\n if (ctx.seen.has(_schema))\n return false;\n ctx.seen.add(_schema);\n const def = _schema._zod.def;\n if (def.type === \"transform\")\n return true;\n if (def.type === \"array\")\n return isTransforming(def.element, ctx);\n if (def.type === \"set\")\n return isTransforming(def.valueType, ctx);\n if (def.type === \"lazy\")\n return isTransforming(def.getter(), ctx);\n if (def.type === \"promise\" ||\n def.type === \"optional\" ||\n def.type === \"nonoptional\" ||\n def.type === \"nullable\" ||\n def.type === \"readonly\" ||\n def.type === \"default\" ||\n def.type === \"prefault\") {\n return isTransforming(def.innerType, ctx);\n }\n if (def.type === \"intersection\") {\n return isTransforming(def.left, ctx) || isTransforming(def.right, ctx);\n }\n if (def.type === \"record\" || def.type === \"map\") {\n return isTransforming(def.keyType, ctx) || isTransforming(def.valueType, ctx);\n }\n if (def.type === \"pipe\") {\n return isTransforming(def.in, ctx) || isTransforming(def.out, ctx);\n }\n if (def.type === \"object\") {\n for (const key in def.shape) {\n if (isTransforming(def.shape[key], ctx))\n return true;\n }\n return false;\n }\n if (def.type === \"union\") {\n for (const option of def.options) {\n if (isTransforming(option, ctx))\n return true;\n }\n return false;\n }\n if (def.type === \"tuple\") {\n for (const item of def.items) {\n if (isTransforming(item, ctx))\n return true;\n }\n if (def.rest && isTransforming(def.rest, ctx))\n return true;\n return false;\n }\n return false;\n}\n/**\n * Creates a toJSONSchema method for a schema instance.\n * This encapsulates the logic of initializing context, processing, extracting defs, and finalizing.\n */\nexport const createToJSONSchemaMethod = (schema, processors = {}) => (params) => {\n const ctx = initializeContext({ ...params, processors });\n process(schema, ctx);\n extractDefs(ctx, schema);\n return finalize(ctx, schema);\n};\nexport const createStandardJSONSchemaMethod = (schema, io, processors = {}) => (params) => {\n const { libraryOptions, target } = params ?? {};\n const ctx = initializeContext({ ...(libraryOptions ?? {}), target, io, processors });\n process(schema, ctx);\n extractDefs(ctx, schema);\n return finalize(ctx, schema);\n};\n","import { extractDefs, finalize, initializeContext, process, } from \"./to-json-schema.js\";\nimport { getEnumValues } from \"./util.js\";\nconst formatMap = {\n guid: \"uuid\",\n url: \"uri\",\n datetime: \"date-time\",\n json_string: \"json-string\",\n regex: \"\", // do not set\n};\n// ==================== SIMPLE TYPE PROCESSORS ====================\nexport const stringProcessor = (schema, ctx, _json, _params) => {\n const json = _json;\n json.type = \"string\";\n const { minimum, maximum, format, patterns, contentEncoding } = schema._zod\n .bag;\n if (typeof minimum === \"number\")\n json.minLength = minimum;\n if (typeof maximum === \"number\")\n json.maxLength = maximum;\n // custom pattern overrides format\n if (format) {\n json.format = formatMap[format] ?? format;\n if (json.format === \"\")\n delete json.format; // empty format is not valid\n // JSON Schema format: \"time\" requires a full time with offset or Z\n // z.iso.time() does not include timezone information, so format: \"time\" should never be used\n if (format === \"time\") {\n delete json.format;\n }\n }\n if (contentEncoding)\n json.contentEncoding = contentEncoding;\n if (patterns && patterns.size > 0) {\n const regexes = [...patterns];\n if (regexes.length === 1)\n json.pattern = regexes[0].source;\n else if (regexes.length > 1) {\n json.allOf = [\n ...regexes.map((regex) => ({\n ...(ctx.target === \"draft-07\" || ctx.target === \"draft-04\" || ctx.target === \"openapi-3.0\"\n ? { type: \"string\" }\n : {}),\n pattern: regex.source,\n })),\n ];\n }\n }\n};\nexport const numberProcessor = (schema, ctx, _json, _params) => {\n const json = _json;\n const { minimum, maximum, format, multipleOf, exclusiveMaximum, exclusiveMinimum } = schema._zod.bag;\n if (typeof format === \"string\" && format.includes(\"int\"))\n json.type = \"integer\";\n else\n json.type = \"number\";\n if (typeof exclusiveMinimum === \"number\") {\n if (ctx.target === \"draft-04\" || ctx.target === \"openapi-3.0\") {\n json.minimum = exclusiveMinimum;\n json.exclusiveMinimum = true;\n }\n else {\n json.exclusiveMinimum = exclusiveMinimum;\n }\n }\n if (typeof minimum === \"number\") {\n json.minimum = minimum;\n if (typeof exclusiveMinimum === \"number\" && ctx.target !== \"draft-04\") {\n if (exclusiveMinimum >= minimum)\n delete json.minimum;\n else\n delete json.exclusiveMinimum;\n }\n }\n if (typeof exclusiveMaximum === \"number\") {\n if (ctx.target === \"draft-04\" || ctx.target === \"openapi-3.0\") {\n json.maximum = exclusiveMaximum;\n json.exclusiveMaximum = true;\n }\n else {\n json.exclusiveMaximum = exclusiveMaximum;\n }\n }\n if (typeof maximum === \"number\") {\n json.maximum = maximum;\n if (typeof exclusiveMaximum === \"number\" && ctx.target !== \"draft-04\") {\n if (exclusiveMaximum <= maximum)\n delete json.maximum;\n else\n delete json.exclusiveMaximum;\n }\n }\n if (typeof multipleOf === \"number\")\n json.multipleOf = multipleOf;\n};\nexport const booleanProcessor = (_schema, _ctx, json, _params) => {\n json.type = \"boolean\";\n};\nexport const bigintProcessor = (_schema, ctx, _json, _params) => {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"BigInt cannot be represented in JSON Schema\");\n }\n};\nexport const symbolProcessor = (_schema, ctx, _json, _params) => {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"Symbols cannot be represented in JSON Schema\");\n }\n};\nexport const nullProcessor = (_schema, ctx, json, _params) => {\n if (ctx.target === \"openapi-3.0\") {\n json.type = \"string\";\n json.nullable = true;\n json.enum = [null];\n }\n else {\n json.type = \"null\";\n }\n};\nexport const undefinedProcessor = (_schema, ctx, _json, _params) => {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"Undefined cannot be represented in JSON Schema\");\n }\n};\nexport const voidProcessor = (_schema, ctx, _json, _params) => {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"Void cannot be represented in JSON Schema\");\n }\n};\nexport const neverProcessor = (_schema, _ctx, json, _params) => {\n json.not = {};\n};\nexport const anyProcessor = (_schema, _ctx, _json, _params) => {\n // empty schema accepts anything\n};\nexport const unknownProcessor = (_schema, _ctx, _json, _params) => {\n // empty schema accepts anything\n};\nexport const dateProcessor = (_schema, ctx, _json, _params) => {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"Date cannot be represented in JSON Schema\");\n }\n};\nexport const enumProcessor = (schema, _ctx, json, _params) => {\n const def = schema._zod.def;\n const values = getEnumValues(def.entries);\n // Number enums can have both string and number values\n if (values.every((v) => typeof v === \"number\"))\n json.type = \"number\";\n if (values.every((v) => typeof v === \"string\"))\n json.type = \"string\";\n json.enum = values;\n};\nexport const literalProcessor = (schema, ctx, json, _params) => {\n const def = schema._zod.def;\n const vals = [];\n for (const val of def.values) {\n if (val === undefined) {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"Literal `undefined` cannot be represented in JSON Schema\");\n }\n else {\n // do not add to vals\n }\n }\n else if (typeof val === \"bigint\") {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"BigInt literals cannot be represented in JSON Schema\");\n }\n else {\n vals.push(Number(val));\n }\n }\n else {\n vals.push(val);\n }\n }\n if (vals.length === 0) {\n // do nothing (an undefined literal was stripped)\n }\n else if (vals.length === 1) {\n const val = vals[0];\n json.type = val === null ? \"null\" : typeof val;\n if (ctx.target === \"draft-04\" || ctx.target === \"openapi-3.0\") {\n json.enum = [val];\n }\n else {\n json.const = val;\n }\n }\n else {\n if (vals.every((v) => typeof v === \"number\"))\n json.type = \"number\";\n if (vals.every((v) => typeof v === \"string\"))\n json.type = \"string\";\n if (vals.every((v) => typeof v === \"boolean\"))\n json.type = \"boolean\";\n if (vals.every((v) => v === null))\n json.type = \"null\";\n json.enum = vals;\n }\n};\nexport const nanProcessor = (_schema, ctx, _json, _params) => {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"NaN cannot be represented in JSON Schema\");\n }\n};\nexport const templateLiteralProcessor = (schema, _ctx, json, _params) => {\n const _json = json;\n const pattern = schema._zod.pattern;\n if (!pattern)\n throw new Error(\"Pattern not found in template literal\");\n _json.type = \"string\";\n _json.pattern = pattern.source;\n};\nexport const fileProcessor = (schema, _ctx, json, _params) => {\n const _json = json;\n const file = {\n type: \"string\",\n format: \"binary\",\n contentEncoding: \"binary\",\n };\n const { minimum, maximum, mime } = schema._zod.bag;\n if (minimum !== undefined)\n file.minLength = minimum;\n if (maximum !== undefined)\n file.maxLength = maximum;\n if (mime) {\n if (mime.length === 1) {\n file.contentMediaType = mime[0];\n Object.assign(_json, file);\n }\n else {\n Object.assign(_json, file); // shared props at root\n _json.anyOf = mime.map((m) => ({ contentMediaType: m })); // only contentMediaType differs\n }\n }\n else {\n Object.assign(_json, file);\n }\n};\nexport const successProcessor = (_schema, _ctx, json, _params) => {\n json.type = \"boolean\";\n};\nexport const customProcessor = (_schema, ctx, _json, _params) => {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"Custom types cannot be represented in JSON Schema\");\n }\n};\nexport const functionProcessor = (_schema, ctx, _json, _params) => {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"Function types cannot be represented in JSON Schema\");\n }\n};\nexport const transformProcessor = (_schema, ctx, _json, _params) => {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"Transforms cannot be represented in JSON Schema\");\n }\n};\nexport const mapProcessor = (_schema, ctx, _json, _params) => {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"Map cannot be represented in JSON Schema\");\n }\n};\nexport const setProcessor = (_schema, ctx, _json, _params) => {\n if (ctx.unrepresentable === \"throw\") {\n throw new Error(\"Set cannot be represented in JSON Schema\");\n }\n};\n// ==================== COMPOSITE TYPE PROCESSORS ====================\nexport const arrayProcessor = (schema, ctx, _json, params) => {\n const json = _json;\n const def = schema._zod.def;\n const { minimum, maximum } = schema._zod.bag;\n if (typeof minimum === \"number\")\n json.minItems = minimum;\n if (typeof maximum === \"number\")\n json.maxItems = maximum;\n json.type = \"array\";\n json.items = process(def.element, ctx, { ...params, path: [...params.path, \"items\"] });\n};\nexport const objectProcessor = (schema, ctx, _json, params) => {\n const json = _json;\n const def = schema._zod.def;\n json.type = \"object\";\n json.properties = {};\n const shape = def.shape;\n for (const key in shape) {\n json.properties[key] = process(shape[key], ctx, {\n ...params,\n path: [...params.path, \"properties\", key],\n });\n }\n // required keys\n const allKeys = new Set(Object.keys(shape));\n const requiredKeys = new Set([...allKeys].filter((key) => {\n const v = def.shape[key]._zod;\n if (ctx.io === \"input\") {\n return v.optin === undefined;\n }\n else {\n return v.optout === undefined;\n }\n }));\n if (requiredKeys.size > 0) {\n json.required = Array.from(requiredKeys);\n }\n // catchall\n if (def.catchall?._zod.def.type === \"never\") {\n // strict\n json.additionalProperties = false;\n }\n else if (!def.catchall) {\n // regular\n if (ctx.io === \"output\")\n json.additionalProperties = false;\n }\n else if (def.catchall) {\n json.additionalProperties = process(def.catchall, ctx, {\n ...params,\n path: [...params.path, \"additionalProperties\"],\n });\n }\n};\nexport const unionProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n // Exclusive unions (inclusive === false) use oneOf (exactly one match) instead of anyOf (one or more matches)\n // This includes both z.xor() and discriminated unions\n const isExclusive = def.inclusive === false;\n const options = def.options.map((x, i) => process(x, ctx, {\n ...params,\n path: [...params.path, isExclusive ? \"oneOf\" : \"anyOf\", i],\n }));\n if (isExclusive) {\n json.oneOf = options;\n }\n else {\n json.anyOf = options;\n }\n};\nexport const intersectionProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n const a = process(def.left, ctx, {\n ...params,\n path: [...params.path, \"allOf\", 0],\n });\n const b = process(def.right, ctx, {\n ...params,\n path: [...params.path, \"allOf\", 1],\n });\n const isSimpleIntersection = (val) => \"allOf\" in val && Object.keys(val).length === 1;\n const allOf = [\n ...(isSimpleIntersection(a) ? a.allOf : [a]),\n ...(isSimpleIntersection(b) ? b.allOf : [b]),\n ];\n json.allOf = allOf;\n};\nexport const tupleProcessor = (schema, ctx, _json, params) => {\n const json = _json;\n const def = schema._zod.def;\n json.type = \"array\";\n const prefixPath = ctx.target === \"draft-2020-12\" ? \"prefixItems\" : \"items\";\n const restPath = ctx.target === \"draft-2020-12\" ? \"items\" : ctx.target === \"openapi-3.0\" ? \"items\" : \"additionalItems\";\n const prefixItems = def.items.map((x, i) => process(x, ctx, {\n ...params,\n path: [...params.path, prefixPath, i],\n }));\n const rest = def.rest\n ? process(def.rest, ctx, {\n ...params,\n path: [...params.path, restPath, ...(ctx.target === \"openapi-3.0\" ? [def.items.length] : [])],\n })\n : null;\n if (ctx.target === \"draft-2020-12\") {\n json.prefixItems = prefixItems;\n if (rest) {\n json.items = rest;\n }\n }\n else if (ctx.target === \"openapi-3.0\") {\n json.items = {\n anyOf: prefixItems,\n };\n if (rest) {\n json.items.anyOf.push(rest);\n }\n json.minItems = prefixItems.length;\n if (!rest) {\n json.maxItems = prefixItems.length;\n }\n }\n else {\n json.items = prefixItems;\n if (rest) {\n json.additionalItems = rest;\n }\n }\n // length\n const { minimum, maximum } = schema._zod.bag;\n if (typeof minimum === \"number\")\n json.minItems = minimum;\n if (typeof maximum === \"number\")\n json.maxItems = maximum;\n};\nexport const recordProcessor = (schema, ctx, _json, params) => {\n const json = _json;\n const def = schema._zod.def;\n json.type = \"object\";\n // For looseRecord with regex patterns, use patternProperties\n // This correctly represents \"only validate keys matching the pattern\" semantics\n // and composes well with allOf (intersections)\n const keyType = def.keyType;\n const keyBag = keyType._zod.bag;\n const patterns = keyBag?.patterns;\n if (def.mode === \"loose\" && patterns && patterns.size > 0) {\n // Use patternProperties for looseRecord with regex patterns\n const valueSchema = process(def.valueType, ctx, {\n ...params,\n path: [...params.path, \"patternProperties\", \"*\"],\n });\n json.patternProperties = {};\n for (const pattern of patterns) {\n json.patternProperties[pattern.source] = valueSchema;\n }\n }\n else {\n // Default behavior: use propertyNames + additionalProperties\n if (ctx.target === \"draft-07\" || ctx.target === \"draft-2020-12\") {\n json.propertyNames = process(def.keyType, ctx, {\n ...params,\n path: [...params.path, \"propertyNames\"],\n });\n }\n json.additionalProperties = process(def.valueType, ctx, {\n ...params,\n path: [...params.path, \"additionalProperties\"],\n });\n }\n // Add required for keys with discrete values (enum, literal, etc.)\n const keyValues = keyType._zod.values;\n if (keyValues) {\n const validKeyValues = [...keyValues].filter((v) => typeof v === \"string\" || typeof v === \"number\");\n if (validKeyValues.length > 0) {\n json.required = validKeyValues;\n }\n }\n};\nexport const nullableProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n const inner = process(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n if (ctx.target === \"openapi-3.0\") {\n seen.ref = def.innerType;\n json.nullable = true;\n }\n else {\n json.anyOf = [inner, { type: \"null\" }];\n }\n};\nexport const nonoptionalProcessor = (schema, ctx, _json, params) => {\n const def = schema._zod.def;\n process(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n};\nexport const defaultProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n process(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n json.default = JSON.parse(JSON.stringify(def.defaultValue));\n};\nexport const prefaultProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n process(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n if (ctx.io === \"input\")\n json._prefault = JSON.parse(JSON.stringify(def.defaultValue));\n};\nexport const catchProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n process(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n let catchValue;\n try {\n catchValue = def.catchValue(undefined);\n }\n catch {\n throw new Error(\"Dynamic catch values are not supported in JSON Schema\");\n }\n json.default = catchValue;\n};\nexport const pipeProcessor = (schema, ctx, _json, params) => {\n const def = schema._zod.def;\n const innerType = ctx.io === \"input\" ? (def.in._zod.def.type === \"transform\" ? def.out : def.in) : def.out;\n process(innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = innerType;\n};\nexport const readonlyProcessor = (schema, ctx, json, params) => {\n const def = schema._zod.def;\n process(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n json.readOnly = true;\n};\nexport const promiseProcessor = (schema, ctx, _json, params) => {\n const def = schema._zod.def;\n process(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n};\nexport const optionalProcessor = (schema, ctx, _json, params) => {\n const def = schema._zod.def;\n process(def.innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = def.innerType;\n};\nexport const lazyProcessor = (schema, ctx, _json, params) => {\n const innerType = schema._zod.innerType;\n process(innerType, ctx, params);\n const seen = ctx.seen.get(schema);\n seen.ref = innerType;\n};\n// ==================== ALL PROCESSORS ====================\nexport const allProcessors = {\n string: stringProcessor,\n number: numberProcessor,\n boolean: booleanProcessor,\n bigint: bigintProcessor,\n symbol: symbolProcessor,\n null: nullProcessor,\n undefined: undefinedProcessor,\n void: voidProcessor,\n never: neverProcessor,\n any: anyProcessor,\n unknown: unknownProcessor,\n date: dateProcessor,\n enum: enumProcessor,\n literal: literalProcessor,\n nan: nanProcessor,\n template_literal: templateLiteralProcessor,\n file: fileProcessor,\n success: successProcessor,\n custom: customProcessor,\n function: functionProcessor,\n transform: transformProcessor,\n map: mapProcessor,\n set: setProcessor,\n array: arrayProcessor,\n object: objectProcessor,\n union: unionProcessor,\n intersection: intersectionProcessor,\n tuple: tupleProcessor,\n record: recordProcessor,\n nullable: nullableProcessor,\n nonoptional: nonoptionalProcessor,\n default: defaultProcessor,\n prefault: prefaultProcessor,\n catch: catchProcessor,\n pipe: pipeProcessor,\n readonly: readonlyProcessor,\n promise: promiseProcessor,\n optional: optionalProcessor,\n lazy: lazyProcessor,\n};\nexport function toJSONSchema(input, params) {\n if (\"_idmap\" in input) {\n // Registry case\n const registry = input;\n const ctx = initializeContext({ ...params, processors: allProcessors });\n const defs = {};\n // First pass: process all schemas to build the seen map\n for (const entry of registry._idmap.entries()) {\n const [_, schema] = entry;\n process(schema, ctx);\n }\n const schemas = {};\n const external = {\n registry,\n uri: params?.uri,\n defs,\n };\n // Update the context with external configuration\n ctx.external = external;\n // Second pass: emit each schema\n for (const entry of registry._idmap.entries()) {\n const [key, schema] = entry;\n extractDefs(ctx, schema);\n schemas[key] = finalize(ctx, schema);\n }\n if (Object.keys(defs).length > 0) {\n const defsSegment = ctx.target === \"draft-2020-12\" ? \"$defs\" : \"definitions\";\n schemas.__shared = {\n [defsSegment]: defs,\n };\n }\n return { schemas };\n }\n // Single schema case\n const ctx = initializeContext({ ...params, processors: allProcessors });\n process(input, ctx);\n extractDefs(ctx, input);\n return finalize(ctx, input);\n}\n","import * as core from \"../core/index.js\";\nimport * as schemas from \"./schemas.js\";\nexport const ZodISODateTime = /*@__PURE__*/ core.$constructor(\"ZodISODateTime\", (inst, def) => {\n core.$ZodISODateTime.init(inst, def);\n schemas.ZodStringFormat.init(inst, def);\n});\nexport function datetime(params) {\n return core._isoDateTime(ZodISODateTime, params);\n}\nexport const ZodISODate = /*@__PURE__*/ core.$constructor(\"ZodISODate\", (inst, def) => {\n core.$ZodISODate.init(inst, def);\n schemas.ZodStringFormat.init(inst, def);\n});\nexport function date(params) {\n return core._isoDate(ZodISODate, params);\n}\nexport const ZodISOTime = /*@__PURE__*/ core.$constructor(\"ZodISOTime\", (inst, def) => {\n core.$ZodISOTime.init(inst, def);\n schemas.ZodStringFormat.init(inst, def);\n});\nexport function time(params) {\n return core._isoTime(ZodISOTime, params);\n}\nexport const ZodISODuration = /*@__PURE__*/ core.$constructor(\"ZodISODuration\", (inst, def) => {\n core.$ZodISODuration.init(inst, def);\n schemas.ZodStringFormat.init(inst, def);\n});\nexport function duration(params) {\n return core._isoDuration(ZodISODuration, params);\n}\n","import * as core from \"../core/index.js\";\nimport { $ZodError } from \"../core/index.js\";\nimport * as util from \"../core/util.js\";\nconst initializer = (inst, issues) => {\n $ZodError.init(inst, issues);\n inst.name = \"ZodError\";\n Object.defineProperties(inst, {\n format: {\n value: (mapper) => core.formatError(inst, mapper),\n // enumerable: false,\n },\n flatten: {\n value: (mapper) => core.flattenError(inst, mapper),\n // enumerable: false,\n },\n addIssue: {\n value: (issue) => {\n inst.issues.push(issue);\n inst.message = JSON.stringify(inst.issues, util.jsonStringifyReplacer, 2);\n },\n // enumerable: false,\n },\n addIssues: {\n value: (issues) => {\n inst.issues.push(...issues);\n inst.message = JSON.stringify(inst.issues, util.jsonStringifyReplacer, 2);\n },\n // enumerable: false,\n },\n isEmpty: {\n get() {\n return inst.issues.length === 0;\n },\n // enumerable: false,\n },\n });\n // Object.defineProperty(inst, \"isEmpty\", {\n // get() {\n // return inst.issues.length === 0;\n // },\n // });\n};\nexport const ZodError = core.$constructor(\"ZodError\", initializer);\nexport const ZodRealError = core.$constructor(\"ZodError\", initializer, {\n Parent: Error,\n});\n// /** @deprecated Use `z.core.$ZodErrorMapCtx` instead. */\n// export type ErrorMapCtx = core.$ZodErrorMapCtx;\n","import * as core from \"../core/index.js\";\nimport { ZodRealError } from \"./errors.js\";\nexport const parse = /* @__PURE__ */ core._parse(ZodRealError);\nexport const parseAsync = /* @__PURE__ */ core._parseAsync(ZodRealError);\nexport const safeParse = /* @__PURE__ */ core._safeParse(ZodRealError);\nexport const safeParseAsync = /* @__PURE__ */ core._safeParseAsync(ZodRealError);\n// Codec functions\nexport const encode = /* @__PURE__ */ core._encode(ZodRealError);\nexport const decode = /* @__PURE__ */ core._decode(ZodRealError);\nexport const encodeAsync = /* @__PURE__ */ core._encodeAsync(ZodRealError);\nexport const decodeAsync = /* @__PURE__ */ core._decodeAsync(ZodRealError);\nexport const safeEncode = /* @__PURE__ */ core._safeEncode(ZodRealError);\nexport const safeDecode = /* @__PURE__ */ core._safeDecode(ZodRealError);\nexport const safeEncodeAsync = /* @__PURE__ */ core._safeEncodeAsync(ZodRealError);\nexport const safeDecodeAsync = /* @__PURE__ */ core._safeDecodeAsync(ZodRealError);\n","import * as core from \"../core/index.js\";\nimport { util } from \"../core/index.js\";\nimport * as processors from \"../core/json-schema-processors.js\";\nimport { createStandardJSONSchemaMethod, createToJSONSchemaMethod } from \"../core/to-json-schema.js\";\nimport * as checks from \"./checks.js\";\nimport * as iso from \"./iso.js\";\nimport * as parse from \"./parse.js\";\nexport const ZodType = /*@__PURE__*/ core.$constructor(\"ZodType\", (inst, def) => {\n core.$ZodType.init(inst, def);\n Object.assign(inst[\"~standard\"], {\n jsonSchema: {\n input: createStandardJSONSchemaMethod(inst, \"input\"),\n output: createStandardJSONSchemaMethod(inst, \"output\"),\n },\n });\n inst.toJSONSchema = createToJSONSchemaMethod(inst, {});\n inst.def = def;\n inst.type = def.type;\n Object.defineProperty(inst, \"_def\", { value: def });\n // base methods\n inst.check = (...checks) => {\n return inst.clone(util.mergeDefs(def, {\n checks: [\n ...(def.checks ?? []),\n ...checks.map((ch) => typeof ch === \"function\" ? { _zod: { check: ch, def: { check: \"custom\" }, onattach: [] } } : ch),\n ],\n }), {\n parent: true,\n });\n };\n inst.with = inst.check;\n inst.clone = (def, params) => core.clone(inst, def, params);\n inst.brand = () => inst;\n inst.register = ((reg, meta) => {\n reg.add(inst, meta);\n return inst;\n });\n // parsing\n inst.parse = (data, params) => parse.parse(inst, data, params, { callee: inst.parse });\n inst.safeParse = (data, params) => parse.safeParse(inst, data, params);\n inst.parseAsync = async (data, params) => parse.parseAsync(inst, data, params, { callee: inst.parseAsync });\n inst.safeParseAsync = async (data, params) => parse.safeParseAsync(inst, data, params);\n inst.spa = inst.safeParseAsync;\n // encoding/decoding\n inst.encode = (data, params) => parse.encode(inst, data, params);\n inst.decode = (data, params) => parse.decode(inst, data, params);\n inst.encodeAsync = async (data, params) => parse.encodeAsync(inst, data, params);\n inst.decodeAsync = async (data, params) => parse.decodeAsync(inst, data, params);\n inst.safeEncode = (data, params) => parse.safeEncode(inst, data, params);\n inst.safeDecode = (data, params) => parse.safeDecode(inst, data, params);\n inst.safeEncodeAsync = async (data, params) => parse.safeEncodeAsync(inst, data, params);\n inst.safeDecodeAsync = async (data, params) => parse.safeDecodeAsync(inst, data, params);\n // refinements\n inst.refine = (check, params) => inst.check(refine(check, params));\n inst.superRefine = (refinement) => inst.check(superRefine(refinement));\n inst.overwrite = (fn) => inst.check(checks.overwrite(fn));\n // wrappers\n inst.optional = () => optional(inst);\n inst.exactOptional = () => exactOptional(inst);\n inst.nullable = () => nullable(inst);\n inst.nullish = () => optional(nullable(inst));\n inst.nonoptional = (params) => nonoptional(inst, params);\n inst.array = () => array(inst);\n inst.or = (arg) => union([inst, arg]);\n inst.and = (arg) => intersection(inst, arg);\n inst.transform = (tx) => pipe(inst, transform(tx));\n inst.default = (def) => _default(inst, def);\n inst.prefault = (def) => prefault(inst, def);\n // inst.coalesce = (def, params) => coalesce(inst, def, params);\n inst.catch = (params) => _catch(inst, params);\n inst.pipe = (target) => pipe(inst, target);\n inst.readonly = () => readonly(inst);\n // meta\n inst.describe = (description) => {\n const cl = inst.clone();\n core.globalRegistry.add(cl, { description });\n return cl;\n };\n Object.defineProperty(inst, \"description\", {\n get() {\n return core.globalRegistry.get(inst)?.description;\n },\n configurable: true,\n });\n inst.meta = (...args) => {\n if (args.length === 0) {\n return core.globalRegistry.get(inst);\n }\n const cl = inst.clone();\n core.globalRegistry.add(cl, args[0]);\n return cl;\n };\n // helpers\n inst.isOptional = () => inst.safeParse(undefined).success;\n inst.isNullable = () => inst.safeParse(null).success;\n inst.apply = (fn) => fn(inst);\n return inst;\n});\n/** @internal */\nexport const _ZodString = /*@__PURE__*/ core.$constructor(\"_ZodString\", (inst, def) => {\n core.$ZodString.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.stringProcessor(inst, ctx, json, params);\n const bag = inst._zod.bag;\n inst.format = bag.format ?? null;\n inst.minLength = bag.minimum ?? null;\n inst.maxLength = bag.maximum ?? null;\n // validations\n inst.regex = (...args) => inst.check(checks.regex(...args));\n inst.includes = (...args) => inst.check(checks.includes(...args));\n inst.startsWith = (...args) => inst.check(checks.startsWith(...args));\n inst.endsWith = (...args) => inst.check(checks.endsWith(...args));\n inst.min = (...args) => inst.check(checks.minLength(...args));\n inst.max = (...args) => inst.check(checks.maxLength(...args));\n inst.length = (...args) => inst.check(checks.length(...args));\n inst.nonempty = (...args) => inst.check(checks.minLength(1, ...args));\n inst.lowercase = (params) => inst.check(checks.lowercase(params));\n inst.uppercase = (params) => inst.check(checks.uppercase(params));\n // transforms\n inst.trim = () => inst.check(checks.trim());\n inst.normalize = (...args) => inst.check(checks.normalize(...args));\n inst.toLowerCase = () => inst.check(checks.toLowerCase());\n inst.toUpperCase = () => inst.check(checks.toUpperCase());\n inst.slugify = () => inst.check(checks.slugify());\n});\nexport const ZodString = /*@__PURE__*/ core.$constructor(\"ZodString\", (inst, def) => {\n core.$ZodString.init(inst, def);\n _ZodString.init(inst, def);\n inst.email = (params) => inst.check(core._email(ZodEmail, params));\n inst.url = (params) => inst.check(core._url(ZodURL, params));\n inst.jwt = (params) => inst.check(core._jwt(ZodJWT, params));\n inst.emoji = (params) => inst.check(core._emoji(ZodEmoji, params));\n inst.guid = (params) => inst.check(core._guid(ZodGUID, params));\n inst.uuid = (params) => inst.check(core._uuid(ZodUUID, params));\n inst.uuidv4 = (params) => inst.check(core._uuidv4(ZodUUID, params));\n inst.uuidv6 = (params) => inst.check(core._uuidv6(ZodUUID, params));\n inst.uuidv7 = (params) => inst.check(core._uuidv7(ZodUUID, params));\n inst.nanoid = (params) => inst.check(core._nanoid(ZodNanoID, params));\n inst.guid = (params) => inst.check(core._guid(ZodGUID, params));\n inst.cuid = (params) => inst.check(core._cuid(ZodCUID, params));\n inst.cuid2 = (params) => inst.check(core._cuid2(ZodCUID2, params));\n inst.ulid = (params) => inst.check(core._ulid(ZodULID, params));\n inst.base64 = (params) => inst.check(core._base64(ZodBase64, params));\n inst.base64url = (params) => inst.check(core._base64url(ZodBase64URL, params));\n inst.xid = (params) => inst.check(core._xid(ZodXID, params));\n inst.ksuid = (params) => inst.check(core._ksuid(ZodKSUID, params));\n inst.ipv4 = (params) => inst.check(core._ipv4(ZodIPv4, params));\n inst.ipv6 = (params) => inst.check(core._ipv6(ZodIPv6, params));\n inst.cidrv4 = (params) => inst.check(core._cidrv4(ZodCIDRv4, params));\n inst.cidrv6 = (params) => inst.check(core._cidrv6(ZodCIDRv6, params));\n inst.e164 = (params) => inst.check(core._e164(ZodE164, params));\n // iso\n inst.datetime = (params) => inst.check(iso.datetime(params));\n inst.date = (params) => inst.check(iso.date(params));\n inst.time = (params) => inst.check(iso.time(params));\n inst.duration = (params) => inst.check(iso.duration(params));\n});\nexport function string(params) {\n return core._string(ZodString, params);\n}\nexport const ZodStringFormat = /*@__PURE__*/ core.$constructor(\"ZodStringFormat\", (inst, def) => {\n core.$ZodStringFormat.init(inst, def);\n _ZodString.init(inst, def);\n});\nexport const ZodEmail = /*@__PURE__*/ core.$constructor(\"ZodEmail\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodEmail.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function email(params) {\n return core._email(ZodEmail, params);\n}\nexport const ZodGUID = /*@__PURE__*/ core.$constructor(\"ZodGUID\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodGUID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function guid(params) {\n return core._guid(ZodGUID, params);\n}\nexport const ZodUUID = /*@__PURE__*/ core.$constructor(\"ZodUUID\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodUUID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function uuid(params) {\n return core._uuid(ZodUUID, params);\n}\nexport function uuidv4(params) {\n return core._uuidv4(ZodUUID, params);\n}\n// ZodUUIDv6\nexport function uuidv6(params) {\n return core._uuidv6(ZodUUID, params);\n}\n// ZodUUIDv7\nexport function uuidv7(params) {\n return core._uuidv7(ZodUUID, params);\n}\nexport const ZodURL = /*@__PURE__*/ core.$constructor(\"ZodURL\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodURL.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function url(params) {\n return core._url(ZodURL, params);\n}\nexport function httpUrl(params) {\n return core._url(ZodURL, {\n protocol: /^https?$/,\n hostname: core.regexes.domain,\n ...util.normalizeParams(params),\n });\n}\nexport const ZodEmoji = /*@__PURE__*/ core.$constructor(\"ZodEmoji\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodEmoji.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function emoji(params) {\n return core._emoji(ZodEmoji, params);\n}\nexport const ZodNanoID = /*@__PURE__*/ core.$constructor(\"ZodNanoID\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodNanoID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function nanoid(params) {\n return core._nanoid(ZodNanoID, params);\n}\nexport const ZodCUID = /*@__PURE__*/ core.$constructor(\"ZodCUID\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodCUID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function cuid(params) {\n return core._cuid(ZodCUID, params);\n}\nexport const ZodCUID2 = /*@__PURE__*/ core.$constructor(\"ZodCUID2\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodCUID2.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function cuid2(params) {\n return core._cuid2(ZodCUID2, params);\n}\nexport const ZodULID = /*@__PURE__*/ core.$constructor(\"ZodULID\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodULID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function ulid(params) {\n return core._ulid(ZodULID, params);\n}\nexport const ZodXID = /*@__PURE__*/ core.$constructor(\"ZodXID\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodXID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function xid(params) {\n return core._xid(ZodXID, params);\n}\nexport const ZodKSUID = /*@__PURE__*/ core.$constructor(\"ZodKSUID\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodKSUID.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function ksuid(params) {\n return core._ksuid(ZodKSUID, params);\n}\nexport const ZodIPv4 = /*@__PURE__*/ core.$constructor(\"ZodIPv4\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodIPv4.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function ipv4(params) {\n return core._ipv4(ZodIPv4, params);\n}\nexport const ZodMAC = /*@__PURE__*/ core.$constructor(\"ZodMAC\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodMAC.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function mac(params) {\n return core._mac(ZodMAC, params);\n}\nexport const ZodIPv6 = /*@__PURE__*/ core.$constructor(\"ZodIPv6\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodIPv6.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function ipv6(params) {\n return core._ipv6(ZodIPv6, params);\n}\nexport const ZodCIDRv4 = /*@__PURE__*/ core.$constructor(\"ZodCIDRv4\", (inst, def) => {\n core.$ZodCIDRv4.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function cidrv4(params) {\n return core._cidrv4(ZodCIDRv4, params);\n}\nexport const ZodCIDRv6 = /*@__PURE__*/ core.$constructor(\"ZodCIDRv6\", (inst, def) => {\n core.$ZodCIDRv6.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function cidrv6(params) {\n return core._cidrv6(ZodCIDRv6, params);\n}\nexport const ZodBase64 = /*@__PURE__*/ core.$constructor(\"ZodBase64\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodBase64.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function base64(params) {\n return core._base64(ZodBase64, params);\n}\nexport const ZodBase64URL = /*@__PURE__*/ core.$constructor(\"ZodBase64URL\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodBase64URL.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function base64url(params) {\n return core._base64url(ZodBase64URL, params);\n}\nexport const ZodE164 = /*@__PURE__*/ core.$constructor(\"ZodE164\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodE164.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function e164(params) {\n return core._e164(ZodE164, params);\n}\nexport const ZodJWT = /*@__PURE__*/ core.$constructor(\"ZodJWT\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodJWT.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function jwt(params) {\n return core._jwt(ZodJWT, params);\n}\nexport const ZodCustomStringFormat = /*@__PURE__*/ core.$constructor(\"ZodCustomStringFormat\", (inst, def) => {\n // ZodStringFormat.init(inst, def);\n core.$ZodCustomStringFormat.init(inst, def);\n ZodStringFormat.init(inst, def);\n});\nexport function stringFormat(format, fnOrRegex, _params = {}) {\n return core._stringFormat(ZodCustomStringFormat, format, fnOrRegex, _params);\n}\nexport function hostname(_params) {\n return core._stringFormat(ZodCustomStringFormat, \"hostname\", core.regexes.hostname, _params);\n}\nexport function hex(_params) {\n return core._stringFormat(ZodCustomStringFormat, \"hex\", core.regexes.hex, _params);\n}\nexport function hash(alg, params) {\n const enc = params?.enc ?? \"hex\";\n const format = `${alg}_${enc}`;\n const regex = core.regexes[format];\n if (!regex)\n throw new Error(`Unrecognized hash format: ${format}`);\n return core._stringFormat(ZodCustomStringFormat, format, regex, params);\n}\nexport const ZodNumber = /*@__PURE__*/ core.$constructor(\"ZodNumber\", (inst, def) => {\n core.$ZodNumber.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.numberProcessor(inst, ctx, json, params);\n inst.gt = (value, params) => inst.check(checks.gt(value, params));\n inst.gte = (value, params) => inst.check(checks.gte(value, params));\n inst.min = (value, params) => inst.check(checks.gte(value, params));\n inst.lt = (value, params) => inst.check(checks.lt(value, params));\n inst.lte = (value, params) => inst.check(checks.lte(value, params));\n inst.max = (value, params) => inst.check(checks.lte(value, params));\n inst.int = (params) => inst.check(int(params));\n inst.safe = (params) => inst.check(int(params));\n inst.positive = (params) => inst.check(checks.gt(0, params));\n inst.nonnegative = (params) => inst.check(checks.gte(0, params));\n inst.negative = (params) => inst.check(checks.lt(0, params));\n inst.nonpositive = (params) => inst.check(checks.lte(0, params));\n inst.multipleOf = (value, params) => inst.check(checks.multipleOf(value, params));\n inst.step = (value, params) => inst.check(checks.multipleOf(value, params));\n // inst.finite = (params) => inst.check(core.finite(params));\n inst.finite = () => inst;\n const bag = inst._zod.bag;\n inst.minValue =\n Math.max(bag.minimum ?? Number.NEGATIVE_INFINITY, bag.exclusiveMinimum ?? Number.NEGATIVE_INFINITY) ?? null;\n inst.maxValue =\n Math.min(bag.maximum ?? Number.POSITIVE_INFINITY, bag.exclusiveMaximum ?? Number.POSITIVE_INFINITY) ?? null;\n inst.isInt = (bag.format ?? \"\").includes(\"int\") || Number.isSafeInteger(bag.multipleOf ?? 0.5);\n inst.isFinite = true;\n inst.format = bag.format ?? null;\n});\nexport function number(params) {\n return core._number(ZodNumber, params);\n}\nexport const ZodNumberFormat = /*@__PURE__*/ core.$constructor(\"ZodNumberFormat\", (inst, def) => {\n core.$ZodNumberFormat.init(inst, def);\n ZodNumber.init(inst, def);\n});\nexport function int(params) {\n return core._int(ZodNumberFormat, params);\n}\nexport function float32(params) {\n return core._float32(ZodNumberFormat, params);\n}\nexport function float64(params) {\n return core._float64(ZodNumberFormat, params);\n}\nexport function int32(params) {\n return core._int32(ZodNumberFormat, params);\n}\nexport function uint32(params) {\n return core._uint32(ZodNumberFormat, params);\n}\nexport const ZodBoolean = /*@__PURE__*/ core.$constructor(\"ZodBoolean\", (inst, def) => {\n core.$ZodBoolean.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.booleanProcessor(inst, ctx, json, params);\n});\nexport function boolean(params) {\n return core._boolean(ZodBoolean, params);\n}\nexport const ZodBigInt = /*@__PURE__*/ core.$constructor(\"ZodBigInt\", (inst, def) => {\n core.$ZodBigInt.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.bigintProcessor(inst, ctx, json, params);\n inst.gte = (value, params) => inst.check(checks.gte(value, params));\n inst.min = (value, params) => inst.check(checks.gte(value, params));\n inst.gt = (value, params) => inst.check(checks.gt(value, params));\n inst.gte = (value, params) => inst.check(checks.gte(value, params));\n inst.min = (value, params) => inst.check(checks.gte(value, params));\n inst.lt = (value, params) => inst.check(checks.lt(value, params));\n inst.lte = (value, params) => inst.check(checks.lte(value, params));\n inst.max = (value, params) => inst.check(checks.lte(value, params));\n inst.positive = (params) => inst.check(checks.gt(BigInt(0), params));\n inst.negative = (params) => inst.check(checks.lt(BigInt(0), params));\n inst.nonpositive = (params) => inst.check(checks.lte(BigInt(0), params));\n inst.nonnegative = (params) => inst.check(checks.gte(BigInt(0), params));\n inst.multipleOf = (value, params) => inst.check(checks.multipleOf(value, params));\n const bag = inst._zod.bag;\n inst.minValue = bag.minimum ?? null;\n inst.maxValue = bag.maximum ?? null;\n inst.format = bag.format ?? null;\n});\nexport function bigint(params) {\n return core._bigint(ZodBigInt, params);\n}\nexport const ZodBigIntFormat = /*@__PURE__*/ core.$constructor(\"ZodBigIntFormat\", (inst, def) => {\n core.$ZodBigIntFormat.init(inst, def);\n ZodBigInt.init(inst, def);\n});\n// int64\nexport function int64(params) {\n return core._int64(ZodBigIntFormat, params);\n}\n// uint64\nexport function uint64(params) {\n return core._uint64(ZodBigIntFormat, params);\n}\nexport const ZodSymbol = /*@__PURE__*/ core.$constructor(\"ZodSymbol\", (inst, def) => {\n core.$ZodSymbol.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.symbolProcessor(inst, ctx, json, params);\n});\nexport function symbol(params) {\n return core._symbol(ZodSymbol, params);\n}\nexport const ZodUndefined = /*@__PURE__*/ core.$constructor(\"ZodUndefined\", (inst, def) => {\n core.$ZodUndefined.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.undefinedProcessor(inst, ctx, json, params);\n});\nfunction _undefined(params) {\n return core._undefined(ZodUndefined, params);\n}\nexport { _undefined as undefined };\nexport const ZodNull = /*@__PURE__*/ core.$constructor(\"ZodNull\", (inst, def) => {\n core.$ZodNull.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.nullProcessor(inst, ctx, json, params);\n});\nfunction _null(params) {\n return core._null(ZodNull, params);\n}\nexport { _null as null };\nexport const ZodAny = /*@__PURE__*/ core.$constructor(\"ZodAny\", (inst, def) => {\n core.$ZodAny.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.anyProcessor(inst, ctx, json, params);\n});\nexport function any() {\n return core._any(ZodAny);\n}\nexport const ZodUnknown = /*@__PURE__*/ core.$constructor(\"ZodUnknown\", (inst, def) => {\n core.$ZodUnknown.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.unknownProcessor(inst, ctx, json, params);\n});\nexport function unknown() {\n return core._unknown(ZodUnknown);\n}\nexport const ZodNever = /*@__PURE__*/ core.$constructor(\"ZodNever\", (inst, def) => {\n core.$ZodNever.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.neverProcessor(inst, ctx, json, params);\n});\nexport function never(params) {\n return core._never(ZodNever, params);\n}\nexport const ZodVoid = /*@__PURE__*/ core.$constructor(\"ZodVoid\", (inst, def) => {\n core.$ZodVoid.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.voidProcessor(inst, ctx, json, params);\n});\nfunction _void(params) {\n return core._void(ZodVoid, params);\n}\nexport { _void as void };\nexport const ZodDate = /*@__PURE__*/ core.$constructor(\"ZodDate\", (inst, def) => {\n core.$ZodDate.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.dateProcessor(inst, ctx, json, params);\n inst.min = (value, params) => inst.check(checks.gte(value, params));\n inst.max = (value, params) => inst.check(checks.lte(value, params));\n const c = inst._zod.bag;\n inst.minDate = c.minimum ? new Date(c.minimum) : null;\n inst.maxDate = c.maximum ? new Date(c.maximum) : null;\n});\nexport function date(params) {\n return core._date(ZodDate, params);\n}\nexport const ZodArray = /*@__PURE__*/ core.$constructor(\"ZodArray\", (inst, def) => {\n core.$ZodArray.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.arrayProcessor(inst, ctx, json, params);\n inst.element = def.element;\n inst.min = (minLength, params) => inst.check(checks.minLength(minLength, params));\n inst.nonempty = (params) => inst.check(checks.minLength(1, params));\n inst.max = (maxLength, params) => inst.check(checks.maxLength(maxLength, params));\n inst.length = (len, params) => inst.check(checks.length(len, params));\n inst.unwrap = () => inst.element;\n});\nexport function array(element, params) {\n return core._array(ZodArray, element, params);\n}\n// .keyof\nexport function keyof(schema) {\n const shape = schema._zod.def.shape;\n return _enum(Object.keys(shape));\n}\nexport const ZodObject = /*@__PURE__*/ core.$constructor(\"ZodObject\", (inst, def) => {\n core.$ZodObjectJIT.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.objectProcessor(inst, ctx, json, params);\n util.defineLazy(inst, \"shape\", () => {\n return def.shape;\n });\n inst.keyof = () => _enum(Object.keys(inst._zod.def.shape));\n inst.catchall = (catchall) => inst.clone({ ...inst._zod.def, catchall: catchall });\n inst.passthrough = () => inst.clone({ ...inst._zod.def, catchall: unknown() });\n inst.loose = () => inst.clone({ ...inst._zod.def, catchall: unknown() });\n inst.strict = () => inst.clone({ ...inst._zod.def, catchall: never() });\n inst.strip = () => inst.clone({ ...inst._zod.def, catchall: undefined });\n inst.extend = (incoming) => {\n return util.extend(inst, incoming);\n };\n inst.safeExtend = (incoming) => {\n return util.safeExtend(inst, incoming);\n };\n inst.merge = (other) => util.merge(inst, other);\n inst.pick = (mask) => util.pick(inst, mask);\n inst.omit = (mask) => util.omit(inst, mask);\n inst.partial = (...args) => util.partial(ZodOptional, inst, args[0]);\n inst.required = (...args) => util.required(ZodNonOptional, inst, args[0]);\n});\nexport function object(shape, params) {\n const def = {\n type: \"object\",\n shape: shape ?? {},\n ...util.normalizeParams(params),\n };\n return new ZodObject(def);\n}\n// strictObject\nexport function strictObject(shape, params) {\n return new ZodObject({\n type: \"object\",\n shape,\n catchall: never(),\n ...util.normalizeParams(params),\n });\n}\n// looseObject\nexport function looseObject(shape, params) {\n return new ZodObject({\n type: \"object\",\n shape,\n catchall: unknown(),\n ...util.normalizeParams(params),\n });\n}\nexport const ZodUnion = /*@__PURE__*/ core.$constructor(\"ZodUnion\", (inst, def) => {\n core.$ZodUnion.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.unionProcessor(inst, ctx, json, params);\n inst.options = def.options;\n});\nexport function union(options, params) {\n return new ZodUnion({\n type: \"union\",\n options: options,\n ...util.normalizeParams(params),\n });\n}\nexport const ZodXor = /*@__PURE__*/ core.$constructor(\"ZodXor\", (inst, def) => {\n ZodUnion.init(inst, def);\n core.$ZodXor.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.unionProcessor(inst, ctx, json, params);\n inst.options = def.options;\n});\n/** Creates an exclusive union (XOR) where exactly one option must match.\n * Unlike regular unions that succeed when any option matches, xor fails if\n * zero or more than one option matches the input. */\nexport function xor(options, params) {\n return new ZodXor({\n type: \"union\",\n options: options,\n inclusive: false,\n ...util.normalizeParams(params),\n });\n}\nexport const ZodDiscriminatedUnion = /*@__PURE__*/ core.$constructor(\"ZodDiscriminatedUnion\", (inst, def) => {\n ZodUnion.init(inst, def);\n core.$ZodDiscriminatedUnion.init(inst, def);\n});\nexport function discriminatedUnion(discriminator, options, params) {\n // const [options, params] = args;\n return new ZodDiscriminatedUnion({\n type: \"union\",\n options,\n discriminator,\n ...util.normalizeParams(params),\n });\n}\nexport const ZodIntersection = /*@__PURE__*/ core.$constructor(\"ZodIntersection\", (inst, def) => {\n core.$ZodIntersection.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.intersectionProcessor(inst, ctx, json, params);\n});\nexport function intersection(left, right) {\n return new ZodIntersection({\n type: \"intersection\",\n left: left,\n right: right,\n });\n}\nexport const ZodTuple = /*@__PURE__*/ core.$constructor(\"ZodTuple\", (inst, def) => {\n core.$ZodTuple.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.tupleProcessor(inst, ctx, json, params);\n inst.rest = (rest) => inst.clone({\n ...inst._zod.def,\n rest: rest,\n });\n});\nexport function tuple(items, _paramsOrRest, _params) {\n const hasRest = _paramsOrRest instanceof core.$ZodType;\n const params = hasRest ? _params : _paramsOrRest;\n const rest = hasRest ? _paramsOrRest : null;\n return new ZodTuple({\n type: \"tuple\",\n items: items,\n rest,\n ...util.normalizeParams(params),\n });\n}\nexport const ZodRecord = /*@__PURE__*/ core.$constructor(\"ZodRecord\", (inst, def) => {\n core.$ZodRecord.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.recordProcessor(inst, ctx, json, params);\n inst.keyType = def.keyType;\n inst.valueType = def.valueType;\n});\nexport function record(keyType, valueType, params) {\n return new ZodRecord({\n type: \"record\",\n keyType,\n valueType: valueType,\n ...util.normalizeParams(params),\n });\n}\n// type alksjf = core.output<core.$ZodRecordKey>;\nexport function partialRecord(keyType, valueType, params) {\n const k = core.clone(keyType);\n k._zod.values = undefined;\n return new ZodRecord({\n type: \"record\",\n keyType: k,\n valueType: valueType,\n ...util.normalizeParams(params),\n });\n}\nexport function looseRecord(keyType, valueType, params) {\n return new ZodRecord({\n type: \"record\",\n keyType,\n valueType: valueType,\n mode: \"loose\",\n ...util.normalizeParams(params),\n });\n}\nexport const ZodMap = /*@__PURE__*/ core.$constructor(\"ZodMap\", (inst, def) => {\n core.$ZodMap.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.mapProcessor(inst, ctx, json, params);\n inst.keyType = def.keyType;\n inst.valueType = def.valueType;\n inst.min = (...args) => inst.check(core._minSize(...args));\n inst.nonempty = (params) => inst.check(core._minSize(1, params));\n inst.max = (...args) => inst.check(core._maxSize(...args));\n inst.size = (...args) => inst.check(core._size(...args));\n});\nexport function map(keyType, valueType, params) {\n return new ZodMap({\n type: \"map\",\n keyType: keyType,\n valueType: valueType,\n ...util.normalizeParams(params),\n });\n}\nexport const ZodSet = /*@__PURE__*/ core.$constructor(\"ZodSet\", (inst, def) => {\n core.$ZodSet.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.setProcessor(inst, ctx, json, params);\n inst.min = (...args) => inst.check(core._minSize(...args));\n inst.nonempty = (params) => inst.check(core._minSize(1, params));\n inst.max = (...args) => inst.check(core._maxSize(...args));\n inst.size = (...args) => inst.check(core._size(...args));\n});\nexport function set(valueType, params) {\n return new ZodSet({\n type: \"set\",\n valueType: valueType,\n ...util.normalizeParams(params),\n });\n}\nexport const ZodEnum = /*@__PURE__*/ core.$constructor(\"ZodEnum\", (inst, def) => {\n core.$ZodEnum.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.enumProcessor(inst, ctx, json, params);\n inst.enum = def.entries;\n inst.options = Object.values(def.entries);\n const keys = new Set(Object.keys(def.entries));\n inst.extract = (values, params) => {\n const newEntries = {};\n for (const value of values) {\n if (keys.has(value)) {\n newEntries[value] = def.entries[value];\n }\n else\n throw new Error(`Key ${value} not found in enum`);\n }\n return new ZodEnum({\n ...def,\n checks: [],\n ...util.normalizeParams(params),\n entries: newEntries,\n });\n };\n inst.exclude = (values, params) => {\n const newEntries = { ...def.entries };\n for (const value of values) {\n if (keys.has(value)) {\n delete newEntries[value];\n }\n else\n throw new Error(`Key ${value} not found in enum`);\n }\n return new ZodEnum({\n ...def,\n checks: [],\n ...util.normalizeParams(params),\n entries: newEntries,\n });\n };\n});\nfunction _enum(values, params) {\n const entries = Array.isArray(values) ? Object.fromEntries(values.map((v) => [v, v])) : values;\n return new ZodEnum({\n type: \"enum\",\n entries,\n ...util.normalizeParams(params),\n });\n}\nexport { _enum as enum };\n/** @deprecated This API has been merged into `z.enum()`. Use `z.enum()` instead.\n *\n * ```ts\n * enum Colors { red, green, blue }\n * z.enum(Colors);\n * ```\n */\nexport function nativeEnum(entries, params) {\n return new ZodEnum({\n type: \"enum\",\n entries,\n ...util.normalizeParams(params),\n });\n}\nexport const ZodLiteral = /*@__PURE__*/ core.$constructor(\"ZodLiteral\", (inst, def) => {\n core.$ZodLiteral.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.literalProcessor(inst, ctx, json, params);\n inst.values = new Set(def.values);\n Object.defineProperty(inst, \"value\", {\n get() {\n if (def.values.length > 1) {\n throw new Error(\"This schema contains multiple valid literal values. Use `.values` instead.\");\n }\n return def.values[0];\n },\n });\n});\nexport function literal(value, params) {\n return new ZodLiteral({\n type: \"literal\",\n values: Array.isArray(value) ? value : [value],\n ...util.normalizeParams(params),\n });\n}\nexport const ZodFile = /*@__PURE__*/ core.$constructor(\"ZodFile\", (inst, def) => {\n core.$ZodFile.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.fileProcessor(inst, ctx, json, params);\n inst.min = (size, params) => inst.check(core._minSize(size, params));\n inst.max = (size, params) => inst.check(core._maxSize(size, params));\n inst.mime = (types, params) => inst.check(core._mime(Array.isArray(types) ? types : [types], params));\n});\nexport function file(params) {\n return core._file(ZodFile, params);\n}\nexport const ZodTransform = /*@__PURE__*/ core.$constructor(\"ZodTransform\", (inst, def) => {\n core.$ZodTransform.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.transformProcessor(inst, ctx, json, params);\n inst._zod.parse = (payload, _ctx) => {\n if (_ctx.direction === \"backward\") {\n throw new core.$ZodEncodeError(inst.constructor.name);\n }\n payload.addIssue = (issue) => {\n if (typeof issue === \"string\") {\n payload.issues.push(util.issue(issue, payload.value, def));\n }\n else {\n // for Zod 3 backwards compatibility\n const _issue = issue;\n if (_issue.fatal)\n _issue.continue = false;\n _issue.code ?? (_issue.code = \"custom\");\n _issue.input ?? (_issue.input = payload.value);\n _issue.inst ?? (_issue.inst = inst);\n // _issue.continue ??= true;\n payload.issues.push(util.issue(_issue));\n }\n };\n const output = def.transform(payload.value, payload);\n if (output instanceof Promise) {\n return output.then((output) => {\n payload.value = output;\n return payload;\n });\n }\n payload.value = output;\n return payload;\n };\n});\nexport function transform(fn) {\n return new ZodTransform({\n type: \"transform\",\n transform: fn,\n });\n}\nexport const ZodOptional = /*@__PURE__*/ core.$constructor(\"ZodOptional\", (inst, def) => {\n core.$ZodOptional.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.optionalProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nexport function optional(innerType) {\n return new ZodOptional({\n type: \"optional\",\n innerType: innerType,\n });\n}\nexport const ZodExactOptional = /*@__PURE__*/ core.$constructor(\"ZodExactOptional\", (inst, def) => {\n core.$ZodExactOptional.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.optionalProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nexport function exactOptional(innerType) {\n return new ZodExactOptional({\n type: \"optional\",\n innerType: innerType,\n });\n}\nexport const ZodNullable = /*@__PURE__*/ core.$constructor(\"ZodNullable\", (inst, def) => {\n core.$ZodNullable.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.nullableProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nexport function nullable(innerType) {\n return new ZodNullable({\n type: \"nullable\",\n innerType: innerType,\n });\n}\n// nullish\nexport function nullish(innerType) {\n return optional(nullable(innerType));\n}\nexport const ZodDefault = /*@__PURE__*/ core.$constructor(\"ZodDefault\", (inst, def) => {\n core.$ZodDefault.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.defaultProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n inst.removeDefault = inst.unwrap;\n});\nexport function _default(innerType, defaultValue) {\n return new ZodDefault({\n type: \"default\",\n innerType: innerType,\n get defaultValue() {\n return typeof defaultValue === \"function\" ? defaultValue() : util.shallowClone(defaultValue);\n },\n });\n}\nexport const ZodPrefault = /*@__PURE__*/ core.$constructor(\"ZodPrefault\", (inst, def) => {\n core.$ZodPrefault.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.prefaultProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nexport function prefault(innerType, defaultValue) {\n return new ZodPrefault({\n type: \"prefault\",\n innerType: innerType,\n get defaultValue() {\n return typeof defaultValue === \"function\" ? defaultValue() : util.shallowClone(defaultValue);\n },\n });\n}\nexport const ZodNonOptional = /*@__PURE__*/ core.$constructor(\"ZodNonOptional\", (inst, def) => {\n core.$ZodNonOptional.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.nonoptionalProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nexport function nonoptional(innerType, params) {\n return new ZodNonOptional({\n type: \"nonoptional\",\n innerType: innerType,\n ...util.normalizeParams(params),\n });\n}\nexport const ZodSuccess = /*@__PURE__*/ core.$constructor(\"ZodSuccess\", (inst, def) => {\n core.$ZodSuccess.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.successProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nexport function success(innerType) {\n return new ZodSuccess({\n type: \"success\",\n innerType: innerType,\n });\n}\nexport const ZodCatch = /*@__PURE__*/ core.$constructor(\"ZodCatch\", (inst, def) => {\n core.$ZodCatch.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.catchProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n inst.removeCatch = inst.unwrap;\n});\nfunction _catch(innerType, catchValue) {\n return new ZodCatch({\n type: \"catch\",\n innerType: innerType,\n catchValue: (typeof catchValue === \"function\" ? catchValue : () => catchValue),\n });\n}\nexport { _catch as catch };\nexport const ZodNaN = /*@__PURE__*/ core.$constructor(\"ZodNaN\", (inst, def) => {\n core.$ZodNaN.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.nanProcessor(inst, ctx, json, params);\n});\nexport function nan(params) {\n return core._nan(ZodNaN, params);\n}\nexport const ZodPipe = /*@__PURE__*/ core.$constructor(\"ZodPipe\", (inst, def) => {\n core.$ZodPipe.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.pipeProcessor(inst, ctx, json, params);\n inst.in = def.in;\n inst.out = def.out;\n});\nexport function pipe(in_, out) {\n return new ZodPipe({\n type: \"pipe\",\n in: in_,\n out: out,\n // ...util.normalizeParams(params),\n });\n}\nexport const ZodCodec = /*@__PURE__*/ core.$constructor(\"ZodCodec\", (inst, def) => {\n ZodPipe.init(inst, def);\n core.$ZodCodec.init(inst, def);\n});\nexport function codec(in_, out, params) {\n return new ZodCodec({\n type: \"pipe\",\n in: in_,\n out: out,\n transform: params.decode,\n reverseTransform: params.encode,\n });\n}\nexport const ZodReadonly = /*@__PURE__*/ core.$constructor(\"ZodReadonly\", (inst, def) => {\n core.$ZodReadonly.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.readonlyProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nexport function readonly(innerType) {\n return new ZodReadonly({\n type: \"readonly\",\n innerType: innerType,\n });\n}\nexport const ZodTemplateLiteral = /*@__PURE__*/ core.$constructor(\"ZodTemplateLiteral\", (inst, def) => {\n core.$ZodTemplateLiteral.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.templateLiteralProcessor(inst, ctx, json, params);\n});\nexport function templateLiteral(parts, params) {\n return new ZodTemplateLiteral({\n type: \"template_literal\",\n parts,\n ...util.normalizeParams(params),\n });\n}\nexport const ZodLazy = /*@__PURE__*/ core.$constructor(\"ZodLazy\", (inst, def) => {\n core.$ZodLazy.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.lazyProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.getter();\n});\nexport function lazy(getter) {\n return new ZodLazy({\n type: \"lazy\",\n getter: getter,\n });\n}\nexport const ZodPromise = /*@__PURE__*/ core.$constructor(\"ZodPromise\", (inst, def) => {\n core.$ZodPromise.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.promiseProcessor(inst, ctx, json, params);\n inst.unwrap = () => inst._zod.def.innerType;\n});\nexport function promise(innerType) {\n return new ZodPromise({\n type: \"promise\",\n innerType: innerType,\n });\n}\nexport const ZodFunction = /*@__PURE__*/ core.$constructor(\"ZodFunction\", (inst, def) => {\n core.$ZodFunction.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.functionProcessor(inst, ctx, json, params);\n});\nexport function _function(params) {\n return new ZodFunction({\n type: \"function\",\n input: Array.isArray(params?.input) ? tuple(params?.input) : (params?.input ?? array(unknown())),\n output: params?.output ?? unknown(),\n });\n}\nexport { _function as function };\nexport const ZodCustom = /*@__PURE__*/ core.$constructor(\"ZodCustom\", (inst, def) => {\n core.$ZodCustom.init(inst, def);\n ZodType.init(inst, def);\n inst._zod.processJSONSchema = (ctx, json, params) => processors.customProcessor(inst, ctx, json, params);\n});\n// custom checks\nexport function check(fn) {\n const ch = new core.$ZodCheck({\n check: \"custom\",\n // ...util.normalizeParams(params),\n });\n ch._zod.check = fn;\n return ch;\n}\nexport function custom(fn, _params) {\n return core._custom(ZodCustom, fn ?? (() => true), _params);\n}\nexport function refine(fn, _params = {}) {\n return core._refine(ZodCustom, fn, _params);\n}\n// superRefine\nexport function superRefine(fn) {\n return core._superRefine(fn);\n}\n// Re-export describe and meta from core\nexport const describe = core.describe;\nexport const meta = core.meta;\nfunction _instanceof(cls, params = {}) {\n const inst = new ZodCustom({\n type: \"custom\",\n check: \"custom\",\n fn: (data) => data instanceof cls,\n abort: true,\n ...util.normalizeParams(params),\n });\n inst._zod.bag.Class = cls;\n // Override check to emit invalid_type instead of custom\n inst._zod.check = (payload) => {\n if (!(payload.value instanceof cls)) {\n payload.issues.push({\n code: \"invalid_type\",\n expected: cls.name,\n input: payload.value,\n inst,\n path: [...(inst._zod.def.path ?? [])],\n });\n }\n };\n return inst;\n}\nexport { _instanceof as instanceof };\n// stringbool\nexport const stringbool = (...args) => core._stringbool({\n Codec: ZodCodec,\n Boolean: ZodBoolean,\n String: ZodString,\n}, ...args);\nexport function json(params) {\n const jsonSchema = lazy(() => {\n return union([string(params), number(), boolean(), _null(), array(jsonSchema), record(string(), jsonSchema)]);\n });\n return jsonSchema;\n}\n// preprocess\n// /** @deprecated Use `z.pipe()` and `z.transform()` instead. */\nexport function preprocess(fn, schema) {\n return pipe(transform(fn), schema);\n}\n","/**\n * Chat widget — json-render catalog definition.\n *\n * This catalog describes all UI components the chat widget understands.\n * The backend streams `ui_spec` events whose element types must match names\n * defined here. Component implementations live in ./registry.tsx.\n *\n * HOW IT WORKS:\n * 1. Backend streams NDJSON events including `ui_spec` events.\n * 2. Each `ui_spec` event contains a `spec: UISpec` field.\n * 3. The `<ChatRenderer>` component feeds specs into json-render's `<Renderer>`.\n * 4. json-render looks up the element `type` in the registry and renders it.\n *\n * CUSTOMISING:\n * Fork this repo, edit ./registry.tsx to swap in your own components.\n * The catalog schema below stays the same — only the visual implementation changes.\n *\n * See: https://github.com/vercel-labs/json-render\n */\n\nimport { z } from 'zod';\n\n// ---------------------------------------------------------------------------\n// Component schemas (Zod)\n// ---------------------------------------------------------------------------\n\nexport const MessageBubbleSchema = z.object({\n role: z.enum(['user', 'assistant']),\n content: z.string(),\n timestamp: z.number().optional(),\n});\n\nexport const ProductCardSchema = z.object({\n sku: z.string(),\n name: z.string(),\n imageUrl: z.string().url().optional(),\n price: z.string().optional(),\n originalPrice: z.string().optional(),\n url: z.string().url(),\n /** Override CTA label (default: \"View product\") */\n ctaLabel: z.string().optional(),\n});\n\nexport const ActionButtonsSchema = z.object({\n buttons: z.array(\n z.object({\n label: z.string(),\n /** Opaque action payload forwarded to the backend when clicked. */\n action: z.object({\n title: z.string(),\n type: z.string(),\n payload: z.unknown().optional(),\n }),\n }),\n ),\n});\n\nexport const TypingIndicatorSchema = z.object({});\n\nexport const DividerSchema = z.object({\n label: z.string().optional(),\n});\n\nconst ComparisonProductSchema = z.object({\n sku: z.string(),\n name: z.string(),\n price: z.string(),\n imageUrl: z.string().optional(),\n rating: z.number().optional(),\n reviewCount: z.number().optional(),\n});\n\nexport const ComparisonTableSchema = z.object({\n recommended: ComparisonProductSchema,\n products: z.array(ComparisonProductSchema),\n attributes: z.array(\n z.object({\n label: z.string(),\n values: z.array(z.string()),\n }),\n ),\n highlights: z.array(z.string()),\n specialCases: z.array(z.string()).optional(),\n recommendedText: z.string().optional(),\n winnerHits: z\n .record(\n z.string(),\n z.object({\n positive: z.array(z.string()).optional(),\n negative: z.array(z.string()).optional(),\n }),\n )\n .optional(),\n productActions: z\n .record(\n z.string(),\n z.object({\n title: z.string(),\n type: z.string(),\n payload: z.unknown().optional(),\n }),\n )\n .optional(),\n});\n\nexport const SentimentLabelSchema = z.object({\n label: z.string(),\n sentiment: z.enum(['positive', 'negative', 'neutral']).optional(),\n});\n\nexport const AITopPickItemSchema = z.object({\n product: z.record(z.string(), z.unknown()),\n role: z.string().optional(),\n reason: z.string().optional(),\n labels: z.array(SentimentLabelSchema).optional(),\n expertQualityScore: z.number().optional(),\n reviewHighlight: z.string().optional(),\n action: z\n .object({\n title: z.string(),\n type: z.string(),\n payload: z.unknown().optional(),\n })\n .optional(),\n});\n\nexport const AITopPicksSchema = z.object({\n suggestions: z.array(AITopPickItemSchema),\n});\n\nexport const GroundingReviewCardSchema = z.object({\n title: z.string().optional(),\n text: z.string().optional(),\n reviewCount: z.string().optional(),\n action: z.object({ title: z.string(), type: z.string(), payload: z.unknown().optional() }),\n});\n\nexport const AIGroupingCardsSchema = z.object({\n entries: z.array(\n z.object({\n name: z.string(),\n image: z.string().optional(),\n description: z.string().optional(),\n action: z.object({ title: z.string(), type: z.string(), payload: z.unknown().optional() }),\n }),\n ),\n});\n\nexport const AISuggestedSearchCardsSchema = z.object({\n entries: z.array(\n z.object({\n shortName: z.string(),\n detailedMessage: z.string().optional(),\n whyDifferent: z.string().optional(),\n image: z.string().optional(),\n action: z.object({ title: z.string(), type: z.string(), payload: z.unknown().optional() }),\n }),\n ),\n});\n\nconst ActionPayloadSchema = z.object({\n title: z.string(),\n type: z.string(),\n payload: z.unknown().optional(),\n});\n\nconst ProductVariantSchema = z.object({\n name: z.string().optional(),\n value: z.string().optional(),\n option_value: z.string().optional(),\n attribute_value: z.string().optional(),\n type: z.string().optional(),\n attribute: z.string().optional(),\n option_name: z.string().optional(),\n attribute_name: z.string().optional(),\n variant_name: z.string().optional(),\n sku: z.string().optional(),\n price: z.union([z.number(), z.string()]).optional(),\n price_discounted: z.union([z.number(), z.string()]).optional(),\n image: z.string().optional(),\n imageUrl: z.string().optional(),\n image_url: z.string().optional(),\n color: z.string().optional(),\n colour: z.string().optional(),\n color_hex: z.string().optional(),\n hex: z.string().optional(),\n swatch: z.string().optional(),\n swatchColor: z.string().optional(),\n in_stock: z.boolean().optional(),\n inStock: z.boolean().optional(),\n});\n\nexport const ProductDetailsPanelSchema = z.object({\n product: z\n .object({\n sku: z.string().optional(),\n name: z.string().optional(),\n images: z.array(z.string()).optional(),\n imageUrl: z.string().optional(),\n rating: z.number().optional(),\n reviewCount: z.number().optional(),\n price: z.string().optional(),\n originalPrice: z.string().optional(),\n discountReason: z.string().optional(),\n discount_reason: z.string().optional(),\n campaignReason: z.string().optional(),\n campaign_reason: z.string().optional(),\n originalPriceStyle: z.enum(['strikethrough', 'inline']).optional(),\n price_original_style: z.enum(['strikethrough', 'inline']).optional(),\n price_discount_rate: z.number().optional(),\n price_async: z.boolean().optional(),\n inStock: z.boolean().optional(),\n promotions: z.array(z.string()).optional(),\n variants: z.array(ProductVariantSchema).optional(),\n variantOptions: z.array(ProductVariantSchema).optional(),\n variant_options: z.array(ProductVariantSchema).optional(),\n productVariants: z.array(ProductVariantSchema).optional(),\n product_variants: z.array(ProductVariantSchema).optional(),\n url: z.string().optional(),\n cartCode: z.string().optional(),\n description: z.string().optional(),\n description_html: z.string().optional(),\n descriptionHtml: z.string().optional(),\n facet_hits: z.record(z.string(), z.unknown()).optional(),\n facetHits: z.record(z.string(), z.unknown()).optional(),\n features: z\n .array(\n z.object({\n name: z.string().optional(),\n key: z.string().optional(),\n label: z.string().optional(),\n value: z.union([z.string(), z.number(), z.boolean()]).optional(),\n }),\n )\n .optional(),\n specifications: z\n .union([z.record(z.string(), z.string()), z.array(z.object({ key: z.string(), value: z.string() }))])\n .optional(),\n })\n .optional(),\n action: ActionPayloadSchema.optional(),\n});\n\nexport const ProductGridSchema = z.object({\n endOfList: z.boolean().optional(),\n});\n\nconst ReviewItemSchema = z.object({\n review_class: z.string().optional(),\n review_text: z.string().optional(),\n review_rating: z.union([z.string(), z.number()]).optional(),\n review_tag: z.string().optional(),\n});\n\nexport const ReviewHighlightsSchema = z.object({\n reviews: z.array(ReviewItemSchema).optional(),\n});\n\nexport const ProsAndConsSchema = z.object({\n productName: z.string().optional(),\n pros: z.array(z.string()).optional(),\n cons: z.array(z.string()).optional(),\n});\n\nexport const CategoriesContainerSchema = z.object({\n groups: z\n .array(\n z.object({\n groupName: z.string(),\n products: z.array(z.record(z.string(), z.unknown())).optional(),\n }),\n )\n .optional(),\n filterTags: z\n .array(\n z.object({\n title: z.string(),\n action: ActionPayloadSchema.optional(),\n }),\n )\n .optional(),\n});\n\nexport const HandoffNoticeSchema = z.object({\n summary: z.string().optional(),\n products_discussed: z.array(z.string()).optional(),\n user_sentiment: z.string().optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Catalog definition\n//\n// When @json-render/core ships a stable defineCatalog() API, replace this\n// plain object with the official defineCatalog() call. For now the schema\n// map acts as both documentation and runtime validation.\n// ---------------------------------------------------------------------------\n\nexport const chatCatalog = {\n components: {\n MessageBubble: {\n schema: MessageBubbleSchema,\n description: 'A single chat message bubble for user or assistant turns.',\n },\n ProductCard: {\n schema: ProductCardSchema,\n description: 'A product card rendered inline in the chat stream.',\n },\n ActionButtons: {\n schema: ActionButtonsSchema,\n description: 'A horizontal row of quick-reply action buttons.',\n },\n TypingIndicator: {\n schema: TypingIndicatorSchema,\n description: 'An animated indicator shown while the assistant is typing.',\n },\n Divider: {\n schema: DividerSchema,\n description: 'A horizontal rule with an optional label.',\n },\n ComparisonTable: {\n schema: ComparisonTableSchema,\n description: 'A product comparison table with recommended pick, attribute rows, and highlights.',\n },\n AITopPicks: {\n schema: AITopPicksSchema,\n description: 'Rich AI-curated product suggestion cards with roles, sentiment labels, scores, and review quotes.',\n },\n GroundingReviewCard: {\n schema: GroundingReviewCardSchema,\n description: 'A card showing review grounding data with review count and CTA.',\n },\n AIGroupingCards: {\n schema: AIGroupingCardsSchema,\n description: 'Category grouping cards with images and labels for product discovery.',\n },\n AISuggestedSearchCards: {\n schema: AISuggestedSearchCardsSchema,\n description: 'Suggested search cards with images, descriptions, and differentiation.',\n },\n ProductDetailsPanel: {\n schema: ProductDetailsPanelSchema,\n description: 'Full product detail view with images, specs, variants, and purchase actions.',\n },\n ProductGrid: {\n schema: ProductGridSchema,\n description: 'A scrollable grid of ProductCard children with optional \"more\" pagination.',\n },\n ReviewHighlights: {\n schema: ReviewHighlightsSchema,\n description: 'A list of highlighted customer reviews with sentiment and ratings.',\n },\n ProsAndCons: {\n schema: ProsAndConsSchema,\n description: 'A pros and cons list for a product.',\n },\n CategoriesContainer: {\n schema: CategoriesContainerSchema,\n description: 'Tabbed product groups with optional filter tag buttons.',\n },\n HandoffNotice: {\n schema: HandoffNoticeSchema,\n description: 'A notice shown when the conversation is escalated to a human agent.',\n },\n },\n} as const;\n\nexport type ChatCatalog = typeof chatCatalog;\nexport type ChatComponentName = keyof ChatCatalog['components'];\n","/**\n * Chat widget -- public entry point.\n *\n * Renders a floating launcher button + slide-in chat drawer inside Shadow DOM\n * for CSS isolation. Handles streaming NDJSON from the backend.\n */\n\nimport type { ActionPayload, PageContext, StreamEvent, StreamEventAction, UISpec } from '../common/types.js';\nimport type { ChatTransportConfig } from '../common/api-paths.js';\nimport type { ActionRouterOptions } from '../common/action-router.js';\nimport type { UISpecRenderHelpers } from '../common/renderer/index.js';\nimport type { BridgeMessage, CommunicationBridgeOptions } from '../common/communication-bridge.js';\nimport type { BackendRequestMeta } from './api.js';\nimport { mergeUISpecRegistry } from '../common/renderer/index.js';\nimport { BaseWidget } from '../common/widget-base.js';\nimport { dispatch } from '../common/events.js';\nimport { uuidv7 } from '../common/uuidv7.js';\nimport { CommunicationBridge } from '../common/communication-bridge.js';\nimport { routeStreamAction } from '../common/action-router.js';\nimport {\n streamStartEvent,\n streamChunkEvent,\n streamDoneEvent,\n streamErrorEvent,\n streamUiSpecEvent,\n llmUsageEvent,\n meteringIncrementEvent,\n chatHistorySnapshotEvent,\n basketAddEvent,\n} from '../common/analytics-events.js';\nimport { sanitizeHtml, isSafeUrl } from '../common/safe-html.js';\nimport { validateImageFile } from './attachment-utils.js';\nimport { sendChatMessage, enrichActionPayload } from './api.js';\nimport { ChatDrawer } from './components/ChatDrawer.js';\nimport { createLauncher } from './components/Launcher.js';\nimport type { LauncherElements } from './components/Launcher.js';\nimport { playTtsAudio } from '../common/tts-player.js';\nimport type { AudioHandle } from '../common/tts-player.js';\nimport {\n renderUISpec,\n createDefaultChatUISpecRegistry,\n defaultChatUnknownUISpecRenderer,\n} from './components/renderUISpec.js';\nimport { renderFloatingComparisonButton } from './components/FloatingComparisonButton.js';\nimport type { TypewriterHandle } from './components/typewriter.js';\nimport { typewriteHtml } from './components/typewriter.js';\nimport { linkProductMentions } from './components/productMentionLinker.js';\nimport { isInputAreaAction } from './components/actionClassifier.js';\nimport type { ThumbnailEntry } from './components/ThumbnailsColumn.js';\nimport {\n clearChoicePrompterDismissState,\n createChoicePrompter,\n isChoicePrompterDismissed,\n recordChoicePrompterDismissedForThread,\n} from './components/ChoicePrompter.js';\nimport type {\n ChatActionChip,\n OpeningContextKey,\n ChatWidgetConfig,\n ChatMessage,\n ChatI18n,\n ChatUISpecRenderContext,\n ChatUISpecRegistry,\n ProductSortState,\n} from './types.js';\nimport { GengageIndexedDB } from '../common/indexed-db.js';\nimport { CHAT_I18N_TR, resolveChatLocale } from './locales/index.js';\nimport { ExtendedModeManager } from './extendedModeManager.js';\nimport { PanelManager, determinePanelUpdateAction } from './panel-manager.js';\nimport { SessionPersistence } from './session-persistence.js';\nimport { ChatPresentationState, getLatestUnreadAssistantThreadId } from './chat-presentation-state.js';\nimport { invalidateChatScrollCache } from './utils/get-chat-scroll-element.js';\nimport { isLikelyConnectivityIssue } from '../common/global-error-toast.js';\nimport { makePillLauncher } from '../common/pill-launcher.js';\nimport { shouldShowStreamErrorAsRedStrip } from './stream-error-display.js';\nimport {\n containsKvkk,\n isKvkkShown,\n markKvkkShown,\n stripKvkkBlock,\n extractKvkkBlock,\n localeToOutputLanguage,\n} from './kvkk.js';\n\nimport chatStyles from './components/chat.css?inline';\nimport * as ga from '../common/ga-datalayer.js';\n\n/**\n * Panel rebuild source for local drilldown history and stream-end snapshots.\n * `productDetailsWithSimilars` keeps PDP + appended similar grid in sync when\n * similars arrive in a second stream chunk (DOM append does not update `spec` alone).\n */\ntype PanelSource =\n | { kind: 'spec'; spec: UISpec }\n | { kind: 'productDetailsWithSimilars'; pdpSpec: UISpec; similarsSpec: UISpec }\n | { kind: 'favorites' };\ntype SendActionOptions = {\n silent?: boolean;\n attachment?: File;\n preservePanel?: boolean;\n isPdpPrime?: boolean;\n preservePills?: boolean;\n};\n\n/** Validate that a string is a safe CSS color value (no url(), expression(), etc.). */\nfunction isSafeCSSColor(value: string): boolean {\n if (value.length > 120) return false;\n // Only allow characters found in valid CSS colors: hex digits, letters, parens,\n // commas, dots, spaces, percent, slash (modern color syntax), and hyphens.\n return /^[a-zA-Z0-9#(),.\\s%/\\-]+$/.test(value);\n}\n\n/** Lightweight runtime check: value looks like an ActionPayload (has a string `type`). */\nfunction isActionLike(value: unknown): value is ActionPayload {\n return typeof value === 'object' && value !== null && typeof (value as Record<string, unknown>).type === 'string';\n}\n\n/**\n * Floating AI chat widget with streaming NDJSON responses, product cards, and comparison tables.\n *\n * @example\n * ```ts\n * import { GengageChat, bootstrapSession } from '@gengage/assistant-fe';\n *\n * const chat = new GengageChat();\n * await chat.init({\n * accountId: 'mystore',\n * middlewareUrl: 'https://chat.gengage.ai',\n * session: { sessionId: bootstrapSession() },\n * });\n * chat.open(); // Programmatically open the drawer\n * ```\n */\nexport class GengageChat extends BaseWidget<ChatWidgetConfig> {\n private static readonly _MAX_COMPARISON_SELECTION = 5;\n private _shadow: ShadowRoot | null = null;\n private _rootEl: HTMLElement | null = null;\n /** Full-viewport scrim when drawer is open (floating/overlay); not used for inline. */\n private _backdropEl: HTMLElement | null = null;\n private _launcher: LauncherElements | null = null;\n private _drawer: ChatDrawer | null = null;\n private _bridge: CommunicationBridge | null = null;\n private _drawerVisible = false;\n /** Set when `config.pillLauncher` is used — `apply()` runs at end of `onInit`. */\n private _pillLauncherApply: (() => Promise<void>) | null = null;\n /**\n * Host scroll blocked via capture-phase `touchmove`/`wheel` + preventDefault.\n * Does not set `overflow` on html/body (avoids breaking host layouts).\n */\n private _hostScrollLockActive = false;\n private readonly _preventHostDocumentTouchMove = (e: TouchEvent): void => {\n if (!this._hostScrollLockActive) return;\n if (this._hostScrollEventShouldReachChatScroller(e)) return;\n e.preventDefault();\n };\n private readonly _preventHostDocumentWheel = (e: WheelEvent): void => {\n if (!this._hostScrollLockActive) return;\n if (this._hostScrollEventShouldReachChatScroller(e)) return;\n e.preventDefault();\n };\n private _messages: ChatMessage[] = [];\n // Bot text accumulation is now closure-local inside _sendAction to prevent\n // corruption when concurrent preservePanel streams write simultaneously.\n private _currentMessageId = 0;\n private _abortControllers = new Set<AbortController>();\n /** Current thread cursor — only messages with threadId <= this are visible. */\n private _currentThreadId: string | null = null;\n /** Most recent threadId ever created — used to detect branch points. */\n private _lastThreadId: string | null = null;\n /** Timestamp when the chat session was created (ISO 8601). */\n private _chatCreatedAt = '';\n /** Last backend-streamed context object — sent back with every request. */\n private _lastBackendContext: import('../common/types.js').BackendContext | null = null;\n private _productSort: ProductSortState = { type: 'related' };\n private _lastSku: string | undefined;\n private _comparisonSelectMode = false;\n private _comparisonSelectedSkus: string[] = [];\n private _comparisonSelectionWarning: string | null = null;\n private _comparisonRefreshRafId: number | null = null;\n /** SKUs of products the user has viewed across panel product grids. */\n private _viewedProductSkus = new Set<string>();\n private _thumbnailEntries: ThumbnailEntry[] = [];\n private _choicePrompterEl: HTMLElement | null = null;\n private _openState: 'full' | 'half' = 'full';\n private _mobileBreakpoint = 768;\n private _isMobileViewport = false;\n private _pdpLaunched = false;\n private _entryContextPrimed = false;\n /** True while the initial silent PDP launch request is in flight. */\n private _pdpPrimingInFlight = false;\n /** User messages queued until silent PDP priming completes. */\n private _queuedUserMessages: Array<{ text: string; attachment?: File }> = [];\n private _productContextUnavailableSku: string | null = null;\n private _i18n: ChatI18n = CHAT_I18N_TR;\n private _extendedModeManager: ExtendedModeManager | null = null;\n /** Active typewriter animation handle — cancelled on new action or drawer close. */\n private _activeTypewriter: TypewriterHandle | null = null;\n /** Active TTS audio handle — stopped on new stream start or drawer close. */\n private _activeTtsHandle: AudioHandle | null = null;\n /** Active request thread ID — guards against stale stream events from cancelled requests. */\n private _activeRequestThreadId: string | null = null;\n /** Accumulated SKU → product item map from outputText events. */\n private _skuToProductItem: Record<string, Record<string, unknown>> = {};\n /** Current conversation mode from the latest outputText event. */\n private _conversationMode: string | null = null;\n /** Whether initialization (including IDB restore) has completed. */\n private _initComplete = false;\n /** Queue of actions received before init completes. Max 10, FIFO discard. */\n private _pendingActions: Array<{\n action: ActionPayload;\n options?: SendActionOptions | undefined;\n }> = [];\n /** Supplemental context received from host via bridge (e.g. PDP detail context). */\n private _bridgeContext: Record<string, unknown> | null = null;\n /** Last cart quantity received from host via bridge. */\n private _cartQuantity: number | null = null;\n private _threadsWithFirstBot: Set<string> = new Set();\n /** Panel state manager (snapshots, topbar, navigation). */\n private _panel: PanelManager | null = null;\n /** Client-side panel navigation stack for local drilldowns (e.g. card → detail). Max 10 entries.\n * Stores rebuild info (UISpec or kind) instead of DOM clones so back navigation\n * produces fresh elements with live event listeners. */\n private _localPanelHistory: Array<{\n source: PanelSource;\n title: string;\n }> = [];\n private static readonly _MAX_PANEL_HISTORY = 10;\n /** Tracks how the current panel content was produced, for history/error-recovery rebuild. */\n private _currentPanelSource: PanelSource | null = null;\n /** IndexedDB session persistence manager. */\n private _session: SessionPersistence | null = null;\n /** Transcript focus, pin-to-bottom, and scroll request coordination. */\n private readonly _presentation = new ChatPresentationState();\n /** Registered event callbacks (GA4 event hooks). Key = event name, value = set of callbacks. */\n private _eventCallbacks = new Map<string, Set<(detail: Record<string, unknown>) => boolean | Promise<boolean>>>();\n /** Last sent action+options for retry on error. */\n private _lastSentAction: {\n action: ActionPayload;\n options?: SendActionOptions | undefined;\n } | null = null;\n /** Consecutive identical error counter for account-inactive detection. */\n private _consecutiveErrorCount = 0;\n /** Last error message text for deduplication. */\n private _lastErrorMessage = '';\n\n protected async onInit(config: ChatWidgetConfig): Promise<void> {\n this._i18n = this._resolveI18n(config);\n this._chatCreatedAt = new Date().toISOString();\n\n if (config.pillLauncher) {\n const kit = makePillLauncher(config.pillLauncher);\n config.launcherImageUrl = kit.launcherImageUrl;\n if (config.headerAvatarUrl === undefined) {\n config.headerAvatarUrl = config.pillLauncher.avatarUrl;\n }\n this._pillLauncherApply = () => kit.apply(this._shadow ?? undefined);\n }\n\n // Create Shadow DOM for CSS isolation\n this._shadow = this.root.attachShadow({ mode: 'open' });\n\n // Inject styles\n const style = document.createElement('style');\n style.textContent = chatStyles;\n this._shadow.appendChild(style);\n\n // Apply theme CSS custom properties on the shadow host so they cascade into the shadow tree.\n // Priority: explicit config fields > theme object entries > CSS defaults.\n const host = this.root as HTMLElement;\n const applyVar = (cssVar: string, value: string | undefined): void => {\n if (value && isSafeCSSColor(value)) host.style.setProperty(cssVar, value);\n };\n // Explicit header color shortcuts\n applyVar('--gengage-chat-header-bg', config.headerBg);\n applyVar('--gengage-chat-header-foreground', config.headerForeground);\n // Arbitrary theme tokens (e.g. theme['--gengage-primary-color'])\n if (config.theme) {\n for (const [key, value] of Object.entries(config.theme)) {\n if (key.startsWith('--') && typeof value === 'string') {\n applyVar(key, value);\n }\n }\n }\n\n this._applyDiscountedPriceColorVar(config.productPriceUi?.discountedPriceColor);\n\n // Create root container\n const rootEl = document.createElement('div');\n rootEl.className = 'gengage-chat-root';\n this._rootEl = rootEl;\n this._shadow.appendChild(rootEl);\n\n const variant = config.variant ?? 'floating';\n if (variant === 'inline') {\n rootEl.classList.add('gengage-chat--inline');\n }\n\n // Dim page + click outside to close (not for inline — drawer is embedded)\n if (variant !== 'inline') {\n const backdropEl = document.createElement('div');\n backdropEl.className = 'gengage-chat-backdrop';\n backdropEl.setAttribute('aria-hidden', 'true');\n backdropEl.setAttribute('role', 'button');\n backdropEl.setAttribute('tabindex', '-1');\n backdropEl.setAttribute('aria-label', this._i18n.closeAriaLabel);\n backdropEl.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n this.close();\n });\n this._backdropEl = backdropEl;\n rootEl.prepend(backdropEl);\n }\n\n // Create launcher (floating variant only — inline/overlay are triggered programmatically)\n if (variant === 'floating') {\n const launcherOpts: import('./components/Launcher.js').LauncherOptions = {\n onClick: () => this.open(),\n ariaLabel: this._i18n.openButton,\n };\n if (config.launcherImageUrl !== undefined) launcherOpts.imageUrl = config.launcherImageUrl;\n else if (config.launcherSvg !== undefined) launcherOpts.svgMarkup = config.launcherSvg;\n if (config.hideMobileLauncher !== undefined) launcherOpts.hideMobile = config.hideMobileLauncher;\n if (config.mobileBreakpoint !== undefined) launcherOpts.mobileBreakpoint = config.mobileBreakpoint;\n if (config.launcherTooltip !== undefined) launcherOpts.tooltip = config.launcherTooltip;\n this._launcher = createLauncher(launcherOpts);\n rootEl.appendChild(this._launcher.container);\n }\n\n // Overlay variant wraps drawer in a backdrop for full-screen modal\n if (variant === 'overlay') {\n rootEl.classList.add('gengage-chat--overlay');\n }\n\n // Create drawer (hidden initially for floating/overlay, visible for inline)\n const drawerContainer = document.createElement('div');\n rootEl.appendChild(drawerContainer);\n\n this._drawer = new ChatDrawer(drawerContainer, {\n i18n: this._i18n,\n onSend: (text, attachment) => this._sendMessage(text, attachment),\n onClose: () => this.close(),\n onAttachment: (file) => this._handleAttachment(file),\n onPanelToggle: () => {\n this._drawer?.persistPanelState(config.accountId);\n },\n onHostShellSync: () => {\n this._applyOpenStateClasses();\n },\n onRollback: (messageId) => this._handleRollback(messageId),\n onPanelBack: () => this._navigatePanelBack(),\n onPanelForward: () => this._panel?.navigateForward(),\n onPanelClose: () => {\n // Mobile ✕: panel is only hidden (content kept); header reopen stays meaningful.\n if (this._isMobileViewport) {\n this._comparisonSelectMode = false;\n this._comparisonSelectedSkus = [];\n return;\n }\n this._localPanelHistory = [];\n this._comparisonSelectMode = false;\n this._comparisonSelectedSkus = [];\n this._currentPanelSource = null;\n },\n headerTitle: config.headerTitle,\n headerAvatarUrl: config.headerAvatarUrl,\n launcherImageUrl: config.launcherImageUrl,\n headerBadge: config.headerBadge,\n headerCartUrl: config.headerCartUrl,\n showHeaderFavorites: typeof config.onFavoritesClick === 'function' || config.headerFavoritesToggle === true,\n onCartClick: () => {\n if (config.headerCartUrl) {\n this._saveSessionAndOpenURL(config.headerCartUrl);\n } else {\n config.onCartClick?.();\n }\n },\n onFavoritesClick: () => {\n ga.trackLikeList();\n if (typeof config.onFavoritesClick === 'function') {\n config.onFavoritesClick();\n return;\n }\n this._openFavoritesPanel();\n },\n getMobileState: () => this._openState ?? 'full',\n getMobileViewport: () => this._isMobileViewport,\n onMobileSnap: (state) => {\n if (state === 'close') {\n this.close();\n } else {\n this._openState = state;\n this._applyOpenStateClasses();\n }\n },\n onThumbnailClick: (threadId) => this._rollbackToThread(threadId),\n onLinkClick: (url) => {\n this._saveSessionAndOpenURL(url);\n },\n voiceEnabled: config.voiceEnabled,\n voiceLang: config.locale\n ? `${config.locale.split('-')[0] ?? 'tr'}-${(config.locale.split('-')[1] ?? config.locale.split('-')[0] ?? 'TR').toUpperCase()}`\n : undefined,\n presentation: {\n onPinnedToBottomChange: (pinned) => {\n this._presentation.pinnedToBottom = pinned;\n },\n onUserInteractingChange: (interacting) => {\n this._presentation.userInteracting = interacting;\n },\n onFormerMessagesHint: () => {\n if (this._presentation.focusedThreadId && this._hasMultipleThreadIds()) {\n this._drawer?.setFormerMessagesButtonVisible(true);\n }\n },\n shouldBlockSoftAutoScroll: () => this._presentation.shouldBlockStreamAutoScroll(),\n onReleasePresentationFocus: () => this._releasePresentationFocus(),\n },\n });\n\n // Extended mode manager for host PDP maximize/minimize\n this._extendedModeManager = new ExtendedModeManager({\n onChange: (extended) => this._panel?.notifyExtension(extended),\n productDetailsInPanel: (config.isDemoWebsite ?? false) && (config.productDetailsExtended ?? false),\n });\n\n // Panel manager for snapshot/topbar/navigation state\n this._panel = new PanelManager({\n drawer: () => this._drawer,\n shadow: () => this._shadow,\n currentThreadId: () => this._currentThreadId,\n bridge: () => this._bridge,\n extendedModeManager: () => this._extendedModeManager,\n i18n: () => this._i18n,\n rollbackToThread: (tid) => this._rollbackToThread(tid),\n });\n\n // Hide drawer initially for floating/overlay variants\n if (variant !== 'inline') {\n this._drawer.getElement().classList.add('gengage-chat-drawer--hidden');\n }\n\n // Restore panel state from session\n const restoredCollapsedPanel = this._drawer.restorePanelState(config.accountId);\n\n // Panel mode: 'collapsed' starts hidden, 'expanded' starts open, 'auto' (default) expands when content arrives\n const panelMode = config.panelMode ?? 'auto';\n if (panelMode === 'collapsed') {\n this._drawer.setPanelCollapsed(true);\n } else if (panelMode === 'expanded') {\n this._drawer.setForceExpanded();\n } else if (!restoredCollapsedPanel) {\n // 'auto': panel starts collapsed — will expand when content arrives via stream\n // (prevents empty panel being visible on page load/session restore)\n }\n\n // Restore session if an explicit handoff signal exists (e.g. SimRel product navigation)\n const restoreSessionId = sessionStorage.getItem('gengage_restore_session_id');\n const restoreSku = sessionStorage.getItem('gengage_restore_sku');\n const hasHandoff = !!(restoreSessionId && restoreSku);\n if (hasHandoff) {\n sessionStorage.removeItem('gengage_restore_session_id');\n sessionStorage.removeItem('gengage_restore_sku');\n }\n\n // IndexedDB persistence — best-effort, non-fatal\n try {\n const idb = new GengageIndexedDB();\n await idb.open();\n this._session = new SessionPersistence(idb);\n await this._restoreFromIndexedDB(hasHandoff);\n } catch {\n // IndexedDB unavailable — continue without persistence\n this._session = new SessionPersistence(null);\n }\n\n // Register public API on window\n this._registerPublicAPI();\n\n // Apply mobileInitialState if configured\n if (config.mobileInitialState !== undefined) {\n this._openState = config.mobileInitialState;\n }\n this._mobileBreakpoint = config.mobileBreakpoint ?? 768;\n\n this._syncViewportState();\n const onResize = () => this._syncViewportState();\n window.addEventListener('resize', onResize, { passive: true });\n this.addCleanup(() => window.removeEventListener('resize', onResize));\n\n // iOS visualViewport keyboard handling\n if (window.visualViewport) {\n const onViewportResize = () => {\n if (!this._drawerVisible || !this._isMobileViewport) return;\n const el = this._drawer?.getElement();\n if (!el) return;\n const offset = window.innerHeight - (window.visualViewport?.height ?? window.innerHeight);\n el.style.setProperty('--gengage-keyboard-offset', `${Math.max(0, offset)}px`);\n };\n window.visualViewport.addEventListener('resize', onViewportResize);\n this.addCleanup(() => window.visualViewport?.removeEventListener('resize', onViewportResize));\n }\n\n // Inline variant starts visible (onShow() is not invoked — mirror presentation state here)\n if (variant === 'inline') {\n this._drawerVisible = true;\n this.isVisible = true;\n this._applyOpenStateClasses();\n this._presentation.setShown(true);\n setTimeout(() => this._maybeAutoAnchorUnreadAssistant(), 60);\n }\n\n // Communication bridge for host ↔ widget messaging\n const bridgeOpts: CommunicationBridgeOptions = {\n namespace: 'chat',\n onMessage: (msg) => this._handleBridgeMessage(msg),\n };\n if (config.allowedOrigins !== undefined) bridgeOpts.allowedOrigins = config.allowedOrigins;\n this._bridge = new CommunicationBridge(bridgeOpts);\n\n // Track initial SKU for page-change detection\n this._lastSku = this.config.pageContext?.sku;\n\n // Mark init complete and drain pending actions queue\n this._initComplete = true;\n for (const pending of this._pendingActions) {\n this._sendAction(pending.action, pending.options);\n }\n this._pendingActions = [];\n\n if (this._pillLauncherApply && variant === 'floating') {\n await this._pillLauncherApply();\n this._pillLauncherApply = null;\n }\n\n dispatch('gengage:chat:ready', {});\n ga.trackInit('chat');\n config.onReady?.();\n }\n\n protected onUpdate(context: Partial<PageContext>): void {\n if (context.sku !== undefined && context.sku !== this._lastSku) {\n this._lastSku = context.sku;\n this._resetForNewPage();\n }\n }\n\n protected onShow(): void {\n this._showDrawer();\n this.emit('open');\n dispatch('gengage:chat:open', { state: this._openState });\n ga.trackShow('chat');\n this.config.onOpen?.();\n\n // Show welcome message on first open with empty history\n this._showWelcomeIfNeeded();\n\n // Prime a contextual opening for blank / homepage / listing states when\n // the merchant configured startup journeys in the SaaS.\n this._maybePrimeEntryContextOpening();\n\n // Auto-launch PDP context on first open when SKU is available\n if (!this._pdpLaunched && this.config.pageContext?.sku) {\n this._pdpLaunched = true;\n this._pdpPrimingInFlight = true;\n this._sendAction(\n {\n title: '',\n type: 'launchSingleProduct',\n payload: {\n sku: this.config.pageContext.sku,\n ...(this._resolveContextualOpeningMessage('product')\n ? { opening_message: this._resolveContextualOpeningMessage('product') }\n : {}),\n ...(this._resolveContextualOpeningGuidance('product')\n ? { opening_guidance: this._resolveContextualOpeningGuidance('product') }\n : {}),\n },\n },\n { silent: true, isPdpPrime: true, preservePills: true },\n );\n }\n }\n\n protected onHide(): void {\n // Floating mode should only hide the drawer, not the launcher button.\n // BaseWidget.hide() toggles `root.style.display = 'none'`, so undo it here.\n if ((this.config.variant ?? 'floating') === 'floating') {\n this.root.style.display = '';\n }\n this._hideDrawer();\n this.emit('close');\n dispatch('gengage:chat:close', {});\n this.config.onClose?.();\n }\n\n protected onDestroy(): void {\n this._releaseHostDocumentScrollLock();\n this._abortAllActiveRequests();\n this._activeTypewriter?.cancel();\n this._activeTypewriter = null;\n this._activeTtsHandle?.stop();\n this._activeTtsHandle = null;\n if (this._comparisonRefreshRafId !== null) {\n cancelAnimationFrame(this._comparisonRefreshRafId);\n this._comparisonRefreshRafId = null;\n }\n this._drawer?.destroy();\n invalidateChatScrollCache();\n this._drawer = null;\n this._bridge?.destroy();\n this._bridge = null;\n this._extendedModeManager = null;\n this._panel?.destroy();\n this._panel = null;\n this._session?.close();\n this._session = null;\n this._localPanelHistory = [];\n this._currentPanelSource = null;\n if (window.gengage) {\n delete window.gengage.chat;\n }\n if (this._shadow) {\n this._shadow.innerHTML = '';\n this._shadow = null;\n }\n this._rootEl = null;\n }\n\n // ---------------------------------------------------------------------------\n // Public API\n // ---------------------------------------------------------------------------\n\n open(options?: { state?: 'full' | 'half'; initialMessage?: string }): void {\n if (options?.state !== undefined) {\n this._openState = options.state;\n if (this._drawerVisible) {\n this._applyOpenStateClasses();\n }\n }\n this.show();\n if (options?.initialMessage !== undefined) {\n this._sendMessage(options.initialMessage);\n }\n }\n\n openWithAction(action: ActionPayload, options?: { sku?: string; state?: 'full' | 'half' }): void {\n if (options?.sku !== undefined) {\n this.update({ sku: options.sku });\n }\n // Mark PDP as launched since we're sending an explicit action\n this._pdpLaunched = true;\n // Action-triggered opens default to half-sheet on mobile\n if (options?.state !== undefined) {\n this._openState = options.state;\n } else if (this._isMobileViewport) {\n this._openState = 'half';\n }\n this.show();\n if (this._drawerVisible) {\n this._applyOpenStateClasses();\n }\n this._sendAction(action);\n }\n\n /** Send a user message programmatically (same flow as typing + submit). */\n sendMessage(text: string): void {\n this._sendMessage(text);\n }\n\n /** Send a backend action programmatically. */\n sendAction(action: ActionPayload, options?: { silent?: boolean }): void {\n this._sendAction(action, options);\n }\n\n close(): void {\n this.hide();\n }\n\n saveSession(sessionId: string, sku: string): void {\n sessionStorage.setItem('gengage_restore_session_id', sessionId);\n sessionStorage.setItem('gengage_restore_sku', sku);\n }\n\n get isOpen(): boolean {\n return this._drawerVisible;\n }\n\n /**\n * Register a callback for integration events (e.g. 'gengage-cart-add', 'gengage-product-favorite').\n * The callback receives the event detail and should return true (success) or false (failure).\n * For add-to-cart, failure triggers an error message in the chat.\n * For product-favorite, failure reverts the heart on the card and shows an error message.\n * @returns unsubscribe function\n */\n addCallback(\n eventName: string,\n callback: (detail: Record<string, unknown>) => boolean | Promise<boolean>,\n ): () => void {\n let set = this._eventCallbacks.get(eventName);\n if (!set) {\n set = new Set();\n this._eventCallbacks.set(eventName, set);\n }\n set.add(callback);\n return () => {\n set!.delete(callback);\n if (set!.size === 0) this._eventCallbacks.delete(eventName);\n };\n }\n\n // ---------------------------------------------------------------------------\n // Private\n // ---------------------------------------------------------------------------\n\n private _applyDiscountedPriceColorVar(mode: import('./types.js').DiscountedPriceColor | undefined): void {\n const host = this.root as HTMLElement;\n if (mode === 'client') {\n host.style.setProperty('--gengage-discounted-price-color', 'var(--client-primary)');\n } else {\n host.style.removeProperty('--gengage-discounted-price-color');\n }\n }\n\n private _abortAllActiveRequests(): void {\n for (const controller of this._abortControllers) {\n controller.abort();\n }\n this._abortControllers.clear();\n }\n\n /**\n * Drop assistant rows that are still \"streaming\" with no text when the user starts\n * a new turn. Otherwise each superseded request leaves an empty model message in\n * `_messages` — it is included in `chatHistory` and can confuse the backend and\n * delay or distort the next response.\n */\n private _pruneEmptyStreamingAssistantPlaceholders(): void {\n const next: ChatMessage[] = [];\n let thumbnailsChanged = false;\n for (const m of this._messages) {\n // Also catch status === 'done': the Stop button handler transitions streaming → done\n // before _sendAction fires, so a stop-then-Enter sequence would otherwise leave an\n // empty assistant turn in chatHistory and confuse the backend.\n const drop =\n m.role === 'assistant' &&\n (m.status === 'streaming' || m.status === 'done') &&\n (m.content == null || m.content.length === 0);\n if (drop) {\n if (m.threadId) {\n this._threadsWithFirstBot.delete(m.threadId);\n this._presentation.finalizeAssistantGroup(m.threadId);\n // Remove inline UISpec nodes (data-thread-id but no data-message-id) that may\n // have arrived before any outputText — removeMessageBubble only targets the bubble.\n this._shadow\n ?.querySelectorAll(`[data-thread-id=\"${CSS.escape(m.threadId)}\"]:not([data-message-id])`)\n .forEach((el) => el.remove());\n if (this._panel) {\n this._panel.threads = this._panel.threads.filter((t) => t !== m.threadId);\n }\n const before = this._thumbnailEntries.length;\n this._thumbnailEntries = this._thumbnailEntries.filter((e) => e.threadId !== m.threadId);\n if (this._thumbnailEntries.length !== before) thumbnailsChanged = true;\n }\n this._drawer?.removeMessageBubble(m.id);\n this._panel?.snapshots.delete(m.id);\n this._panel?.snapshotTypes.delete(m.id);\n continue;\n }\n next.push(m);\n }\n this._messages.length = 0;\n this._messages.push(...next);\n if (thumbnailsChanged) this._drawer?.setThumbnails(this._thumbnailEntries);\n }\n\n /** Reset all chat state when navigating to a different SKU/page. */\n private _resetForNewPage(): void {\n // Invalidate any in-flight stream callbacks so they discard themselves\n // via the `threadId !== this._activeRequestThreadId` guard.\n this._activeRequestThreadId = null;\n // Abort in-flight streams\n this._abortAllActiveRequests();\n // Cancel active typewriter/TTS\n this._activeTypewriter?.cancel();\n this._activeTypewriter = null;\n this._activeTtsHandle?.stop();\n this._activeTtsHandle = null;\n // Clear messages\n this._messages.length = 0;\n this._drawer?.clearMessages();\n // Clear panel\n this._drawer?.clearPanel();\n this._currentPanelSource = null;\n this._panel!.snapshots.clear();\n this._panel!.threads = [];\n // Clear thumbnails\n this._thumbnailEntries = [];\n this._drawer?.setThumbnails([]);\n // Reset comparison state\n this._comparisonSelectMode = false;\n this._comparisonSelectedSkus = [];\n this._viewedProductSkus.clear();\n // Reset thread cursors\n this._currentThreadId = null;\n this._lastThreadId = null;\n this._lastBackendContext = null;\n this._chatCreatedAt = new Date().toISOString();\n // Allow PDP auto-launch for new SKU\n this._pdpLaunched = false;\n this._entryContextPrimed = false;\n this._pdpPrimingInFlight = false;\n this._queuedUserMessages = [];\n this._productContextUnavailableSku = null;\n this._presentation.reset();\n this._drawer?.setPresentationFocus(null);\n this._drawer?.setFormerMessagesButtonVisible(false);\n }\n\n /**\n * Same side effects as stream UISpec `clearPanel`: hide/clear the assistant panel without\n * `restoreOrClearPanel()`. Used for `clearPanel` chunks and PDP chat layout (`productDetailsExtended` off).\n */\n private _clearAssistantPanelLikeStreamClearPanel(): void {\n this._choicePrompterEl?.remove();\n this._choicePrompterEl = null;\n this._drawer?.clearPanel();\n this._currentPanelSource = null;\n this._thumbnailEntries = [];\n this._drawer?.setThumbnails([]);\n this._comparisonSelectMode = false;\n this._comparisonSelectedSkus = [];\n this._comparisonSelectionWarning = null;\n if (this._panel) {\n this._panel.currentType = null;\n this._panel.updateExtendedMode('');\n this._panel.updateTopBar('');\n }\n }\n\n private _flushPresentationScroll(): void {\n const req = this._presentation.scrollRequest;\n if (!req || !this._drawer) return;\n let handled = false;\n if (req.type === 'thread' && req.threadId) {\n handled = this._drawer.scrollThreadIntoView(req.threadId, req.behavior);\n if (!handled) {\n this._drawer.scrollToBottomPresentation(req.behavior);\n handled = true;\n }\n } else if (req.type === 'bottom') {\n this._drawer.scrollToBottomPresentation(req.behavior);\n handled = true;\n }\n if (handled) {\n this._presentation.consumeScrollRequest(req.id);\n }\n }\n\n private _releasePresentationFocus(): void {\n this._presentation.releaseFocusedThread();\n this._drawer?.setPresentationFocus(null);\n this._drawer?.setFormerMessagesButtonVisible(false);\n }\n\n private _hasMultipleThreadIds(): boolean {\n const ids = new Set<string>();\n for (const m of this._messages) {\n if (m.threadId) ids.add(m.threadId);\n }\n return ids.size > 1;\n }\n\n private _orderedThreadIds(): string[] {\n const ordered: string[] = [];\n const seen = new Set<string>();\n for (const m of this._messages) {\n if (m.threadId && !seen.has(m.threadId)) {\n seen.add(m.threadId);\n ordered.push(m.threadId);\n }\n }\n return ordered;\n }\n\n private _maybeAutoAnchorUnreadAssistant(): void {\n if (!this._drawer || !this._presentation.shown) return;\n const threadIds = this._orderedThreadIds();\n if (threadIds.length === 0) return;\n const latestUnread = getLatestUnreadAssistantThreadId(threadIds, this._presentation);\n if (!latestUnread) return;\n const gid = `${latestUnread}:assistant`;\n if (this._presentation.lastAutoAnchoredGroupId === gid) return;\n if (this._presentation.userInteracting && !this._presentation.pinnedToBottom) return;\n if (this._drawer.scrollThreadIntoView(latestUnread, 'smooth')) {\n this._presentation.markGroupAutoAnchored(gid);\n }\n }\n\n private _handleBridgeMessage(msg: BridgeMessage): void {\n switch (msg.type) {\n case 'openChat':\n this.open();\n break;\n case 'closeChat':\n this.close();\n break;\n case 'startNewChatWithLauncherAction': {\n // Start a new chat with a preset launcher action.\n // Legacy sends payload as the action directly: { type, title, requestDetails }\n // Clean-room also accepts nested: { action: { type, title, ... } }\n const payload = msg.payload as Record<string, unknown> | undefined;\n const nested = payload?.action;\n // Prefer nested shape, fall back to payload-is-action (legacy compat)\n const action = isActionLike(nested) ? nested : isActionLike(payload) ? payload : null;\n if (action) {\n this._sendAction(action, { silent: true });\n }\n this.open();\n break;\n }\n case 'startNewChatWithDetailContext': {\n // Start chat with product detail context (e.g., from PDP page).\n // Legacy shape: { action: {...}, sku: \"...\" }\n const ctx = msg.payload as Record<string, unknown> | undefined;\n if (ctx && typeof ctx === 'object') {\n this._bridgeContext = ctx;\n // Extract SKU and update page context so auto-PDP launch uses it\n const sku = ctx.sku as string | undefined;\n if (sku) {\n this.update({ sku });\n }\n // Extract and send the action (legacy sent action + premade context)\n if (isActionLike(ctx.action)) {\n this._pdpLaunched = true; // Skip auto-launch, explicit action takes priority\n this.open();\n this._sendAction(ctx.action);\n break;\n }\n }\n this.open();\n break;\n }\n case 'launcherAction': {\n // Send action to active chat from external trigger\n const payload = msg.payload as Record<string, unknown> | undefined;\n const action = payload?.action;\n if (action && typeof action === 'object' && 'type' in (action as Record<string, unknown>)) {\n this._sendAction(action as ActionPayload);\n }\n break;\n }\n case 'scrollToBottom':\n this._presentation.requestScrollToBottom('smooth');\n setTimeout(() => this._flushPresentationScroll(), 40);\n break;\n case 'addToCardHandler': {\n // Host notifies widget of add-to-cart result\n this._bridge?.send('addToCardResult', msg.payload);\n break;\n }\n case 'cartQuantityHandler': {\n // Host sends updated cart quantity\n const payload = msg.payload as Record<string, unknown> | undefined;\n if (payload && 'quantity' in payload && typeof payload.quantity === 'number') {\n this._cartQuantity = payload.quantity;\n }\n break;\n }\n case 'favoritesCountHandler': {\n const payload = msg.payload as Record<string, unknown> | undefined;\n const count = payload?.count;\n if (typeof count === 'number' && count >= 0) {\n this._drawer?.updateFavoritesBadge(count);\n }\n break;\n }\n case 'minimizeRequestedByUser':\n this._extendedModeManager?.setHiddenByUser(true);\n break;\n case 'bgColorChange': {\n const payload = msg.payload as Record<string, unknown> | undefined;\n const color = payload?.color;\n if (typeof color === 'string' && isSafeCSSColor(color) && this._shadow) {\n (this._shadow.host as HTMLElement).style.setProperty('--gengage-chat-bg', color);\n }\n break;\n }\n default:\n break;\n }\n }\n\n private _registerPublicAPI(): void {\n if (!window.gengage) window.gengage = {};\n window.gengage.chat = {\n open: (opts) => this.open(opts),\n openWithAction: (action, opts) => this.openWithAction(action, opts),\n sendMessage: (text) => this.sendMessage(text),\n sendAction: (action, opts) => this.sendAction(action, opts),\n close: () => this.close(),\n saveSession: (sid, sku) => this.saveSession(sid, sku),\n get isOpen() {\n return false;\n }, // Placeholder, overridden below\n on: (event, handler) => this.on(event, handler),\n trackCheckout: (type, data) => this.trackCheckout(type, data),\n flushMeteringSummary: (data) => this.flushMeteringSummary(data),\n addCallback: (eventName, callback) => this.addCallback(eventName, callback),\n };\n // Fix isOpen getter to reflect actual state\n Object.defineProperty(window.gengage.chat, 'isOpen', {\n get: () => this._drawerVisible,\n });\n }\n\n private _showDrawer(): void {\n if (this._drawerVisible) return;\n this._drawerVisible = true;\n const el = this._drawer?.getElement();\n if (el) {\n el.classList.remove('gengage-chat-drawer--hidden');\n }\n this._applyOpenStateClasses();\n this._drawer?.trapFocus();\n if (!(this._isMobileViewport && this._openState === 'half')) {\n this._drawer?.focusInput();\n }\n this._extendedModeManager?.setChatShown(true);\n this._presentation.setShown(true);\n setTimeout(() => this._maybeAutoAnchorUnreadAssistant(), 60);\n }\n\n /** Show welcome message and starter pills on first open with empty history. */\n private _showWelcomeIfNeeded(): void {\n if (this._messages.length !== 0) return;\n\n const contextKey = this._resolveOpeningContextKey();\n const openingActions = this._resolveContextualOpeningActions(contextKey);\n if (openingActions.length > 0) {\n this._drawer?.setPills(\n openingActions.map((action) => ({\n label: action.title,\n onAction: () => this._sendAction(this._resolveContextualOpeningAction(action, contextKey)),\n })),\n );\n }\n\n if (contextKey === 'product' && this.config.pageContext?.sku) {\n return;\n }\n\n if (this._shouldPrimeContextualOpening(contextKey)) {\n return;\n }\n\n const welcomeMessage = this._resolveContextualOpeningMessage(contextKey);\n if (!welcomeMessage) return;\n\n const welcomeMsg: ChatMessage = {\n id: uuidv7(),\n role: 'assistant',\n content: welcomeMessage,\n timestamp: Date.now(),\n status: 'done',\n };\n this._messages.push(welcomeMsg);\n this._drawer?.addMessage(welcomeMsg);\n }\n\n private _resolveOpeningContextKey(): OpeningContextKey {\n switch (this.config.pageContext?.pageType) {\n case 'home':\n return 'home';\n case 'search':\n case 'plp':\n return 'listing';\n case 'pdp':\n return 'product';\n default:\n return 'default';\n }\n }\n\n private _resolveContextualOpeningMessage(contextKey = this._resolveOpeningContextKey()): string | undefined {\n return (\n this.config.openingMessagesByContext?.[contextKey] ??\n (contextKey !== 'default' ? this.config.openingMessagesByContext?.default : undefined) ??\n this.config.welcomeMessage\n );\n }\n\n private _resolveContextualOpeningGuidance(contextKey = this._resolveOpeningContextKey()): string | undefined {\n return (\n this.config.openingGuidanceByContext?.[contextKey] ??\n (contextKey !== 'default' ? this.config.openingGuidanceByContext?.default : undefined)\n );\n }\n\n private _resolveContextualOpeningActions(contextKey = this._resolveOpeningContextKey()): ChatActionChip[] {\n const contextualActions =\n this.config.welcomeActionsByContext?.[contextKey] ??\n (contextKey !== 'default' ? this.config.welcomeActionsByContext?.default : undefined);\n\n if (contextualActions?.length) {\n return contextualActions.filter((action) => typeof action?.title === 'string' && action.title.trim().length > 0);\n }\n\n return (this.config.welcomeActions ?? [])\n .filter((title) => typeof title === 'string' && title.trim().length > 0)\n .map((title) => ({ title }));\n }\n\n private _resolveContextualOpeningAction(\n action: ChatActionChip,\n contextKey = this._resolveOpeningContextKey(),\n ): ActionPayload {\n const title = action.title.trim();\n const primarySku = this.config.pageContext?.sku ?? this._readContextStringField('sku');\n const visibleSkus = this._readContextStringListField('visible_skus') ?? [];\n\n if (action.icon === 'review' && primarySku) {\n return {\n title,\n type: 'reviewSummary',\n payload: { sku: primarySku },\n };\n }\n\n if (action.icon === 'similar' && primarySku) {\n return {\n title,\n type: 'findSimilar',\n payload: { sku: primarySku },\n };\n }\n\n if (action.icon === 'compare' && contextKey === 'listing' && visibleSkus.length >= 2) {\n return {\n title,\n type: 'getComparisonTable',\n payload: { sku_list: visibleSkus.slice(0, 2) },\n };\n }\n\n return {\n title,\n type: 'user_message',\n payload: title,\n };\n }\n\n private _readContextStringField(key: string): string | undefined {\n const pageContext = this.config.pageContext as Record<string, unknown> | undefined;\n const extra =\n pageContext?.extra && typeof pageContext.extra === 'object' && !Array.isArray(pageContext.extra)\n ? (pageContext.extra as Record<string, unknown>)\n : undefined;\n const altCamel = key.replace(/_([a-z])/g, (_match, letter: string) => letter.toUpperCase());\n const candidate = pageContext?.[key] ?? extra?.[key] ?? pageContext?.[altCamel] ?? extra?.[altCamel];\n return typeof candidate === 'string' && candidate.trim().length > 0 ? candidate.trim() : undefined;\n }\n\n private _readContextStringListField(key: string): string[] | undefined {\n const pageContext = this.config.pageContext as Record<string, unknown> | undefined;\n const extra =\n pageContext?.extra && typeof pageContext.extra === 'object' && !Array.isArray(pageContext.extra)\n ? (pageContext.extra as Record<string, unknown>)\n : undefined;\n const altCamel = key.replace(/_([a-z])/g, (_match, letter: string) => letter.toUpperCase());\n const candidate = pageContext?.[key] ?? extra?.[key] ?? pageContext?.[altCamel] ?? extra?.[altCamel];\n if (!Array.isArray(candidate)) return undefined;\n const values = candidate.filter((item): item is string => typeof item === 'string' && item.trim().length > 0);\n return values.length > 0 ? values : undefined;\n }\n\n private _buildEntryOpeningPageDetails(): Record<string, unknown> | undefined {\n const pageDetails: Record<string, unknown> = {};\n\n pageDetails.url = this.config.pageContext?.url ?? window.location.href;\n\n const pageTitle = this._readContextStringField('page_title');\n if (pageTitle) pageDetails.page_title = pageTitle;\n\n const pageDescription = this._readContextStringField('page_description');\n if (pageDescription) pageDetails.page_description = pageDescription;\n\n const searchQuery = this._readContextStringField('search_query');\n if (searchQuery) pageDetails.search_query = searchQuery;\n\n const visibleSkus = this._readContextStringListField('visible_skus');\n if (visibleSkus?.length) pageDetails.visible_skus = visibleSkus;\n\n const popularSearches = this._readContextStringListField('popular_searches');\n if (popularSearches?.length) pageDetails.popular_searches = popularSearches;\n\n const categoryPath = this.config.pageContext?.categoryTree ?? this._readContextStringListField('category_path');\n if (categoryPath?.length) pageDetails.category_path = categoryPath;\n\n return Object.keys(pageDetails).length > 0 ? pageDetails : undefined;\n }\n\n private _shouldPrimeContextualOpening(contextKey = this._resolveOpeningContextKey()): boolean {\n if (this._messages.length !== 0 || this._entryContextPrimed || this._pdpPrimingInFlight) return false;\n if (contextKey === 'product') return false;\n return Boolean(\n this.config.openingMessagesByContext ||\n this.config.openingGuidanceByContext ||\n this.config.welcomeActionsByContext,\n );\n }\n\n private _maybePrimeEntryContextOpening(): void {\n const contextKey = this._resolveOpeningContextKey();\n if (!this._shouldPrimeContextualOpening(contextKey)) return;\n\n this._entryContextPrimed = true;\n\n const payload: Record<string, unknown> = {\n text: '',\n is_entry_context_opening: 1,\n opening_context_key: contextKey,\n };\n\n const pageDetails = this._buildEntryOpeningPageDetails();\n if (pageDetails) payload.page_details = pageDetails;\n\n const openingMessage = this._resolveContextualOpeningMessage(contextKey);\n if (openingMessage) payload.opening_message = openingMessage;\n\n const openingGuidance = this._resolveContextualOpeningGuidance(contextKey);\n if (openingGuidance) payload.opening_guidance = openingGuidance;\n\n this._sendAction(\n {\n title: '',\n type: 'user_message',\n payload,\n },\n { silent: true, preservePills: true },\n );\n }\n\n private _hideDrawer(): void {\n if (!this._drawerVisible) return;\n this._drawer?.releaseFocus();\n this._activeTypewriter?.cancel();\n this._activeTypewriter = null;\n this._activeTtsHandle?.stop();\n this._activeTtsHandle = null;\n this._drawerVisible = false;\n // Reset mobile open state so next launcher open defaults to full-screen\n this._openState = 'full';\n // Don't clear panel — preserve it so reopening the drawer shows the same\n // panel state. clearPanel() is reserved for SKU/page resets and stale\n // loading skeleton cleanup after streams.\n const el = this._drawer?.getElement();\n if (el) {\n el.classList.add('gengage-chat-drawer--hidden');\n }\n this._applyOpenStateClasses();\n this._extendedModeManager?.setChatShown(false);\n this._presentation.setShown(false);\n this._drawer?.setPresentationFocus(null);\n this._drawer?.setFormerMessagesButtonVisible(false);\n }\n\n private _syncViewportState(): void {\n if (!this._rootEl) return;\n this._isMobileViewport = window.innerWidth <= this._mobileBreakpoint;\n this._rootEl.classList.toggle('gengage-chat-root--mobile', this._isMobileViewport);\n\n if (this._launcher) {\n const hideLauncher = this._isMobileViewport && this.config.hideMobileLauncher === true;\n this._launcher.container.classList.toggle('gengage-chat-launcher--hidden-mobile', hideLauncher);\n }\n\n this._applyOpenStateClasses();\n }\n\n /**\n * Scroll lock + dimming backdrop apply only in this state:\n * - overlay: drawer open (full-screen modal)\n * - floating: drawer open and side panel visible (split / “maximized” layout)\n */\n private _isMaximizedForHostChrome(): boolean {\n if (!this._drawerVisible) return false;\n const variant = this.config.variant ?? 'floating';\n if (variant === 'inline') return false;\n if (variant === 'overlay') return true;\n return this._drawer?.isPanelVisible() ?? false;\n }\n\n private _applyOpenStateClasses(): void {\n if (!this._rootEl) return;\n const mobileHalf = this._drawerVisible && this._isMobileViewport && this._openState === 'half';\n const mobileFull = this._drawerVisible && this._isMobileViewport && this._openState === 'full';\n const maximizedHost = this._isMaximizedForHostChrome();\n this._rootEl.classList.toggle('gengage-chat-root--open', this._drawerVisible);\n this._rootEl.classList.toggle('gengage-chat-root--mobile-half', mobileHalf);\n this._rootEl.classList.toggle('gengage-chat-root--mobile-full', mobileFull);\n this._rootEl.classList.toggle('gengage-chat-root--maximized-host-chrome', maximizedHost);\n if (this._backdropEl) {\n const backdropIsScrim = maximizedHost && (this.config.variant ?? 'floating') !== 'inline';\n this._backdropEl.setAttribute('aria-hidden', backdropIsScrim ? 'false' : 'true');\n }\n this._syncHostDocumentScrollLock();\n }\n\n private _syncHostDocumentScrollLock(): void {\n if (typeof document === 'undefined') return;\n const variant = this.config.variant ?? 'floating';\n if (variant === 'inline') {\n this._releaseHostDocumentScrollLock();\n return;\n }\n if (this._isMaximizedForHostChrome()) {\n this._applyHostDocumentScrollLock();\n } else {\n this._releaseHostDocumentScrollLock();\n }\n }\n\n /**\n * When true, we do not preventDefault — the event may drive chat-internal scrolling.\n * Full-viewport backdrop sits inside the shadow tree, so `composedPath()` always hits\n * the host; we must not treat \"inside widget\" as \"allow scroll\" for that case.\n */\n private _hostScrollEventShouldReachChatScroller(ev: Event): boolean {\n try {\n const path = ev.composedPath();\n if (!path.includes(this.root)) return false;\n\n for (const n of path) {\n if (n === this.root) break;\n if (!(n instanceof HTMLElement)) continue;\n\n if (this._backdropEl && (n === this._backdropEl || this._backdropEl.contains(n))) {\n return false;\n }\n\n const cs = window.getComputedStyle(n);\n const canY = (cs.overflowY === 'auto' || cs.overflowY === 'scroll') && n.scrollHeight > n.clientHeight + 1;\n const canX = (cs.overflowX === 'auto' || cs.overflowX === 'scroll') && n.scrollWidth > n.clientWidth + 1;\n if (canY || canX) return true;\n }\n return false;\n } catch {\n return false;\n }\n }\n\n private _applyHostDocumentScrollLock(): void {\n if (typeof document === 'undefined' || this._hostScrollLockActive) return;\n document.addEventListener('touchmove', this._preventHostDocumentTouchMove, { capture: true, passive: false });\n document.addEventListener('wheel', this._preventHostDocumentWheel, { capture: true, passive: false });\n this._hostScrollLockActive = true;\n }\n\n private _releaseHostDocumentScrollLock(): void {\n if (typeof document === 'undefined' || !this._hostScrollLockActive) return;\n document.removeEventListener('touchmove', this._preventHostDocumentTouchMove, { capture: true });\n document.removeEventListener('wheel', this._preventHostDocumentWheel, { capture: true });\n this._hostScrollLockActive = false;\n }\n\n private _handleAttachment(file: File): void {\n const result = validateImageFile(file);\n if (!result.ok) {\n const message = result.reason === 'invalid_type' ? this._i18n.invalidFileType : this._i18n.fileTooLarge;\n dispatch('gengage:global:error', {\n message,\n source: 'chat' as const,\n });\n return;\n }\n this._drawer?.stageAttachment(file);\n }\n\n private _sendMessage(text: string, attachment?: File): void {\n if (this._pdpPrimingInFlight) {\n this._queuedUserMessages.push(attachment !== undefined ? { text, attachment } : { text });\n return;\n }\n\n // First outgoing user message: mark KVKK accepted so `kvkkApproved` is true on this request;\n // if banner is still visible on a later send, dismiss it without requiring ×.\n const isFirstUserMessage = !this._messages.some((m) => m.role === 'user');\n if (isFirstUserMessage || this._drawer?.isKvkkBannerVisible()) {\n markKvkkShown(this.config.accountId);\n this._drawer?.hideKvkkBanner();\n }\n\n ga.trackMessageSent();\n // Track conversation start on first user message in a new thread\n const hasUserMessages = this._messages.some((m) => m.role === 'user');\n if (!hasUserMessages) {\n ga.trackConversationStart();\n }\n // Image upload from Chat Input uses findSimilar (not inputText) per wire protocol\n const action: ActionPayload =\n attachment !== undefined\n ? { title: text, type: 'findSimilar', payload: text ? { text } : {} }\n : { title: text, type: 'user_message', payload: text };\n if (attachment !== undefined) {\n this._sendAction(action, { attachment });\n } else {\n this._sendAction(action);\n }\n }\n\n private _flushQueuedUserMessages(): void {\n if (this._pdpPrimingInFlight || this._queuedUserMessages.length === 0) return;\n const queued = [...this._queuedUserMessages];\n this._queuedUserMessages = [];\n for (const item of queued) {\n this._sendMessage(item.text, item.attachment);\n }\n }\n\n private _sendAction(action: ActionPayload, options?: SendActionOptions): void {\n // Cancel any running typewriter animation and TTS playback\n this._activeTypewriter?.cancel();\n this._activeTypewriter = null;\n this._activeTtsHandle?.stop();\n this._activeTtsHandle = null;\n\n // Track last action for retry on error\n this._lastSentAction = { action, options };\n\n // Defer actions until init completes\n if (!this._initComplete) {\n if (this._pendingActions.length < 10) {\n this._pendingActions.push({ action, options });\n }\n return;\n }\n\n // Remove ChoicePrompter on new action\n this._choicePrompterEl?.remove();\n this._choicePrompterEl = null;\n // Fresh user request: reset ChoicePrompter dismiss so it can show again on the next grid\n if (!options?.preservePanel) {\n clearChoicePrompterDismissState();\n }\n\n // Clear comparison mode when starting a new request (unless preservePanel).\n // Exception: getComparisonTable actions keep comparison state visible during\n // the request so the floating button and checkboxes stay as loading feedback.\n // State is cleared once the ComparisonTable UISpec actually renders in the panel.\n if (!options?.preservePanel && this._comparisonSelectMode && action.type !== 'getComparisonTable') {\n this._comparisonSelectMode = false;\n this._comparisonSelectedSkus = [];\n }\n\n // Clear local panel history on new requests (fresh panel context)\n if (!options?.preservePanel) {\n this._localPanelHistory = [];\n }\n\n // Branch deletion: if user is sending from a rewound position, prune future messages\n if (this._currentThreadId && this._lastThreadId && this._lastThreadId > this._currentThreadId) {\n const cutoff = this._currentThreadId;\n // Remove future messages from array\n const removed = this._messages.filter((m) => m.threadId !== undefined && m.threadId > cutoff);\n this._messages = this._messages.filter((m) => !m.threadId || m.threadId <= cutoff);\n // Remove their DOM nodes\n for (const msg of removed) {\n this._shadow?.querySelector(`[data-message-id=\"${CSS.escape(msg.id)}\"]`)?.remove();\n this._panel!.snapshots.delete(msg.id);\n this._panel!.snapshotTypes.delete(msg.id);\n }\n // Remove orphaned inline UISpec elements\n const orphanedUISpecs = this._shadow?.querySelectorAll(`[data-thread-id]`);\n orphanedUISpecs?.forEach((el) => {\n if (el instanceof HTMLElement && el.dataset['threadId'] && el.dataset['threadId'] > cutoff) {\n el.remove();\n }\n });\n }\n\n // Clear previous suggestion pills and input chips\n if (!options?.preservePills) {\n this._drawer?.setPills([]);\n }\n this._drawer?.clearInputAreaChips();\n\n // Notify host that assistant is responding\n this._bridge?.send('isResponding', true);\n\n // Generate thread ID for this request-response cycle\n const threadId = uuidv7();\n this._currentThreadId = threadId;\n this._lastThreadId = threadId;\n // Preserve the active grid intent during product drilldowns. A product click\n // should not relabel an existing search-result panel as \"similar products\".\n if (this._panel && action.type !== 'launchSingleProduct') {\n this._panel.lastActionType = action.type;\n }\n // For preservePanel actions (like/addToCart), don't overwrite _activeRequestThreadId\n // to avoid silencing concurrent streams. Instead, track validity locally.\n const isPreservePanel = options?.preservePanel === true;\n const isPdpAutoLaunch =\n action.type === 'launchSingleProduct' && options?.silent === true && options?.isPdpPrime === true;\n if (!isPreservePanel) {\n this._activeRequestThreadId = threadId;\n }\n\n // Align presentation focus with this thread *before* appending the user bubble.\n // Otherwise addMessage()'s collapse pass uses the previous focus and marks the\n // new bubble as presentation-collapsed until a later setPresentationFocus — which\n // can yield wrong scroll targets (offsetTop on display:none) and “empty” transcript.\n if (!options?.silent && !isPreservePanel) {\n this._drawer?.setPresentationFocus(threadId);\n }\n\n // Add user message to UI (skip for silent/auto-launch actions)\n if (!options?.silent) {\n const userText =\n typeof action.payload === 'string'\n ? action.payload\n : typeof (action.payload as Record<string, unknown>)?.['text'] === 'string'\n ? ((action.payload as Record<string, unknown>)['text'] as string)\n : action.title;\n // Retry deduplication: skip adding a duplicate user bubble when retrying\n const lastMsg = this._messages.length > 0 ? this._messages[this._messages.length - 1] : undefined;\n const isDuplicate = lastMsg !== undefined && lastMsg.role === 'user' && lastMsg.content === userText;\n if (!isDuplicate) {\n const userMsg = this._createMessage('user', userText);\n userMsg.threadId = threadId;\n if (options?.attachment !== undefined) {\n userMsg.attachment = options.attachment;\n }\n this._drawer?.addMessage(userMsg);\n this._messages.push(userMsg);\n }\n }\n\n const shouldShortCircuitUnavailableContext =\n !options?.silent &&\n this._hasUnavailableProductContext() &&\n (action.type === 'user_message' || action.type === 'inputText');\n if (shouldShortCircuitUnavailableContext) {\n const fallback = this._i18n.productNotFoundMessage;\n const botMsg = this._createMessage('assistant', fallback);\n botMsg.threadId = threadId;\n botMsg.status = 'done';\n this._messages.push(botMsg);\n this._ensureAssistantMessageRendered(botMsg);\n this._drawer?.updateBotMessage(botMsg.id, fallback);\n this._drawer?.setPresentationFocus(threadId);\n this._bridge?.send('isResponding', false);\n this.emit('message', botMsg);\n this._persistToIndexedDB().catch(() => {\n /* non-fatal */\n });\n return;\n }\n\n // Preserve panel during the request — don't clear or show loading skeleton\n // until the backend explicitly signals new panel content (panelLoading event).\n // Exception: getComparisonTable shows the panel skeleton immediately (desktop + mobile)\n // so users see progress while the table streams in.\n // Captures the panel source (UISpec/kind) so it can be re-rendered with fresh\n // event listeners if the backend fails to deliver new panel content.\n let prePanelSource = this._currentPanelSource;\n let prePanelSourceCaptured = false;\n const capturePanelSourceIfNeeded = (): void => {\n if (prePanelSourceCaptured || options?.preservePanel) return;\n prePanelSource = this._currentPanelSource;\n prePanelSourceCaptured = true;\n };\n const restoreOrClearPanel = (): void => {\n if (!this._drawer?.isPanelLoading()) return;\n if (prePanelSource) {\n const ctx = this._buildRenderContext();\n const el = this._renderPanelFromSource(prePanelSource, ctx);\n this._drawer.setPanelContent(el);\n this._drawer.setDividerPreviewEnabled(this._shouldUseDividerPreviewForSource(prePanelSource));\n this._currentPanelSource = prePanelSource;\n } else {\n this._drawer.clearPanel();\n this._currentPanelSource = null;\n }\n prePanelSource = null;\n };\n\n if (action.type === 'getComparisonTable') {\n this._drawer?.showPanelLoading('comparisonTable');\n this._panel?.updateTopBarForLoading('comparisonTable');\n }\n\n if (!options?.silent && !isPreservePanel) {\n this._pruneEmptyStreamingAssistantPlaceholders();\n }\n\n // Show typing indicator\n this._drawer?.showTypingIndicator();\n // Use a local text accumulator per stream invocation to prevent corruption\n // when multiple concurrent preservePanel streams write to the same state.\n let localBotText = '';\n\n // Create bot message placeholder\n const botMsg = this._createMessage('assistant', '');\n botMsg.threadId = threadId;\n botMsg.status = 'streaming';\n if (options?.silent) botMsg.silent = true;\n // Note: silent flag only skips the USER message above — bot response is always rendered\n this._messages.push(botMsg);\n\n this._presentation.registerAssistantActivity(threadId);\n this._presentation.requestThreadFocus(threadId, 'smooth');\n // User-visible non-preservePanel: focus was already set before the user bubble.\n if (options?.silent || isPreservePanel) {\n this._drawer?.setPresentationFocus(threadId);\n }\n this._drawer?.setFormerMessagesButtonVisible(false);\n setTimeout(() => this._flushPresentationScroll(), 40);\n\n // Abort previous request(s) — skip for preservePanel to avoid killing concurrent streams\n if (!options?.preservePanel) {\n this._abortAllActiveRequests();\n }\n\n const transport: ChatTransportConfig = {\n middlewareUrl: this.config.middlewareUrl,\n ...(this.config.accountId ? { accountId: this.config.accountId } : {}),\n };\n if (options?.attachment !== undefined) {\n transport.attachment = options.attachment;\n }\n\n const visibleMessages = this._getVisibleMessages();\n const chatHistory = visibleMessages\n // Keep assistant messages even when empty (panel-only responses) so the\n // backend sees the proper alternating user/model turn structure. Exclude\n // the current bot placeholder (just created, not yet populated).\n .filter((m) => m !== botMsg && (m.content || m.role === 'assistant'))\n .slice(-50)\n .map((m) => ({\n role: m.role === 'user' ? 'user' : 'model',\n content: m.content ?? '',\n }));\n\n // Build meta object for backend\n const meta: BackendRequestMeta = {\n outputLanguage: localeToOutputLanguage(this.config.locale),\n parentUrl: window.location.href,\n windowWidth: String(window.innerWidth),\n windowHeight: String(window.innerHeight),\n selfUrl: '',\n id: this.config.session?.sessionId ?? '',\n userId: this.config.session?.userId ?? '',\n appId: this.config.accountId,\n threads: [],\n createdAt: this._chatCreatedAt,\n kvkkApproved: isKvkkShown(this.config.accountId),\n voiceEnabled: this.config.voiceEnabled ?? false,\n threadId,\n isControlGroup: this.config.session?.abTestVariant === 'control',\n isMobile: this._isMobileViewport,\n };\n if (this.config.session?.viewId !== undefined) {\n meta.viewId = this.config.session.viewId;\n }\n\n // Enrich action payload with fields the backend expects\n const enrichedAction = enrichActionPayload(action, {\n pageContext: this.config.pageContext,\n backendContext: this._lastBackendContext,\n isMobile: this._isMobileViewport,\n });\n\n const request: import('./api.js').ProcessActionRequest = {\n account_id: this.config.accountId,\n session_id: this.config.session?.sessionId ?? '',\n correlation_id: this.config.session?.sessionId ?? '',\n type: enrichedAction.type,\n locale: this.config.locale ?? 'tr',\n meta,\n context: {\n // Spread backend context (panel, message_id, etc.) but preserve FE's\n // authoritative chatHistory — the backend's stale `messages` must not\n // overwrite the up-to-date conversation history built by the widget.\n ...(this._lastBackendContext ?? {}),\n messages: chatHistory,\n // Backend reads session_id from context.\n session_id: this.config.session?.sessionId ?? '',\n },\n };\n\n // Only set optional fields when values exist (exactOptionalPropertyTypes)\n if (this.config.session?.userId !== undefined) {\n request.user_id = this.config.session.userId;\n }\n if (this.config.session?.viewId !== undefined) {\n request.view_id = this.config.session.viewId;\n }\n if (enrichedAction.payload !== undefined) {\n request.payload = enrichedAction.payload;\n }\n if (this.config.pageContext?.sku !== undefined) {\n request.sku = this.config.pageContext.sku;\n }\n if (this.config.pageContext?.pageType !== undefined) {\n request.page_type = this.config.pageContext.pageType;\n }\n\n // Analytics tracking state for this request\n const requestId = crypto.randomUUID();\n const streamStart = Date.now();\n let chunkIndex = 0;\n let panelLoadingSeen = false;\n let panelContentReceived = false;\n /** Desktop: panel shows ProductGrid / Categories — AI picks can render above instead of in chat. */\n let panelListEligibleForAiZone = false;\n let aiAnalysisUiReceivedForPanel = false;\n let streamDone = false;\n /** AITopPicks / AIGroupingCards often arrive before product_list; flush when grid mounts. */\n let pendingPanelAiSpec: UISpec | null = null;\n\n const syncPanelAiAnalysisZone = (): void => {\n if (!this._drawer) return;\n if (this._isMobileViewport || !panelListEligibleForAiZone) {\n this._drawer.setPanelAiZoneState('hidden');\n return;\n }\n if (aiAnalysisUiReceivedForPanel) return;\n if (!streamDone) {\n this._drawer.setPanelAiZoneState('analyzing', { analyzingLabel: this._i18n.aiAnalysisAnalyzingLabel });\n } else {\n this._drawer.setPanelAiZoneState('hidden');\n }\n };\n\n this.track(\n streamStartEvent(this.analyticsContext(), {\n endpoint: 'process_action',\n request_id: requestId,\n widget: 'chat',\n }),\n );\n\n let streamController: AbortController | null = null;\n streamController = sendChatMessage(\n request,\n {\n onTextChunk: (content, isFinal, extra) => {\n if (!isPreservePanel && threadId !== this._activeRequestThreadId) return;\n localBotText += content;\n this._drawer?.removeTypingIndicator();\n\n // Store enrichment data from outputText for downstream rendering\n if (extra?.skuToProductItem) {\n this._skuToProductItem = { ...this._skuToProductItem, ...extra.skuToProductItem };\n }\n if (extra?.conversationMode) {\n this._conversationMode = extra.conversationMode;\n }\n\n this.track(\n streamChunkEvent(this.analyticsContext(), {\n request_id: requestId,\n chunk_index: chunkIndex++,\n widget: 'chat',\n }),\n );\n\n if (!this._drawer) return;\n\n // KVKK filtering: always strip KVKK block from display text.\n // Show banner on first encounter only.\n let displayText = localBotText;\n if (isFinal && containsKvkk(displayText)) {\n const acctId = this.config.accountId;\n if (!isKvkkShown(acctId)) {\n const kvkkHtml = extractKvkkBlock(displayText);\n if (kvkkHtml) {\n this._drawer?.showKvkkBanner(kvkkHtml, () => {\n this._drawer?.hideKvkkBanner();\n markKvkkShown(acctId);\n });\n } else {\n // No KVKK block found — mark as shown so we don't re-check\n markKvkkShown(acctId);\n }\n }\n displayText = stripKvkkBlock(displayText);\n }\n\n // Check if we already have a bot bubble in the DOM (query by message ID, not :last-child)\n const existingBubble = this._shadow?.querySelector(\n `[data-message-id=\"${botMsg.id}\"] .gengage-chat-bubble-text`,\n );\n if (existingBubble) {\n existingBubble.innerHTML = sanitizeHtml(displayText);\n } else {\n botMsg.content = displayText;\n if (botMsg.role === 'assistant' && botMsg.threadId && !this._threadsWithFirstBot.has(botMsg.threadId)) {\n this._threadsWithFirstBot.add(botMsg.threadId);\n this._drawer.markFirstBotMessage(botMsg.id);\n }\n this._drawer.addMessage(botMsg);\n }\n\n if (isFinal) {\n botMsg.content = displayText;\n botMsg.status = 'done';\n ga.trackMessageReceived();\n\n // Apply typewriter animation to the final bot text\n const bubbleTextEl = this._shadow?.querySelector(\n `[data-message-id=\"${botMsg.id}\"] .gengage-chat-bubble-text`,\n ) as HTMLElement | null;\n if (bubbleTextEl) {\n this._activeTypewriter?.cancel();\n const mentions = extra?.productMentions;\n this._activeTypewriter = typewriteHtml({\n container: bubbleTextEl,\n html: sanitizeHtml(displayText),\n onTick: () => this._drawer?.scrollToBottomIfNeeded(),\n onComplete: () => {\n this._activeTypewriter = null;\n // Link product mentions after typewriter finishes\n if (mentions && mentions.length > 0 && bubbleTextEl) {\n linkProductMentions({\n container: bubbleTextEl,\n mentions,\n onProductClick: (sku) => {\n this._sendAction({\n title: mentions.find((m) => m.sku === sku)?.short_name ?? sku,\n type: 'launchSingleProduct',\n payload: { sku },\n });\n },\n });\n }\n },\n });\n }\n }\n },\n onUISpec: (spec, widget, panelHint, clearPanel) => {\n if (!isPreservePanel && threadId !== this._activeRequestThreadId) return;\n if (widget !== 'chat') return;\n\n // StreamEventUISpec.clearPanel: hide/clear the assistant panel. Do not use\n // restoreOrClearPanel() here — that restores the pre-loading snapshot while the skeleton\n // is shown and breaks intended panel-dismiss behavior.\n if (clearPanel) {\n this._clearAssistantPanelLikeStreamClearPanel();\n panelLoadingSeen = false;\n }\n\n const rootElement = spec.elements[spec.root];\n const componentType = rootElement?.type ?? 'unknown';\n const similarsAppendGrid = componentType === 'ProductGrid' && rootElement?.props?.['similarsAppend'] === true;\n /** PDP akışında yan panel kapalı: tam detay + benzer ürün grid’i yalnızca sohbette. */\n const skipSidePanelForUISpec =\n this.config.productDetailsExtended !== true &&\n (componentType === 'ProductDetailsPanel' || similarsAppendGrid);\n if (skipSidePanelForUISpec && !clearPanel) {\n this._clearAssistantPanelLikeStreamClearPanel();\n panelLoadingSeen = false;\n }\n const effectivePanelHint =\n componentType === 'ProductDetailsPanel' && panelHint !== 'panel' ? ('panel' as const) : panelHint;\n this.track(\n streamUiSpecEvent(this.analyticsContext(), {\n request_id: requestId,\n chunk_index: chunkIndex,\n component_type: componentType,\n widget: 'chat',\n }),\n );\n\n const renderContext = this._buildRenderContext();\n renderContext.isStreaming = true;\n\n // GA dataLayer: track component-specific events\n if (componentType === 'ComparisonTable') {\n const products = rootElement?.props?.['products'];\n ga.trackCompareReceived(Array.isArray(products) ? products.length : 0);\n }\n if (componentType === 'ProductGrid') {\n const childCount = rootElement?.children?.length ?? 0;\n ga.trackSearch(undefined, childCount);\n }\n\n const panelSpec = effectivePanelHint === 'panel' && this._panel ? this._panel.toPanelSpec(spec) : spec;\n\n if (effectivePanelHint === 'panel' && this._panel && !skipSidePanelForUISpec) {\n const isFirstPanelContentInStream = !panelContentReceived;\n panelContentReceived = true;\n\n const panelAction = determinePanelUpdateAction({\n componentType,\n similarsAppend: rootElement?.props?.['similarsAppend'] === true,\n currentPanelType: this._panel.currentType,\n hasPanelContent: this._drawer?.hasPanelContent() ?? false,\n isPanelLoading: this._drawer?.isPanelLoading() ?? false,\n isFirstPanelContentInStream,\n });\n\n renderContext.panelProductListHeading = undefined;\n if (componentType === 'ProductGrid') {\n if (panelAction === 'appendSimilars') {\n renderContext.panelProductListHeading = this._i18n.similarProductsLabel ?? 'Similar Products';\n } else {\n this._applyPanelListHeadingToContext(renderContext, { kind: 'spec', spec: panelSpec });\n }\n }\n\n if (panelAction === 'appendSimilars') {\n this._appendSimilarsToPanel(panelSpec, renderContext);\n } else if (panelAction === 'append') {\n this._drawer?.appendPanelContent(this._renderUISpec(panelSpec, renderContext));\n if (this._comparisonSelectMode) {\n this._refreshComparisonUI();\n }\n } else {\n // Reset comparison state when new panel content replaces the grid\n this._comparisonSelectMode = false;\n this._comparisonSelectedSkus = [];\n this._comparisonSelectionWarning = null;\n this._drawer?.setComparisonDockContent(null);\n this._drawer?.setPanelContent(this._renderUISpec(panelSpec, renderContext));\n this._currentPanelSource = { kind: 'spec', spec: panelSpec };\n this._panel.currentType = componentType;\n }\n this._drawer?.setDividerPreviewEnabled((this._panel.currentType ?? componentType) === 'ProductGrid');\n\n if (componentType === 'ProductDetailsPanel' && action.type === 'launchSingleProduct') {\n this._clearUnavailableProductContext();\n }\n\n // Track panel thread and update topbar + extended mode\n if (botMsg.threadId && !this._panel.threads.includes(botMsg.threadId)) {\n this._panel.threads.push(botMsg.threadId);\n }\n // Use the primary panel type for title (don't let appended grids overwrite it).\n // Backend-provided panelTitle (e.g. search results title) takes precedence.\n const titleType = this._panel.currentType ?? componentType;\n const backendTitle = rootElement?.props?.['panelTitle'] as string | undefined;\n this._panel.updateTopBar(titleType, backendTitle);\n this._panel.updateExtendedMode(componentType);\n\n // Desktop AI analysis zone: list/grid in panel → analyzing strip until Top Picks / groupings\n if (componentType === 'ProductGrid' || componentType === 'CategoriesContainer') {\n panelListEligibleForAiZone = !this._isMobileViewport;\n // Top Picks / groupings may have streamed before product_list — apply now that panel + zone exist\n if (pendingPanelAiSpec) {\n const flushCtx = this._buildRenderContext();\n flushCtx.isStreaming = true;\n const aiEl = this._renderUISpec(pendingPanelAiSpec, flushCtx);\n aiAnalysisUiReceivedForPanel = true;\n this._drawer?.setPanelAiZoneState('results', { resultEl: aiEl });\n pendingPanelAiSpec = null;\n }\n } else if (panelAction !== 'appendSimilars' && panelAction !== 'append') {\n panelListEligibleForAiZone = false;\n pendingPanelAiSpec = null;\n this._drawer?.setPanelAiZoneState('hidden');\n }\n }\n\n // ProductDetailsPanel goes to the panel, but also render a compact\n // horizontal ProductSummaryCard in chat messages (production parity\n // with the prior engine's LaunchSingleProduct component).\n // Silent PDP auto-prime (`isPdpAutoLaunch`) still uses a silent bot row for\n // history, but users should see the same inline summary + chip row as when\n // they open a product from chat (non-silent launchSingleProduct).\n if (\n componentType === 'ProductDetailsPanel' &&\n effectivePanelHint === 'panel' &&\n (!botMsg.silent || isPdpAutoLaunch)\n ) {\n const product = rootElement?.props?.['product'] as Record<string, unknown> | undefined;\n if (product) {\n const inlineSpec: UISpec = {\n root: 'root',\n elements: {\n root: {\n type: 'ProductSummaryCard',\n props: { product },\n },\n },\n };\n const messagesContainer = this._shadow?.querySelector('.gengage-chat-messages');\n if (messagesContainer) {\n const inline = this._renderUISpec(inlineSpec, renderContext);\n if (botMsg.threadId) {\n inline.dataset['threadId'] = botMsg.threadId;\n }\n const bubble = this._shadow?.querySelector(`[data-message-id=\"${botMsg.id}\"]`) as HTMLElement | null;\n if (bubble && bubble.parentNode === messagesContainer) {\n bubble.after(inline);\n } else {\n messagesContainer.appendChild(inline);\n }\n inline.scrollIntoView({ behavior: 'auto', block: 'end' });\n this._drawer?.refreshPresentationCollapsed();\n panelContentReceived = true;\n }\n }\n }\n\n const isAiAnalysisComponent = componentType === 'AITopPicks' || componentType === 'AIGroupingCards';\n let routeAiAnalysisToPanel = false;\n let deferAiPanelUntilGrid = false;\n if (skipSidePanelForUISpec && similarsAppendGrid) {\n renderContext.panelProductListHeading = this._i18n.similarProductsLabel ?? 'Similar Products';\n }\n if (isAiAnalysisComponent && !this._isMobileViewport && !botMsg.silent) {\n if (panelListEligibleForAiZone) {\n const aiEl = this._renderUISpec(spec, renderContext);\n aiAnalysisUiReceivedForPanel = true;\n this._drawer?.setPanelAiZoneState('results', { resultEl: aiEl });\n routeAiAnalysisToPanel = true;\n pendingPanelAiSpec = null;\n } else {\n pendingPanelAiSpec = spec;\n deferAiPanelUntilGrid = true;\n }\n }\n\n const inlineOkWhenSilentPrime = isPdpAutoLaunch && componentType === 'GroundingReviewCard';\n const shouldRenderInline =\n (!botMsg.silent || inlineOkWhenSilentPrime) &&\n (effectivePanelHint !== 'panel' ||\n componentType === 'ProductCard' ||\n (skipSidePanelForUISpec && componentType === 'ProductGrid')) &&\n componentType !== 'ActionButtons' && // ActionButtons render as bottom pills only\n !routeAiAnalysisToPanel &&\n !(deferAiPanelUntilGrid && isAiAnalysisComponent);\n\n if (shouldRenderInline) {\n const messagesContainer = this._shadow?.querySelector('.gengage-chat-messages');\n if (messagesContainer) {\n const inline = this._renderUISpec(spec, renderContext);\n if (botMsg.threadId) {\n inline.dataset['threadId'] = botMsg.threadId;\n }\n messagesContainer.appendChild(inline);\n inline.scrollIntoView({ behavior: 'auto', block: 'end' });\n this._drawer?.refreshPresentationCollapsed();\n if (skipSidePanelForUISpec && componentType === 'ProductGrid') {\n panelContentReceived = true;\n }\n }\n }\n\n // Track product thumbnails for ThumbnailsColumn\n if ((componentType === 'ProductGrid' || componentType === 'ProductCard') && botMsg.threadId) {\n const childIds = rootElement?.children ?? [];\n const products =\n componentType === 'ProductGrid'\n ? (childIds\n .map((id) => spec.elements[id]?.props?.['product'] as Record<string, unknown> | undefined)\n .filter(Boolean) as Record<string, unknown>[])\n : ([rootElement?.props?.['product'] as Record<string, unknown> | undefined].filter(Boolean) as Record<\n string,\n unknown\n >[]);\n\n for (const product of products) {\n const sku = product['sku'] as string | undefined;\n const imageUrl = product['imageUrl'] as string | undefined;\n if (sku && imageUrl) {\n this._thumbnailEntries.push({ sku, imageUrl, threadId: botMsg.threadId });\n }\n if (sku) {\n this._viewedProductSkus.add(sku);\n }\n }\n this._drawer?.setThumbnails(this._thumbnailEntries);\n }\n\n // Send preview images to host for launcher thumbnails\n if (componentType === 'ProductGrid' || componentType === 'ProductDetailsPanel') {\n const previewProducts =\n componentType === 'ProductGrid'\n ? ((rootElement?.children ?? [])\n .map((id) => spec.elements[id]?.props?.['product'] as Record<string, unknown> | undefined)\n .filter(Boolean) as Record<string, unknown>[])\n : ([\n (rootElement?.props?.['product'] ?? rootElement?.props) as Record<string, unknown> | undefined,\n ].filter(Boolean) as Record<string, unknown>[]);\n const previewImageUrls = previewProducts\n .map((p) => p['imageUrl'] as string | undefined)\n .filter((url): url is string => typeof url === 'string')\n .slice(0, 5);\n if (previewImageUrls.length > 0) {\n this._bridge?.send('previewImages', { images: previewImageUrls });\n }\n }\n\n // ChoicePrompter: panel ProductGrid with 2+ products, comparison mode off\n const productGridChildCount = rootElement?.children?.length ?? 0;\n if (\n componentType === 'ProductGrid' &&\n effectivePanelHint === 'panel' &&\n !skipSidePanelForUISpec &&\n productGridChildCount > 1 &&\n !this._comparisonSelectMode &&\n !isChoicePrompterDismissed(this._currentThreadId ?? '')\n ) {\n this._choicePrompterEl?.remove();\n this._shadow?.querySelectorAll('.gengage-chat-choice-prompter').forEach((el) => el.remove());\n this._choicePrompterEl = createChoicePrompter({\n heading: this._i18n.choicePrompterHeading,\n suggestion: this._i18n.choicePrompterSuggestion,\n ctaLabel: this._i18n.choicePrompterCta,\n threadId: this._currentThreadId ?? '',\n dismissAriaLabel: this._i18n.dismissAriaLabel,\n onCtaClick: () => {\n this._comparisonSelectMode = true;\n this._choicePrompterEl = null;\n this._refreshComparisonUI();\n },\n onDismiss: () => {\n this._choicePrompterEl = null;\n },\n });\n // Mount in the panel float anchor — the prompter floats at the\n // bottom-right of the details pane (panel), not the conversation pane.\n const mountEl = this._shadow?.querySelector('.gengage-chat-panel-float');\n if (mountEl) {\n mountEl.appendChild(this._choicePrompterEl);\n\n // Dismiss ChoicePrompter when the mobile keyboard opens (viewport shrinks)\n if (this._isMobileViewport && window.visualViewport) {\n const prompterRef = this._choicePrompterEl;\n const dismissOnKeyboard = (): void => {\n const heightRatio = window.visualViewport!.height / window.innerHeight;\n if (heightRatio < 0.75) {\n prompterRef.remove();\n if (this._choicePrompterEl === prompterRef) {\n this._choicePrompterEl = null;\n }\n window.visualViewport!.removeEventListener('resize', dismissOnKeyboard);\n }\n };\n window.visualViewport.addEventListener('resize', dismissOnKeyboard);\n }\n } else {\n this._choicePrompterEl = null;\n }\n }\n\n // Extract suggestion pills / input-area chips from ActionButtons UISpec\n if (componentType === 'ActionButtons') {\n const buttons = rootElement?.props?.['buttons'] as\n | Array<{\n label: string;\n action: ActionPayload;\n icon?: string;\n image?: string;\n description?: string;\n }>\n | undefined;\n if (buttons && buttons.length > 0) {\n const inputChips: Array<{ label: string; icon?: string | undefined; action: ActionPayload }> = [];\n const pillButtons: typeof buttons = [];\n\n for (const btn of buttons) {\n if (isInputAreaAction(btn)) {\n const chip: { label: string; icon?: string | undefined; action: ActionPayload } = {\n label: btn.label,\n action: btn.action,\n };\n if (btn.icon) chip.icon = btn.icon;\n inputChips.push(chip);\n } else {\n pillButtons.push(btn);\n }\n }\n\n if (inputChips.length > 0) {\n this._drawer?.setInputAreaChips(\n inputChips.map((chip) => ({\n label: chip.label,\n onAction: () => this._sendAction(chip.action),\n ...(chip.icon ? { icon: chip.icon } : {}),\n })),\n );\n }\n\n if (pillButtons.length > 0) {\n this._drawer?.setPills(\n pillButtons.map((btn) => {\n const pill: {\n label: string;\n onAction: () => void;\n icon?: string;\n image?: string;\n description?: string;\n } = {\n label: btn.label,\n onAction: () => this._sendAction(btn.action),\n };\n if (btn.icon) pill.icon = btn.icon;\n if (btn.image) pill.image = btn.image;\n if (btn.description) pill.description = btn.description;\n return pill;\n }),\n );\n }\n }\n }\n\n syncPanelAiAnalysisZone();\n botMsg.uiSpec = spec;\n },\n onAction: (event: StreamEvent) => {\n if (!isPreservePanel && threadId !== this._activeRequestThreadId) return;\n if (event.type === 'action') {\n const routerOpts: ActionRouterOptions = {};\n if (this.config.actionHandling?.unknownActionPolicy !== undefined) {\n routerOpts.unknownActionPolicy = this.config.actionHandling.unknownActionPolicy;\n }\n if (this.config.actionHandling?.allowScriptCall !== undefined) {\n routerOpts.allowScriptCall = this.config.actionHandling.allowScriptCall;\n }\n routeStreamAction(\n event as StreamEventAction,\n {\n openChat: () => this.open(),\n navigate: (params) => {\n if (!isSafeUrl(params.url)) return;\n this._bridge?.send('navigate', params);\n if (params.newTab) {\n window.open(params.url, '_blank', 'noopener,noreferrer');\n } else {\n window.location.href = params.url;\n }\n },\n saveSession: (params) => this.saveSession(params.sessionId, params.sku),\n addToCart: (params) => {\n dispatch('gengage:similar:add-to-cart', params);\n },\n scriptCall: (params) => {\n dispatch('gengage:chat:script-call', params);\n this.config.onScriptCall?.(params);\n },\n },\n routerOpts,\n );\n }\n },\n onMetadata: (event: StreamEvent) => {\n if (!isPreservePanel && threadId !== this._activeRequestThreadId) return;\n if (event.type === 'metadata' && event.meta) {\n // Store backend context for sending with next request\n if (\n event.meta.panel !== undefined ||\n event.meta.messages !== undefined ||\n event.meta.message_id !== undefined\n ) {\n this._lastBackendContext = event.meta as import('../common/types.js').BackendContext;\n }\n\n // Panel loading indicator\n if (event.meta.panelLoading) {\n const pendingType =\n typeof event.meta.panelPendingType === 'string' ? event.meta.panelPendingType : undefined;\n const suppressProductDetailsSkeleton =\n this.config.productDetailsExtended !== true &&\n (pendingType === 'productDetails' || pendingType === 'productDetailsSimilars');\n if (!suppressProductDetailsSkeleton) {\n panelLoadingSeen = true;\n panelContentReceived = false;\n // Snapshot current panel before replacing with skeleton\n capturePanelSourceIfNeeded();\n if (this._panel) this._panel.currentType = null;\n this._drawer?.showPanelLoading(pendingType);\n // Set panel topbar title immediately so it's not an empty white bar\n if (pendingType) {\n this._panel?.updateTopBarForLoading(pendingType);\n }\n }\n }\n\n // Optional voice payload emitted by backend when voiceEnabled is true.\n if (event.meta.voice) {\n // Dispatch cancelable event — host can call preventDefault() to suppress built-in playback\n const voiceEvent = new CustomEvent('gengage:chat:voice', {\n detail: { payload: event.meta.voice },\n bubbles: false,\n cancelable: true,\n });\n const allowed = window.dispatchEvent(voiceEvent);\n ga.trackVoiceInput();\n\n // Built-in TTS playback (skipped if host called preventDefault)\n if (allowed) {\n const voicePayload = event.meta.voice as { audio_base64?: string; content_type?: string };\n if (voicePayload.audio_base64) {\n this._activeTtsHandle?.stop();\n this._activeTtsHandle = playTtsAudio(\n voicePayload.audio_base64,\n voicePayload.content_type ?? 'audio/ogg',\n );\n }\n }\n }\n\n if (event.meta.redirectTarget || event.meta.redirect) {\n dispatch('gengage:chat:redirect', {\n target: event.meta.redirectTarget ?? null,\n payload: event.meta.redirect ?? null,\n });\n }\n\n // Analyze animation — show panel loading skeleton with pulse\n if (event.meta.analyzeAnimation && this.config.productDetailsExtended === true) {\n panelLoadingSeen = true;\n panelContentReceived = false;\n capturePanelSourceIfNeeded();\n if (this._panel) this._panel.currentType = null;\n this._drawer?.showPanelLoading();\n // Default to product details title during analyze\n this._panel?.updateTopBarForLoading('productDetails');\n }\n\n // Thinking step messages — accumulate as checklist in typing indicator\n if (event.meta.loading) {\n const thinkingMessages = Array.isArray(event.meta.thinkingMessages)\n ? event.meta.thinkingMessages.filter((item): item is string => typeof item === 'string')\n : [];\n if (thinkingMessages.length > 0) {\n this._drawer?.setThinkingSteps(thinkingMessages);\n }\n if (typeof event.meta.loadingText === 'string') {\n this._drawer?.addThinkingStep(event.meta.loadingText);\n this._bridge?.send('loadingMessage', { text: event.meta.loadingText });\n }\n }\n\n // Forward visitor engagement data to host page\n if (event.meta.visitorDataResponse) {\n this._bridge?.send('engagingMessage', event.meta.visitorDataResponse);\n }\n\n // Forward form events to host via bridge (Otokoc-specific)\n if (event.meta.formType) {\n this._bridge?.send('glovOtokoc', {\n type: event.meta.formType,\n data: event.meta.formPayload,\n });\n }\n\n // Forward launcher content to host via bridge\n if (event.meta.launcherContent) {\n this._bridge?.send('launcherContent', event.meta.launcherContent);\n }\n\n dispatch('gengage:chat:metadata', { payload: event.meta });\n\n // Extract LLM usage from metadata if present\n const meta = event.meta;\n if (typeof meta.prompt_tokens === 'number' && typeof meta.completion_tokens === 'number') {\n this.track(\n llmUsageEvent(this.analyticsContext(), {\n model: event.model ?? 'unknown',\n prompt_tokens: meta.prompt_tokens as number,\n completion_tokens: meta.completion_tokens as number,\n total_tokens:\n (meta.total_tokens as number) ??\n (meta.prompt_tokens as number) + (meta.completion_tokens as number),\n }),\n );\n }\n }\n },\n onError: (err) => {\n if (streamController) this._abortControllers.delete(streamController);\n // Skip error handling for aborted/superseded requests (including when no active request)\n if (!isPreservePanel && threadId !== this._activeRequestThreadId) return;\n streamDone = true;\n this._activeTypewriter?.cancel();\n this._activeTypewriter = null;\n syncPanelAiAnalysisZone();\n pendingPanelAiSpec = null;\n this._bridge?.send('isResponding', false);\n this._bridge?.send('loadingMessage', { text: null });\n this._drawer?.removeTypingIndicator();\n this._drawer?.clearInputAreaChips();\n // Capture panel state before resetting — needed for error gating below\n const hadPanelContent = panelContentReceived;\n if (panelLoadingSeen && !panelContentReceived) restoreOrClearPanel();\n panelLoadingSeen = false;\n panelContentReceived = false;\n // When the stream already delivered partial content (bot text, panel, etc.),\n // still show backend error text + recovery pills — not only clear stale chips.\n const hasContent =\n botMsg.silent ||\n (botMsg.content != null && botMsg.content.length > 0) ||\n localBotText.length > 0 ||\n hadPanelContent;\n const shouldSuppressOfflineError =\n typeof navigator !== 'undefined' && navigator.onLine === false && isLikelyConnectivityIssue(err);\n\n const removeAssistantPlaceholderBubble = (): void => {\n this._shadow?.querySelector(`[data-message-id=\"${CSS.escape(botMsg.id)}\"]`)?.remove();\n const idx = this._messages.indexOf(botMsg);\n if (idx >= 0) this._messages.splice(idx, 1);\n };\n\n let placeholderBubbleRemoved = false;\n\n const applyStreamErrorRecovery = (): void => {\n if (shouldSuppressOfflineError) return;\n this.emit('error', err);\n const errMsg = err.message;\n if (errMsg === this._lastErrorMessage) {\n this._consecutiveErrorCount++;\n } else {\n this._consecutiveErrorCount = 1;\n this._lastErrorMessage = errMsg;\n }\n const backendDetail = err.message.trim();\n const displayText = backendDetail.length > 0 ? backendDetail : this._i18n.errorMessage;\n const recoveryActions = {\n onRetry: () => {\n if (this._lastSentAction) {\n this._sendAction(this._lastSentAction.action, this._lastSentAction.options);\n }\n },\n onNewQuestion: () => {\n this._drawer?.focusInput();\n },\n };\n\n if (this._consecutiveErrorCount >= 2) {\n removeAssistantPlaceholderBubble();\n placeholderBubbleRemoved = true;\n this._drawer?.showErrorWithRecovery(this._i18n.accountInactiveMessage, recoveryActions);\n return;\n }\n\n if (shouldShowStreamErrorAsRedStrip(err, displayText)) {\n removeAssistantPlaceholderBubble();\n placeholderBubbleRemoved = true;\n this._drawer?.showErrorWithRecovery(displayText, recoveryActions);\n return;\n }\n\n botMsg.content = displayText;\n botMsg.status = 'done';\n const bubbleHtml = sanitizeHtml(displayText.replace(/\\r\\n/g, '\\n').split('\\n').join('<br />'));\n this._ensureAssistantMessageRendered(botMsg);\n this._drawer?.updateBotMessage(botMsg.id, bubbleHtml);\n this._drawer?.showRecoveryPillsOnly(recoveryActions);\n };\n\n if (!hasContent) {\n if (isPdpAutoLaunch || this._hasUnavailableProductContext()) {\n this._drawer?.setPills([]);\n // Show soft fallback instead of generic error for auto-launch\n const fallback = this._i18n.productNotFoundMessage;\n botMsg.content = fallback;\n botMsg.status = 'done';\n this._ensureAssistantMessageRendered(botMsg);\n this._drawer?.updateBotMessage(botMsg.id, fallback);\n this._markUnavailableProductContext();\n } else {\n applyStreamErrorRecovery();\n if (shouldSuppressOfflineError) {\n return;\n }\n }\n } else {\n this._drawer?.setPills([]);\n if (!botMsg.silent) {\n applyStreamErrorRecovery();\n }\n }\n if (isPdpAutoLaunch) {\n this._pdpPrimingInFlight = false;\n this._flushQueuedUserMessages();\n }\n if (!placeholderBubbleRemoved && botMsg.status === 'streaming') {\n botMsg.status = 'error';\n }\n\n // Skip analytics for suppressed offline errors (consistent with the\n // early return in the !hasContent branch above).\n if (!shouldSuppressOfflineError) {\n this.track(\n streamErrorEvent(this.analyticsContext(), {\n request_id: requestId,\n error_code: 'STREAM_ERROR',\n error_message: err.message,\n widget: 'chat',\n }),\n );\n }\n },\n onDone: () => {\n if (streamController) this._abortControllers.delete(streamController);\n // Skip cleanup for aborted/superseded requests\n if (!isPreservePanel && threadId !== this._activeRequestThreadId) return;\n streamDone = true;\n syncPanelAiAnalysisZone();\n // product_list never arrived but AI Top Picks / groupings were deferred — show in chat\n if (pendingPanelAiSpec) {\n const flushCtx = this._buildRenderContext();\n flushCtx.isStreaming = false;\n const messagesContainer = this._shadow?.querySelector('.gengage-chat-messages');\n if (messagesContainer) {\n const inline = this._renderUISpec(pendingPanelAiSpec, flushCtx);\n if (botMsg.threadId) inline.dataset['threadId'] = botMsg.threadId;\n messagesContainer.appendChild(inline);\n inline.scrollIntoView({ behavior: 'auto', block: 'end' });\n this._drawer?.refreshPresentationCollapsed();\n }\n pendingPanelAiSpec = null;\n }\n this._activeRequestThreadId = null;\n // Reset consecutive error counter on successful stream completion\n this._consecutiveErrorCount = 0;\n this._lastErrorMessage = '';\n this._bridge?.send('isResponding', false);\n this._bridge?.send('loadingMessage', { text: null });\n this._drawer?.removeTypingIndicator();\n if (panelLoadingSeen && !panelContentReceived) restoreOrClearPanel();\n panelLoadingSeen = false;\n panelContentReceived = false;\n // Detect failed PDP auto-launch: silent launch action that produced\n // no visible content (no bot text, no panel). Show a soft fallback\n // message so the user isn't left with an empty chat.\n if (isPdpAutoLaunch && !localBotText && !panelContentReceived) {\n const fallback = this._i18n.productNotFoundMessage;\n botMsg.content = fallback;\n this._ensureAssistantMessageRendered(botMsg);\n this._drawer?.updateBotMessage(botMsg.id, fallback);\n this._markUnavailableProductContext();\n }\n if (isPdpAutoLaunch) {\n this._pdpPrimingInFlight = false;\n const hadQueuedMessages = this._queuedUserMessages.length > 0;\n this._flushQueuedUserMessages();\n if (!hadQueuedMessages) {\n this._ensurePdpPrimeSuggestedUiIfNeeded();\n }\n }\n\n if (botMsg.status === 'streaming') {\n botMsg.status = 'done';\n ga.trackMessageReceived();\n }\n this._presentation.finalizeAssistantGroup(threadId);\n\n // Reveal the comparison toggle button (hidden during streaming) with fade-in\n const hiddenCompareBtn = this._shadow?.querySelector('.gengage-chat-comparison-toggle-btn--hidden');\n if (hiddenCompareBtn) {\n hiddenCompareBtn.classList.remove('gengage-chat-comparison-toggle-btn--hidden');\n hiddenCompareBtn.classList.add('gengage-chat-comparison-toggle-btn--reveal');\n }\n\n this.emit('message', botMsg);\n\n // Snapshot current panel content for this message's history.\n // Pass a rebuild function so restored panels have live event listeners.\n const panelSource = this._currentPanelSource;\n this._panel?.snapshotForMessage(\n botMsg.id,\n panelSource\n ? () => {\n const ctx = this._buildRenderContext();\n return this._renderPanelFromSource(panelSource, ctx);\n }\n : undefined,\n );\n\n this.track(\n streamDoneEvent(this.analyticsContext(), {\n request_id: requestId,\n latency_ms: Date.now() - streamStart,\n chunk_count: chunkIndex,\n widget: 'chat',\n }),\n );\n\n this.track(\n meteringIncrementEvent(this.analyticsContext(), {\n meter_key: 'chat_request',\n quantity: 1,\n unit: 'request',\n }),\n );\n\n this.track(\n chatHistorySnapshotEvent(this.analyticsContext(), {\n message_count: this._messages.length,\n history_ref: this.config.session?.sessionId ?? '',\n redaction_level: 'none',\n }),\n );\n\n // Persist session to IndexedDB (fire-and-forget)\n this._persistToIndexedDB().catch(() => {\n /* non-fatal */\n });\n },\n },\n transport,\n );\n this._abortControllers.add(streamController);\n\n // Show \"Stop generating\" button for user-visible streams\n if (!options?.silent && !isPreservePanel) {\n const ctrl = streamController;\n this._drawer?.showStopButton(() => {\n ctrl.abort();\n this._abortControllers.delete(ctrl);\n this._drawer?.removeTypingIndicator();\n this._bridge?.send('isResponding', false);\n this._bridge?.send('loadingMessage', { text: null });\n if (botMsg.status === 'streaming') {\n botMsg.status = 'done';\n }\n });\n }\n }\n\n /** Return messages visible at the current thread cursor. */\n private _getVisibleMessages(): ChatMessage[] {\n const msgs = this._messages.filter((m) => !m.silent);\n if (!this._currentThreadId) return msgs;\n const cutoff = this._currentThreadId;\n return msgs.filter((m) => !m.threadId || m.threadId <= cutoff);\n }\n\n /** Handle rollback-on-click from a user message bubble. */\n private _appendSimilarsToPanel(spec: UISpec, ctx: import('../chat/types.js').ChatUISpecRenderContext): void {\n if (!this._drawer) return;\n const panelEl = this._drawer.getPanelContentElement();\n if (!panelEl) return;\n ctx.panelProductListHeading = this._i18n.similarProductsLabel ?? 'Similar Products';\n const grid = this._renderUISpec(spec, ctx);\n grid.classList.add('gengage-chat-product-details-similars');\n panelEl.appendChild(grid);\n this._mergePanelSourceWithSimilars(spec);\n }\n\n /** After similars grid is appended, extend panel source so back/history/snapshot rebuild includes it. */\n private _mergePanelSourceWithSimilars(similarsSpec: UISpec): void {\n const prev = this._currentPanelSource;\n if (prev?.kind === 'spec' && this._panel?.currentType === 'ProductDetailsPanel') {\n this._currentPanelSource = {\n kind: 'productDetailsWithSimilars',\n pdpSpec: prev.spec,\n similarsSpec,\n };\n }\n }\n\n /** Re-render PDP plus similar-products block (matches `_appendSimilarsToPanel` structure). */\n private _renderProductDetailsWithSimilars(\n pdpSpec: UISpec,\n similarsSpec: UISpec,\n ctx: ChatUISpecRenderContext,\n ): HTMLElement {\n this._applyPanelListHeadingToContext(ctx, {\n kind: 'productDetailsWithSimilars',\n pdpSpec,\n similarsSpec,\n });\n const panelEl = this._renderUISpec(pdpSpec, ctx);\n const grid = this._renderUISpec(similarsSpec, ctx);\n grid.classList.add('gengage-chat-product-details-similars');\n panelEl.appendChild(grid);\n return panelEl;\n }\n\n /** Sets ctx.panelProductListHeading for ProductGrid / PDP+similars rebuilds. */\n private _applyPanelListHeadingToContext(ctx: ChatUISpecRenderContext, source: PanelSource): void {\n ctx.panelProductListHeading = undefined;\n if (!this._panel) return;\n if (source.kind === 'spec') {\n const root = source.spec.elements[source.spec.root];\n if (root?.type === 'ProductGrid') {\n const n = root.children?.length ?? 0;\n if (n > 0) {\n ctx.panelProductListHeading = this._panel.titleForComponent(\n 'ProductGrid',\n (root.props?.['panelTitle'] as string | undefined) ?? undefined,\n );\n }\n }\n } else if (source.kind === 'productDetailsWithSimilars') {\n const simRoot = source.similarsSpec.elements[source.similarsSpec.root];\n if (simRoot?.type === 'ProductGrid' && (simRoot.children?.length ?? 0) > 0) {\n ctx.panelProductListHeading = this._i18n.similarProductsLabel ?? 'Similar Products';\n }\n }\n }\n\n private _renderPanelFromSource(source: PanelSource, ctx: ChatUISpecRenderContext): HTMLElement {\n this._applyPanelListHeadingToContext(ctx, source);\n if (source.kind === 'favorites') {\n return this._buildFavoritesPageEl();\n }\n if (source.kind === 'productDetailsWithSimilars') {\n return this._renderProductDetailsWithSimilars(source.pdpSpec, source.similarsSpec, ctx);\n }\n return this._renderUISpec(source.spec, ctx);\n }\n\n private _handleRollback(messageId: string): void {\n const msg = this._messages.find((m) => m.id === messageId);\n if (!msg?.threadId) return;\n this._rollbackToThread(msg.threadId);\n }\n\n private _ensurePdpPrimeSuggestedUiIfNeeded(): void {\n const sku = this.config.pageContext?.sku;\n if (!sku || !this._drawer) return;\n if (this._hasUnavailableProductContext()) return;\n\n const contextKey: OpeningContextKey = 'product';\n const configured = this._resolveContextualOpeningActions(contextKey);\n if (configured.length > 0) {\n this._drawer.setInputAreaChips(\n configured.map((chip) => ({\n label: chip.title,\n onAction: () => this._sendAction(this._resolveContextualOpeningAction(chip, contextKey)),\n ...(chip.icon ? { icon: chip.icon } : {}),\n })),\n );\n return;\n }\n\n this._drawer.setInputAreaChips([\n {\n label: this._i18n.groundingReviewCta,\n icon: 'review',\n onAction: () =>\n this._sendAction({\n title: this._i18n.customerReviewsTitle,\n type: 'reviewSummary',\n payload: { sku },\n }),\n },\n {\n label: this._i18n.findSimilarLabel,\n icon: 'similar',\n onAction: () =>\n this._sendAction({\n title: this._i18n.findSimilarLabel,\n type: 'findSimilar',\n payload: { sku },\n }),\n },\n ]);\n }\n\n /** Rewind the conversation to the given thread. */\n private _rollbackToThread(threadId: string): void {\n // Validate thread ID exists in known threads\n if (this._panel && this._panel.threads.length > 0 && !this._panel.threads.includes(threadId)) {\n // Check if any message has this threadId as fallback\n if (!this._messages.some((m) => m.threadId === threadId)) {\n return; // Invalid thread ID — silently ignore\n }\n }\n this._currentThreadId = threadId;\n this._extendedModeManager?.setHiddenByUser(false);\n\n // Presentation collapse (single-thread focus) only at the conversation tip.\n // When navigating back to an older panel thread, show the full transcript up to\n // this thread — same effect as \"show earlier messages\" without an extra tap.\n // Forward navigation to the tip restores collapse + scroll-to-reveal UX.\n const atConversationTip = this._lastThreadId != null && threadId === this._lastThreadId;\n if (atConversationTip) {\n this._presentation.setFocusedThreadId(threadId);\n this._drawer?.setPresentationFocus(threadId);\n } else {\n this._presentation.releaseFocusedThread();\n this._drawer?.setPresentationFocus(null);\n }\n this._drawer?.setFormerMessagesButtonVisible(false);\n\n // Toggle visibility of messages after the cutoff\n for (const msg of this._messages) {\n const bubble = this._shadow?.querySelector(`[data-message-id=\"${CSS.escape(msg.id)}\"]`);\n if (!bubble) continue;\n if (msg.threadId && msg.threadId > threadId) {\n bubble.classList.add('gengage-chat-bubble--hidden');\n } else {\n bubble.classList.remove('gengage-chat-bubble--hidden');\n }\n }\n\n // Hide inline UISpec elements from future threads\n this._shadow?.querySelectorAll('[data-thread-id]').forEach((el) => {\n if (el instanceof HTMLElement && el.dataset['threadId'] && el.dataset['threadId'] > threadId) {\n el.classList.add('gengage-chat-bubble--hidden');\n } else if (el instanceof HTMLElement) {\n el.classList.remove('gengage-chat-bubble--hidden');\n }\n });\n\n // Restore panel snapshot from the target thread's bot message\n const targetBot = this._messages.find((m) => m.role === 'assistant' && m.threadId === threadId);\n const restored = targetBot ? this._panel?.restoreForMessage(targetBot.id) : false;\n if (!restored) {\n this._drawer?.clearPanel();\n this._currentPanelSource = null;\n }\n if (restored && targetBot) {\n // Update panel source so drilldown history captures the correct context.\n // We can't reconstruct the exact spec, so clear it to prevent stale history pushes.\n this._currentPanelSource = null;\n }\n // Always update topbar navigation state for the new thread position\n const panelType = this._panel!.currentType ?? '';\n this._panel?.updateTopBar(panelType);\n\n // Clear suggestion pills (they belong to the latest thread)\n this._drawer?.setPills([]);\n\n requestAnimationFrame(() => {\n this._drawer?.scrollThreadIntoView(threadId, 'auto');\n });\n\n // Load context from IndexedDB for the target thread so the next request\n // sends the correct historical context, then prune future entries.\n if (this._session?.db && this.config.session?.sessionId) {\n const sid = this.config.session.sessionId;\n void (async () => {\n try {\n const ctx = await this._session?.db?.loadContext(sid, threadId);\n if (ctx) this._lastBackendContext = ctx.context;\n await this._session?.db?.deleteContextsAfterThread(sid, threadId);\n } catch {\n /* non-fatal */\n }\n })();\n }\n }\n\n // ---------------------------------------------------------------------------\n // IndexedDB persistence (delegates to SessionPersistence)\n // ---------------------------------------------------------------------------\n\n private async _persistToIndexedDB(): Promise<void> {\n if (!this._session || !this.config.session?.sessionId) return;\n await this._session.persist({\n userId: this.config.session.userId ?? '',\n appId: this.config.accountId,\n sessionId: this.config.session.sessionId,\n messages: this._messages,\n currentThreadId: this._currentThreadId,\n lastThreadId: this._lastThreadId,\n chatCreatedAt: this._chatCreatedAt,\n panelSnapshots: this._panel?.snapshots ?? new Map(),\n panelThreads: this._panel?.threads ?? [],\n thumbnailEntries: this._thumbnailEntries,\n lastBackendContext: this._lastBackendContext,\n sku: this.config.pageContext?.sku,\n });\n }\n\n private _isSameOriginUrl(url: string): boolean {\n try {\n if (!url.trim()) return false;\n const parsed = new URL(url, window.location.href);\n return parsed.origin === window.location.origin;\n } catch {\n return false;\n }\n }\n\n private _markUnavailableProductContext(): void {\n this._productContextUnavailableSku = this.config.pageContext?.sku ?? null;\n }\n\n private _clearUnavailableProductContext(): void {\n this._productContextUnavailableSku = null;\n }\n\n private _hasUnavailableProductContext(): boolean {\n const currentSku = this.config.pageContext?.sku;\n return currentSku !== undefined && currentSku.length > 0 && this._productContextUnavailableSku === currentSku;\n }\n\n private _ensureAssistantMessageRendered(msg: ChatMessage): void {\n const bubble = this._shadow?.querySelector(`[data-message-id=\"${CSS.escape(msg.id)}\"]`);\n if (bubble || !this._drawer) return;\n if (msg.role === 'assistant' && msg.threadId && !this._threadsWithFirstBot.has(msg.threadId)) {\n this._threadsWithFirstBot.add(msg.threadId);\n this._drawer.addMessage(msg);\n this._drawer.markFirstBotMessage(msg.id);\n return;\n }\n this._drawer.addMessage(msg);\n }\n\n private async _saveSessionAndOpenURL(url: string): Promise<void> {\n if (!this._session) return;\n await this._session.saveAndOpenURL(url, () => this._persistToIndexedDB(), this._bridge);\n }\n\n private async _loadPayload(threadId: string, messageId: string): Promise<import('../common/types.js').UISpec | null> {\n if (!this._session) return null;\n return this._session.loadPayload(threadId, messageId);\n }\n\n /**\n * Attempt to restore chat session from IndexedDB.\n * Always restores when IDB has session data for the current sessionId.\n * Best-effort — failures are silently ignored.\n */\n private async _restoreFromIndexedDB(shouldRestore: boolean): Promise<void> {\n if (!this._session?.db) return;\n const sessionId = this.config.session?.sessionId;\n if (!sessionId) return;\n\n const userId = this.config.session?.userId ?? '';\n const appId = this.config.accountId;\n\n // Always restore favorites (user preference, not session state)\n await this._session.loadFavorites(userId, appId);\n this._drawer?.updateFavoritesBadge(this._session.favoritedSkus.size);\n\n // Only restore chat state on explicit handoff (e.g. SimRel product navigation)\n if (!shouldRestore) return;\n\n const session = await this._session.db?.loadSession(userId, appId, sessionId);\n if (!session || session.messages.length === 0) return;\n\n // Don't restore a session saved for a different SKU\n const currentSku = this.config.pageContext?.sku;\n if (currentSku && session.sku && session.sku !== currentSku) return;\n\n // Prevent duplicate auto-launch: session already has messages, so PDP launch already happened\n this._pdpLaunched = true;\n\n // Lock auto-scroll during restore to prevent visual jump\n this._drawer?.lockScrollForRestore();\n\n // Restore thread cursors and creation timestamp\n this._currentThreadId = session.currentThreadId;\n this._lastThreadId = session.lastThreadId;\n // Validate thread invariants — corrupted IDB data must not break navigation\n if (this._currentThreadId && this._lastThreadId && this._currentThreadId > this._lastThreadId) {\n this._currentThreadId = this._lastThreadId;\n }\n this._chatCreatedAt = session.createdAt;\n\n // Restore panel threads and thumbnail entries\n if (session.panelThreads) {\n this._panel!.threads = session.panelThreads;\n }\n if (session.thumbnailEntries) {\n this._thumbnailEntries = session.thumbnailEntries;\n this._drawer?.setThumbnails(this._thumbnailEntries);\n }\n\n // Restore panel snapshots from serialized HTML (sanitize for defense-in-depth)\n if (session.panelSnapshotHtml) {\n for (const [msgId, html] of Object.entries(session.panelSnapshotHtml)) {\n const container = document.createElement('div');\n container.innerHTML = sanitizeHtml(html);\n this._panel!.snapshots.set(msgId, container);\n }\n }\n\n // Track highest message ID to avoid collisions with new messages\n let maxMsgNum = 0;\n\n // Replay messages into DOM\n for (const msg of session.messages) {\n const chatMsg: ChatMessage = {\n id: msg.id,\n role: msg.role,\n timestamp: msg.timestamp,\n status: msg.status,\n };\n if (msg.threadId !== undefined) chatMsg.threadId = msg.threadId;\n if (msg.content !== undefined) chatMsg.content = msg.content;\n if (msg.silent) chatMsg.silent = true;\n\n this._messages.push(chatMsg);\n\n // Skip silent messages from DOM rendering\n if (chatMsg.silent) continue;\n\n if (chatMsg.role === 'assistant' && chatMsg.threadId && !this._threadsWithFirstBot.has(chatMsg.threadId)) {\n this._threadsWithFirstBot.add(chatMsg.threadId);\n this._drawer?.markFirstBotMessage(chatMsg.id);\n }\n this._drawer?.addMessage(chatMsg);\n\n // Track message ID counter\n const idNum = parseInt(msg.id.replace('msg-', ''), 10);\n if (!isNaN(idNum) && idNum > maxMsgNum) maxMsgNum = idNum;\n\n // Re-render inline UISpec elements for bot messages (load from payload store)\n if (chatMsg.role === 'assistant' && chatMsg.threadId) {\n const uiSpec = await this._loadPayload(chatMsg.threadId, chatMsg.id);\n if (uiSpec) {\n chatMsg.uiSpec = uiSpec;\n this._restoreInlineUISpec(chatMsg);\n // Clear after render to maintain lean pattern\n delete chatMsg.uiSpec;\n }\n }\n }\n\n // Advance message ID counter past restored messages\n if (maxMsgNum > this._currentMessageId) {\n this._currentMessageId = maxMsgNum;\n }\n\n // Restore backend context with fallback chain\n if (this._currentThreadId) {\n let ctx = await this._session.db?.loadContext(sessionId, this._currentThreadId);\n if (!ctx) {\n ctx = await this._session.db?.loadLatestContext(sessionId);\n }\n if (ctx) this._lastBackendContext = ctx.context;\n }\n\n // Restore panel for the current thread's latest bot message\n if (this._currentThreadId) {\n const panelBot = [...this._messages]\n .reverse()\n .find((m) => m.role === 'assistant' && m.threadId === this._currentThreadId && !m.silent);\n if (panelBot && this._panel!.snapshots.has(panelBot.id)) {\n this._panel?.restoreForMessage(panelBot.id);\n }\n }\n\n // Apply thread visibility — hide messages from future threads\n if (this._currentThreadId) {\n const cutoff = this._currentThreadId;\n for (const msg of this._messages) {\n if (msg.threadId && msg.threadId > cutoff) {\n const bubble = this._shadow?.querySelector(`[data-message-id=\"${CSS.escape(msg.id)}\"]`);\n bubble?.classList.add('gengage-chat-bubble--hidden');\n }\n }\n this._shadow?.querySelectorAll('[data-thread-id]').forEach((el) => {\n if (el instanceof HTMLElement && el.dataset['threadId'] && el.dataset['threadId'] > cutoff) {\n el.classList.add('gengage-chat-bubble--hidden');\n }\n });\n }\n\n // Update panel topbar if we have panel threads\n if (this._panel!.threads.length > 0 && this._currentThreadId) {\n const lastPanelThread = this._panel!.threads[this._panel!.threads.length - 1];\n if (lastPanelThread) {\n const lastPanelBot = [...this._messages]\n .reverse()\n .find((m) => m.role === 'assistant' && m.threadId === lastPanelThread);\n if (lastPanelBot?.threadId) {\n const uiSpec = await this._loadPayload(lastPanelBot.threadId, lastPanelBot.id);\n if (uiSpec) {\n const rootEl = uiSpec.elements[uiSpec.root];\n if (rootEl) {\n this._panel?.updateTopBar(rootEl.type);\n }\n }\n }\n }\n }\n\n this._presentation.releaseFocusedThread();\n this._drawer?.setPresentationFocus(null);\n\n // After lockout expires, scroll to last thread boundary instead of absolute bottom\n setTimeout(() => {\n this._drawer?.scrollToLastThread();\n }, 550);\n }\n\n /**\n * Toggle comparison mode or individual SKU selection, then refresh the DOM.\n * Extracted so both the render-context callback and DOM-created checkboxes\n * share the same state-mutation + refresh path.\n */\n /**\n * Panel back navigation: pop local drilldown history first (e.g. card→detail),\n * then fall back to thread-level history.\n */\n private _navigatePanelBack(): void {\n const prev = this._localPanelHistory.pop();\n if (prev) {\n const ctx = this._buildRenderContext();\n const el = this._renderPanelFromSource(prev.source, ctx);\n this._drawer?.setPanelContent(el);\n this._drawer?.setDividerPreviewEnabled(this._shouldUseDividerPreviewForSource(prev.source));\n this._currentPanelSource = prev.source;\n const canBack = this._localPanelHistory.length > 0 || (this._panel?.threads.length ?? 0) > 1;\n this._drawer?.updatePanelTopBar(canBack, false, prev.title);\n return;\n }\n // On mobile, when there is no local history left, back = hide the side panel\n // (content is preserved so it can be reopened via the header button)\n if (this._isMobileViewport) {\n this._drawer?.hideMobilePanel();\n return;\n }\n this._panel?.navigateBack();\n }\n\n private _shouldUseDividerPreviewForSpec(spec: UISpec): boolean {\n return spec.elements[spec.root]?.type === 'ProductGrid';\n }\n\n private _shouldUseDividerPreviewForSource(source: PanelSource | null): boolean {\n return source?.kind === 'spec' ? this._shouldUseDividerPreviewForSpec(source.spec) : false;\n }\n\n private _toggleComparisonSku(sku: string): void {\n if (sku === '') {\n this._comparisonSelectMode = !this._comparisonSelectMode;\n this._comparisonSelectionWarning = null;\n if (this._comparisonSelectMode) {\n recordChoicePrompterDismissedForThread(this._currentThreadId ?? '');\n this._choicePrompterEl?.remove();\n this._choicePrompterEl = null;\n this._shadow?.querySelectorAll('.gengage-chat-choice-prompter').forEach((el) => el.remove());\n }\n if (!this._comparisonSelectMode) {\n this._comparisonSelectedSkus = [];\n this._comparisonSelectionWarning = null;\n ga.trackCompareClear();\n }\n } else {\n const idx = this._comparisonSelectedSkus.indexOf(sku);\n if (idx >= 0) {\n this._comparisonSelectedSkus = this._comparisonSelectedSkus.filter((s) => s !== sku);\n this._comparisonSelectionWarning = null;\n } else {\n if (this._comparisonSelectedSkus.length >= GengageChat._MAX_COMPARISON_SELECTION) {\n this._comparisonSelectionWarning =\n this._i18n.compareMaxHint ?? `You can select up to ${GengageChat._MAX_COMPARISON_SELECTION} products`;\n if (this._comparisonRefreshRafId !== null) {\n cancelAnimationFrame(this._comparisonRefreshRafId);\n }\n this._comparisonRefreshRafId = requestAnimationFrame(() => {\n this._comparisonRefreshRafId = null;\n this._refreshComparisonUI();\n });\n return;\n }\n this._comparisonSelectedSkus = [...this._comparisonSelectedSkus, sku];\n this._comparisonSelectionWarning = null;\n ga.trackComparePreselection(sku);\n }\n }\n // Debounce: cancel any pending refresh and schedule a new one to batch rapid toggles\n if (this._comparisonRefreshRafId !== null) {\n cancelAnimationFrame(this._comparisonRefreshRafId);\n }\n this._comparisonRefreshRafId = requestAnimationFrame(() => {\n this._comparisonRefreshRafId = null;\n this._refreshComparisonUI();\n });\n }\n\n /**\n * Refresh the panel DOM to reflect the current comparison state without\n * full re-render. Updates: toggle button active class, checkbox overlays\n * on product cards, and the floating comparison button.\n */\n private _refreshComparisonUI(): void {\n const panelEl = this._shadow?.querySelector('.gengage-chat-panel');\n if (!panelEl) {\n this._drawer?.setComparisonDockContent(null);\n return;\n }\n\n const gridWrapper = panelEl.querySelector('.gengage-chat-product-grid-wrapper');\n const grid = gridWrapper?.querySelector('.gengage-chat-product-grid');\n if (!gridWrapper || !grid) {\n this._drawer?.setComparisonDockContent(null);\n return;\n }\n\n // 1. Toggle comparison button active state\n const toggleBtn = gridWrapper.querySelector('.gengage-chat-comparison-toggle-btn');\n if (toggleBtn) {\n toggleBtn.classList.toggle('gengage-chat-comparison-toggle-btn--active', this._comparisonSelectMode);\n }\n\n // 2. Add or remove checkbox overlays on product cards\n if (this._comparisonSelectMode) {\n const cards = grid.querySelectorAll<HTMLElement>('.gengage-chat-product-card[data-sku]');\n for (const card of cards) {\n if (card.parentElement?.classList.contains('gengage-chat-comparison-select-wrapper')) {\n // Already wrapped — sync selected state\n const wrapper = card.parentElement;\n const selected = this._comparisonSelectedSkus.includes(card.dataset['sku']!);\n wrapper.classList.toggle('gengage-chat-comparison-select-wrapper--selected', selected);\n const toggle = wrapper.querySelector<HTMLButtonElement>('.gengage-chat-comparison-checkbox');\n if (toggle) {\n toggle.dataset['selected'] = selected ? 'true' : 'false';\n toggle.setAttribute('aria-pressed', selected ? 'true' : 'false');\n const icon = toggle.querySelector('.gengage-chat-comparison-checkbox-icon');\n const label = toggle.querySelector('.gengage-chat-comparison-checkbox-label');\n if (icon) {\n icon.innerHTML = selected\n ? '<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 6L9 17l-5-5\"/></svg>'\n : '<span class=\"gengage-chat-comparison-checkbox-dot\"></span>';\n }\n if (label) {\n label.textContent = selected\n ? (this._i18n.comparisonSelectedLabel ?? 'Selected')\n : (this._i18n.comparisonSelectLabel ?? 'Select to compare');\n }\n }\n continue;\n }\n const sku = card.dataset['sku']!;\n const wrapper = document.createElement('div');\n wrapper.className = 'gengage-chat-comparison-select-wrapper';\n const selected = this._comparisonSelectedSkus.includes(sku);\n if (selected) wrapper.classList.add('gengage-chat-comparison-select-wrapper--selected');\n const toggle = document.createElement('button');\n toggle.type = 'button';\n toggle.className = 'gengage-chat-comparison-checkbox';\n toggle.dataset['selected'] = selected ? 'true' : 'false';\n toggle.setAttribute('aria-pressed', selected ? 'true' : 'false');\n const icon = document.createElement('span');\n icon.className = 'gengage-chat-comparison-checkbox-icon';\n icon.innerHTML = selected\n ? '<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 6L9 17l-5-5\"/></svg>'\n : '<span class=\"gengage-chat-comparison-checkbox-dot\"></span>';\n const label = document.createElement('span');\n label.className = 'gengage-chat-comparison-checkbox-label';\n label.textContent = selected\n ? (this._i18n.comparisonSelectedLabel ?? 'Selected')\n : (this._i18n.comparisonSelectLabel ?? 'Select to compare');\n toggle.appendChild(icon);\n toggle.appendChild(label);\n toggle.addEventListener('click', (e) => {\n e.stopPropagation();\n this._toggleComparisonSku(sku);\n });\n card.parentNode!.insertBefore(wrapper, card);\n wrapper.appendChild(toggle);\n wrapper.appendChild(card);\n // Allow clicking anywhere on the card (not just the tiny checkbox) to toggle selection\n wrapper.classList.add('gds-clickable');\n wrapper.addEventListener('click', (e) => {\n if ((e.target as HTMLElement).closest('.gengage-chat-comparison-checkbox')) return;\n e.stopPropagation();\n this._toggleComparisonSku(sku);\n });\n }\n } else {\n // Remove all checkbox wrappers\n const wrappers = grid.querySelectorAll('.gengage-chat-comparison-select-wrapper');\n for (const wrapper of wrappers) {\n const card = wrapper.querySelector('.gengage-chat-product-card');\n if (card && wrapper.parentNode) {\n wrapper.parentNode.insertBefore(card, wrapper);\n wrapper.remove();\n }\n }\n }\n\n // 3. Update the slim bottom-docked comparison bar\n const existingFloating = gridWrapper.querySelector('.gengage-chat-comparison-floating-btn');\n existingFloating?.remove();\n if (this._comparisonSelectMode) {\n const dock = renderFloatingComparisonButton(this._comparisonSelectedSkus, this._buildRenderContext());\n if (this._isMobileViewport) {\n this._drawer?.setComparisonDockContent(dock);\n } else {\n this._drawer?.setComparisonDockContent(null);\n gridWrapper.appendChild(dock);\n }\n } else {\n this._drawer?.setComparisonDockContent(null);\n }\n }\n\n private _parseAddToCartActionPayload(payload: unknown): { sku: string; cartCode: string; quantity: number } | null {\n if (typeof payload !== 'object' || payload === null) return null;\n const rec = payload as Record<string, unknown>;\n const sku = this._coerceAddToCartString(rec['sku']);\n const cartCode = this._coerceAddToCartString(rec['cartCode'] ?? rec['cart_code']);\n let quantity = 1;\n if (typeof rec['quantity'] === 'number' && Number.isFinite(rec['quantity']) && rec['quantity'] > 0) {\n quantity = Math.max(1, Math.floor(rec['quantity']));\n }\n if (!sku || !cartCode) return null;\n return { sku, cartCode, quantity };\n }\n\n private _coerceAddToCartString(value: unknown): string {\n if (typeof value === 'string' && value.length > 0) return value;\n if (typeof value === 'number' && Number.isFinite(value)) return String(value);\n return '';\n }\n\n private _runChatAddToCartFlow(params: { sku: string; cartCode: string; quantity: number }): void {\n if (this.config.onAddToCart !== undefined) {\n try {\n const result: unknown = this.config.onAddToCart(params);\n if (result instanceof Promise) result.catch((err: unknown) => console.error('[gengage] onAddToCart', err));\n } catch (err) {\n console.error('[gengage] onAddToCart', err);\n }\n }\n ga.trackCartAdd(params.sku, params.quantity);\n const detail = {\n ...params,\n sessionId: this.config.session?.sessionId ?? null,\n };\n dispatch('gengage:chat:add-to-cart', detail);\n this._bridge?.send('addToCart', params);\n void this._runEventCallbacks('gengage-cart-add', detail as unknown as Record<string, unknown>);\n this.track(\n basketAddEvent(this.analyticsContext(), {\n attribution_source: 'chat',\n attribution_action_id: crypto.randomUUID(),\n cart_value: 0, // Host page should enrich via event listener\n currency: this.config.pricing?.currencyCode ?? 'TRY',\n line_items: params.quantity,\n sku: params.sku,\n }),\n );\n // Sends a normalized payload to the backend regardless of the original action's\n // title or extra fields — the backend expects exactly { sku, cart_code, quantity }.\n this._sendAction(\n {\n title: this._i18n.addToCartButton ?? 'Add to Cart',\n type: 'addToCart',\n payload: { sku: params.sku, cart_code: params.cartCode, quantity: params.quantity },\n },\n { preservePanel: true },\n );\n const toastMsg = this._i18n.addedToCartToast ?? 'Added to cart';\n this._drawer?.showCartToast(toastMsg);\n this._drawer?.flashCartBadge();\n }\n\n /**\n * Build a ChatUISpecRenderContext with all callbacks wired up.\n * Used both during streaming and during session restore.\n */\n private _buildRenderContext(): ChatUISpecRenderContext {\n const ctx: ChatUISpecRenderContext = {\n onAction: (action) => {\n ga.trackSuggestedQuestion(action.title, action.type);\n if (action.type === 'addToCart') {\n const addParams = this._parseAddToCartActionPayload(action.payload);\n if (addParams) {\n this._runChatAddToCartFlow(addParams);\n return; // handled — skip the generic _sendAction fallthrough below\n }\n // Parse failed (missing sku/cartCode): fall through to _sendAction as a best-effort\n }\n if (action.type === 'launchSingleProduct') {\n this._drawer?.setDividerPreviewEnabled(false);\n const sku =\n typeof action.payload === 'object' && action.payload !== null && 'sku' in action.payload\n ? String((action.payload as Record<string, unknown>).sku)\n : '';\n if (sku) ga.trackProductDetail(sku, action.title);\n }\n if (action.type === 'findSimilar') {\n const sku =\n typeof action.payload === 'object' && action.payload !== null && 'sku' in action.payload\n ? String((action.payload as Record<string, unknown>).sku)\n : '';\n ga.trackFindSimilars(sku);\n }\n if (action.type === 'getComparisonTable') {\n ga.trackCompareSelected(this._comparisonSelectedSkus);\n }\n // addToCart/like actions should preserve the current panel (product cards stay visible)\n const preservePanel = action.type === 'addToCart' || action.type === 'like';\n this._sendAction(action, preservePanel ? { preservePanel: true } : undefined);\n },\n onProductClick: (params) => {\n ga.trackProductDetail(params.sku);\n // Demo mode: load product in-chat via launchSingleProduct (no navigation)\n // Production mode: navigate to product page (chat auto-opens on new page)\n const shouldNavigate = this.config.isDemoWebsite !== true && this._isSameOriginUrl(params.url);\n if (!shouldNavigate) {\n const displayTitle = params.name?.trim() ? params.name.trim() : params.sku;\n this._sendAction({\n title: displayTitle,\n type: 'launchSingleProduct',\n payload: { sku: params.sku },\n });\n } else {\n dispatch('gengage:similar:product-click', {\n sku: params.sku,\n url: params.url,\n sessionId: this.config.session?.sessionId ?? null,\n });\n this._saveSessionAndOpenURL(params.url);\n }\n },\n onAddToCart: (params) => {\n this._runChatAddToCartFlow(params);\n },\n onProductSelect: (product) => {\n // Save current panel source to local history so back button can re-render it\n if (this._currentPanelSource) {\n const currentTitle = this._drawer?.getPanelTopBarTitle() ?? '';\n this._localPanelHistory.push({ source: this._currentPanelSource, title: currentTitle });\n if (this._localPanelHistory.length > GengageChat._MAX_PANEL_HISTORY) this._localPanelHistory.shift();\n }\n const summaryCtx = this._buildRenderContext();\n const summarySpec: import('../common/types.js').UISpec = {\n root: 'root',\n elements: {\n root: { type: 'ProductSummaryCard', props: { product } },\n },\n };\n const messagesContainer = this._shadow?.querySelector('.gengage-chat-messages');\n if (messagesContainer) {\n const summaryEl = this._renderUISpec(summarySpec, summaryCtx);\n if (this._currentThreadId) {\n summaryEl.dataset['threadId'] = this._currentThreadId;\n }\n messagesContainer.appendChild(summaryEl);\n summaryEl.scrollIntoView({ behavior: 'auto', block: 'end' });\n this._drawer?.refreshPresentationCollapsed();\n }\n if (this.config.productDetailsExtended !== true) {\n this._clearAssistantPanelLikeStreamClearPanel();\n return;\n }\n const detailSpec: import('../common/types.js').UISpec = {\n root: 'root',\n elements: {\n root: {\n type: 'ProductDetailsPanel',\n props: { product },\n },\n },\n };\n this._drawer?.setPanelContent(this._renderUISpec(detailSpec, ctx));\n this._drawer?.setDividerPreviewEnabled(false);\n this._currentPanelSource = { kind: 'spec', spec: detailSpec };\n this._drawer?.updatePanelTopBar(true, false, this._i18n.panelTitleProductDetails);\n },\n i18n: this._i18n,\n pricing: this.config.pricing,\n productPriceUi: this.config.productPriceUi,\n productSort: this._productSort,\n onSortChange: (sort) => {\n this._productSort = sort;\n },\n comparisonSelectMode: this._comparisonSelectMode,\n comparisonSelectedSkus: this._comparisonSelectedSkus,\n comparisonMaxSelection: GengageChat._MAX_COMPARISON_SELECTION,\n comparisonSelectionWarning: this._comparisonSelectionWarning,\n onToggleComparisonSku: (sku) => {\n this._toggleComparisonSku(sku);\n },\n favoritedSkus: this._session?.favoritedSkus ?? new Set(),\n onFavoriteToggle: (sku, product) => {\n void this._toggleProductFavorite(sku, product);\n },\n isMobile: this._isMobileViewport,\n };\n return ctx;\n }\n\n private async _toggleFavorite(sku: string, product: Record<string, unknown>): Promise<void> {\n if (!this._session) return;\n const userId = this.config.session?.userId ?? '';\n const appId = this.config.accountId;\n await this._session.toggleFavorite(userId, appId, sku, product);\n this._drawer?.updateFavoritesBadge(this._session.favoritedSkus.size);\n }\n\n /** Revert optimistic heart UI after a failed host favorite callback. */\n private _revertFavoriteHeartUi(sku: string): void {\n const btns = this._shadow?.querySelectorAll(`[data-gengage-favorite-sku=\"${CSS.escape(sku)}\"]`);\n if (!btns?.length) return;\n for (const btn of btns) {\n if (!(btn instanceof HTMLButtonElement)) continue;\n btn.classList.toggle('gengage-chat-favorite-btn--active');\n const svg = btn.querySelector('svg');\n if (svg) {\n svg.setAttribute('fill', btn.classList.contains('gengage-chat-favorite-btn--active') ? 'currentColor' : 'none');\n }\n }\n }\n\n /**\n * Product-card favorite: dispatches window + bridge for the host, then runs `addCallback('gengage-product-favorite')`\n * handlers when registered. If none are registered, falls back to IDB favorites + optional `like` backend action.\n */\n private async _toggleProductFavorite(sku: string, product: Record<string, unknown>): Promise<void> {\n const wasLiked = this._session?.favoritedSkus.has(sku) ?? false;\n const favorited = !wasLiked;\n const detail = {\n sku,\n product,\n favorited,\n sessionId: this.config.session?.sessionId ?? null,\n };\n\n dispatch('gengage:chat:product-favorite', detail);\n this._bridge?.send('productFavorite', detail);\n\n const callbacks = this._eventCallbacks.get('gengage-product-favorite');\n if (callbacks && callbacks.size > 0) {\n for (const cb of callbacks) {\n try {\n const result = cb(detail);\n const success = result instanceof Promise ? await result : result;\n if (success === false) {\n this._revertFavoriteHeartUi(sku);\n this._handleCallbackFailure('gengage-product-favorite', detail);\n return;\n }\n } catch {\n this._revertFavoriteHeartUi(sku);\n this._handleCallbackFailure('gengage-product-favorite', detail);\n return;\n }\n }\n // All callbacks succeeded — sync in-memory state so the next click reads the correct direction.\n // IDB is intentionally skipped: the host owns persistence in callback mode.\n if (this._session) {\n if (favorited) {\n this._session.favoritedSkus.add(sku);\n } else {\n this._session.favoritedSkus.delete(sku);\n }\n this._drawer?.updateFavoritesBadge(this._session.favoritedSkus.size);\n }\n return;\n }\n\n await this._toggleFavorite(sku, product);\n if (favorited) {\n ga.trackLikeProduct(sku);\n const productName = (product['name'] as string | undefined) ?? sku;\n this._sendAction(\n {\n title: productName,\n type: 'like',\n payload: { sku },\n },\n { preservePanel: true },\n );\n }\n }\n\n private _openFavoritesPanel(): void {\n if (!this._drawer) return;\n\n // Save current panel source to local history so back button can re-render it\n if (this._currentPanelSource) {\n const currentTitle = this._drawer.getPanelTopBarTitle() ?? '';\n this._localPanelHistory.push({ source: this._currentPanelSource, title: currentTitle });\n if (this._localPanelHistory.length > GengageChat._MAX_PANEL_HISTORY) this._localPanelHistory.shift();\n }\n\n this._drawer.setPanelContent(this._buildFavoritesPageEl());\n this._drawer.setDividerPreviewEnabled(false);\n this._currentPanelSource = { kind: 'favorites' };\n this._drawer.updatePanelTopBar(true, false, this._i18n.favoritesPageTitle);\n }\n\n private _buildFavoritesPageEl(): HTMLElement {\n const favorites = this._session?.getFavoriteProducts() ?? [];\n\n if (favorites.length === 0) {\n const empty = document.createElement('div');\n empty.className = 'gengage-chat-favorites-empty';\n\n const icon = document.createElement('div');\n icon.className = 'gengage-chat-favorites-empty-icon';\n icon.innerHTML = `<svg width=\"40\" height=\"40\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z\"/></svg>`;\n empty.appendChild(icon);\n\n const text = document.createElement('p');\n text.textContent = this._i18n.emptyFavoritesMessage;\n empty.appendChild(text);\n\n return empty;\n }\n\n // Convert favorites to product records and render as ProductGrid UISpec\n const elements: import('../common/types.js').UISpec['elements'] = {};\n const childKeys: string[] = [];\n\n for (const [i, fav] of favorites.entries()) {\n const key = `card_${i}`;\n childKeys.push(key);\n elements[key] = {\n type: 'ProductCard',\n props: {\n product: {\n sku: fav.sku,\n name: fav.name,\n imageUrl: fav.imageUrl,\n price: fav.price,\n } as Record<string, unknown>,\n },\n };\n }\n\n elements['grid'] = { type: 'ProductGrid', children: childKeys };\n\n const spec: import('../common/types.js').UISpec = { root: 'grid', elements };\n return this._renderUISpec(spec, this._buildRenderContext());\n }\n\n /**\n * Run registered callbacks for a GA4 event.\n * If any callback returns false or throws, handle the failure (e.g. show error for cart-add).\n */\n private async _runEventCallbacks(eventName: string, detail: Record<string, unknown>): Promise<void> {\n const callbacks = this._eventCallbacks.get(eventName);\n if (!callbacks || callbacks.size === 0) return;\n\n for (const cb of callbacks) {\n try {\n const result = cb(detail);\n const success = result instanceof Promise ? await result : result;\n if (success === false) {\n this._handleCallbackFailure(eventName, detail);\n return;\n }\n } catch {\n this._handleCallbackFailure(eventName, detail);\n return;\n }\n }\n }\n\n /**\n * Handle a callback failure — for add-to-cart, show an error message in chat.\n */\n private _handleCallbackFailure(eventName: string, _detail: Record<string, unknown>): void {\n if (eventName === 'gengage-cart-add') {\n const errorText = this._i18n.cartAddErrorMessage;\n const botMsg = this._createMessage('assistant', errorText);\n if (this._currentThreadId) botMsg.threadId = this._currentThreadId;\n this._messages.push(botMsg);\n this._drawer?.addMessage(botMsg);\n // Note: _sendAction is NOT called here — onAddToCart already sent the backend\n // request unconditionally. Sending again would duplicate the cart-add action.\n }\n if (eventName === 'gengage-product-favorite') {\n const errorText = this._i18n.favoriteToggleErrorMessage;\n const botMsg = this._createMessage('assistant', errorText);\n if (this._currentThreadId) botMsg.threadId = this._currentThreadId;\n this._messages.push(botMsg);\n this._drawer?.addMessage(botMsg);\n }\n }\n\n /**\n * Re-render inline UISpec elements for a restored bot message.\n * Inserts them into the messages container after the message bubble.\n */\n private _restoreInlineUISpec(chatMsg: ChatMessage): void {\n if (!chatMsg.uiSpec || !this._drawer) return;\n const spec = chatMsg.uiSpec;\n const rootElement = spec.elements[spec.root];\n if (!rootElement) return;\n\n const componentType = rootElement.type;\n\n // ActionButtons are rendered as pills/chips, not inline\n if (componentType === 'ActionButtons') return;\n\n // Panel-only components should not be rendered inline.\n // Note: panelHint is a StreamEvent property not stored in UISpec elements,\n // so we identify panel-only status by component type.\n // ProductDetailsPanel is panel-only but gets a compact ProductSummaryCard below.\n // ComparisonTable is always panel-only.\n // ProductGrid with similarsAppend is panel-appended unless PDP is chat-only layout.\n if (componentType === 'ComparisonTable') return;\n if (\n componentType === 'ProductGrid' &&\n rootElement.props?.['similarsAppend'] === true &&\n this.config.productDetailsExtended === true\n )\n return;\n\n const renderContext = this._buildRenderContext();\n const messagesContainer = this._shadow?.querySelector('.gengage-chat-messages');\n if (!messagesContainer) return;\n\n // ProductDetailsPanel: synthesize a compact ProductSummaryCard for inline rendering\n if (componentType === 'ProductDetailsPanel') {\n const product = rootElement.props?.['product'] as Record<string, unknown> | undefined;\n if (!product) return;\n const inlineSpec: UISpec = {\n root: 'root',\n elements: { root: { type: 'ProductSummaryCard', props: { product } } },\n };\n const inline = this._renderUISpec(inlineSpec, renderContext);\n if (chatMsg.threadId) inline.dataset['threadId'] = chatMsg.threadId;\n messagesContainer.appendChild(inline);\n this._drawer?.refreshPresentationCollapsed();\n return;\n }\n\n const inline = this._renderUISpec(spec, renderContext);\n if (chatMsg.threadId) {\n inline.dataset['threadId'] = chatMsg.threadId;\n }\n messagesContainer.appendChild(inline);\n this._drawer?.refreshPresentationCollapsed();\n }\n\n private _createMessage(role: 'user' | 'assistant', content: string): ChatMessage {\n this._currentMessageId++;\n return {\n id: `msg-${this._currentMessageId}`,\n role,\n content,\n timestamp: Date.now(),\n status: 'done',\n };\n }\n\n private _resolveI18n(config: ChatWidgetConfig): ChatI18n {\n const base = resolveChatLocale(config.locale);\n return { ...base, ...config.i18n };\n }\n\n private _resolveUISpecRegistry(): ChatUISpecRegistry {\n const baseRegistry = createDefaultChatUISpecRegistry();\n return mergeUISpecRegistry(baseRegistry, this.config.renderer?.registry);\n }\n\n private _renderUISpec(spec: UISpec, context: ChatUISpecRenderContext): HTMLElement {\n const registry = this._resolveUISpecRegistry();\n const unknownRenderer = this.config.renderer?.unknownRenderer ?? defaultChatUnknownUISpecRenderer;\n const defaultRender = (inputSpec: UISpec, inputContext: ChatUISpecRenderContext) =>\n renderUISpec(inputSpec, inputContext, registry, unknownRenderer);\n\n const override = this.config.renderer?.renderUISpec;\n if (!override) return defaultRender(spec, context);\n\n const helpers: UISpecRenderHelpers<ChatUISpecRenderContext> = {\n registry,\n unknownRenderer,\n defaultRender,\n };\n return override(spec, context, helpers);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Convenience factory\n// ---------------------------------------------------------------------------\n\nexport function createChatWidget(): GengageChat {\n return new GengageChat();\n}\n\nexport type {\n ChatWidgetConfig,\n ChatMessage,\n ChatSession,\n ChatUIComponents,\n ChatI18n,\n ChatRendererConfig,\n ChatUISpecRenderContext,\n ChatUISpecRegistry,\n ProductSortState,\n SerializableChatMessage,\n} from './types.js';\nexport {\n renderUISpec,\n createDefaultChatUISpecRegistry,\n defaultChatUnknownUISpecRenderer,\n} from './components/renderUISpec.js';\nexport type { UISpecRenderContext } from './components/renderUISpec.js';\nexport { chatCatalog } from './catalog.js';\nexport type { ChatCatalog, ChatComponentName } from './catalog.js';\nexport {\n getChatScrollElement,\n invalidateChatScrollCache,\n CHAT_SCROLL_ELEMENT_ID,\n} from './utils/get-chat-scroll-element.js';\nexport { ChatPresentationState } from './chat-presentation-state.js';\nexport type { GroupReadState, PresentationGroupMeta, ScrollRequest } from './chat-presentation-state.js';\n"],"x_google_ignoreList":[62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78],"mappings":"0EAEA,IAAa,GAAmE,CAAE,UAAS,mBAAoB,CAC7G,GAAI,CAAC,EAAQ,UAAY,EAAQ,SAAS,SAAW,EACnD,OAAO,KAGT,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,IAAK,IAAM,KAAW,EAAQ,SAAU,CACtC,IAAM,EAAW,EAAc,EAAQ,CACnC,GAAU,EAAQ,YAAY,EAAS,CAE7C,OAAO,GAGT,SAAgB,EAAmC,EAAiE,CAClH,IAAM,EAAY,SAAS,cAAc,MAAM,CAI/C,GAHA,EAAU,UAAY,EAAQ,mBAG1B,CADW,EAAQ,KAAK,SAAS,EAAQ,KAAK,MACrC,OAAO,EAEpB,IAAM,EACJ,EAAQ,iBAAoB,EAExB,EAAiB,GAA0C,CAC/D,IAAM,EAAU,EAAQ,KAAK,SAAS,GACtC,GAAI,CAAC,EAAS,OAAO,KAErB,IAAM,EAAW,EAAQ,SAAS,EAAQ,MAW1C,OAVI,EACK,EAAS,CACd,YACA,UACA,KAAM,EAAQ,KACd,QAAS,EAAQ,QACjB,gBACD,CAAC,CAGG,EAAgB,CACrB,YACA,UACA,KAAM,EAAQ,KACd,QAAS,EAAQ,QACjB,gBACD,CAAC,EAGE,EAAW,EAAc,EAAQ,KAAK,KAAK,CAEjD,OADI,GAAU,EAAU,YAAY,EAAS,CACtC,ECjDT,SAAgB,EACd,EACA,EAC6B,CAC7B,GAAI,CAAC,EAAW,OAAO,EAEvB,IAAM,EAAsC,CAAE,GAAG,EAAM,CACvD,IAAK,GAAM,CAAC,EAAe,KAAa,OAAO,QAAQ,EAAU,CAC1D,IACL,EAAO,GAAiB,GAE1B,OAAO,ECMT,SAAgB,EACd,EACA,EAKgB,CAChB,IAAM,EAAwB,CAC5B,WAAY,eACZ,WAAY,EAAI,WAChB,WAAY,EAAI,WAChB,eAAgB,EAAI,eACpB,QAAS,CACP,SAAU,EAAQ,SAClB,WAAY,EAAQ,WACrB,CACF,CAMD,OALI,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAQ,SAAW,IAAA,KAAW,EAAM,OAAS,EAAQ,QACrD,EAAI,YAAc,IAAA,KAAW,EAAM,UAAY,EAAI,WACnD,EAAI,MAAQ,IAAA,KAAW,EAAM,IAAM,EAAI,KACpC,EAGT,SAAgB,EACd,EACA,EAMgB,CAChB,IAAM,EAAwC,CAC5C,WAAY,EAAQ,WACpB,YAAa,EAAQ,YACtB,CACG,EAAQ,aAAe,IAAA,KAAW,EAAa,WAAa,EAAQ,YAExE,IAAM,EAAwB,CAC5B,WAAY,eACZ,WAAY,EAAI,WAChB,WAAY,EAAI,WAChB,eAAgB,EAAI,eACpB,QAAS,EACV,CAMD,OALI,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAQ,SAAW,IAAA,KAAW,EAAM,OAAS,EAAQ,QACrD,EAAI,YAAc,IAAA,KAAW,EAAM,UAAY,EAAI,WACnD,EAAI,MAAQ,IAAA,KAAW,EAAM,IAAM,EAAI,KACpC,EAGT,SAAgB,EACd,EACA,EAMgB,CAChB,IAAM,EAAwB,CAC5B,WAAY,iBACZ,WAAY,EAAI,WAChB,WAAY,EAAI,WAChB,eAAgB,EAAI,eACpB,QAAS,CACP,WAAY,EAAQ,WACpB,YAAa,EAAQ,YACrB,eAAgB,EAAQ,eACzB,CACF,CAMD,OALI,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAQ,SAAW,IAAA,KAAW,EAAM,OAAS,EAAQ,QACrD,EAAI,YAAc,IAAA,KAAW,EAAM,UAAY,EAAI,WACnD,EAAI,MAAQ,IAAA,KAAW,EAAM,IAAM,EAAI,KACpC,EAGT,SAAgB,EACd,EACA,EAMgB,CAChB,IAAM,EAAwB,CAC5B,WAAY,cACZ,WAAY,EAAI,WAChB,WAAY,EAAI,WAChB,eAAgB,EAAI,eACpB,QAAS,CACP,WAAY,EAAQ,WACpB,WAAY,EAAQ,WACpB,YAAa,EAAQ,YACtB,CACF,CAMD,OALI,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAQ,SAAW,IAAA,KAAW,EAAM,OAAS,EAAQ,QACrD,EAAI,YAAc,IAAA,KAAW,EAAM,UAAY,EAAI,WACnD,EAAI,MAAQ,IAAA,KAAW,EAAM,IAAM,EAAI,KACpC,EAGT,SAAgB,EACd,EACA,EAMgB,CAChB,IAAM,EAAwB,CAC5B,WAAY,eACZ,WAAY,EAAI,WAChB,WAAY,EAAI,WAChB,eAAgB,EAAI,eACpB,QAAS,CACP,WAAY,EAAQ,WACpB,WAAY,EAAQ,WACpB,cAAe,EAAQ,cACxB,CACF,CAMD,OALI,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAQ,SAAW,IAAA,KAAW,EAAM,OAAS,EAAQ,QACrD,EAAI,YAAc,IAAA,KAAW,EAAM,UAAY,EAAI,WACnD,EAAI,MAAQ,IAAA,KAAW,EAAM,IAAM,EAAI,KACpC,EAOT,SAAgB,EACd,EACA,EAOgB,CAChB,IAAM,EAAwC,CAC5C,MAAO,EAAQ,MACf,cAAe,EAAQ,cACvB,kBAAmB,EAAQ,kBAC3B,aAAc,EAAQ,aACvB,CACG,EAAQ,WAAa,IAAA,KAAW,EAAa,SAAW,EAAQ,UAEpE,IAAM,EAAwB,CAC5B,WAAY,YACZ,WAAY,EAAI,WAChB,WAAY,EAAI,WAChB,eAAgB,EAAI,eACpB,QAAS,EACV,CAKD,OAJI,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAI,YAAc,IAAA,KAAW,EAAM,UAAY,EAAI,WACnD,EAAI,MAAQ,IAAA,KAAW,EAAM,IAAM,EAAI,KACpC,EAOT,SAAgB,EACd,EACA,EAKgB,CAChB,IAAM,EAAwB,CAC5B,WAAY,qBACZ,WAAY,EAAI,WAChB,WAAY,EAAI,WAChB,eAAgB,EAAI,eACpB,QAAS,CACP,UAAW,EAAQ,UACnB,SAAU,EAAQ,SAClB,KAAM,EAAQ,KACf,CACF,CAKD,OAJI,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAI,YAAc,IAAA,KAAW,EAAM,UAAY,EAAI,WACnD,EAAI,MAAQ,IAAA,KAAW,EAAM,IAAM,EAAI,KACpC,EAGT,SAAgB,EACd,EACA,EAKgB,CAChB,IAAM,EAAwB,CAC5B,WAAY,mBACZ,WAAY,EAAI,WAChB,WAAY,EAAI,WAChB,eAAgB,EAAI,eACpB,QAAS,CACP,UAAW,EAAQ,UACnB,SAAU,EAAQ,SAClB,KAAM,EAAQ,KACf,CACF,CAKD,OAJI,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAI,YAAc,IAAA,KAAW,EAAM,UAAY,EAAI,WACnD,EAAI,MAAQ,IAAA,KAAW,EAAM,IAAM,EAAI,KACpC,EAOT,SAAgB,EACd,EACA,EAKgB,CAChB,IAAM,EAAwB,CAC5B,WAAY,wBACZ,WAAY,EAAI,WAChB,WAAY,EAAI,WAChB,eAAgB,EAAI,eACpB,QAAS,CACP,cAAe,EAAQ,cACvB,YAAa,EAAQ,YACrB,gBAAiB,EAAQ,gBAC1B,CACF,CAKD,OAJI,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAI,YAAc,IAAA,KAAW,EAAM,UAAY,EAAI,WACnD,EAAI,MAAQ,IAAA,KAAW,EAAM,IAAM,EAAI,KACpC,EAmCT,SAAgB,EACd,EACA,EAQgB,CAChB,IAAM,EAAwB,CAC5B,WAAY,aACZ,WAAY,EAAI,WAChB,WAAY,EAAI,WAChB,eAAgB,EAAI,eACpB,OAAQ,EAAQ,mBAChB,QAAS,CACP,mBAAoB,EAAQ,mBAC5B,sBAAuB,EAAQ,sBAC/B,WAAY,EAAQ,WACpB,SAAU,EAAQ,SAClB,WAAY,EAAQ,WACpB,IAAK,EAAQ,IACd,CACF,CAKD,OAJI,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAI,YAAc,IAAA,KAAW,EAAM,UAAY,EAAI,WACnD,EAAI,MAAQ,IAAA,KAAW,EAAM,IAAM,EAAI,KACpC,EAGT,SAAgB,EACd,EACA,EAOgB,CAChB,IAAM,EAAwB,CAC5B,WAAY,iBACZ,WAAY,EAAI,WAChB,WAAY,EAAI,WAChB,eAAgB,EAAI,eACpB,OAAQ,EAAQ,mBAChB,QAAS,CACP,mBAAoB,EAAQ,mBAC5B,sBAAuB,EAAQ,sBAC/B,WAAY,EAAQ,WACpB,SAAU,EAAQ,SAClB,WAAY,EAAQ,WACrB,CACF,CAKD,OAJI,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAI,YAAc,IAAA,KAAW,EAAM,UAAY,EAAI,WACnD,EAAI,MAAQ,IAAA,KAAW,EAAM,IAAM,EAAI,KACpC,EAGT,SAAgB,EACd,EACA,EAOgB,CAChB,IAAM,EAAwB,CAC5B,WAAY,oBACZ,WAAY,EAAI,WAChB,WAAY,EAAI,WAChB,eAAgB,EAAI,eACpB,OAAQ,EAAQ,mBAChB,QAAS,CACP,mBAAoB,EAAQ,mBAC5B,sBAAuB,EAAQ,sBAC/B,WAAY,EAAQ,WACpB,SAAU,EAAQ,SAClB,WAAY,EAAQ,WACrB,CACF,CAKD,OAJI,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAI,UAAY,IAAA,KAAW,EAAM,QAAU,EAAI,SAC/C,EAAI,YAAc,IAAA,KAAW,EAAM,UAAY,EAAI,WACnD,EAAI,MAAQ,IAAA,KAAW,EAAM,IAAM,EAAI,KACpC,EC3YT,IAAM,EAAe,IAAI,IAAI,qIAiC5B,CAAC,CAGI,EAAkB,IAAI,IAAI,CAC9B,SACA,SACA,SACA,QACA,OACA,QACA,WACA,SACA,SACA,QACA,OACA,OACA,WACA,WACD,CAAC,CAOI,EAA6C,CACjD,IAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,CACvB,EAAG,IAAI,IAAI,CAAC,OAAQ,SAAU,MAAM,CAAC,CACrC,IAAK,IAAI,IAAI,CAAC,MAAO,MAAO,QAAS,SAAS,CAAC,CAC/C,IAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,CACvB,KAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,CACxB,EAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,CACtB,CAED,SAAS,EAAsB,EAAwB,CAErD,MAAO,sBAAsB,KAAK,EAAM,CAI1C,IAAM,GAAsB,IAAI,IAAI,ynBAuDnC,CAAC,CAGI,EAAsB,gFAO5B,SAAS,EAAiB,EAAqB,CAC7C,IAAM,EAAiB,EAAE,CACzB,IAAK,IAAM,KAAQ,EAAI,MAAM,IAAI,CAAE,CACjC,IAAM,EAAU,EAAK,MAAM,CAC3B,GAAI,CAAC,EAAS,SACd,IAAM,EAAW,EAAQ,QAAQ,IAAI,CACrC,GAAI,IAAa,GAAI,SACrB,IAAM,EAAW,EAAQ,MAAM,EAAG,EAAS,CAAC,MAAM,CAAC,aAAa,CAC1D,EAAQ,EAAQ,MAAM,EAAW,EAAE,CAAC,MAAM,CAC3C,GAAoB,IAAI,EAAS,GAClC,EAAoB,KAAK,EAAM,EACnC,EAAK,KAAK,EAAQ,EAEpB,OAAO,EAAK,KAAK,KAAK,CAGxB,SAAS,EAAa,EAAY,EAAoB,CACpD,GAAI,EAAK,WAAa,KAAK,UAAW,OAEtC,GAAI,EAAK,WAAa,KAAK,aAAc,CACvC,EAAK,YAAY,YAAY,EAAK,CAClC,OAGF,IAAM,EAAK,EACL,EAAM,EAAG,QAAQ,aAAa,CAGpC,GAAI,EAAgB,IAAI,EAAI,CAAE,CAC5B,EAAG,YAAY,YAAY,EAAG,CAC9B,OAIF,GAAI,CAAC,EAAa,IAAI,EAAI,CAAE,CAC1B,IAAM,EAAW,MAAM,KAAK,EAAG,WAAW,CAC1C,IAAK,IAAM,KAAS,EAClB,EAAO,aAAa,EAAO,EAAG,CAEhC,EAAO,YAAY,EAAG,CAEtB,IAAK,IAAM,KAAS,EAClB,EAAa,EAAO,EAAO,CAE7B,OAIF,IAAM,EAAgB,EAAc,MAAQ,IAAI,IAC1C,EAAa,EAAc,IAAQ,IAAI,IACvC,EAAQ,MAAM,KAAK,EAAG,WAAW,CAEvC,IAAK,IAAM,KAAQ,EAAO,CACxB,IAAM,EAAO,EAAK,KAAK,aAAa,CAEpC,GAAI,CAAC,EAAc,IAAI,EAAK,EAAI,CAAC,EAAW,IAAI,EAAK,CAAE,CACrD,EAAG,gBAAgB,EAAK,KAAK,CAC7B,SAIF,GAAI,EAAsB,EAAK,MAAM,CAAE,CACrC,EAAG,gBAAgB,EAAK,KAAK,CAC7B,SAIF,GAAI,IAAS,QAAS,CACpB,IAAM,EAAY,EAAiB,EAAK,MAAM,CAC1C,EACF,EAAG,aAAa,QAAS,EAAU,CAEnC,EAAG,gBAAgB,QAAQ,CAE7B,UAKJ,GAAI,IAAQ,IAAK,CACf,IAAM,EAAO,EAAG,aAAa,OAAO,CACpC,GAAI,IAAS,KAAM,CACjB,IAAM,EAAU,EAAK,MAAM,CAAC,aAAa,CACrC,CAAC,EAAQ,WAAW,UAAU,EAAI,CAAC,EAAQ,WAAW,WAAW,EAAI,CAAC,EAAQ,WAAW,UAAU,EACrG,EAAG,gBAAgB,OAAO,CAI9B,EAAG,aAAa,SAAU,SAAS,CACnC,EAAG,aAAa,MAAO,sBAAsB,CAG/C,GAAI,IAAQ,MAAO,CACjB,IAAM,EAAM,EAAG,aAAa,MAAM,CAC9B,IAAQ,OACM,EAAI,MAAM,CAAC,aAAa,CAC3B,WAAW,WAAW,EACjC,EAAG,gBAAgB,MAAM,EAM/B,IAAM,EAAW,MAAM,KAAK,EAAG,WAAW,CAC1C,IAAK,IAAM,KAAS,EAClB,EAAa,EAAO,EAAG,CAY3B,IAAM,EAAqB,CAAC,QAAS,SAAS,CAG9C,SAAgB,EAAe,EAAsB,CACnD,GAAI,CACF,OAAO,EAAmB,SAAS,IAAI,IAAI,EAAI,CAAC,SAAS,MACnD,CACN,MAAO,IAQX,SAAgB,EAAU,EAAsB,CAE9C,GAAI,EAAI,WAAW,IAAI,EAAI,CAAC,EAAI,WAAW,KAAK,CAAE,MAAO,GACzD,GAAI,CACF,IAAM,EAAS,IAAI,IAAI,EAAI,CAC3B,OAAO,EAAmB,SAAS,EAAO,SAAS,MAC7C,CACN,MAAO,IAQX,SAAgB,EAAiB,EAAiB,EAAc,EAAqB,EAC/E,IAAS,QAAU,IAAS,QAC1B,CAAC,EAAU,EAAM,EAEvB,EAAG,aAAa,EAAM,EAAM,CAG9B,SAAgB,EAAa,EAAqB,CAChD,GAAI,CAAC,EAAK,MAAO,GAGjB,IAAM,EADM,IAAI,WAAW,CAAC,gBAAgB,EAAK,YAAY,CAC5C,KAEX,EAAW,MAAM,KAAK,EAAK,WAAW,CAC5C,IAAK,IAAM,KAAS,EAClB,EAAa,EAAO,EAAK,CAG3B,OAAO,EAAK,UCrSd,SAAgB,EAAqC,EAAS,EAAwC,CACpG,OAAO,cAAc,IAAI,YAAY,EAAM,CAAE,SAAQ,QAAS,GAAO,CAAC,CAAC,CAczE,SAAgB,EACd,EACA,EACY,CACZ,IAAM,EAAY,GAAa,CAC7B,EAAS,EAA4C,OAAO,EAG9D,OADA,OAAO,iBAAiB,EAAM,EAAS,KAC1B,OAAO,oBAAoB,EAAM,EAAS,CCWzD,SAAgB,GAA2B,CAGzC,IAAM,EAFW,OAAO,oBAAsB,eAAe,QAAQ,qBAAqB,EAAI,MAEhE,OAAO,YAAY,CAQjD,MANA,QAAO,mBAAqB,EAC5B,eAAe,QAAQ,qBAAsB,EAAU,CAElD,OAAO,UAAS,OAAO,QAAU,EAAE,EACxC,OAAO,QAAQ,UAAY,EAEpB,EAkCT,SAAgB,GAAe,EAAqD,CAElF,MAAO,CACL,UAFgB,GAAW,WAAa,GAAkB,CAG1D,GAAG,EACJ,CChGH,IAAa,GAA2C,CACtD,wBAAyB,OACzB,+BAAgC,OAChC,uBAAwB,QACxB,8BAA+B,OAC/B,+BAAgC,OAChC,oCAAqC,QACrC,iCAAkC,QAClC,iCAAkC,SAClC,8BAA+B,OAE/B,4BAA6B,QAE7B,6BAA8B,OAE9B,+BAAgC,UAEhC,mBAAoB,OACpB,gBAAiB,OACjB,iBAAkB,OAClB,gBAAiB,QACjB,uBAAwB,UACxB,mBAAoB,UACpB,eAAgB,UAChB,mBAAoB,yBACrB,CAED,SAAgB,EAAuB,EAAkC,CAIvE,OAHK,EAGE,CAAE,GAAG,GAA6B,GAAG,EAAO,CAF1C,CAAE,GAAG,GAA6B,CC7B7C,IAAa,EAAiC,CAG5C,aAAc,UACd,kBAAmB,UACnB,gBAAiB,UACjB,gBAAiB,UACjB,aAAc,UACd,WAAY,yFACZ,SAAU,OACV,OAAQ,OAER,uBAAwB,QACxB,sBAAuB,QACvB,oBAAqB,OACrB,kCAAmC,UACnC,8BAA+B,UAE/B,uBAAwB,QACxB,8BAA+B,OAC/B,+BAAgC,OAChC,oCAAqC,QACrC,iCAAkC,QAClC,iCAAkC,SAClC,8BAA+B,OAC/B,4BAA6B,QAC7B,6BAA8B,UAC9B,+BAAgC,UAEhC,mBAAoB,UACpB,yBAA0B,6CAC1B,0BAA2B,6CAC3B,0BAA2B,yCAC3B,wBAAyB,yCACzB,sBAAuB,UACvB,sBAAuB,+CAEvB,iBAAkB,UAClB,kBAAmB,UACnB,iBAAkB,UAClB,uBAAwB,UACxB,sBAAuB,UACvB,qBAAsB,UACtB,kBAAmB,UACnB,oBAAqB,yBAErB,iBAAkB,UAClB,mBAAoB,UACpB,eAAgB,UAChB,iBAAkB,UAElB,kBAAmB,yBACnB,mBAAoB,yBACpB,kBAAmB,yBAEnB,aAAc,qEACd,aAAc,oCACd,aAAc,qCAEd,mBAAoB,OACpB,gBAAiB,OACjB,iBAAkB,OAClB,gBAAiB,QAEjB,YAAa,UACb,YAAa,UACb,UAAW,UACX,SAAU,UACV,WAAY,UAEZ,oBAAqB,UACrB,kBAAmB,UACnB,mBAAoB,8EACrB,CC7EK,EAAU,4BACV,GAAW,6BACX,EAAqB,qCACrB,GAAsB,KACtB,GAAkB,KAClB,GAAkB,KAClB,GAAkB,CACtB,wBACA,iBACA,iBACA,eACA,mBACA,gBACA,aACA,UACA,sBACA,0BACA,0BACA,sBACA,0BACD,CAEG,GAAqB,GACrB,EAAqD,KAEnD,GAA8B,CAClC,mBACA,gBACA,0BACA,eACA,eACA,gBACA,iBACD,CAMD,SAAgB,GAA0B,EAA0B,CAClE,GAAI,OAAO,UAAc,KAAe,UAAU,SAAW,GAC3D,MAAO,GAGT,IAAM,EAAU,aAAiB,MAAQ,EAAM,QAAQ,MAAM,CAAG,GAGhE,OAFK,EAEE,GAA4B,KAAM,GAAY,EAAQ,KAAK,EAAQ,CAAC,CAFtD,GAmBvB,SAAgB,IAAyC,CACnD,IAAsB,OAAO,OAAW,KAAe,OAAO,SAAa,MAI/E,GAAqB,GACrB,EAAO,uBAAyB,GAAW,CACzC,GAAqB,EAAO,EAC5B,EAGJ,SAAgB,GAAqB,EAA6D,CAChG,GAAI,OAAO,SAAa,IAAa,OACrC,IAAM,EAAU,EAAO,QAAQ,MAAM,CACrC,GAAI,CAAC,EAAS,OAEd,IAAc,CACd,IAAM,EAAO,IAAY,CACzB,GAAkB,EAAK,CACvB,EAAK,UAAY,GAEjB,IAAM,EAAQ,SAAS,cAAc,UAAU,CAC/C,EAAM,UAAY,mDAClB,EAAM,aAAa,OAAQ,SAAS,CACpC,EAAM,aAAa,YAAa,SAAS,CAEzC,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,6BAClB,EAAM,YAAc,GAAY,EAAO,OAAO,CAE9C,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,+BACjB,EAAK,YAAc,EAEnB,EAAM,YAAY,EAAM,CACxB,EAAM,YAAY,EAAK,CACvB,EAAK,YAAY,EAAM,CACvB,EAAK,UAAU,IAAI,EAAmB,CAEtC,AAEE,KADA,aAAa,EAAa,CACX,MAGb,EAAO,SAAW,KAItB,EAAe,eAAiB,CAC9B,IAAyB,EACxB,GAAc,EAAO,WAAW,CAAC,EAGtC,SAAgB,IAAgC,CAC9C,GAAI,OAAO,SAAa,IAAa,OACrC,IAAM,EAAO,SAAS,eAAe,EAAQ,CACxC,IAEL,EAAK,UAAU,OAAO,EAAmB,CACzC,EAAK,UAAY,GAEjB,AAEE,KADA,aAAa,EAAa,CACX,OAInB,SAAS,GAAY,EAAyE,CAC5F,OAAQ,EAAR,CACE,IAAK,OACH,MAAO,eACT,IAAK,MACH,MAAO,cACT,IAAK,SACH,MAAO,iBACT,QACE,MAAO,sBAIb,SAAS,IAA0B,CACjC,IAAM,EAAW,SAAS,eAAe,EAAQ,CACjD,GAAI,aAAoB,YAAa,OAAO,EAE5C,IAAM,EAAO,SAAS,cAAc,MAAM,CAI1C,MAHA,GAAK,GAAK,EACV,EAAK,UAAY,4BACjB,SAAS,KAAK,YAAY,EAAK,CACxB,EAGT,SAAS,GAAkB,EAAyB,CAClD,GAAI,OAAO,SAAa,KAAe,OAAO,OAAW,IAAa,OACtE,IAAM,EAAS,SAAS,cACtB,8FACD,CACD,GAAI,CAAC,EAAQ,OACb,IAAM,EAAW,OAAO,iBAAiB,EAAO,CAChD,IAAK,IAAM,KAAQ,GAAiB,CAClC,IAAM,EAAQ,EAAS,iBAAiB,EAAK,CAAC,MAAM,CAChD,EACF,EAAK,MAAM,YAAY,EAAM,EAAM,CAEnC,EAAK,MAAM,eAAe,EAAK,EAKrC,SAAS,GAAc,EAA6B,CAIlD,OAHI,OAAO,GAAe,UAAY,CAAC,OAAO,SAAS,EAAW,CACzD,GAEF,KAAK,IAAI,GAAiB,KAAK,IAAI,GAAiB,KAAK,MAAM,EAAW,CAAC,CAAC,CAGrF,SAAS,IAAqB,CAC5B,GAAI,SAAS,eAAe,GAAS,CAAE,OAEvC,IAAM,EAAQ,SAAS,cAAc,QAAQ,CAC7C,EAAM,GAAK,GACX,IAAM,EAAc,EAAkB,mBAAqB,EAAkB,iBAAmB,UAC1F,EAAc,EAAkB,mBAAqB,EAAkB,iBAAmB,UAC1F,EAAgB,EAAkB,qBAAuB,yBACzD,EAAQ,EAAkB,YAAc,UACxC,EAAU,EAAkB,eAAiB,qCAC7C,EAAa,EAAkB,kBAAoB,OACnD,EAAY,EAAkB,iBAAmB,UACvD,EAAM,YAAc;GACnB,EAAQ;;;;;;;GAOR,EAAQ,GAAG,EAAmB;;;GAG9B,EAAQ;;;sCAG2B,EAAW;mFACkC,EAAM,+BAA+B,EAAc;qEACjE,EAAM;yEACF,EAAM,4BAA4B,EAAY;oEACnD,EAAM,6BAA6B,EAAY;6DACtD,EAAQ;;4CAEzB,KAAK,UAAU,EAAkB,YAAc,4DAA4D,CAAC;;;;;GAKrJ,EAAQ;;;;6BAIkB,EAAU;;GAEpC,EAAQ;;;;;;;;;;;;;;EAeT,SAAS,KAAK,YAAY,EAAM,CCxOlC,IAAI,GAA2B,KAE/B,SAAS,IAAqB,CAC5B,GAAI,KAAa,KAAM,OAAO,GAC9B,GAAI,CACF,GAAW,aAAa,QAAQ,gBAAgB,GAAK,SAC/C,CACN,GAAW,GAEb,OAAO,GAIT,SAAgB,GAAS,EAAkB,EAAiB,EAAsB,CAChF,GAAI,CAAC,IAAW,CAAE,OAClB,IAAM,EAAkB,CAAC,YAAY,EAAS,GAAI,EAAQ,CACtD,IAAS,IAAA,IAAW,EAAK,KAAK,EAAK,CAEvC,QAAQ,MAAM,GAAG,EAAK,CCExB,IAAsB,GAAtB,KAEoC,8BAGZ,sBACI,kBAEG,IAAI,mBACe,EAAE,gBAC9B,oBACE,GAMtB,MAAM,KAAK,EAAgC,CACzC,GAAI,KAAK,cAAe,CACtB,QAAQ,KAAK,+DAA+D,CAC5E,OAGF,IAAM,EAAc,EAAuB,EAAO,MAAM,CAExD,KAAK,OAAS,CACZ,GAAG,EACH,MAAO,EACP,QAAS,GAAe,EAAO,QAAQ,CACxC,CAED,KAAK,KAAO,KAAK,cAAc,EAAO,YAAY,CAClD,KAAK,YAAY,EAAY,CAC7B,IAAkC,CAGlC,IAAM,EAAM,EAAO,yBAA2B,GAAU,KAAK,OAAO,EAAM,CAAC,CAC3E,KAAK,UAAU,KAAK,EAAI,CAExB,GAAS,YAAa,GAAG,KAAK,YAAY,KAAK,OAAQ,CACrD,UAAW,EAAO,UAClB,IAAK,EAAO,aAAa,IAC1B,CAAC,CAEF,GAAI,CACF,MAAM,KAAK,OAAO,KAAK,OAAO,OACvB,EAAK,CAEZ,MADA,KAAK,SAAS,CACR,EAEJ,KAAK,cACT,KAAK,cAAgB,GACrB,GAAS,YAAa,GAAG,KAAK,YAAY,KAAK,QAAQ,CACvD,KAAK,KAAK,QAAQ,EAGpB,OAAO,EAAqC,CACrC,KAAK,gBACN,KAAK,OAAO,YACd,KAAK,OAAS,CACZ,GAAG,KAAK,OACR,YAAa,CAAE,GAAG,KAAK,OAAO,YAAa,GAAG,EAAS,CACxD,CACQ,EAAQ,WAAa,IAAA,KAE9B,KAAK,OAAS,CAAE,GAAG,KAAK,OAAQ,YAAa,EAAwB,EAEvE,KAAK,SAAS,EAAQ,CACtB,KAAK,KAAK,iBAAkB,KAAK,OAAO,YAAY,EAGtD,MAAa,CACP,KAAK,YACT,KAAK,UAAY,GACjB,KAAK,KAAK,MAAM,QAAU,GAC1B,KAAK,QAAQ,CACb,KAAK,KAAK,OAAO,EAGnB,MAAa,CACN,KAAK,YACV,KAAK,UAAY,GACjB,KAAK,KAAK,MAAM,QAAU,OAC1B,KAAK,QAAQ,CACb,KAAK,KAAK,OAAO,EAGnB,SAAgB,CACV,KAAK,cACT,KAAK,YAAc,GACnB,KAAK,KAAK,UAAU,CACpB,KAAK,UAAU,QAAS,GAAO,GAAI,CAAC,CACpC,KAAK,UAAU,OAAS,EACxB,KAAK,UAAU,OAAO,CACtB,KAAK,WAAW,CAChB,KAAK,OAAO,iBAAiB,SAAS,CAClC,KAAK,UACP,KAAK,KAAK,QAAQ,CAElB,KAAK,KAAK,UAAY,GAExB,KAAK,cAAgB,IAGvB,GAAG,EAAe,EAAiC,CAGjD,OAFK,KAAK,UAAU,IAAI,EAAM,EAAE,KAAK,UAAU,IAAI,EAAO,IAAI,IAAM,CACpE,KAAK,UAAU,IAAI,EAAM,CAAE,IAAI,EAAQ,KAC1B,KAAK,UAAU,IAAI,EAAM,EAAE,OAAO,EAAQ,CAIzD,cACE,EACA,EAOM,CACN,IAAM,EAAU,IAAS,QAAU,EAAqB,EACxD,KAAK,MAAM,EAAQ,KAAK,kBAAkB,CAAE,EAAK,CAAC,CAIpD,qBAAqB,EAAmE,CACtF,KAAK,MAAM,EAAqB,KAAK,kBAAkB,CAAE,EAAK,CAAC,CAkBjE,KAAe,EAAe,GAAG,EAAuB,CACtD,KAAK,UAAU,IAAI,EAAM,EAAE,QAAS,GAAM,EAAE,GAAG,EAAK,CAAC,CAIvD,WAAqB,EAAsB,CACzC,KAAK,UAAU,KAAK,EAAG,CAIzB,MAAgB,EAA6B,CAC3C,KAAK,OAAO,iBAAiB,MAAM,EAAM,CAI3C,kBAA+C,CAC7C,IAAM,EAAwB,CAC5B,WAAY,KAAK,OAAO,UACxB,WAAY,KAAK,OAAO,SAAS,WAAa,GAC9C,eAAgB,KAAK,OAAO,SAAS,WAAa,GACnD,CAQD,OAPI,KAAK,OAAO,SAAS,SAAW,IAAA,KAAW,EAAI,QAAU,KAAK,OAAO,QAAQ,QAC7E,KAAK,OAAO,SAAS,SAAW,IAAA,KAAW,EAAI,QAAU,KAAK,OAAO,QAAQ,QAC7E,KAAK,OAAO,aAAa,WAAa,IAAA,KAAW,EAAI,UAAY,KAAK,OAAO,YAAY,UACzF,KAAK,OAAO,aAAa,MAAQ,IAAA,KAAW,EAAI,IAAM,KAAK,OAAO,YAAY,KAC9E,KAAK,OAAO,SAAS,gBAAkB,IAAA,KAAW,EAAI,gBAAkB,KAAK,OAAO,QAAQ,eAC5F,KAAK,OAAO,SAAS,qBAAuB,IAAA,KAC9C,EAAI,sBAAwB,KAAK,OAAO,QAAQ,oBAC3C,EAOT,cAAsB,EAA4C,CAChE,GAAI,aAAkB,YAAa,OAAO,EAC1C,GAAI,OAAO,GAAW,SAAU,CAC9B,IAAM,EAAK,SAAS,cAA2B,EAAO,CACtD,GAAI,CAAC,EAAI,MAAU,MAAM,sCAAsC,EAAO,GAAG,CACzE,OAAO,EAKT,IAAM,EAAM,SAAS,cAAc,MAAM,CAIzC,MAHA,GAAI,QAAQ,cAAmB,KAAK,YAAY,KAAK,aAAa,CAClE,SAAS,KAAK,aAAa,EAAK,SAAS,KAAK,WAAW,CACzD,KAAK,UAAY,GACV,EAGT,YAAoB,EAA2B,CACxC,KACL,KAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,EAAM,CAC9C,GAAI,IAAU,IAAA,GAAW,CACvB,IAAM,EAAO,EAAI,WAAW,KAAK,CAAG,EAAM,aAAa,GAAQ,EAAI,GACnE,KAAK,KAAK,MAAM,YAAY,EAAM,EAAM,KAMhD,SAAS,GAAQ,EAAqB,CACpC,OAAO,EAAI,QAAQ,WAAY,MAAM,CAAC,aAAa,CCxOrD,SAAgB,IAAiB,CAC/B,IAAM,EAAM,KAAK,KAAK,CAChB,EAAQ,IAAI,WAAW,GAAG,CAChC,OAAO,gBAAgB,EAAM,CAG7B,EAAM,GAAM,EAAM,GAAK,GAAM,IAC7B,EAAM,GAAM,EAAM,GAAK,GAAM,IAC7B,EAAM,GAAM,EAAM,GAAK,GAAM,IAC7B,EAAM,GAAM,EAAM,GAAK,GAAM,IAC7B,EAAM,GAAM,EAAM,GAAK,EAAK,IAC5B,EAAM,GAAK,EAAM,IAGjB,EAAM,GAAM,EAAM,GAAM,GAAQ,IAGhC,EAAM,GAAM,EAAM,GAAM,GAAQ,IAEhC,IAAM,EAAM,MAAM,KAAK,EAAQ,GAAM,EAAE,SAAS,GAAG,CAAC,SAAS,EAAG,IAAI,CAAC,CAAC,KAAK,GAAG,CAC9E,MAAO,GAAG,EAAI,MAAM,EAAG,EAAE,CAAC,GAAG,EAAI,MAAM,EAAG,GAAG,CAAC,GAAG,EAAI,MAAM,GAAI,GAAG,CAAC,GAAG,EAAI,MAAM,GAAI,GAAG,CAAC,GAAG,EAAI,MAAM,GAAG,GCO1G,IAAa,GAAb,KAAiC,CAQ/B,YAAY,EAAqC,gBAJpB,IAAI,oBAEZ,GAGnB,KAAK,WAAa,EAAQ,UAI1B,KAAK,gBAAkB,EAAQ,gBAAkB,CAAC,SAAS,OAAO,CAClE,KAAK,WAAa,EAAQ,UAEtB,KAAK,gBAAgB,SAAS,IAAI,EAAI,IAAY,EACpD,QAAQ,KAAK,kGAAkG,CAGjH,KAAK,iBAAoB,GAAwB,KAAK,mBAAmB,EAAM,CAC/E,OAAO,iBAAiB,UAAW,KAAK,iBAAiB,CAI3D,KAAK,EAAc,EAAyB,CAC1C,GAAI,KAAK,WAAY,OAErB,IAAM,EAAiE,CACrE,UAAW,KAAK,WAChB,OACD,CACG,IAAY,IAAA,KACd,EAAO,QAAU,GAGnB,OAAO,cACL,IAAI,YAAY,yBAA0B,CACxC,SACA,QAAS,GACV,CAAC,CACH,CAOH,GAAG,EAAc,EAAoC,CAC9C,KAAK,UAAU,IAAI,EAAK,EAC3B,KAAK,UAAU,IAAI,EAAM,IAAI,IAAM,CAGrC,IAAM,EAAW,KAAK,UAAU,IAAI,EAAK,CAGzC,OAFA,EAAS,IAAI,EAAQ,KAER,CACX,EAAS,OAAO,EAAQ,CACpB,EAAS,OAAS,GACpB,KAAK,UAAU,OAAO,EAAK,EAMjC,SAAgB,CACV,KAAK,aACT,KAAK,WAAa,GAClB,OAAO,oBAAoB,UAAW,KAAK,iBAAiB,CAC5D,KAAK,UAAU,OAAO,EAOxB,mBAA2B,EAA2B,CAIpD,GAHI,KAAK,YAGL,CAAC,KAAK,iBAAiB,EAAM,OAAO,CAAE,OAG1C,IAAM,EAAgB,EAAM,KAI5B,GAHI,CAAC,GAAkB,EAAK,EAGxB,EAAK,UAAY,KAAK,WAAY,OAEtC,IAAM,EAAqB,CAAE,KAAM,EAAK,KAAM,CAC1C,EAAK,UAAY,IAAA,KACnB,EAAI,QAAU,EAAK,SAIrB,KAAK,aAAa,EAAI,CAGtB,IAAM,EAAW,KAAK,UAAU,IAAI,EAAI,KAAK,CAC7C,GAAI,EACF,IAAK,IAAM,KAAW,EACpB,EAAQ,EAAI,QAAQ,CAK1B,iBAAyB,EAAyB,CAEhD,OADI,KAAK,gBAAgB,SAAS,IAAI,CAAS,GACxC,KAAK,gBAAgB,SAAS,EAAO,GAShD,SAAS,GAAkB,EAA6E,CACtG,GAAI,OAAO,GAAS,WAAY,EAAe,MAAO,GACtD,IAAM,EAAM,EACZ,OAAO,OAAO,EAAI,SAAe,UAAY,OAAO,EAAI,MAAY,SAQtE,SAAS,IAAsB,CAC7B,GAAI,CACF,OAAO,OAAO,QAAY,KAAA,QAAA,IAAA,WAA6C,kBACjE,CACN,MAAO,IClJX,IAAM,GAA2D,QAEjE,SAAgB,GACd,EACA,EACA,EAA+B,EAAE,CAC3B,CACN,IAAM,EAAS,EAAM,OACf,EAAS,EAAQ,QAAU,GAGjC,OAFA,GAAS,SAAU,mBAAmB,EAAO,OAAQ,EAAO,CAEpD,EAAO,KAAf,CACE,IAAK,YACH,EAAS,WAAW,EAAO,QAAQ,CACnC,OAEF,IAAK,WAAY,CACf,GAAI,OAAO,EAAO,KAAQ,SAAU,CAClC,GAAoB,EAAQ,EAAU,EAAS,EAAO,CACtD,OAEF,GAAI,CAAC,EAAU,EAAO,IAAI,CAAE,CAC1B,EAAO,KAAK,8CAA+C,EAAO,IAAI,CACtE,OAEF,IAAM,EAAS,OAAO,EAAO,QAAW,UAAY,EAAO,OAAS,IAAA,GACpE,GAAI,EAAS,SAAU,CACrB,EAAS,SAAS,CAAE,IAAK,EAAO,IAAK,GAAI,IAAW,IAAA,IAAa,CAAE,SAAQ,CAAG,CAAC,CAC/E,OAEF,GAAgB,EAAO,IAAK,EAAO,CACnC,OAEF,IAAK,eACH,GAAI,OAAO,EAAO,WAAc,UAAY,OAAO,EAAO,KAAQ,SAAU,CAC1E,GAAoB,EAAQ,EAAU,EAAS,EAAO,CACtD,OAEF,EAAS,cAAc,CAAE,UAAW,EAAO,UAAW,IAAK,EAAO,IAAK,CAAC,CACxE,OAEF,IAAK,cACH,GACE,OAAO,EAAO,KAAQ,UACtB,OAAO,EAAO,UAAa,UAC3B,OAAO,EAAO,UAAa,SAC3B,CACA,GAAoB,EAAQ,EAAU,EAAS,EAAO,CACtD,OAEF,EAAS,YAAY,CACnB,IAAK,EAAO,IACZ,SAAU,EAAO,SACjB,SAAU,EAAO,SAClB,CAAC,CACF,OAEF,IAAK,cAAe,CAClB,GAAI,EAAQ,kBAAoB,GAAO,CACrC,GAAoB,EAAQ,EAAU,EAAS,EAAO,CACtD,OAEF,GAAI,OAAO,EAAO,MAAS,SAAU,CACnC,GAAoB,EAAQ,EAAU,EAAS,EAAO,CACtD,OAEF,IAAM,EAAU,GAAS,EAAO,QAAQ,CAAG,EAAO,QAAU,IAAA,GAC5D,EAAS,aAAa,CAAE,KAAM,EAAO,KAAM,GAAI,IAAY,IAAA,IAAa,CAAE,UAAS,CAAG,CAAC,CACvF,OAEF,QACE,GAAoB,EAAQ,EAAU,EAAS,EAAO,EAK5D,SAAS,GACP,EACA,EACA,EACA,EACM,CACN,IAAM,EAAS,EAAQ,qBAAuB,iBAC9C,GAAI,IAAW,WAAY,CACzB,EAAS,UAAU,EAAO,CACrB,EAAS,SACZ,EAAO,KAAK,6DAA8D,EAAO,CAEnF,OAGF,GAAI,IAAW,QACb,MAAU,MAAM,kCAAmC,EAA8B,OAAO,CAG1F,EAAO,KAAK,mCAAoC,EAAO,CAGzD,SAAS,GAAgB,EAAa,EAAwB,CACxD,YAAO,OAAW,KACtB,IAAI,CAAC,EAAU,EAAI,CAAE,CACnB,QAAQ,KAAK,8CAA+C,EAAI,CAChE,OAEF,GAAI,EAAQ,CACV,OAAO,KAAK,EAAK,SAAU,sBAAsB,CACjD,OAEF,OAAO,SAAS,KAAO,GAGzB,SAAS,GAAS,EAAkD,CAClE,OAAO,OAAO,GAAU,YAAY,GAAkB,CAAC,MAAM,QAAQ,EAAM,CCpI7E,IAAa,GAAqB,CAAC,aAAc,YAAa,aAAa,CAK3E,SAAgB,GAAkB,EAA8B,CAO9D,OANM,GAAyC,SAAS,EAAK,KAAK,CAG9D,EAAK,KAAA,QACA,CAAE,GAAI,GAAO,OAAQ,YAAa,CAEpC,CAAE,GAAI,GAAM,CALV,CAAE,GAAI,GAAO,OAAQ,eAAgB,CCehD,SAAS,GAAqB,EAAsC,CAClE,OAAO,OAAO,GAAU,YAAY,GAAkB,OAAQ,EAAkC,MAAY,SAS9G,SAAS,GAAsB,EAAwB,CACrD,IAAM,EAAkB,EAAE,CACtB,EAAQ,EACR,EAAW,GACX,EAAS,GACT,EAAQ,EAEZ,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAQ,IAAK,CACpC,IAAM,EAAK,EAAK,GAChB,GAAI,EAAQ,CACV,EAAS,GACT,SAEF,GAAI,IAAO,MAAQ,EAAU,CAC3B,EAAS,GACT,SAEF,GAAI,IAAO,IAAK,CACd,EAAW,CAAC,EACZ,SAEE,IACA,IAAO,KAAK,IACZ,IAAO,MACT,IACI,IAAU,IACZ,EAAM,KAAK,EAAK,MAAM,EAAO,EAAI,EAAE,CAAC,CACpC,EAAQ,EAAI,KAIlB,OAAO,EAAM,OAAS,EAAI,EAAQ,CAAC,EAAK,CAwB1C,SAAS,GAAY,EAAc,EAAiC,CAClE,IAAM,EAAU,EAAK,MAAM,CAC3B,GAAI,CAAC,GAAW,EAAQ,WAAW,IAAI,CAAE,MAAO,GAEhD,IAAM,EAAU,EAAQ,WAAW,SAAS,CAAG,EAAQ,MAAM,EAAE,CAAG,EAClE,GAAI,IAAY,SAEd,OADA,EAAQ,UAAU,CACX,GAIT,GAAI,CACF,IAAM,EAAQ,KAAK,MAAM,EAAQ,CAajC,OAZK,GAAqB,EAAM,EAMhC,GAAS,SAAU,UAAU,EAAM,OAAQ,EAAM,CACjD,EAAQ,QAAQ,EAAM,CAClB,EAAM,OAAS,QACjB,EAAQ,UAAU,CACX,IAEF,IARE,QASH,CAGN,IAAM,EAAQ,GAAsB,EAAQ,CAC5C,GAAI,EAAM,OAAS,EAAG,CACpB,IAAK,IAAM,KAAQ,EACjB,GAAI,CACF,IAAM,EAAQ,KAAK,MAAM,EAAK,CAC9B,GAAI,CAAC,GAAqB,EAAM,CAAE,SAElC,GADA,EAAQ,QAAQ,EAAM,CAClB,EAAM,OAAS,OAEjB,OADA,EAAQ,UAAU,CACX,QAEH,EAMV,MAAO,GAMT,MAAO,IAcX,eAAsB,GAAc,EAAoB,EAAuC,CAC7F,GAAI,CAAC,EAAS,GAAI,CAChB,EAAQ,UAAc,MAAM,QAAQ,EAAS,OAAO,IAAI,EAAS,aAAa,CAAC,CAC/E,OAGF,GAAI,CAAC,EAAS,KAAM,CAClB,EAAQ,UAAc,MAAM,kDAAkD,CAAC,CAC/E,OAGF,IAAM,EAAS,EAAS,KAAK,WAAW,CAClC,EAAU,IAAI,YAAY,QAAQ,CACpC,EAAS,GACP,EAAc,EAAQ,eAAiB,IAEvC,EAAwB,OAAO,eAAe,CAChD,EAAkB,GAEtB,GAAI,CACF,OAAa,CAGX,IAAM,EAAc,EAAO,MAAM,CAC7B,EACE,EAAS,MAAO,EAAc,EAChC,QAAQ,KAAK,CACX,EAAY,KAAM,IAChB,aAAa,EAAM,CACZ,GACP,CACF,IAAI,QAAuC,GAAY,CACrD,EAAQ,eAAiB,EAAQ,EAAsB,CAAE,EAAY,EACrE,CACH,CAAC,CACF,GAEJ,GAAI,IAAW,EAAuB,CAGpC,EAAkB,GAClB,MAAM,EAAO,QAAQ,CACrB,MAGF,GAAM,CAAE,OAAM,SAAU,EACxB,GAAI,EAAM,MAEV,GAAU,EAAQ,OAAO,EAAO,CAAE,OAAQ,GAAM,CAAC,CAGjD,IAAM,EAAQ,EAAO,MAAM;EAAK,CAEhC,EAAS,EAAM,KAAK,EAAI,GAExB,IAAK,IAAM,KAAQ,EACjB,GAAI,GAAY,EAAM,EAAQ,CAAE,OAOpC,GAAI,EAAO,MAAM,EACX,GAAY,EAAQ,EAAQ,CAAE,aAE7B,EAAK,CACZ,GAAI,aAAe,cAAgB,EAAI,OAAS,aAAc,OAC9D,EAAQ,UAAU,aAAe,MAAQ,EAAU,MAAM,OAAO,EAAI,CAAC,CAAC,QAC9D,CAGH,GACH,EAAO,aAAa,CAQxB,EAAQ,UAAU,CC5NpB,IAAM,GAAe,EAEf,IAAgB,EAAe,IAA0D,CAC7F,IAAM,EAAa,EAChB,QAAQ,OAAQ,IAAI,CACpB,MAAM,CACN,QAAQ,6BAA8B,GAAG,CAE5C,GAAI,CAAC,EACH,MAAO,GAGT,IAAM,EAAmB,GAAS,sBAC9B,EAAW,QAAQ,qBAAsB,GAAG,CAAC,MAAM,CACnD,EAMJ,OAJK,EAIE,EAAiB,MAAM,MAAM,CAAC,MAAM,EAAG,EAAE,CAAC,KAAK,IAAI,CAAC,MAAM,CAHxD,IAML,IAAsB,EAAgB,IACrC,EAIE,EACJ,MAAM,yEAAyE,CAC/E,IAAK,GAAS,GAAa,EAAM,EAAQ,CAAC,CAC1C,OAAO,QAAQ,CANT,EAAE,CAYb,SAAgB,GAA2B,EAAgD,CACzF,IAAM,GAAoB,EAAO,kBAAoB,EAAE,EAAE,QAAS,GAChE,GAAmB,EAAS,CAAE,sBAAuB,GAAM,CAAC,CAC7D,CACK,EAAiB,EAAiB,QAAQ,EAAS,IAAU,EAAiB,QAAQ,EAAQ,GAAK,EAAM,CAC/G,GAAI,EAAe,OAAS,EAC1B,OAAO,EAAe,MAAM,EAAG,GAAa,CAG9C,IAAM,EAAmB,CACvB,GAAG,GAAmB,EAAO,iBAAkB,CAAE,sBAAuB,GAAM,CAAC,CAC/E,GAAG,GAAmB,EAAO,WAAW,CACzC,CACD,OAAO,EACJ,QAAQ,EAAS,IAAU,EAAiB,QAAQ,EAAQ,GAAK,EAAM,CACvE,MAAM,EAAG,GAAa,CAI3B,SAAgB,GAA+B,EAA8C,CAC3F,OAAO,GAA2B,EAAO,CAAC,KAAK,MAAM,CCkVvD,SAAgB,GAAkB,EAAkD,CAElF,GAAI,OADS,EAAI,MACG,SAAU,OAAO,KAErC,GAAI,GAAwB,EAAI,CAC9B,OAAO,EAGT,IAAM,EAAQ,EAEd,OAAQ,EAAM,KAAd,CACE,IAAK,aACH,OAAO,GAAgB,EAAsB,CAC/C,IAAK,mBACH,OAAO,GAAsB,EAA4B,CAC3D,IAAK,cACH,OAAO,GAAiB,EAAuB,CACjD,IAAK,iBACH,OAAO,GAAoB,EAA0B,CACvD,IAAK,yBACH,OAAO,GAA4B,EAAkC,CACvE,IAAK,kBACH,OAAO,GAAqB,EAA2B,CACzD,IAAK,UACH,OAAO,GAAa,EAAmB,CACzC,IAAK,gBACH,OAAO,IAAoB,CAC7B,IAAK,UACH,OAAO,GAAa,EAAmB,CACzC,IAAK,eACH,OAAO,GAAkB,EAAwB,CACnD,IAAK,iBACH,OAAO,GAAoB,EAA0B,CACvD,IAAK,WACH,OAAO,GAAc,EAAoB,CAC3C,IAAK,QACH,OAAO,GAAa,EAAiB,CACvC,IAAK,QACH,OAAO,GAAU,EAAgB,CACnC,IAAK,iBACH,OAAO,GAAoB,EAA0B,CACvD,IAAK,OACH,OAAO,GAAkB,EAAwB,CACnD,IAAK,cACH,OAAO,GAAiB,EAAuB,CACjD,IAAK,aACH,OAAO,GAAuB,EAA6B,CAC7D,IAAK,YACH,OAAO,GAAsB,EAA4B,CAC3D,IAAK,mBACH,OAAO,GAAsB,EAA4B,CAC3D,IAAK,uBACH,OAAO,GAA0B,EAAgC,CACnE,IAAK,qBACH,OAAO,GAAwB,EAA8B,CAC/D,IAAK,sBACH,OAAO,GAAyB,EAA+B,CACjE,IAAK,cACH,OAAO,GAAiB,EAAuB,CACjD,IAAK,qBACH,OAAO,GAAwB,EAA8B,CAC/D,IAAK,QACH,OAAO,GAAW,EAAiB,CACrC,IAAK,sBACH,OAAO,GAAyB,EAA+B,CACjE,IAAK,qBACH,OAAO,IAAyB,CAClC,IAAK,YACH,OAAO,GAAe,EAAqB,CAC7C,IAAK,cACL,IAAK,gBACL,IAAK,qBACL,IAAK,iBACH,OAAO,GAAe,EAAqB,CAC7C,IAAK,kBACH,OAAO,GAAqB,EAA2B,CACzD,IAAK,UACH,OAAO,GAAa,EAAmB,CACzC,QAIE,OAAO,MAIb,SAAS,GAAwB,EAAuC,CACtE,IAAM,EAAO,EAAI,KACjB,GAAI,OAAO,GAAS,SAAU,MAAO,GAErC,OAAQ,EAAR,CACE,IAAK,WACH,OAAO,OAAO,EAAI,WAAiB,UAAY,OAAO,EAAI,OAAa,SACzE,IAAK,aACH,OAAO,OAAO,EAAI,SAAe,SACnC,IAAK,UAAW,CACd,IAAM,EAAS,EAAI,OACnB,GAAI,IAAW,QAAU,IAAW,OAAS,IAAW,SAAU,MAAO,GACzE,IAAM,EAAO,GAAS,EAAI,KAAQ,CAElC,OADK,EACE,OAAO,EAAK,MAAY,UAAY,GAAS,EAAK,SAAY,GAAK,KADxD,GAGpB,IAAK,SAAU,CACb,IAAM,EAAS,GAAS,EAAI,OAAU,CACtC,OAAO,IAAW,MAAQ,OAAO,EAAO,MAAY,SAEtD,IAAK,QACH,OAAO,OAAO,EAAI,MAAY,UAAY,OAAO,EAAI,SAAe,SACtE,IAAK,OACH,MAAO,GACT,QACE,MAAO,IAIb,SAAS,GAAgB,EAA8D,CACrF,IAAM,EAAa,EAAoB,EAAM,QAAQ,KAAM,EAAM,QAAQ,WAAW,EAAI,GAClF,EAAY,EAAoB,EAAM,QAAQ,WAAY,EAAM,QAAQ,KAAK,EAAI,EACvF,GAAI,EAAM,QAAQ,SAChB,MAAO,CACL,KAAM,QACN,KAAM,gBACN,QAAS,GAAa,4BACvB,CAEH,IAAM,EAA+B,CACnC,KAAM,aACN,QAAS,EACT,MAAO,GACR,CAGK,EAAW,EAAM,QAAQ,iBAC3B,MAAM,QAAQ,EAAS,EAAI,EAAS,OAAS,IAC/C,EAAO,gBAAkB,EAAS,OAC/B,GACC,OAAO,GAAM,YAAY,GAAc,OAAO,EAAE,KAAW,UAAY,OAAO,EAAE,YAAkB,SACrG,EAGH,IAAM,EAAS,EAAM,QAAQ,oBACzB,GAAU,OAAO,GAAW,UAAY,CAAC,MAAM,QAAQ,EAAO,GAChE,EAAO,iBAAmB,GAG5B,IAAM,EAAW,EAAM,QAAQ,kBAK/B,OAJI,OAAO,GAAa,UAAY,IAClC,EAAO,iBAAmB,GAGrB,EAGT,SAAS,GAAsB,EAA8C,CAe3E,OAAO,IAdU,EAAM,QAAQ,SAAW,EAAE,EAAE,IAAK,GAAW,CAC5D,IAAM,EAAQ,EAAoB,EAAO,MAAM,EAAI,GAC7C,EAAgB,GAAuB,EAAO,eAAgB,EAAM,CACpE,EAA6B,EAC/B,CACE,QACA,OAAQ,EACT,CACD,KAIJ,OAHK,GACD,OAAO,EAAO,MAAS,WAAU,EAAO,KAAO,EAAO,MACtD,OAAO,EAAO,OAAU,WAAU,EAAO,MAAQ,EAAO,OACrD,GAHa,MAIpB,CACsC,OAAO,GAAc,CAAE,OAAO,CAGxE,SAAS,GAAiB,EAAyC,CACjE,IAAM,EAAO,GAAuB,EAAM,QAAQ,cAAgB,EAAE,CAAE,OAAO,CAC7E,EAAK,UAAY,QAEjB,IAAM,EAAO,EAAK,KAAK,SAAS,EAAK,KAAK,MAO1C,OANI,IACE,OAAO,EAAM,QAAQ,QAAW,WAAU,EAAK,MAAQ,CAAE,GAAG,EAAK,MAAO,OAAQ,EAAM,QAAQ,OAAQ,EACtG,OAAO,EAAM,QAAQ,aAAgB,YACvC,EAAK,MAAQ,CAAE,GAAG,EAAK,MAAO,UAAW,EAAM,QAAQ,YAAa,EAClE,OAAO,EAAM,QAAQ,OAAU,WAAU,EAAK,MAAQ,CAAE,GAAG,EAAK,MAAO,WAAY,EAAM,QAAQ,MAAO,GAEvG,EAGT,SAAS,GAAoB,EAA4C,CACvE,IAAM,EAAU,EAAM,QAAQ,eAC9B,GAAI,CAAC,EACH,OAAO,GAAiB,OAAO,CAEjC,IAAM,EAAa,GAAoB,EAAQ,CAK/C,MAAO,CACL,KAAM,UACN,OAAQ,OACR,KAAM,CACJ,KAAM,OACN,SAAU,CACR,KAAM,CACJ,KAAM,sBACN,MAAO,CAAE,QAZK,CACpB,GAAI,EACJ,GAAG,EACJ,CASwC,CAClC,CACF,CACF,CACD,UAAW,QACZ,CAGH,SAAS,GAA4B,EAAoD,CACvF,IAAM,EAAO,GAAuB,EAAM,QAAQ,iBAAmB,EAAE,CAAE,OAAO,CAE1E,EAAO,EAAK,KAAK,SAAS,EAAK,KAAK,MAE1C,OADI,IAAM,EAAK,MAAQ,CAAE,GAAG,EAAK,MAAO,eAAgB,GAAM,EACvD,CAAE,GAAG,EAAM,UAAW,QAAS,CAGxC,SAAS,GAAqB,EAA6C,CACzE,IAAM,EAAW,EAAM,QAAQ,0BAA4B,EAAE,CACvD,EAAgB,EAAM,QACtB,EAAY,CAChB,gBAAiB,EAAc,gBAC/B,mBAAoB,EAAc,mBAClC,uBAAwB,EAAc,uBACtC,uBAAwB,EAAc,uBACtC,cAAe,EAAc,cAC7B,oBAAqB,EAAc,oBACnC,qBAAsB,EAAc,qBACpC,eAAgB,EAAc,eAC9B,YAAa,EAAc,YAC3B,GAAI,EAAM,QAAQ,8BAAgC,EAAE,CACrD,CACK,EAAQ,EAAM,QAAQ,MACtB,EAAe,EAAM,QAAQ,cAG7B,EAAqD,EAAE,CAC7D,IAAK,IAAM,KAAK,EAAU,CACxB,IAAM,EAAO,GAAoB,EAAE,CACnC,EAAmB,KAAK,EAA2C,CAGrE,IAAM,EAAa,GAA0B,EAAO,EAAoB,EAAW,EAAa,CAG5F,EACA,GAAW,uBACb,EAAiB,EAAU,uBAClB,GAAW,gBAAkB,EAAU,eAAe,OAAS,IAExE,EAAiB,EAAU,eAAe,IAAI,gBAAgB,KAAO,EAAU,eAAe,IAAI,KAIpG,IAAM,EAAc,EACf,EAAmB,KAAM,GAAM,EAAE,MAAW,EAAe,EAAI,EAAmB,GACnF,EAAmB,GAGjB,EAAuB,EAAE,CAC/B,GAAI,MAAM,QAAQ,GAAW,gBAAgB,KACtC,IAAM,KAAQ,EAAU,gBACvB,OAAO,GAAS,UAAU,EAAW,KAAK,EAAK,CAKvD,IAAM,EAAe,GAAoB,GAAW,uBAAuB,CAGrE,EAAkB,GAAW,mBAG7B,EAAa,GAAW,YAGxB,EAA0D,EAAE,CAClE,IAAK,IAAM,KAAK,EAAoB,CAClC,IAAM,EAAM,EAAE,IACV,IACF,EAAe,GAAO,CACpB,MAAQ,EAAE,MAAsB,EAChC,KAAM,sBACN,QAAS,CAAE,MAAK,CACjB,EAIL,IAAM,EAAiC,CACrC,SAAU,EACV,aACA,aACA,iBACD,CAiBD,OAfI,IAAa,EAAM,YAAiB,GACpC,EAAa,OAAS,IAAG,EAAM,aAAkB,GACjD,IAAiB,EAAM,gBAAqB,GAC5C,IAAY,EAAM,WAAgB,GAGlC,OAAO,GAAW,iBAAoB,WACxC,EAAM,mBAAwB,EAAU,iBAItC,GAAW,yBACb,EAAM,sBAA2B,EAAU,wBAGtC,CACL,KAAM,UACN,OAAQ,OACR,KAAM,CACJ,KAAM,OACN,SAAU,CACR,KAAM,CACJ,KAAM,kBACN,QACD,CACF,CACF,CACD,UAAW,QACZ,CAGH,SAAS,GACP,EACA,EACA,EACA,EAC4C,CAC5C,GAAI,CAAC,EAAO,MAAO,EAAE,CAErB,IAAM,EAAU,OAAO,QAAQ,EAAM,CACrC,GAAI,EAAQ,SAAW,EAAG,MAAO,EAAE,CAEnC,IAAM,EAAa,EAAQ,KAAK,GAChC,GAAI,MAAM,QAAQ,EAAW,CAAE,CAC7B,IAAM,EAAe,EAAU,eAAiB,EAAU,qBAAuB,EAAE,CAC7E,EAAa,EAAU,sBAAwB,OAAO,KAAK,EAAM,CACjE,EAAyD,EAAE,CACjE,IAAK,IAAM,KAAa,EAAY,CAClC,IAAM,EAAS,EAAM,GACrB,GAAI,CAAC,GAAU,CAAC,MAAM,QAAQ,EAAO,CAAE,SACvC,IAAM,EAAQ,EAAa,IAAc,EACzC,EAAW,KAAK,CAAE,QAAO,OAAQ,EAAO,IAAK,GAAO,OAAO,GAAM,SAAW,EAAI,OAAO,GAAK,GAAG,CAAE,CAAE,CAAC,CAEtG,OAAO,EAGT,IAAM,EAAS,EACT,EAAc,EAAS,IAAK,GAAY,OAAO,EAAQ,KAAU,GAAG,CAAC,CAAC,OAAQ,GAAQ,EAAI,OAAS,EAAE,CACrG,EAAe,EAAU,eAAiB,EAAU,qBAAuB,EAAE,CAQ7E,GANJ,GAAgB,EAAa,OAAS,EAClC,EACA,EAAU,sBAAwB,EAAU,qBAAqB,OAAS,EACxE,EAAU,qBACV,GAAwB,EAAO,EAEN,OAC9B,GAAU,IAAU,QAAU,IAAU,cAAgB,CAAC,EAAM,SAAS,SAAS,CACnF,CACK,EAAyD,EAAE,CAEjE,IAAK,IAAM,KAAa,EAAY,CAClC,IAAM,EAAS,EAAY,IAAK,GAAQ,CACtC,IAAM,EAAM,EAAO,GACnB,GAAI,CAAC,GAAO,OAAO,GAAQ,SAAU,MAAO,GAC5C,IAAM,EAAa,EAAI,GAAG,EAAU,SAC9B,EAAY,EAAI,GACtB,OAAO,GAAyB,GAAc,EAAU,EACxD,CACF,GAAI,EAAO,MAAO,GAAU,EAAM,SAAW,EAAE,CAAE,SACjD,IAAM,EAAQ,EAAa,IAAc,EACzC,EAAW,KAAK,CAAE,QAAO,SAAQ,CAAC,CAGpC,OAAO,EAGT,SAAS,GAAwB,EAA2D,CAC1F,IAAM,EAAmB,EAAE,CACrB,EAAO,IAAI,IACjB,IAAK,IAAM,KAAO,OAAO,OAAO,EAAO,CACjC,MAAC,GAAO,OAAO,GAAQ,UAC3B,IAAK,IAAM,KAAO,OAAO,KAAK,EAAI,CAC5B,EAAK,IAAI,EAAI,GACjB,EAAK,IAAI,EAAI,CACb,EAAO,KAAK,EAAI,EAGpB,OAAO,EAGT,SAAS,GAAyB,EAAwB,CAGxD,OAFI,OAAO,GAAU,SAAiB,EAClC,OAAO,GAAU,UAAY,OAAO,GAAU,UAAkB,OAAO,EAAM,CAC1E,GAGT,SAAS,GAAa,EAAuC,CAC3D,MAAO,CACL,KAAM,WACN,UAAW,GACX,MAAO,GACP,KAAM,CACJ,MAAO,EAAM,QAAQ,MACrB,SAAU,EAAM,QAAQ,SACxB,WAAY,EAAM,QAAQ,WAC3B,CACF,CAGH,SAAS,IAAsC,CAC7C,MAAO,CAAE,KAAM,OAAQ,CAGzB,SAAS,GAAa,EAAuC,CAC3D,MAAO,CACL,KAAM,WACN,UAAW,GACX,MAAO,GACP,KAAM,CACJ,QAAS,GACT,YAAa,EAAM,QAAQ,KAC3B,iBAAkB,EAAM,QAAQ,kBAChC,eAAgB,EAAM,QAAQ,aAAe,GAC9C,CACF,CAGH,SAAS,GAAkB,EAA4C,CACrE,MAAO,CACL,KAAM,WACN,UAAW,GACX,MAAO,GACP,KAAM,CACJ,QAAS,GACT,aAAc,GACd,iBAAkB,EAAM,SAAS,aACjC,YAAa,EAAM,SAAS,KAC7B,CACF,CAGH,SAAS,GAAoB,EAA8C,CACzE,MAAO,CACL,KAAM,WACN,UAAW,GACX,MAAO,GACP,KAAM,CACJ,QAAS,GACT,oBAAqB,GACrB,iBAAkB,EAAM,SAAS,aACjC,YAAa,EAAM,SAAS,KAC7B,CACF,CAGH,SAAS,GAAc,EAA4D,CACjF,IAAM,EAAM,EAAoB,EAAM,QAAQ,IAAI,CAYlD,OAXI,EACK,CACL,KAAM,SACN,OAAQ,CACN,KAAM,WACN,MACA,OAAQ,EAAM,QAAQ,UAAY,GACnC,CACF,CAGI,CACL,KAAM,WACN,UAAW,GACX,MAAO,GACP,KAAM,CACJ,SAAU,EAAM,QAChB,eAAgB,EAAoB,EAAM,QAAQ,GAAG,CACtD,CACF,CAGH,SAAS,GAAa,EAAkC,CAItD,MAAO,CACL,KAAM,QACN,KAAM,gBACN,QALA,EAAoB,EAAM,SAAS,KAAM,EAAM,SAAS,QAAS,EAAM,SAAS,MAAM,EACtF,4BAKD,CAGH,SAAS,GAAU,EAAqC,CACtD,MAAO,CACL,KAAM,WACN,UAAW,GACX,MAAO,GACP,KAAM,CACJ,KAAM,GACP,CACF,CAGH,SAAS,GAAoB,EAAsC,CACjE,IAAM,EAAU,EAAM,SAAW,EAAE,CAGnC,GADsB,MAAM,QAAQ,EAAQ,YAAe,CACxC,CACjB,IAAM,EAAiD,CACrD,YAAa,EAAQ,YACtB,CACK,EAAe,OAAO,EAAQ,MAAY,SAAW,EAAQ,KAAU,IAAA,GACvE,EAAQ,OAAO,EAAQ,OAAa,SAAW,EAAQ,MAAW,IAAA,GAIxE,OAHI,IAAiB,IAAA,KAAW,EAAgB,KAAO,GACnD,IAAU,IAAA,KAAW,EAAgB,MAAQ,GAE1C,GAAsB,CAC3B,KAAM,YACN,QAAS,EACV,CAAC,CAGJ,IAAM,EAAS,GAAS,EAAQ,OAAU,CAC1C,GAAI,EAAQ,CACV,IAAM,EAAmD,CACvD,SACD,CACK,EAAO,OAAO,EAAQ,MAAY,SAAW,EAAQ,KAAU,IAAA,GAC/D,EAAW,OAAO,EAAQ,WAAiB,SAAW,EAAQ,UAAe,IAAA,GAC7E,EAAQ,OAAO,EAAQ,OAAa,SAAW,EAAQ,MAAW,IAAA,GAKxE,OAJI,IAAS,IAAA,KAAW,EAAiB,KAAO,GAC5C,IAAa,IAAA,KAAW,EAAiB,UAAY,GACrD,IAAU,IAAA,KAAW,EAAiB,MAAQ,GAE3C,GAAuB,CAC5B,KAAM,aACN,QAAS,EACV,CAAC,CAGJ,IAAM,EAAO,OAAO,EAAQ,MAAY,SAAW,EAAQ,KAAU,GACrE,GAAI,EAAM,CACR,IAAM,EAAiD,CAAE,OAAM,CACzD,EAAe,OAAO,EAAQ,MAAY,SAAW,EAAQ,KAAU,IAAA,GACvE,EAAQ,OAAO,EAAQ,OAAa,SAAW,EAAQ,MAAW,IAAA,GAClE,EAAa,GAAS,EAAQ,QAAW,EAAI,IAAA,GAKnD,OAJI,IAAiB,IAAA,KAAW,EAAoB,KAAO,GACvD,IAAe,IAAA,KAAW,EAAoB,QAAU,GACxD,IAAU,IAAA,KAAW,EAAoB,MAAQ,GAE9C,GAAkB,CACvB,KAAM,OACN,QAAS,EACV,CAAC,CAGJ,MAAO,CACL,KAAM,WACN,UAAW,GACX,MAAO,GACP,KAAM,CACJ,eAAgB,EACjB,CACF,CAGH,SAAS,GAAkB,EAA0C,CACnE,IAAM,EAAiC,CACrC,KAAM,EAAoB,EAAM,QAAQ,KAAK,EAAI,GAClD,CAID,OAHI,OAAO,EAAM,QAAQ,OAAU,WAAU,EAAM,MAAW,EAAM,QAAQ,OACxE,EAAM,QAAQ,UAAY,IAAA,KAAW,EAAM,QAAa,EAAM,QAAQ,SAEnE,CACL,KAAM,UACN,OAAQ,MACR,KAAM,CACJ,KAAM,OACN,SAAU,CACR,KAAM,CACJ,KAAM,kBACN,QACD,CACF,CACF,CACF,CAGH,SAAS,GAAiB,EAAyC,CACjE,OAAO,GAAyB,EAAM,QAAS,MAAM,CAGvD,SAAS,GAAuB,EAA+C,CAC7E,IAAM,EAAQ,EAAoB,EAAM,QAAQ,KAAK,EAAI,GACnD,EAAS,GAAuB,EAAM,QAAQ,OAAQ,EAAM,CAClE,GAAI,EAAQ,CACV,IAAM,EAAiC,CACrC,MAAO,GAAS,EAAO,MACvB,SACD,CAID,OAHI,OAAO,EAAM,QAAQ,WAAc,WAAU,EAAM,MAAW,EAAM,QAAQ,WAC5E,OAAO,EAAM,QAAQ,OAAU,WAAU,EAAM,MAAW,EAAM,QAAQ,OAErE,CACL,KAAM,UACN,OAAQ,MACR,KAAM,CACJ,KAAM,OACN,SAAU,CACR,KAAM,CACJ,KAAM,eACN,QACD,CACF,CACF,CACF,CAGH,IAAM,EAA6C,CACjD,KAAM,EACP,CAGD,OAFI,OAAO,EAAM,QAAQ,OAAU,WAAU,EAAgB,MAAQ,EAAM,QAAQ,OAE5E,GAAkB,CACvB,KAAM,OACN,QAAS,EACV,CAAC,CAGJ,SAAS,GAAsB,EAA8C,CAgB3E,OAAO,IAfU,EAAM,QAAQ,aAAe,EAAE,EAAE,IAAK,GAAW,CAChE,IAAM,EAAQ,EAAoB,EAAO,MAAM,EAAI,GAC7C,EAAgB,GAAuB,EAAO,eAAgB,EAAM,CACpE,EAA6B,EAC/B,CACE,QACA,OAAQ,EACT,CACD,KAIJ,OAHK,GACD,OAAO,EAAO,MAAS,WAAU,EAAO,KAAO,EAAO,MACtD,OAAO,EAAO,OAAU,WAAU,EAAO,MAAQ,EAAO,OACrD,GAHa,MAIpB,CAEsC,OAAO,GAAc,CAAE,MAAM,CAGvE,SAAS,GAAsB,EAA8C,CAY3E,IAAM,EAAiC,CAAE,SAXxB,EAAM,QAAQ,SAAW,EAAE,EAAE,IAAK,GAAS,CAC1D,IAAM,EAAkC,EAAE,CAO1C,OANI,OAAO,EAAK,cAAiB,WAAU,EAAO,aAAkB,EAAK,cACrE,OAAO,EAAK,aAAgB,WAAU,EAAO,YAAiB,EAAK,cACnE,OAAO,EAAK,eAAkB,UAAY,OAAO,EAAK,eAAkB,YAC1E,EAAO,cAAmB,EAAK,eAE7B,OAAO,EAAK,YAAe,WAAU,EAAO,WAAgB,EAAK,YAC9D,GACP,CAEgD,CAGlD,OAFI,OAAO,EAAM,QAAQ,KAAQ,WAAU,EAAM,IAAS,EAAM,QAAQ,KAEjE,CACL,KAAM,UACN,OAAQ,OACR,KAAM,CACJ,KAAM,OACN,SAAU,CACR,KAAM,CACJ,KAAM,mBACN,QACD,CACF,CACF,CACF,CAGH,SAAS,GAAiB,EAAyC,CACjE,IAAM,EAAiC,EAAE,CAKzC,OAJI,MAAM,QAAQ,EAAM,QAAQ,KAAK,GAAE,EAAM,KAAU,EAAM,QAAQ,MACjE,MAAM,QAAQ,EAAM,QAAQ,KAAK,GAAE,EAAM,KAAU,EAAM,QAAQ,MACjE,OAAO,EAAM,QAAQ,cAAiB,WAAU,EAAM,YAAiB,EAAM,QAAQ,cAElF,CACL,KAAM,UACN,OAAQ,OACR,KAAM,CACJ,KAAM,OACN,SAAU,CACR,KAAM,CACJ,KAAM,cACN,QACD,CACF,CACF,CACF,CAGH,SAAS,GAAyB,EAAmD,CACnF,MAAO,CACL,KAAM,WACN,UAAW,GACX,MAAO,GACP,KAAM,CACJ,oBAAqB,EAAM,QAC5B,CACF,CAGH,SAAS,GAA0B,EAAwE,CACzG,IAAM,EAAc,EAAM,QAAQ,qBAAuB,EAAE,CACrD,EAAmC,EAAE,CAE3C,IAAK,IAAI,EAAI,EAAG,EAAI,EAAY,OAAQ,IAAK,CAC3C,IAAM,EAAa,EAAY,GAC/B,GAAI,CAAC,EAAY,SAEjB,IAAM,EAAU,GAA8B,EAAW,CACzD,GAAI,CAAC,EAAS,SAEd,IAAM,EAAgC,CAAE,UAAS,CAE3C,EAAS,GAAuB,EAAW,eAAgB,EAAQ,KAAK,CAC1E,IAAQ,EAAK,OAAY,GACzB,OAAO,EAAW,MAAS,WAAU,EAAK,KAAU,EAAW,MAC/D,OAAO,EAAW,QAAW,WAAU,EAAK,OAAY,EAAW,QACnE,OAAO,EAAW,kBAAqB,WAAU,EAAK,gBAAqB,EAAW,kBACtF,MAAM,QAAQ,EAAW,OAAO,GAAE,EAAK,OAAY,EAAW,QAC9D,OAAO,EAAW,sBAAyB,WAC7C,EAAK,mBAAwB,EAAW,sBAE1C,EAAM,KAAK,EAAK,CAclB,OAXI,EAAM,SAAW,EACZ,CACL,KAAM,WACN,UAAW,GACX,MAAO,GACP,KAAM,CACJ,qBAAsB,EAAM,QAAQ,qBAAuB,EAAE,CAC9D,CACF,CAGI,CACL,KAAM,UACN,OAAQ,OACR,KAAM,CACJ,KAAM,OACN,SAAU,CACR,KAAM,CACJ,KAAM,aACN,MAAO,CAAE,YAAa,EAAO,CAC9B,CACF,CACF,CACF,CAGH,SAAS,GAAwB,EAAsE,CACrG,IAAM,EAAmB,EAAM,QAAQ,mBAAqB,EAAE,CACxD,EAA0C,EAAE,CAElD,IAAK,IAAI,EAAI,EAAG,EAAI,EAAiB,OAAQ,IAAK,CAChD,IAAM,EAAW,EAAiB,GAClC,GAAI,CAAC,EAAU,SACf,IAAM,EAAQ,EAAoB,EAAS,KAAK,EAAI,GAC9C,EACJ,EAAS,KAAO,EAAS,IAAI,OAAS,EAAI,CAAE,KAAM,cAAe,QAAS,CAAE,IAAK,EAAS,IAAK,CAAE,CAAG,IAAA,GAChG,EAAS,GAAuB,EAAS,gBAAkB,EAAiB,EAAM,CACxF,GAAI,CAAC,EAAQ,SAEb,IAAM,EAAiC,CAAE,KAAM,EAAO,SAAQ,CAC9D,GAAI,MAAM,QAAQ,EAAS,OAAO,CAAE,CAClC,IAAM,EAAiB,EAAS,OAAO,OAAQ,GAAM,OAAO,GAAM,SAAS,CACvE,EAAe,OAAS,IAAG,EAAM,OAAY,GAE/C,OAAO,EAAS,OAAU,WAAU,EAAM,MAAW,EAAS,OAClE,EAAQ,KAAK,EAAM,CAcrB,OAXI,EAAQ,SAAW,EACd,CACL,KAAM,WACN,UAAW,GACX,MAAO,GACP,KAAM,CACJ,mBAAoB,EAAM,QAAQ,mBAAqB,EAAE,CAC1D,CACF,CAGI,CACL,KAAM,UACN,OAAQ,OACR,KAAM,CACJ,KAAM,OACN,SAAU,CACR,KAAM,CACJ,KAAM,kBACN,MAAO,CAAE,UAAS,CACnB,CACF,CACF,CACF,CAGH,SAAS,GAAyB,EAAuE,CACvG,IAAM,EAAW,EAAM,QAAQ,oBAAsB,EAAE,CACjD,EAA0C,EAAE,CAElD,IAAK,IAAI,EAAI,EAAG,EAAI,EAAS,OAAQ,IAAK,CACxC,IAAM,EAAS,EAAS,GACxB,GAAI,CAAC,EAAQ,SAEb,IAAM,EACJ,EAAoB,EAAO,WAAY,EAAO,iBAAkB,EAAO,sBAAsB,EAC7F,UAAU,EAAI,IACV,EAA2C,EAAE,CAC7C,EAAO,EAAoB,EAAO,sBAAsB,CAC1D,IAAM,EAAgB,KAAU,GAEpC,IAAM,EAAiB,GADM,GAAS,EAAO,eAAe,EACL,QAAW,CAC5D,EAAmB,GAAiB,WACtC,EAAO,YAAc,MAAM,QAAQ,EAAO,WAAW,CACvD,EAAgB,WAAgB,EAAO,WAC9B,MAAM,QAAQ,EAAiB,GACxC,EAAgB,WAAgB,EAAiB,OAAQ,GAAuB,OAAO,GAAQ,SAAS,EAE1G,IAAM,EAAM,EAAoB,EAAO,IAAK,EAAO,2BAA4B,GAAiB,IAAO,CACnG,IAAK,EAAgB,IAAS,GAClC,EAAgB,kBAAuB,EAEvC,IAAM,EAAoC,CAAE,KAAM,YAAa,QAAS,EAAiB,CACnF,EAAkB,GAAuB,EAAO,eAAgB,EAAU,CAC1E,EACJ,GAAiB,OAAS,eAAiB,OAAO,EAAgB,MAAY,SAC1E,GAAuB,EAAiB,EAAU,CACjD,GAAmB,GAAuB,EAAiB,EAAU,CAC5E,GAAI,CAAC,EAAQ,SAEb,IAAM,EAAiC,CAAE,YAAW,SAAQ,CACtD,EAAkB,EAAoB,EAAO,sBAAsB,CACrE,GAAmB,IAAoB,IAAW,EAAM,gBAAqB,GACjF,IAAM,EAAc,GAA+B,EAAO,CACtD,GAEE,IAAgB,GAAa,KADtB,GAAmB,MAE5B,EAAM,aAAkB,GAGxB,OAAO,EAAO,OAAU,WAAU,EAAM,MAAW,EAAO,OAC9D,EAAQ,KAAK,EAAM,CAcrB,OAXI,EAAQ,SAAW,EACd,CACL,KAAM,WACN,UAAW,GACX,MAAO,GACP,KAAM,CACJ,oBAAqB,EAAM,QAAQ,oBAAsB,EAAE,CAC5D,CACF,CAGI,CACL,KAAM,UACN,OAAQ,OACR,KAAM,CACJ,KAAM,OACN,SAAU,CACR,KAAM,CACJ,KAAM,yBACN,MAAO,CAAE,UAAS,CACnB,CACF,CACF,CACF,CAGH,SAAS,GAAwB,EAAsE,CACrG,IAAM,EAAI,EAAM,QAEV,EAAS,GADQ,EAAE,gBAAkB,EAAE,gBAG3C,EAAoB,EAAE,aAAc,EAAE,YAAgB,EAAE,KAAM,EAAE,MAAM,EAAI,uBAC3E,CACD,GAAI,CAAC,EACH,MAAO,CACL,KAAM,WACN,UAAW,GACX,MAAO,GACP,KAAM,CACJ,gBAAiB,EAClB,CACF,CAGH,IAAM,EAAiC,CAAE,SAAQ,CAMjD,OALI,EAAE,QAAO,EAAM,MAAW,EAAE,OAC5B,EAAE,OAAM,EAAM,KAAU,EAAE,MAC1B,EAAE,aAAc,EAAM,YAAiB,EAAE,aACpC,OAAO,EAAE,aAAmB,UAAY,EAAE,YAAe,MAAM,GAAE,EAAM,YAAiB,EAAE,aAE5F,CACL,KAAM,UACN,OAAQ,OACR,KAAM,CACJ,KAAM,OACN,SAAU,CACR,KAAM,CACJ,KAAM,sBACN,QACD,CACF,CACF,CACF,CAGH,SAAS,IAA+C,CACtD,MAAO,CACL,KAAM,WACN,UAAW,GACX,MAAO,GACP,KAAM,CACJ,iBAAkB,GACnB,CACF,CAGH,SAAS,GAAW,EAAqC,CACvD,MAAO,CACL,KAAM,WACN,UAAW,GACX,MAAO,GACP,KAAM,CACJ,MAAO,EAAM,QACd,CACF,CAGH,SAAS,GAAe,EAAuC,CAC7D,IAAM,EAAY,EAAM,QAAQ,YAAc,EAAE,CAC1C,EAAsE,EAAE,CAE9E,IAAK,IAAM,KAAS,EAAW,CAC7B,IAAM,EAAY,EAAM,YAAc,GAChC,GAAY,EAAM,cAAgB,EAAE,EAAE,IAAI,GAAoB,CACpE,EAAO,KAAK,CAAE,YAAW,WAAU,CAAC,CAGtC,IAAM,EAAoG,EAAE,CAC5G,IAAK,IAAM,KAAO,EAAM,QAAQ,aAAe,EAAE,CAAE,CACjD,IAAM,EAAQ,EAAI,OAAS,GAC3B,GAAI,CAAC,EAAO,SACZ,IAAM,EAAS,GAAuB,EAAI,eAAgB,EAAM,CAC1D,EAAqC,CAAE,QAAO,CAChD,IAAQ,EAAM,OAAS,GAC3B,EAAW,KAAK,EAAM,CAGxB,MAAO,CACL,KAAM,UACN,OAAQ,OACR,KAAM,CACJ,KAAM,OACN,SAAU,CACR,KAAM,CACJ,KAAM,sBACN,MAAO,CAAE,SAAQ,aAAY,CAC9B,CACF,CACF,CACD,UAAW,QACZ,CAGH,SAAS,GAAe,EAAyC,CAC/D,MAAO,CACL,KAAM,WACN,UAAW,GACX,MAAO,GACP,KAAM,CACJ,SAAU,EAAM,KAChB,YAAa,EAAM,SAAW,EAAE,CACjC,CACF,CAGH,SAAS,GAAa,EAAqC,CACzD,IAAM,EAAiC,EAAE,CASzC,OARI,OAAO,EAAM,SAAS,SAAY,WAAU,EAAM,QAAa,EAAM,QAAQ,SAC7E,MAAM,QAAQ,EAAM,SAAS,mBAAmB,GAClD,EAAM,mBAAwB,EAAM,QAAQ,oBAE1C,OAAO,EAAM,SAAS,gBAAmB,WAC3C,EAAM,eAAoB,EAAM,QAAQ,gBAGnC,CACL,KAAM,UACN,OAAQ,OACR,KAAM,CACJ,KAAM,OACN,SAAU,CACR,KAAM,CACJ,KAAM,gBACN,QACD,CACF,CACF,CACF,CAGH,SAAS,GAAqB,EAA+C,CAC3E,MAAO,CACL,KAAM,WACN,UAAW,GACX,MAAO,GACP,KAAM,CACJ,gBAAiB,EAAM,SAAW,EAAE,CACrC,CACF,CAWH,SAAS,GAAyB,EAAwB,EAAuC,CAC/F,IAAM,EAAsC,EAAE,CACxC,EAAqB,EAAE,CAE7B,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,OAAQ,IAAK,CACvC,IAAM,EAAQ,EAAQ,GACtB,GAAI,CAAC,EAAO,SACZ,IAAM,EAAK,UAAU,IACrB,EAAS,KAAK,EAAG,CACjB,IAAM,EAAiC,CACrC,MAAO,EAAM,MACb,OAAQ,EAAM,OACf,CACG,EAAM,OAAS,IAAA,KAAW,EAAM,KAAU,EAAM,MAChD,EAAM,QAAU,IAAA,KAAW,EAAM,MAAW,EAAM,OAClD,EAAM,cAAgB,IAAA,KAAW,EAAM,YAAiB,EAAM,aAElE,EAAS,GAAM,CACb,KAAM,eACN,QACD,CAoBH,MAjBA,GAAS,KAAU,CACjB,KAAM,gBACN,MAAO,CACL,QAAS,EAAQ,IAAK,GAAU,CAC9B,IAAM,EAA+B,CACnC,MAAO,EAAM,MACb,OAAQ,EAAM,OACf,CAID,OAHI,EAAM,QAAU,IAAA,KAAW,EAAI,MAAW,EAAM,OAChD,EAAM,cAAgB,IAAA,KAAW,EAAI,YAAiB,EAAM,aAC5D,EAAM,OAAS,IAAA,KAAW,EAAI,KAAU,EAAM,MAC3C,GACP,CACH,CACD,SAAU,EACX,CAEM,CACL,KAAM,UACN,SACA,KAAM,CAAE,KAAM,OAAQ,WAAU,CACjC,CAGH,SAAS,GAAuB,EAAuB,EAAuC,CAC5F,IAAM,EAAsC,EAAE,CACxC,EAAqB,EAAE,CAE7B,IAAK,IAAI,EAAI,EAAG,EAAI,EAAS,OAAQ,IAAK,CACxC,IAAM,EAAU,EAAS,GACzB,GAAI,CAAC,EAAS,SACd,IAAM,EAAa,GAAoB,EAAQ,CACzC,EAAK,WAAW,IACtB,EAAS,KAAK,EAAG,CACjB,IAAM,EAAiC,CAAE,QAAS,EAAY,MAAO,EAAG,CACpE,EAAW,MACb,EAAM,OAAY,CAChB,MAAO,EAAW,KAClB,KAAM,sBACN,QAAS,CAAE,IAAK,EAAW,IAAK,CACjC,EAEH,EAAS,GAAM,CACb,KAAM,cACN,QACD,CASH,MANA,GAAS,KAAU,CACjB,KAAM,cACN,MAAO,CAAE,OAAQ,OAAQ,CACzB,SAAU,EACX,CAEM,CACL,KAAM,UACN,SACA,KAAM,CAAE,KAAM,OAAQ,WAAU,CACjC,CAGH,SAAS,GAAyB,EAAoB,EAAuC,CAC3F,MAAO,CACL,KAAM,UACN,SACA,KAAM,CACJ,KAAM,OACN,SAAU,CACR,KAAM,CACJ,KAAM,cACN,MAAO,CAAE,QAAS,GAAoB,EAAQ,CAAE,MAAO,EAAG,CAC3D,CACF,CACF,CACF,CAGH,SAAS,GAAiB,EAAuC,CAC/D,MAAO,CACL,KAAM,UACN,SACA,KAAM,CACJ,KAAM,OACN,SAAU,CACR,KAAM,CACJ,KAAM,gBACN,MAAO,CAAE,QAAS,EAAE,CAAE,CACtB,SAAU,EAAE,CACb,CACF,CACF,CACF,CAGH,SAAS,GAA8B,EAA6D,CAClG,IAAM,EAAc,EAAoB,EAAW,IAAI,CACjD,EAAe,EAAoB,EAAW,WAAW,CAGzD,EAAkC,CAAE,GAF5B,GAAS,EAAW,aAAa,EACxB,EAC4B,CAC7C,EAAqB,EAAoB,EAAW,gBAAiB,EAAW,eAAkB,CAOxG,OALE,GACA,CAAC,EAAoB,EAAO,gBAA+B,EAAO,eAA6B,GAE/F,EAAO,gBAAqB,GAEvB,GAA0B,EAAQ,EAAa,EAAa,CAGrE,SAAS,GACP,EACA,EACA,EAC0B,CAC1B,IAAM,EAAM,EAAoB,EAAI,IAAQ,EAAY,CAClD,EAAO,EAAoB,EAAI,KAAS,EAAa,CAC3D,GAAI,CAAC,GAAO,CAAC,EAAM,OAAO,KAE1B,IAAM,EAAqB,CACzB,MACA,OACD,CAEK,EAAQ,EAAoB,EAAI,MAAS,CAC3C,IAAO,EAAQ,MAAQ,GAE3B,IAAM,EAAM,EAAoB,EAAI,IAAO,CACvC,IAAK,EAAQ,IAAM,GAEvB,IAAM,EAAS,GAAY,EAAI,OAAU,CACzC,GAAI,EAAO,OAAS,EAClB,EAAQ,OAAS,MACZ,CACL,IAAM,EAAQ,EAAoB,EAAI,MAAU,EAAI,UAAc,EAAI,SAAY,CAC9E,IAAO,EAAQ,OAAS,CAAC,EAAM,EAGrC,IAAM,EAAkB,GAAS,EAAI,iBAAoB,CACrD,IAAoB,IAAA,KAAW,EAAQ,iBAAmB,GAC9D,IAAM,EAAQ,GAAS,EAAI,MAAS,CAChC,IAAU,IAAA,KAAW,EAAQ,MAAQ,GACzC,IAAM,EAAS,GAAS,EAAI,OAAU,CAClC,IAAW,IAAA,KAAW,EAAQ,OAAS,GAC3C,IAAM,EAAc,GAAS,EAAI,aAAgB,EAAI,GAAS,EAAI,YAAe,CAC7E,IAAgB,IAAA,KAAW,EAAQ,aAAe,GACtD,IAAM,EAAW,EAAoB,EAAI,UAAc,EAAI,SAAY,CACnE,IAAU,EAAQ,UAAY,GAC9B,OAAO,EAAI,UAAgB,YAAW,EAAQ,SAAW,EAAI,UAC7D,OAAO,EAAI,SAAe,YAAW,EAAQ,SAAW,EAAI,SAEhE,IAAM,EAAiB,EAAoB,EAAI,gBAAoB,EAAI,eAAkB,CAGzF,OAFI,IAAgB,EAAQ,gBAAkB,GAEvC,GAAoB,EAAQ,CAGrC,SAAS,GACP,EACA,EAC2D,CAC3D,IAAM,EAAU,GAAS,EAAe,CACxC,GAAI,CAAC,EAAS,OAAO,KAErB,IAAM,EAAO,EAAQ,KACrB,GAAI,OAAO,GAAS,UAAY,EAAK,SAAW,EAAG,OAAO,KAE1D,IAAM,EAA6D,CACjE,MAAO,GAAS,EAChB,OACD,CAED,OADI,EAAQ,UAAe,IAAA,KAAW,EAAO,QAAU,EAAQ,SACxD,EAGT,SAAS,GAAS,EAAgD,CAEhE,MADI,CAAC,GAAS,OAAO,GAAU,UAAY,MAAM,QAAQ,EAAM,CAAS,KACjE,EAGT,SAAS,EAAoB,GAAG,EAAuC,CACrE,IAAK,IAAM,KAAS,EAClB,GAAI,OAAO,GAAU,SAAU,CAC7B,IAAM,EAAU,EAAM,MAAM,CAC5B,GAAI,EAAQ,OAAS,EAAG,OAAO,GAMrC,SAAS,GAAY,EAA0B,CAE7C,OADK,MAAM,QAAQ,EAAM,CAClB,EAAM,OAAQ,GAAyB,OAAO,GAAS,UAAY,EAAK,OAAS,EAAE,CADxD,EAAE,CAItC,SAAS,GAAoB,EAA0B,CACrD,GAAI,OAAO,GAAU,SAAU,CAC7B,IAAM,EAAU,EAAM,MAAM,CAC5B,OAAO,EAAU,CAAC,EAAQ,CAAG,EAAE,CAGjC,OADK,MAAM,QAAQ,EAAM,CAClB,EACJ,OAAQ,GAAyB,OAAO,GAAS,SAAS,CAC1D,IAAK,GAAS,EAAK,MAAM,CAAC,CAC1B,OAAQ,GAAS,EAAK,OAAS,EAAE,CAJF,EAAE,CAOtC,SAAS,GAAS,EAAoC,CACpD,GAAI,OAAO,GAAU,UAAY,OAAO,SAAS,EAAM,CAAE,OAAO,EAChE,GAAI,OAAO,GAAU,SAAU,CAG7B,IAAI,EACJ,AAGE,EAHE,EAAM,SAAS,IAAI,EAAI,EAAM,SAAS,IAAI,CAC/B,EAAM,QAAQ,MAAO,GAAG,CAAC,QAAQ,IAAK,IAAI,CAE1C,EAAM,QAAQ,IAAK,IAAI,CAEtC,IAAM,EAAS,OAAO,EAAW,CACjC,GAAI,OAAO,SAAS,EAAO,CAAE,OAAO,GAKxC,SAAS,GAAiB,EAAyC,CACjE,OAAO,GAAU,KA+BnB,IAAM,GAA6C,IAAI,IAAI,CACzD,MACA,OACA,QACA,SACA,QACA,mBACA,sBACA,iBACA,kBACA,MACA,SACA,eACA,YACA,WACA,cACA,mBACA,WACA,iBACA,aACA,aACA,eACA,iBACA,WACA,aACA,aACD,CAAC,CAEF,SAAgB,GAAoB,EAAiC,CACnE,IAAM,EAAc,EAAE,kBAAoB,MAAQ,EAAE,iBAAmB,EACjE,EAAQ,EAAc,EAAE,iBAAmB,EAAE,MAC7C,EAAgB,GAAe,EAAE,OAAS,KAAO,EAAE,MAAQ,IAAA,GAE7D,EACA,GAAiB,MAAQ,GAAS,MAAQ,EAAgB,EAC5D,EAAkB,KAAK,OAAQ,EAAgB,GAAS,EAAiB,IAAI,CACpE,EAAE,qBAAuB,MAAQ,EAAE,oBAAsB,IAClE,EAAkB,EAAE,qBAGtB,IAAM,EAAQ,EAAoB,EAAE,MAAM,CACpC,EAAO,EAAoB,EAAE,KAAK,EAAI,EAAE,IACxC,EAAiB,GAAS,CAAC,EAAK,aAAa,CAAC,WAAW,EAAM,aAAa,CAAC,CAAG,GAAG,EAAM,GAAG,IAAS,EAErG,EAA4B,CAChC,IAAK,EAAE,IACP,KAAM,EACN,IAAK,EAAoB,EAAE,IAAI,EAAI,GACpC,CAEK,EAAQ,EAAE,SAAS,GACrB,IAAO,EAAO,SAAW,GACzB,EAAE,QAAU,EAAE,OAAO,OAAS,IAAG,EAAO,OAAS,EAAE,QACnD,GAAS,OAAM,EAAO,MAAQ,OAAO,EAAM,EAC3C,GAAiB,OAAM,EAAO,cAAgB,OAAO,EAAc,EACnE,IAAoB,IAAA,KAAW,EAAO,gBAAkB,GACxD,IAAU,IAAA,KAAW,EAAO,MAAQ,GACpC,EAAE,SAAW,IAAA,KAAW,EAAO,OAAS,EAAE,QAC1C,EAAE,eAAiB,IAAA,KAAW,EAAO,YAAc,EAAE,cACrD,EAAE,YAAc,IAAA,KAAW,EAAO,SAAW,EAAE,WAC/C,EAAE,WAAa,IAAA,KAAW,EAAO,QAAU,EAAE,UAC7C,EAAE,UAAY,EAAE,SAAS,OAAS,IAAG,EAAO,SAAW,EAAE,UACzD,EAAE,kBAAoB,IAAA,KAAW,EAAO,eAAiB,EAAE,iBAC3D,EAAE,YAAc,EAAE,WAAW,OAAS,IAAG,EAAO,WAAa,EAAE,YAC/D,EAAE,cAAgB,IAAA,KAAW,EAAO,YAAc,EAAE,aACpD,EAAE,mBAAqB,IAAA,KAAW,EAAO,gBAAkB,EAAE,kBAC7D,EAAE,UAAY,EAAE,SAAS,OAAS,IAAG,EAAO,SAAW,EAAE,UACzD,EAAE,iBAAmB,IAAA,KAAW,EAAO,eAAiB,EAAE,gBAC1D,EAAE,aAAY,EAAO,UAAY,EAAE,YACnC,EAAE,aAAe,IAAA,KAAW,EAAO,UAAY,EAAE,YAGrD,IAAM,EAAM,EACN,EAAkC,EAAE,CACtC,EAAY,GAChB,IAAK,IAAM,KAAO,OAAO,KAAK,EAAI,CAC3B,GAAsB,IAAI,EAAI,GACjC,EAAO,GAAO,EAAI,GAClB,EAAY,IAKhB,OAFI,IAAW,EAAO,OAAS,GAExB,ECrwDT,IAAM,GAA8D,CAClE,eAAgB,kBAChB,gBAAiB,mBACjB,iBAAkB,oBAClB,kBAAmB,qBACpB,CAED,SAAgB,GAAuB,EAAwB,CAC7D,GAAI,IAAU,IAAA,GACZ,MAAU,MAAM,uFAAuF,CAEzG,IAAM,EAAM,EAAM,MAAM,CAExB,OADI,IAAQ,GAAW,GAChB,EAAI,QAAQ,OAAQ,GAAG,CAGhC,SAAgB,GAAqB,EAA4B,EAAqC,CAEpG,MAAO,GADS,GAAuB,GAAQ,cAAc,CAC3C,OAAO,GAAoB,KCiC/C,SAAgB,GACd,EACA,EACoD,CACpD,IAAM,EAAO,EAAO,KACd,EACJ,EAAO,SAAW,MAAQ,OAAO,EAAO,SAAY,UAAY,CAAC,MAAM,QAAQ,EAAO,QAAQ,CACzF,EAAO,QACR,EAAE,CAGF,EAAS,GAAgE,CAC7E,IAAM,EAAS,CAAE,GAAG,EAAU,CAC9B,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,EAAU,CAC5C,KAAO,IACX,EAAO,GAAO,GAGlB,OAAO,GAGT,OAAQ,EAAR,CACE,IAAK,YAAa,CAChB,IAAM,EAAqC,CACzC,YAAa,EACd,CAID,OAHI,EAAI,aAAa,QAAO,EAAU,aAAkB,EAAI,YAAY,OAElE,sBAAuB,IAAW,EAAU,kBAAuB,GAClE,CAAE,GAAG,EAAQ,QAAS,EAAM,EAAU,CAAE,CAGjD,IAAK,cAAe,CAClB,IAAM,EAAqC,CACzC,YAAa,EACd,CAKD,OAJI,EAAO,QACT,EAAU,KAAU,EAAO,MAC3B,EAAU,MAAW,EAAO,OAEvB,CAAE,GAAG,EAAQ,QAAS,EAAM,EAAU,CAAE,CAGjD,IAAK,qBAEH,OAAO,EAGT,IAAK,YAAa,CAChB,IAAM,EAAqC,EAAE,CAE7C,MADM,kBAAmB,IAAW,EAAU,cAAmB,IAC1D,CAAE,GAAG,EAAQ,QAAS,EAAM,EAAU,CAAE,CAGjD,IAAK,gBAAiB,CACpB,IAAM,EAAqC,EAAE,CAK7C,OAJI,EAAI,aAAa,KAAO,EAAE,QAAS,KACrC,EAAU,IAAS,EAAI,YAAY,KAEjC,OAAO,KAAK,EAAU,CAAC,SAAW,EAAU,EACzC,CAAE,GAAG,EAAQ,QAAS,EAAM,EAAU,CAAE,CAGjD,QACE,OAAO,GA0Bb,IAAM,GAA0C,CAC9C,aAAc,YACf,CAQD,SAAS,GAAiB,EAAuC,CAC/D,GAAM,CAAE,SAAQ,KAAM,EAAU,QAAS,EAAa,GAAG,GAAS,EAE5D,EAAU,GAAY,GAAQ,MAAQ,YACtC,EAAa,GAAe,GAAQ,QACpC,EAAa,GAAgB,IAAY,EACzC,EAAgC,CACpC,GAAG,EACH,KAAM,EACP,CAKD,OAJI,IAAe,IAAA,KAEjB,EAAK,QAAU,OAAO,GAAe,SAAW,CAAE,KAAM,EAAY,CAAG,GAElE,KAAK,UAAU,EAAK,CAG7B,SAAgB,GACd,EACA,EACA,EACiB,CACjB,IAAM,EAAM,GAAqB,iBAAkB,EAAU,CACvD,EAAa,IAAI,gBA2GvB,OAzGY,SAA2B,CACrC,GAAI,CACF,IAAM,EAAc,GAAiB,EAAQ,CAGvC,EAAc,EAAU,aAAe,IAAA,GAEzC,EACJ,GAAI,EAAa,CACf,IAAM,EAAW,IAAI,SACrB,EAAS,OAAO,UAAW,EAAY,CACnC,EAAU,aAAe,IAAA,IAC3B,EAAS,OAAO,aAAc,EAAU,WAAW,CAErD,EAAY,CACV,OAAQ,OACR,KAAM,EACN,OAAQ,EAAW,OACpB,MAED,EAAY,CACV,OAAQ,OACR,QAAS,CAAE,eAAgB,mBAAoB,CAC/C,KAAM,EACN,OAAQ,EAAW,OACpB,CAGH,IAAM,EAAW,MAAM,MAAM,EAAK,EAAU,CAE5C,GAAI,CAAC,EAAS,GAAI,CAChB,IAAI,EAAS,EAAS,WACtB,GAAI,CACF,IAAM,EAAO,MAAM,EAAS,MAAM,CAC5B,EACH,EAAiC,QACjC,EAAiC,SACjC,EAAiC,MAChC,OAAO,GAAQ,WAAU,EAAS,QAChC,EAGR,EAAU,QAAY,MAAM,QAAQ,EAAS,OAAO,IAAI,IAAS,CAAC,CAClE,OAIF,IAAI,EAAY,GACV,MAAiB,CACjB,IACJ,EAAY,GACZ,EAAU,QAAQ,GAGpB,MAAM,GAAc,EAAU,CAC5B,QAAU,GAAuB,CAC/B,IAAM,EAAa,GAAkB,EAA4C,CAE5E,KAEL,OAAQ,EAAW,KAAnB,CACE,IAAK,aACH,EAAU,YAAY,EAAW,QAAS,EAAW,QAAU,GAAM,CACnE,gBAAiB,EAAW,gBAC5B,iBAAkB,EAAW,iBAC7B,iBAAkB,EAAW,iBAC9B,CAAC,CACF,MACF,IAAK,UACH,EAAU,SACR,EAAW,KACX,EAAW,OACX,EAAW,UACX,EAAW,aAAe,GAC3B,CACD,MACF,IAAK,SACH,EAAU,SAAS,EAAW,CAC9B,MACF,IAAK,WACH,EAAU,WAAW,EAAW,CAChC,MACF,IAAK,QACH,EAAU,QAAY,MAAM,EAAW,QAAQ,CAAC,CAChD,MACF,IAAK,OACH,GAAU,CACV,QAGN,QAAS,EAAU,QACnB,OAAQ,EAAW,OACpB,CAAC,CAKF,GAAU,OACH,EAAK,CACZ,GAAI,aAAe,cAAgB,EAAI,OAAS,aAAc,OAC9D,EAAU,QAAQ,aAAe,MAAQ,EAAU,MAAM,OAAO,EAAI,CAAC,CAAC,KAIhE,CACH,ECrST,IAAM,GAAmB,CAAC,aAAc,qBAAqB,CACvD,GAAqB,CAAC,qBAAsB,6BAA6B,CAW/E,SAAgB,IAA0C,CACxD,GAAI,OAAO,OAAW,IAAa,MAAO,GAC1C,IAAM,EAAS,IAAI,gBAAgB,OAAO,SAAS,OAAO,CAC1D,GAAI,GAAiB,KAAM,GAAM,EAAO,IAAI,EAAE,GAAK,IAAI,CAAE,MAAO,GAChE,GAAI,CACF,OAAO,GAAmB,KAAM,GAAM,OAAO,aAAa,QAAQ,EAAE,GAAK,IAAI,MACvE,CACN,MAAO,IAIX,SAAgB,GAAoB,EAAe,EAAiB,EAAyB,CAC3F,GAAI,CAAC,IAAgC,CAAE,OACvC,IAAM,GAAO,OAAO,mCAAqC,GAAK,EAC9D,OAAO,kCAAoC,EAC3C,IAAM,EAAe,CAAE,MAAK,KAAM,IAAI,MAAM,CAAC,aAAa,CAAE,QAAO,UAAS,UAAS,CAChF,OAAO,oCAAmC,OAAO,kCAAoC,EAAE,EAC5F,OAAO,kCAAkC,KAAK,EAAM,CAChD,OAAO,kCAAkC,OAAS,KAAK,OAAO,kCAAkC,OAAO,CAC3G,IAAM,EAAS,wBAAwB,EAAI,IAAI,EAAM,IAAI,IAErD,IAAY,IAAA,GAAW,QAAQ,MAAM,EAAO,CAE3C,QAAQ,MAAM,EAAQ,EAAQ,CC/BrC,IAAa,GAAyB,sBAElC,GAAiC,KAGrC,SAAgB,GAA0B,EAA8B,CACtE,GAAa,EAGf,SAAgB,IAA2C,CAczD,OAbI,IAAc,GAAW,YACpB,IAET,GAAa,KAEC,SAAS,eAAe,GAAuB,GAK7D,GAAoB,cAAe,sDAAuD,CACxF,GAAI,GACL,CAAC,CACK,OAGT,SAAgB,IAAkC,CAChD,GAAa,KCjCf,IAAa,GAAyB,CACpC,YAAa,cACb,iBAAkB,qBAClB,WAAY,SACZ,YAAa,QACb,WAAY,aACZ,cAAe,cACf,UAAW,cACX,aAAc,0CACd,YAAa,cACb,eAAgB,iBAChB,uBAAwB,CACtB,2CACA,iDACA,oCACD,CACD,qBAAsB,CACpB,kCACA,uCACA,sCACD,CACD,0BAA2B,CACzB,8BACA,sDACA,0CACD,CACD,gBAAiB,WACjB,gBAAiB,aACjB,kBAAmB,aACnB,sBAAuB,eACvB,gBAAiB,WACjB,wBAAyB,yEACzB,uBAAwB,eACxB,gBAAiB,oDACjB,aAAc,yCACd,gBAAiB,uBACjB,WAAY,gBACZ,cAAe,mBACf,oBAAqB,oBACrB,YAAa,gBACb,mBAAoB,gBACpB,wBAAyB,uBACzB,cAAe,aACf,YAAa,WACb,aAAc,yBACd,cAAe,yBACf,qBAAsB,kBACtB,gBAAiB,cACjB,eAAgB,qBAChB,eAAgB,iCAChB,sBAAuB,0BACvB,wBAAyB,UACzB,yBAA0B,iFAC1B,yBAA0B,sCAC1B,yBAA0B,cAC1B,0BAA2B,iBAC3B,4BAA6B,0BAC7B,qBAAsB,cACtB,wBAAyB,kBACzB,aAAc,SACd,gBAAiB,UACjB,iBAAkB,mBAClB,qBAAsB,gBACtB,qBAAsB,iBACtB,sBAAuB,sBACvB,yBAA0B,sCAC1B,kBAAmB,qBACnB,cAAe,oBACf,qBAAsB,iBACtB,gBAAiB,cACjB,iBAAkB,iBAClB,YAAa,SACb,eAAgB,iBAChB,kBAAmB,oBACnB,uBAAwB,iBACxB,gBAAiB,uBACjB,oBAAqB,gBACrB,kBAAmB,qBACnB,oBAAqB,0BACrB,eAAgB,QAChB,eAAgB,gBAChB,YAAa,cACb,eAAgB,gBAChB,kBAAmB,4CACnB,sBAAuB,+BACvB,WAAY,sBACZ,eAAgB,kCAChB,uBAAwB,8EACxB,eAAgB,gBAChB,eAAgB,iEAChB,cAAe,UACf,mBAAoB,cACpB,mBAAoB,mBACpB,oBAAqB,kBACrB,qBAAsB,oBACtB,qBAAsB,kBACtB,sBAAuB,mBACvB,qBAAsB,WACtB,yBAA0B,mBAC1B,kBAAmB,qBACnB,cAAe,OACf,iBAAkB,QAClB,oBAAqB,eACrB,iBAAkB,QAClB,oBAAqB,uDACrB,2BAA4B,mDAC5B,qBAAsB,SACtB,qBAAsB,UACtB,cAAe,QACf,cAAe,QACf,+BAAgC,uBAChC,6BAA8B,qBAC9B,sBAAuB,uBACvB,eAAgB,cAChB,uBAAwB,oBACxB,uBAAwB,gEACxB,mBAAoB,cACpB,sBAAuB,sFACvB,yBAA0B,4BAC1B,yBAA0B,6BAC1B,wBAAyB,sBAC1B,CCzHY,GAAyB,CACpC,YAAa,iBACb,iBAAkB,iCAClB,WAAY,OACZ,YAAa,QACb,WAAY,YACZ,cAAe,WACf,UAAW,qBACX,aAAc,0CACd,YAAa,QACb,eAAgB,cAChB,uBAAwB,CACtB,kCACA,yCACA,uCACD,CACD,qBAAsB,CACpB,sCACA,iCACA,8BACD,CACD,0BAA2B,CACzB,oCACA,mCACA,4BACD,CACD,gBAAiB,MACjB,gBAAiB,eACjB,kBAAmB,eACnB,sBAAuB,eACvB,gBAAiB,QACjB,wBAAyB,qEACzB,uBAAwB,eACxB,gBAAiB,+CACjB,aAAc,kCACd,gBAAiB,oBACjB,WAAY,WACZ,cAAe,aACf,oBAAqB,mBACrB,YAAa,eACb,mBAAoB,eACpB,wBAAyB,4BACzB,cAAe,WACf,YAAa,cACb,aAAc,oBACd,cAAe,oBACf,qBAAsB,gBACtB,gBAAiB,UACjB,eAAgB,6BAChB,eAAgB,kCAChB,sBAAuB,oBACvB,wBAAyB,WACzB,yBAA0B,gEAC1B,yBAA0B,6BAC1B,yBAA0B,kBAC1B,0BAA2B,mBAC3B,4BAA6B,qBAC7B,qBAAsB,aACtB,wBAAyB,iBACzB,aAAc,WACd,gBAAiB,eACjB,iBAAkB,eAClB,qBAAsB,iBACtB,qBAAsB,aACtB,sBAAuB,gBACvB,yBAA0B,kCAC1B,kBAAmB,mBACnB,cAAe,YACf,qBAAsB,mBACtB,gBAAiB,cACjB,iBAAkB,gBAClB,YAAa,QACb,eAAgB,eAChB,kBAAmB,iBACnB,uBAAwB,qBACxB,gBAAiB,iBACjB,oBAAqB,kBACrB,kBAAmB,oBACnB,oBAAqB,2BACrB,eAAgB,QAChB,eAAgB,aAChB,YAAa,cACb,eAAgB,eAChB,kBAAmB,gDACnB,sBAAuB,4BACvB,WAAY,qBACZ,eAAgB,kCAChB,uBAAwB,gFACxB,eAAgB,kBAChB,eAAgB,0DAChB,cAAe,UACf,mBAAoB,eACpB,mBAAoB,eACpB,oBAAqB,mBACrB,qBAAsB,mBACtB,qBAAsB,eACtB,sBAAuB,gBACvB,qBAAsB,cACtB,yBAA0B,mBAC1B,kBAAmB,2BACnB,cAAe,OACf,iBAAkB,UAClB,oBAAqB,cACrB,iBAAkB,UAClB,oBAAqB,iEACrB,2BAA4B,kDAC5B,qBAAsB,WACtB,qBAAsB,WACtB,cAAe,WACf,cAAe,WACf,+BAAgC,sBAChC,6BAA8B,oBAC9B,sBAAuB,uBACvB,eAAgB,YAChB,uBAAwB,qBACxB,uBAAwB,8DACxB,mBAAoB,eACpB,sBAAuB,qDACvB,yBAA0B,0BAC1B,yBAA0B,wBAC1B,wBAAyB,oBAC1B,CCvHD,SAAS,GAAgB,EAAyB,CAEhD,OADK,EACE,EAAO,aAAa,CAAC,MAAM,IAAI,CAAC,IAAM,KADzB,KAItB,SAAgB,GAAkB,EAA2B,CAC3D,OAAQ,GAAgB,EAAO,CAA/B,CACE,IAAK,KACH,OAAO,GACT,QACE,OAAO,ICgGb,SAAgB,IAAiC,CAC/C,OAAO,IAAiC,GAAK,KAG/C,SAAS,IAAuE,CAC9E,IAAM,EAAI,WACV,OAAQ,EAAE,mBAAqB,EAAE,yBAA2B,KAG9D,IAAa,GAAb,KAAwB,CAYtB,YAAY,EAAgC,EAA6B,kBAXjB,iBACtB,yBAC2B,gCAC7B,wBAKN,uBACD,EAGvB,KAAK,UAAY,EACjB,KAAK,KAAO,GAAS,MAAQ,QAC7B,KAAK,iBAAmB,GAAS,kBAAoB,KACrD,KAAK,WAAa,GAAS,YAAc,GAG3C,IAAI,OAAyB,CAC3B,OAAO,KAAK,OAMd,OAAc,CACZ,GAAI,KAAK,SAAW,YAAa,OAEjC,IAAM,EAAO,IAAiC,CAC9C,GAAI,CAAC,EAAM,CACT,KAAK,SAAS,QAAQ,CACtB,KAAK,UAAU,UAAU,gBAAiB,mDAAmD,CAC7F,OAIF,GAAW,WAAW,kBAAoB,QAAe,CAAC,WAAW,gBAAiB,CACpF,KAAK,SAAS,QAAQ,CACtB,KAAK,UAAU,UAAU,cAAe,8BAA8B,CACtE,OAGF,KAAK,sBAAwB,GAC7B,KAAK,gBAAkB,GAEvB,IAAM,EAAc,IAAI,EACxB,EAAY,WAAa,GACzB,EAAY,eAAiB,GAC7B,EAAY,KAAO,KAAK,KACxB,EAAY,gBAAkB,EAE9B,EAAY,YAAgB,CAC1B,KAAK,SAAS,YAAY,EAG5B,EAAY,SAAY,GAAkC,CACxD,KAAK,mBAAmB,CAExB,IAAI,EAAU,GACV,EAAc,GAElB,IAAK,IAAI,EAAI,EAAM,YAAa,EAAI,EAAM,QAAQ,OAAQ,IAAK,CAC7D,IAAM,EAAS,EAAM,QAAQ,GAC7B,GAAI,CAAC,EAAQ,SACb,IAAM,EAAM,EAAO,GACd,IACD,EAAO,QACT,GAAe,EAAI,WAEnB,GAAW,EAAI,YAIf,IACF,KAAK,uBAAyB,EAC9B,KAAK,UAAU,UAAU,KAAK,sBAAsB,EAGlD,GACF,KAAK,UAAU,YAAY,KAAK,sBAAwB,EAAQ,CAI9D,KAAK,YAAc,KAAK,uBAC1B,KAAK,mBAAmB,EAI5B,EAAY,QAAW,GAAuC,CAC5D,IAAM,EAAO,GAAa,EAAM,MAAM,CAElC,KAAK,kBAAoB,EAAM,QAAU,aAAe,EAAM,QAAU,aAG5E,KAAK,SAAS,QAAQ,CACtB,KAAK,UAAU,UAAU,EAAM,EAAM,SAAW,EAAM,MAAM,GAG9D,EAAY,UAAc,CAGxB,GAFA,KAAK,mBAAmB,CAEpB,KAAK,SAAW,aAAe,CAAC,KAAK,gBAAiB,CACxD,IAAM,EAAM,KAAK,KAAK,CAGtB,GAAI,EAAM,KAAK,eAAiB,IAAK,CACnC,KAAK,SAAS,OAAO,CACrB,OAEF,KAAK,eAAiB,EACtB,GAAI,CACF,EAAY,OAAO,MACb,CACN,KAAK,SAAS,OAAO,CAEvB,OAEF,KAAK,SAAS,OAAO,EAGvB,KAAK,YAAc,EAEnB,GAAI,CACF,EAAY,OAAO,MACb,CACN,KAAK,SAAS,QAAQ,CACtB,KAAK,UAAU,UAAU,UAAW,sCAAsC,EAO9E,MAAe,CAGb,GAFA,KAAK,gBAAkB,GACvB,KAAK,mBAAmB,CACpB,KAAK,YAAa,CACpB,GAAI,CACF,KAAK,YAAY,MAAM,MACjB,EAGR,KAAK,YAAc,KAGrB,OADA,KAAK,SAAS,OAAO,CACd,KAAK,sBAMd,OAAc,CAIZ,GAHA,KAAK,gBAAkB,GACvB,KAAK,mBAAmB,CACxB,KAAK,sBAAwB,GACzB,KAAK,YAAa,CACpB,GAAI,CACF,KAAK,YAAY,OAAO,MAClB,EAGR,KAAK,YAAc,KAErB,KAAK,SAAS,OAAO,CAIvB,SAAgB,CACd,KAAK,OAAO,CAGd,SAAiB,EAA8B,CACzC,KAAK,SAAW,IAClB,KAAK,OAAS,EACd,KAAK,UAAU,gBAAgB,EAAM,EAIzC,mBAAkC,CAChC,KAAK,mBAAmB,CACxB,KAAK,aAAe,eAAiB,CACnC,IAAM,EAAO,KAAK,MAAM,CACpB,EAAK,MAAM,EACb,KAAK,UAAU,eAAe,EAAK,MAAM,CAAC,EAE3C,KAAK,iBAAiB,CAG3B,mBAAkC,CAC5B,KAAK,eAAiB,OACxB,aAAa,KAAK,aAAa,CAC/B,KAAK,aAAe,QAK1B,SAAS,GAAa,EAAoC,CACxD,OAAQ,EAAR,CACE,IAAK,cACH,MAAO,cACT,IAAK,YACH,MAAO,YACT,IAAK,gBACH,MAAO,gBACT,IAAK,UACH,MAAO,UACT,IAAK,UACH,MAAO,UACT,QACE,MAAO,WCjUb,SAAgB,GAAiB,EAAyC,CACxE,IAAM,EAAS,SAAS,cAAc,MAAM,CAC5C,EAAO,UAAY,uEACnB,EAAO,QAAQ,YAAiB,cAChC,EAAO,aAAa,OAAQ,QAAQ,CAEpC,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,4BACpB,EAAQ,QAAQ,YAAiB,eACjC,EAAQ,UAAY,EAAa,EAAQ,YAAY,CACrD,EAAO,YAAY,EAAQ,CAE3B,IAAM,EAAU,SAAS,cAAc,SAAS,CAShD,MARA,GAAQ,UAAY,4BACpB,EAAQ,QAAQ,YAAiB,eACjC,EAAQ,KAAO,SACf,EAAQ,aAAa,aAAc,EAAQ,gBAAkB,uBAAuB,CACpF,EAAQ,YAAc,IACtB,EAAQ,iBAAiB,QAAS,EAAQ,UAAU,CACpD,EAAO,YAAY,EAAQ,CAEpB,ECTT,IAAa,GAAb,KAAyB,CAQvB,YAAY,EAA6B,CACvC,KAAK,IAAM,SAAS,cAAc,MAAM,CACxC,KAAK,IAAI,UAAY,wCACrB,KAAK,IAAI,QAAQ,YAAiB,eAElC,KAAK,SAAW,SAAS,cAAc,SAAS,CAChD,KAAK,SAAS,UAAY,oEAC1B,KAAK,SAAS,QAAQ,YAAiB,oBACvC,KAAK,SAAS,KAAO,SACrB,KAAK,SAAS,SAAW,GACzB,KAAK,SAAS,aAAa,aAAc,EAAQ,eAAiB,OAAO,CACzE,KAAK,SAAS,MAAQ,EAAQ,eAAiB,OAC/C,KAAK,SAAS,UACZ,8NACF,KAAK,SAAS,iBAAiB,YAAe,EAAQ,QAAQ,CAAC,CAE/D,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,uCAEtB,KAAK,SAAW,SAAS,cAAc,OAAO,CAC9C,KAAK,SAAS,UAAY,kCAC1B,KAAK,SAAS,QAAQ,YAAiB,qBACvC,EAAU,YAAY,KAAK,SAAS,CAEpC,KAAK,YAAc,SAAS,cAAc,SAAS,CACnD,KAAK,YAAY,UAAY,uEAC7B,KAAK,YAAY,QAAQ,YAAiB,uBAC1C,KAAK,YAAY,KAAO,SACxB,KAAK,YAAY,SAAW,GAC5B,KAAK,YAAY,aAAa,aAAc,EAAQ,kBAAoB,UAAU,CAClF,KAAK,YAAY,MAAQ,EAAQ,kBAAoB,UACrD,KAAK,YAAY,UACf,6NACF,KAAK,YAAY,iBAAiB,YAAe,EAAQ,WAAW,CAAC,CAErE,KAAK,WAAa,SAAS,cAAc,MAAM,CAC/C,KAAK,WAAW,UAAY,oCAC5B,KAAK,WAAW,QAAQ,YAAiB,uBAIzC,KAAK,UAAY,SAAS,cAAc,SAAS,CACjD,KAAK,UAAU,UAAY,qEAC3B,KAAK,UAAU,QAAQ,YAAiB,qBACxC,KAAK,UAAU,KAAO,SACtB,KAAK,UAAU,aAAa,aAAc,EAAQ,qBAAuB,cAAc,CACvF,KAAK,UAAU,MAAQ,EAAQ,qBAAuB,cACtD,KAAK,UAAU,UACb,+MACF,KAAK,UAAU,iBAAiB,YAAe,EAAQ,WAAW,CAAC,CAEnE,KAAK,IAAI,YAAY,KAAK,SAAS,CACnC,KAAK,IAAI,YAAY,EAAU,CAC/B,KAAK,IAAI,YAAY,KAAK,WAAW,CACrC,KAAK,IAAI,YAAY,KAAK,YAAY,CACtC,KAAK,IAAI,YAAY,KAAK,UAAU,CAGtC,OAAO,EAAkB,EAAqB,EAAqB,CACjE,KAAK,SAAS,SAAW,CAAC,EAC1B,KAAK,YAAY,SAAW,CAAC,EAC7B,KAAK,SAAS,OAAS,CAAC,EACxB,KAAK,YAAY,OAAS,CAAC,EAC3B,KAAK,SAAS,YAAc,EAC5B,KAAK,SAAS,MAAQ,EAGxB,YAA0B,CACxB,OAAO,KAAK,IAGd,SAAS,EAAqB,CAC5B,KAAK,SAAS,YAAc,EAG9B,WAAW,EAAqC,CAC9C,KAAK,WAAW,iBAAiB,CAC5B,GACL,KAAK,WAAW,YAAY,EAAU,CAGxC,UAAmB,CACjB,OAAO,KAAK,SAAS,aAAe,KC1F3B,GAAb,KAA8B,CAI5B,YAAY,EAAkC,CAC5C,KAAK,kBAAoB,EAAQ,iBAEjC,KAAK,IAAM,SAAS,cAAc,MAAM,CACxC,KAAK,IAAI,UAAY,iCACrB,KAAK,IAAI,MAAM,QAAU,OAG3B,YAA0B,CACxB,OAAO,KAAK,IAGd,WAAW,EAAiC,CAE1C,IAAM,EAAO,IAAI,IACX,EAA2B,EAAE,CACnC,IAAK,IAAM,KAAS,EACb,EAAK,IAAI,EAAM,IAAI,GACtB,EAAK,IAAI,EAAM,IAAI,CACnB,EAAO,KAAK,EAAM,EAItB,KAAK,IAAI,UAAY,GAErB,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAQ,SAAS,cAAc,SAAS,CAK9C,GAJA,EAAM,KAAO,SACb,EAAM,UAAY,6BAClB,EAAM,MAAQ,EAAM,IAEhB,EAAe,EAAM,SAAS,CAAE,CAClC,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,UAAY,6BAChB,EAAI,IAAM,EAAM,SAChB,EAAI,IAAM,EAAM,IAChB,EAAI,MAAQ,GACZ,EAAI,OAAS,GACb,EAAM,YAAY,EAAI,CAGxB,EAAM,iBAAiB,YAAe,CACpC,KAAK,kBAAkB,EAAM,SAAS,EACtC,CAEF,KAAK,IAAI,YAAY,EAAM,EAI/B,MAAa,CACX,KAAK,IAAI,MAAM,QAAU,GAG3B,MAAa,CACX,KAAK,IAAI,MAAM,QAAU,SC9DvB,GAAiD,CACrD,OACE,kOACF,OACE,uLACF,KAAM,gRACN,QACE,oQACH,CAyEK,GAAyB,GACzB,GAA2B,KAS3B,GAA6B,2DAGnC,eAAe,GAAuB,EAA8C,CAClF,IAAK,IAAM,KAAY,EACrB,IAAK,IAAM,KAAQ,EAAS,MACrB,KAAK,WAAW,SAAS,CAC9B,GAAI,CACF,IAAM,EAAO,MAAM,EAAS,QAAQ,EAAK,CACzC,GAAI,CAAC,GAAQ,EAAK,OAAS,EAAG,SAC9B,IAAM,EAAO,GAAQ,EAAK,MAAQ,YAC5B,EAAM,IAAS,YAAc,MAAQ,IAAS,aAAe,OAAS,MACtE,EAAO,SAAS,KAAK,KAAK,CAAC,GAAG,IACpC,OAAO,IAAI,KAAK,CAAC,EAAK,CAAE,EAAM,CAAE,KAAM,EAAM,CAAC,MACvC,CACN,SAMN,IAAK,IAAM,KAAY,EAChB,KAAS,MAAM,SAAS,YAAY,CACzC,GAAI,CAIF,IAAM,GAFO,MADA,MAAM,EAAS,QAAQ,YAAY,EACxB,MAAM,EACT,MAAM,GAA2B,GAC5B,GAC1B,GAAI,CAAC,GAAW,EAAQ,OAAS,IAAW,SAE5C,IAAM,EAAM,MADA,MAAM,MAAM,EAAQ,EACV,MAAM,CAC5B,GAAI,CAAC,GAAO,EAAI,OAAS,EAAG,SAC5B,IAAM,EAAO,EAAI,MAAQ,YACzB,GAAI,CAAC,CAAC,aAAc,YAAa,aAAa,CAAC,SAAS,EAAK,CAAE,SAC/D,IAAM,EAAM,IAAS,YAAc,MAAQ,IAAS,aAAe,OAAS,MAC5E,OAAO,IAAI,KAAK,CAAC,EAAI,CAAE,SAAS,KAAK,KAAK,CAAC,GAAG,IAAO,CAAE,KAAM,EAAM,CAAC,MAC9D,CACN,SAIJ,OAAO,KAST,eAAe,GAAyB,EAA8D,CACpG,GAAI,CAIF,IAAM,EAAI,IAAgB,OAAO,UAAU,WAAW,MAAS,WAAa,UAAU,UAAU,MAAM,CAAG,MAGzG,OAFK,EAEE,GADO,MAAM,EACgB,CAFrB,UAGT,EAGR,OAAO,KAGT,IAAa,GAAb,KAAwB,CAqEtB,YAAY,EAAwB,EAA4B,uBAhEhB,wBAIxB,wBACE,+BACO,uBAGkB,IAAA,yBACE,IAAA,2BACX,wBAIkB,IAAA,oBACK,IAAA,qBACL,IAAA,wBAIlC,2BACG,mBAQa,yBACP,EAAE,0BACM,IAAI,qBACN,kBACG,wBACpB,mBACH,qCACa,kCAEE,iBACY,EAAE,wBACe,oCACT,0BACV,0BAEM,qCACA,6BACG,mCACpB,6BACH,oBACM,8BACR,qCACS,qBACD,mBACO,wBACD,wBACA,qBACG,6BACG,kCACD,gCACc,+BACD,qCACM,KAGlE,KAAK,SAAW,EAChB,KAAK,KAAO,CAAE,GAAG,GAAc,GAAG,EAAQ,KAAM,CAChD,KAAK,OAAS,EAAQ,OAClB,EAAQ,gBAAkB,IAAA,KAC5B,KAAK,eAAiB,EAAQ,eAE5B,EAAQ,kBAAoB,IAAA,KAC9B,KAAK,iBAAmB,EAAQ,iBAE9B,EAAQ,eAAiB,IAAA,KAC3B,KAAK,cAAgB,EAAQ,cAE3B,EAAQ,aAAe,IAAA,KACzB,KAAK,YAAc,EAAQ,YAEzB,EAAQ,cAAgB,IAAA,KAC1B,KAAK,aAAe,EAAQ,aAE1B,EAAQ,eACV,KAAK,cAAgB,IAEnB,EAAQ,YAAc,IAAA,KACxB,KAAK,WAAa,EAAQ,WAG5B,KAAK,KAAO,SAAS,cAAc,MAAM,CACzC,KAAK,KAAK,UAAY,gCACtB,KAAK,KAAK,QAAQ,YAAiB,cACnC,KAAK,KAAK,aAAa,OAAQ,SAAS,CACxC,KAAK,KAAK,aAAa,aAAc,KAAK,KAAK,aAAe,OAAO,CACrE,KAAK,KAAK,aAAa,aAAc,OAAO,CAE5C,IAAM,EAAS,2BACT,EAAS,SAAS,cAAc,OAAO,CAC7C,EAAO,GAAK,EACZ,EAAO,UAAY,kBACnB,EAAO,YAAc,KAAK,KAAK,aAAe,wBAC9C,KAAK,KAAK,YAAY,EAAO,CAC7B,KAAK,KAAK,aAAa,mBAAoB,EAAO,CAGlD,IAAI,EAAmC,KACvC,CACE,IAAM,EAAW,SAAS,cAAc,MAAM,CAC9C,EAAS,UAAY,6BACrB,EAAS,QAAQ,YAAiB,qBAClC,EAAS,aAAa,cAAe,OAAO,CAC5C,EAAS,MAAM,cAAgB,OAC/B,EAAY,EAId,IAAM,EAAS,SAAS,cAAc,MAAM,CAC5C,EAAO,UAAY,uCACnB,EAAO,QAAQ,YAAiB,cAEhC,IAAM,EAAa,SAAS,cAAc,MAAM,CAChD,EAAW,UAAY,2BACvB,EAAW,QAAQ,YAAiB,mBAEpC,IAAM,EAAY,EAAQ,iBAAmB,EAAQ,iBAC/C,EACJ,OAAO,EAAQ,iBAAoB,UACnC,EAAQ,gBAAgB,OAAS,GACjC,EAAQ,kBAAoB,EAAQ,iBACtC,GAAI,EAAW,CACb,IAAM,EAAS,SAAS,cAAc,MAAM,CAC5C,EAAO,UAAY,6BACf,GAAe,EAAO,UAAU,IAAI,mCAAmC,CAC3E,EAAO,QAAQ,YAAiB,qBAChC,EAAO,IAAM,EACb,EAAO,IAAM,EAAQ,aAAe,YACpC,EAAW,YAAY,EAAO,CAGhC,IAAM,EAAa,SAAS,cAAc,MAAM,CAChD,EAAW,UAAY,2BACvB,EAAW,QAAQ,YAAiB,mBAEpC,IAAM,EAAW,SAAS,cAAc,MAAM,CAC9C,EAAS,UAAY,gCACrB,EAAS,QAAQ,YAAiB,wBAClC,IAAM,EAAQ,SAAS,cAAc,OAAO,CAM5C,GALA,EAAM,UAAY,4BAClB,EAAM,QAAQ,YAAiB,oBAC/B,EAAM,YAAc,EAAQ,aAAe,KAAK,KAAK,aAAe,iBACpE,EAAS,YAAY,EAAM,CAEvB,EAAQ,YAAa,CACvB,IAAM,EAAQ,SAAS,cAAc,OAAO,CAC5C,EAAM,UAAY,sDAClB,EAAM,QAAQ,YAAiB,oBAC/B,EAAM,YAAc,EAAQ,YAC5B,EAAS,YAAY,EAAM,CAE7B,EAAW,YAAY,EAAS,CAEhC,IAAM,EAAU,SAAS,cAAc,IAAI,CAC3C,EAAQ,UAAY,8BACpB,EAAQ,QAAQ,YAAiB,yBACjC,EAAQ,KAAO,sBACf,EAAQ,OAAS,SACjB,EAAQ,IAAM,sBACd,EAAQ,UACN,ynBAIS,KAAK,KAAK,YACrB,EAAW,YAAY,EAAQ,CAE/B,EAAW,YAAY,EAAW,CAClC,EAAO,YAAY,EAAW,CAE9B,IAAM,EAAc,SAAS,cAAc,MAAM,CACjD,EAAY,UAAY,4BACxB,EAAY,QAAQ,YAAiB,sBAGrC,CACE,IAAM,EAAY,SAAS,cAAc,SAAS,CAClD,EAAU,KAAO,SACjB,EAAU,UACR,mGACF,EAAU,QAAQ,YAAiB,2BACnC,EAAU,aAAa,aAAc,KAAK,KAAK,mBAAmB,CAClE,EAAU,UAAY,gPACtB,EAAU,iBAAiB,YAAe,KAAK,yBAAyB,CAAC,CACzE,EAAY,YAAY,EAAU,CAClC,KAAK,gBAAkB,EAKzB,CACE,IAAM,EAAU,SAAS,cAAc,SAAS,CAChD,EAAQ,KAAO,SACf,EAAQ,UAAY,6DACpB,EAAQ,QAAQ,YAAiB,mBACjC,EAAQ,aAAa,aAAc,KAAK,KAAK,cAAc,CAC3D,EAAQ,UAAY,mSACpB,EAAQ,iBAAiB,YAAe,EAAQ,eAAe,CAAC,CAChE,EAAY,YAAY,EAAQ,CAChC,KAAK,SAAW,EAIlB,GAAI,EAAQ,UAAW,CACrB,IAAM,EAAa,SAAS,cAAc,SAAS,CACnD,EAAW,UAAY,mFACvB,EAAW,QAAQ,YAAiB,uBACpC,EAAW,KAAO,SAClB,EAAW,aAAa,aAAc,KAAK,KAAK,cAAc,CAC9D,EAAW,MAAQ,KAAK,KAAK,cAC7B,EAAW,UAAY,kPACvB,EAAW,iBAAiB,YAAe,EAAQ,aAAa,CAAC,CACjE,EAAY,YAAY,EAAW,CAGrC,IAAM,EAAW,SAAS,cAAc,SAAS,CAQjD,GAPA,EAAS,UAAY,wDACrB,EAAS,QAAQ,YAAiB,oBAClC,EAAS,KAAO,SAChB,EAAS,aAAa,aAAc,KAAK,KAAK,YAAY,CAC1D,EAAS,UAAY,+MACrB,EAAS,iBAAiB,QAAS,EAAQ,QAAQ,CAE/C,EAAQ,oBAAqB,CAC/B,IAAM,EAAS,SAAS,cAAc,SAAS,CAC/C,EAAO,UAAY,0FACnB,EAAO,QAAQ,YAAiB,wBAChC,EAAO,KAAO,SACd,EAAO,aAAa,aAAc,KAAK,KAAK,mBAAmB,CAC/D,EAAO,UAAY,+SAEnB,IAAM,EAAQ,SAAS,cAAc,OAAO,CAC5C,EAAM,UAAY,gCAClB,EAAM,QAAQ,YAAiB,8BAC/B,EAAM,aAAa,cAAe,OAAO,CACzC,EAAM,MAAM,QAAU,OACtB,EAAO,YAAY,EAAM,CACzB,KAAK,YAAc,EAEnB,EAAO,iBAAiB,YAAe,CACrC,EAAQ,oBAAoB,EAC5B,CACF,EAAY,YAAY,EAAO,CAGjC,EAAY,YAAY,EAAS,CAE7B,GAAW,EAAO,aAAa,EAAW,EAAO,WAAW,CAChE,EAAO,YAAY,EAAY,CAI/B,CACE,IACI,EAAa,EACb,EAAY,EACZ,EAAW,GAET,EAAe,GAAkB,CAIrC,GAHI,EAAE,KAAK,SAAS,qBAAqB,EAAI,OAAO,YAAc,MAEnD,EAAE,OACN,QAAQ,oCAAoC,CAAE,OACzD,IAAM,EAAI,EAAE,iBAAiB,GACxB,IACL,EAAa,EAAE,QACf,EAAY,EACZ,EAAW,GACX,KAAK,KAAK,MAAM,WAAa,SAGzB,EAAc,GAAkB,CACpC,GAAI,CAAC,EAAU,OACf,IAAM,EAAI,EAAE,iBAAiB,GAC7B,GAAI,CAAC,EAAG,OACR,EAAY,EAAE,QAAU,EAExB,IAAM,GADe,EAAQ,kBAAkB,EAAI,UACb,OAAS,KAAK,IAAI,EAAG,EAAU,CAAG,EACxE,EAAE,gBAAgB,CAClB,KAAK,KAAK,MAAM,UAAY,cAAc,EAAa,MAGnD,MAAkB,CACtB,GAAI,CAAC,EAAU,OACf,EAAW,GACX,IAAM,EAAe,EAAQ,kBAAkB,EAAI,OAE/C,EACJ,AAKE,EALE,EAAY,GACF,IAAiB,OAAS,OAAS,QACtC,EAAY,KAAmB,IAAiB,OAC7C,OAEA,EAGd,KAAK,KAAK,MAAM,WAAa,GACzB,IAAc,SAChB,KAAK,KAAK,MAAM,UAAY,mBAC5B,eAAiB,CACf,KAAK,KAAK,MAAM,UAAY,GAC5B,EAAQ,eAAe,QAAQ,EAC9B,IAAI,GAEP,KAAK,KAAK,MAAM,UAAY,GAC5B,EAAQ,eAAe,EAAU,EAEnC,EAAY,GAGR,MAAqB,CACpB,IACL,EAAW,GACX,EAAY,EACZ,KAAK,KAAK,MAAM,WAAa,GAC7B,KAAK,KAAK,MAAM,UAAY,KAG9B,EAAO,iBAAiB,aAAc,EAAa,CAAE,QAAS,GAAM,CAAC,CACrE,EAAO,iBAAiB,YAAa,EAAY,CAAE,QAAS,GAAO,CAAC,CACpE,EAAO,iBAAiB,WAAY,EAAW,CAAE,QAAS,GAAM,CAAC,CACjE,EAAO,iBAAiB,cAAe,EAAc,CAAE,QAAS,GAAM,CAAC,CACvE,KAAK,UAAU,SAAW,CACxB,EAAO,oBAAoB,aAAc,EAAY,CACrD,EAAO,oBAAoB,YAAa,EAAW,CACnD,EAAO,oBAAoB,WAAY,EAAU,CACjD,EAAO,oBAAoB,cAAe,EAAa,EACvD,CAIJ,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,oBACjB,EAAK,QAAQ,YAAiB,YAG9B,KAAK,SAAW,SAAS,cAAc,MAAM,CAC7C,KAAK,SAAS,UAAY,+BAC1B,KAAK,SAAS,QAAQ,YAAiB,aAGvC,KAAK,aAAe,IAAI,GAAY,CAClC,WAAc,EAAQ,eAAe,CACrC,cAAiB,EAAQ,kBAAkB,CAC3C,YAAe,CACT,EAAQ,qBAAqB,EAAI,GACnC,KAAK,iBAAiB,CAEtB,KAAK,YAAY,CAEnB,EAAQ,gBAAgB,EAE1B,cAAe,KAAK,KAAK,cACzB,iBAAkB,KAAK,KAAK,iBAC5B,oBAAqB,KAAK,KAAK,oBAChC,CAAC,CACF,KAAK,SAAS,YAAY,KAAK,aAAa,YAAY,CAAC,CAGzD,IAAM,MAAsB,KAAK,yBAAyB,CAC1D,KAAK,SAAS,iBAAiB,SAAU,EAAe,CAAE,QAAS,GAAM,CAAC,CAC1E,KAAK,UAAU,SAAW,KAAK,SAAS,oBAAoB,SAAU,EAAc,CAAC,CAErF,EAAK,YAAY,KAAK,SAAS,CAG/B,KAAK,WAAa,SAAS,cAAc,MAAM,CAC/C,KAAK,WAAW,UAAY,gEAC5B,KAAK,WAAW,QAAQ,YAAiB,qBACzC,KAAK,WAAW,aAAa,OAAQ,YAAY,CACjD,KAAK,WAAW,aAAa,aAAc,KAAK,KAAK,qBAAqB,CAC1E,KAAK,WAAW,aAAa,QAAS,KAAK,KAAK,qBAAqB,CACrE,KAAK,kBAAoB,SAAS,cAAc,MAAM,CACtD,KAAK,kBAAkB,UAAY,qCACnC,KAAK,kBAAkB,QAAQ,YAAiB,6BAChD,KAAK,kBAAkB,aAAa,cAAe,OAAO,CAC1D,KAAK,WAAW,YAAY,KAAK,kBAAkB,CACnD,IAAM,EAAU,SAAS,cAAc,SAAS,CAChD,EAAQ,UAAY,0DACpB,EAAQ,QAAQ,YAAiB,4BACjC,EAAQ,KAAO,SACf,EAAQ,aAAa,aAAc,KAAK,KAAK,qBAAqB,CAClE,EAAQ,aAAa,QAAS,KAAK,KAAK,qBAAqB,CAC7D,EAAQ,YAAc,IACtB,EAAQ,iBAAiB,YAAe,CACtC,GAAI,KAAK,wBAAyB,CAChC,KAAK,wBAA0B,GAC/B,OAEF,KAAK,aAAa,CAClB,KAAK,kBAAkB,EACvB,CACF,IAAI,EAA6B,KAC7B,EAA6B,KAE3B,GAAuB,GAAsB,CACjD,GAAI,EAAE,KAAK,SAAS,qBAAqB,EAAI,OAAO,YAAc,KAAM,OACxE,IAAM,EAAQ,EAAM,iBAAiB,GAChC,IACL,EAAc,EAAM,QACpB,EAAc,EAAM,UAEhB,EAAqB,GAAsB,CAE/C,GADI,EAAE,KAAK,SAAS,qBAAqB,EAAI,OAAO,YAAc,MAC9D,IAAgB,MAAQ,IAAgB,KAAM,OAClD,IAAM,EAAQ,EAAM,iBAAiB,GACrC,GAAI,CAAC,EAAO,OAEZ,IAAM,EAAS,EAAM,QAAU,EACzB,EAAS,EAAM,QAAU,EAK/B,GAJA,EAAc,KACd,EAAc,KAGV,KAAK,IAAI,EAAO,CAAG,IAAkB,KAAK,IAAI,EAAO,CAAG,KAAK,IAAI,EAAO,CAAE,OAC9E,IAAM,EAAgB,EAAS,EAC3B,IAAkB,KAAK,kBAE3B,KAAK,wBAA0B,GAC/B,KAAK,kBAAkB,EAAc,CACrC,KAAK,kBAAkB,GAEzB,KAAK,WAAW,iBAAiB,aAAc,GAAqB,CAAE,QAAS,GAAM,CAAC,CACtF,KAAK,WAAW,iBAAiB,WAAY,EAAmB,CAAE,QAAS,GAAM,CAAC,CAClF,KAAK,UAAU,SAAW,CACxB,KAAK,WAAW,oBAAoB,aAAc,GAAoB,CACtE,KAAK,WAAW,oBAAoB,WAAY,EAAkB,EAClE,CACF,KAAK,WAAW,YAAY,EAAQ,CACpC,EAAK,YAAY,KAAK,WAAW,CAGjC,IAAM,EAAe,SAAS,cAAc,MAAM,CAClD,EAAa,UAAY,4BACzB,EAAa,QAAQ,YAAiB,oBACtC,KAAK,gBAAkB,EACvB,EAAa,YAAY,EAAO,CAGhC,IAAM,EAAa,SAAS,cAAc,MAAM,CAChD,EAAW,UAAY,uEACvB,EAAW,QAAQ,YAAiB,mBACpC,EAAW,aAAa,OAAQ,SAAS,CACzC,EAAW,aAAa,YAAa,SAAS,CAC9C,EAAW,YAAc,KAAK,KAAK,eAC/B,OAAO,UAAc,KAAe,CAAC,UAAU,QACjD,EAAW,UAAU,IAAI,oCAAoC,CAE/D,EAAa,YAAY,EAAW,CAEpC,IAAM,MAAkB,EAAW,UAAU,IAAI,oCAAoC,CAC/E,MAAiB,EAAW,UAAU,OAAO,oCAAoC,CACvF,OAAO,iBAAiB,UAAW,EAAU,CAC7C,OAAO,iBAAiB,SAAU,EAAS,CAC3C,KAAK,UAAU,SAAW,CACxB,OAAO,oBAAoB,UAAW,EAAU,CAChD,OAAO,oBAAoB,SAAU,EAAS,EAC9C,CAGF,KAAK,UAAY,SAAS,cAAc,MAAM,CAC9C,KAAK,UAAU,UAAY,yBAC3B,KAAK,UAAU,QAAQ,YAAiB,iBACxC,EAAa,YAAY,KAAK,UAAU,CAGxC,KAAK,WAAa,SAAS,cAAc,MAAM,CAC/C,KAAK,WAAW,GAAK,GACrB,KAAK,WAAW,UAAY,wBAC5B,KAAK,WAAW,QAAQ,YAAiB,gBACzC,KAAK,WAAW,aAAa,OAAQ,MAAM,CAC3C,KAAK,WAAW,aAAa,YAAa,SAAS,CACnD,KAAK,WAAW,aAAa,cAAe,QAAQ,CACpD,KAAK,WAAW,aAAa,aAAc,KAAK,KAAK,sBAAsB,CAC3E,GAA0B,KAAK,WAAW,CAE1C,IAAM,EAAY,SAAS,cAAc,SAAS,CAClD,EAAU,KAAO,SACjB,EAAU,UAAY,4CACtB,EAAU,QAAQ,YAAiB,8BACnC,EAAU,YAAc,KAAK,KAAK,yBAClC,EAAU,aAAa,aAAc,KAAK,KAAK,yBAAyB,CACxE,EAAU,MAAM,QAAU,OAC1B,EAAU,iBAAiB,YAAe,CACxC,KAAK,SAAS,cAAc,8BAA8B,EAC1D,CACF,KAAK,WAAW,YAAY,EAAU,CACtC,KAAK,mBAAqB,EAE1B,IAAM,MAAoC,CACxC,KAAK,sBAAwB,KAAK,KAAK,CAAG,KAIxC,EAAmB,GACjB,MAAa,KAAK,SAAS,aAC3B,MAAyB,CACzB,IACJ,EAAmB,GACnB,0BAA4B,CAC1B,EAAmB,GACnB,GAAM,CAAE,YAAW,eAAc,gBAAiB,KAAK,WACjD,EAAqB,EAAe,EAAY,EACtD,KAAK,gBAAkB,EAAqB,GAK5C,IAAM,EADmB,KAAK,oBAE1B,EAHwB,GAIxB,EALyB,GAOvB,EAAM,KAAK,KAAK,CAChB,EAAuB,EAAM,KAAK,yBAClC,EAA0B,CAAC,GAAU,EAAM,KAAK,sBAChD,EAAsB,EAAuB,GAAQ,EAEvD,IAAW,KAAK,sBAClB,KAAK,oBAAsB,EAC3B,GAAM,EAAE,yBAAyB,EAAO,EAEtC,IAAwB,KAAK,+BAC/B,KAAK,6BAA+B,EACpC,GAAM,EAAE,0BAA0B,EAAoB,GAExD,GAEJ,KAAK,WAAW,iBAAiB,SAAU,EAAkB,CAAE,QAAS,GAAM,CAAC,CAC/E,KAAK,UAAU,SAAW,CACxB,KAAK,WAAW,oBAAoB,SAAU,EAAiB,EAC/D,CAEF,IAAM,EAAW,GAAkB,CACjC,GAA6B,CACzB,EAAE,OAAS,IAAM,KAAK,4BACxB,KAAK,SAAS,cAAc,wBAAwB,EAGxD,KAAK,WAAW,iBAAiB,QAAS,EAAS,CAAE,QAAS,GAAM,CAAC,CACrE,KAAK,UAAU,SAAW,KAAK,WAAW,oBAAoB,QAAS,EAAQ,CAAC,CAEhF,IAAM,GAAgB,GAAkB,CACtC,GAA6B,CAC7B,KAAK,aAAe,EAAE,QAAQ,IAAI,SAAW,MAEzC,GAAe,GAAkB,CACrC,GAA6B,CAC7B,IAAM,EAAI,EAAE,QAAQ,IAAI,QAClB,EAAQ,KAAK,aACf,OAAO,GAAM,UAAY,OAAO,GAAU,UAAY,EAAI,EAAQ,IAAM,KAAK,4BAC/E,KAAK,SAAS,cAAc,wBAAwB,EAGxD,KAAK,WAAW,iBAAiB,aAAc,GAAc,CAAE,QAAS,GAAM,CAAC,CAC/E,KAAK,WAAW,iBAAiB,YAAa,GAAa,CAAE,QAAS,GAAM,CAAC,CAC7E,KAAK,UAAU,SAAW,CACxB,KAAK,WAAW,oBAAoB,aAAc,GAAa,CAC/D,KAAK,WAAW,oBAAoB,YAAa,GAAY,EAC7D,CAEF,EAAa,YAAY,KAAK,WAAW,CAGzC,KAAK,kBAAoB,IAAI,GAAiB,CAC5C,iBAAmB,GAAa,EAAQ,mBAAmB,EAAS,CACrE,CAAC,CACF,KAAK,SAAS,YAAY,KAAK,kBAAkB,YAAY,CAAC,CAI9D,KAAK,iBAAmB,SAAS,cAAc,MAAM,CACrD,KAAK,iBAAiB,UAAY,2BAClC,KAAK,iBAAiB,QAAQ,YAAiB,4BAC/C,KAAK,SAAS,YAAY,KAAK,iBAAiB,CAEhD,KAAK,0BAA0B,CAG/B,KAAK,SAAW,SAAS,cAAc,MAAM,CAC7C,KAAK,SAAS,UAAY,qBAC1B,KAAK,SAAS,QAAQ,YAAiB,wBACvC,KAAK,SAAS,aAAa,OAAQ,UAAU,CAC7C,KAAK,SAAS,aAAa,aAAc,KAAK,KAAK,qBAAqB,CACxE,KAAK,SAAS,MAAM,QAAU,OAE9B,IAAM,EAAc,SAAS,cAAc,MAAM,CACjD,EAAY,UAAY,4BACxB,EAAY,QAAQ,YAAiB,+BACrC,KAAK,SAAS,YAAY,EAAY,CAEtC,IAAM,EAAa,SAAS,cAAc,SAAS,CACnD,EAAW,UAAY,iDACvB,EAAW,QAAQ,YAAiB,6BACpC,EAAW,KAAO,SAClB,EAAW,aAAa,aAAc,KAAK,KAAK,yBAAyB,CACzE,EAAW,YAAc,IACzB,EAAW,iBAAiB,YAAe,CACzC,EAAY,SAAS,CAAE,KAAM,IAAK,SAAU,SAAU,CAAC,EACvD,CACF,KAAK,SAAS,YAAY,EAAW,CAGrC,IAAI,EAAkB,GAChB,OAAsB,CACtB,IACJ,EAAkB,GAClB,0BAA4B,CAC1B,EAAkB,GAClB,IAAM,EAAQ,EAAY,WAAa,EAAY,aAAe,EAAY,YAAc,EAC5F,EAAW,MAAM,QAAU,EAAQ,OAAS,IAC5C,GAEJ,EAAY,iBAAiB,SAAU,GAAe,CAAE,QAAS,GAAM,CAAC,CACxE,KAAK,UAAU,SAAW,CACxB,EAAY,oBAAoB,SAAU,GAAc,EACxD,CAEF,EAAa,YAAY,KAAK,SAAS,CAGvC,KAAK,cAAgB,SAAS,cAAc,MAAM,CAClD,KAAK,cAAc,UAAY,2BAC/B,KAAK,cAAc,QAAQ,YAAiB,mBAC5C,KAAK,cAAc,MAAM,QAAU,OACnC,EAAa,YAAY,KAAK,cAAc,CAG5C,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,0BACtB,EAAU,QAAQ,YAAiB,kBAEnC,KAAK,QAAU,SAAS,cAAc,WAAW,CACjD,KAAK,QAAQ,UAAY,qBACzB,KAAK,QAAQ,QAAQ,YAAiB,aACtC,KAAK,QAAQ,KAAO,EACpB,KAAK,QAAQ,YAAc,KAAK,KAAK,iBAGrC,KAAK,QAAQ,iBAAiB,YAAe,CAEvC,KAAK,eAAiB,MACxB,qBAAqB,KAAK,aAAa,CAEzC,KAAK,aAAe,0BAA4B,CAC9C,KAAK,aAAe,KACpB,KAAK,QAAQ,MAAM,OAAS,OAC5B,KAAK,QAAQ,MAAM,OAAS,GAAG,KAAK,IAAI,KAAK,QAAQ,aAAc,IAAI,CAAC,KACxE,CACF,KAAK,oBAAoB,EACzB,CAGF,KAAK,QAAQ,iBAAiB,UAAY,GAAM,CAC1C,EAAE,MAAQ,WACK,KAAK,SAAS,qBAAqB,EAAI,OAAO,YAAc,MAC7D,CAAC,EAAE,YACjB,EAAE,gBAAgB,CAClB,KAAK,SAAS,GAIlB,CAEF,KAAK,QAAQ,iBAAiB,QAAU,GAAM,CAC5C,IAAM,EAAK,EAAE,cACb,GAAI,CAAC,EAAI,OACT,IAAI,EAAoB,KAClB,EAAK,EAAG,QAAQ,GACtB,GAAI,GAAM,EAAG,KAAK,WAAW,SAAS,CACpC,EAAO,UACE,EAAG,OAAO,OACnB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAG,MAAM,OAAQ,IAAK,CACxC,IAAM,EAAO,EAAG,MAAM,GACtB,GAAI,GAAM,OAAS,QAAU,EAAK,KAAK,WAAW,SAAS,CAAE,CAC3D,IAAM,EAAI,EAAK,WAAW,CAC1B,GAAI,EAAG,CACL,EAAO,EACP,QAKJ,IACF,EAAE,gBAAgB,CAClB,KAAK,qBAAqB,EAAK,GAEjC,CAGF,KAAK,WAAa,SAAS,cAAc,QAAQ,CACjD,KAAK,WAAW,KAAO,OACvB,KAAK,WAAW,OAAS,kCACzB,KAAK,WAAW,MAAM,QAAU,OAChC,KAAK,WAAW,iBAAiB,aAAgB,CAC/C,IAAM,EAAO,KAAK,WAAW,QAAQ,GACjC,GACF,KAAK,qBAAqB,EAAK,CAEjC,KAAK,WAAW,MAAQ,IACxB,CAGF,IAAM,GAAa,SAAS,cAAc,MAAM,CAChD,GAAW,UAAY,2BACvB,GAAW,QAAQ,YAAiB,mBACpC,KAAK,cAAgB,GAErB,IAAM,GAAY,SAAS,cAAc,SAAS,CAClD,KAAK,WAAa,GAClB,GAAU,UAAY,gDACtB,GAAU,QAAQ,YAAiB,qBACnC,GAAU,KAAO,SACjB,GAAU,aAAa,aAAc,KAAK,KAAK,kBAAkB,CACjE,GAAU,aAAa,gBAAiB,OAAO,CAC/C,GAAU,aAAa,gBAAiB,QAAQ,CAChD,GAAU,UAAY,uRACtB,GAAU,iBAAiB,QAAU,GAAO,CAC1C,EAAG,iBAAiB,CACpB,KAAK,mBAAmB,EACxB,CAEF,IAAM,GAAa,SAAS,cAAc,MAAM,CAChD,KAAK,cAAgB,GACrB,GAAW,UAAY,oCACvB,GAAW,QAAQ,YAAiB,mBACpC,GAAW,aAAa,OAAQ,OAAO,CACvC,GAAW,aAAa,SAAU,GAAG,CAErC,IAAM,GAAiB,SAAS,cAAc,SAAS,CACvD,GAAe,KAAO,SACtB,GAAe,UAAY,sDAC3B,GAAe,QAAQ,YAAiB,gCACxC,GAAe,aAAa,OAAQ,WAAW,CAC/C,GAAe,UACb,2VAEgD,KAAK,KAAK,sBAAsB,SAClF,GAAe,iBAAiB,QAAU,GAAO,CAC/C,EAAG,iBAAiB,CACpB,KAAK,kBAAkB,CACvB,KAAK,WAAW,OAAO,EACvB,CAEF,IAAM,GAAM,SAAS,cAAc,MAAM,CACzC,GAAI,UAAY,+BAChB,GAAI,QAAQ,YAAiB,6BAC7B,GAAI,aAAa,cAAe,OAAO,CAEvC,IAAM,EAAW,SAAS,cAAc,SAAS,CACjD,EAAS,KAAO,SAChB,EAAS,UAAY,sDACrB,EAAS,QAAQ,YAAiB,yBAClC,EAAS,aAAa,OAAQ,WAAW,CACzC,EAAS,UACP,ufAQgD,KAAK,KAAK,gBAAgB,SAC5E,EAAS,iBAAiB,QAAU,GAAO,CACzC,EAAG,iBAAiB,CAEpB,IAAM,EAAW,OAAO,UAAU,WAAW,MAAS,WAAa,UAAU,UAAU,MAAM,CAAG,IAAA,GAC3F,KAAK,6BAA6B,EAAS,EAChD,CAEF,GAAW,YAAY,GAAe,CACtC,GAAW,YAAY,GAAI,CAC3B,GAAW,YAAY,EAAS,CAChC,GAAW,YAAY,GAAU,CACjC,GAAW,YAAY,GAAW,CAGlC,KAAK,cAAgB,SAAS,cAAc,MAAM,CAClD,KAAK,cAAc,UACjB,wFACF,KAAK,cAAc,QAAQ,YAAiB,0BAC5C,IAAM,GAAe,SAAS,cAAc,MAAM,CAClD,GAAa,UAAY,wCACzB,GAAa,QAAQ,YAAiB,gCACtC,GAAa,IAAM,GACnB,KAAK,aAAe,SAAS,cAAc,OAAO,CAClD,KAAK,aAAa,UAAY,+BAC9B,KAAK,aAAa,QAAQ,YAAiB,+BAC3C,IAAM,GAAY,SAAS,cAAc,SAAS,CAClD,GAAU,UAAY,uDACtB,GAAU,QAAQ,YAAiB,iCACnC,GAAU,KAAO,SACjB,GAAU,aAAa,aAAc,KAAK,KAAK,uBAAuB,CACtE,GAAU,YAAc,IACxB,GAAU,iBAAiB,YAAe,KAAK,iBAAiB,CAAC,CACjE,KAAK,cAAc,YAAY,GAAa,CAC5C,KAAK,cAAc,YAAY,KAAK,aAAa,CACjD,KAAK,cAAc,YAAY,GAAU,CAEzC,KAAK,QAAU,SAAS,cAAc,SAAS,CAC/C,KAAK,QAAQ,UAAY,4CACzB,KAAK,QAAQ,QAAQ,YAAiB,YACtC,KAAK,QAAQ,KAAO,SACpB,KAAK,QAAQ,SAAW,GACxB,KAAK,QAAQ,aAAa,aAAc,KAAK,KAAK,WAAW,CAC7D,KAAK,QAAQ,QAAQ,QAAa,KAAK,KAAK,WAC5C,KAAK,sBAAsB,OAAO,CAClC,KAAK,QAAQ,iBAAiB,YAAe,CAC3C,GAAI,KAAK,iBAAkB,CACzB,IAAM,EAAS,KAAK,iBACpB,KAAK,gBAAgB,CACrB,GAAQ,CACR,OAEF,KAAK,SAAS,EACd,CAGF,EAAU,iBAAiB,WAAa,GAAM,CAC5C,EAAE,gBAAgB,CAClB,EAAU,UAAU,IAAI,oCAAoC,EAC5D,CACF,EAAU,iBAAiB,gBAAmB,CAC5C,EAAU,UAAU,OAAO,oCAAoC,EAC/D,CACF,EAAU,iBAAiB,OAAS,GAAM,CACxC,EAAE,gBAAgB,CAClB,EAAU,UAAU,OAAO,oCAAoC,CAC/D,IAAM,EAAO,EAAE,cAAc,MAAM,GAC/B,GACF,KAAK,qBAAqB,EAAK,EAEjC,CAGF,IAAM,GAAO,SAAS,cAAc,MAAM,CAC1C,GAAK,UAAY,0CACjB,GAAK,QAAQ,YAAiB,mBAC9B,GAAK,YAAY,GAAW,CAC5B,GAAK,YAAY,KAAK,QAAQ,CAG1B,KAAK,eAAiB,IAAuB,GAC/C,KAAK,QAAU,SAAS,cAAc,SAAS,CAC/C,KAAK,QAAQ,UAAY,6CACzB,KAAK,QAAQ,QAAQ,YAAiB,kBACtC,KAAK,QAAQ,KAAO,SACpB,KAAK,QAAQ,aAAa,aAAc,KAAK,KAAK,YAAY,CAC9D,KAAK,QAAQ,UACX,8UAMF,KAAK,QAAQ,iBAAiB,YAAe,KAAK,cAAc,CAAC,CACjE,GAAK,YAAY,KAAK,QAAQ,CAE9B,KAAK,YAAc,IAAI,GACrB,CACE,UAAY,GAAS,CACf,KAAK,eAAiB,OACxB,qBAAqB,KAAK,aAAa,CACvC,KAAK,aAAe,MAEtB,KAAK,QAAQ,MAAQ,EACrB,KAAK,QAAQ,MAAM,OAAS,OAC5B,KAAK,QAAQ,MAAM,OAAS,GAAG,KAAK,IAAI,KAAK,QAAQ,aAAc,IAAI,CAAC,KAE1E,QAAU,GAAS,CACjB,KAAK,QAAQ,MAAQ,GAEvB,aAAe,GAAS,CACtB,KAAK,QAAQ,MAAQ,EACrB,KAAK,SAAS,UAAU,OAAO,+BAA+B,CAC9D,KAAK,SAAS,EAEhB,cAAgB,GAAU,CACpB,IAAU,YACZ,KAAK,SAAS,UAAU,IAAI,+BAA+B,CAE3D,KAAK,SAAS,UAAU,OAAO,+BAA+B,EAGlE,SAAU,EAAO,IAAa,CAC5B,KAAK,SAAS,UAAU,OAAO,+BAA+B,EAEjE,CACD,CAAE,KAAM,KAAK,WAAY,CAC1B,EAGH,GAAK,YAAY,KAAK,QAAQ,CAE9B,EAAU,YAAY,KAAK,cAAc,CACzC,EAAU,YAAY,KAAK,WAAW,CACtC,EAAU,YAAY,GAAK,CAC3B,EAAa,YAAY,EAAU,CAEnC,EAAK,YAAY,EAAa,CAE9B,KAAK,sBAAwB,SAAS,cAAc,MAAM,CAC1D,KAAK,sBAAsB,UAAY,oCACvC,KAAK,sBAAsB,QAAQ,YAAiB,uBACpD,EAAK,YAAY,KAAK,sBAAsB,CAE5C,KAAK,KAAK,YAAY,EAAK,CAG3B,KAAK,sBAAsB,EAAa,CACxC,KAAK,sBAAsB,KAAK,SAAS,CAGzC,IAAM,GAAS,SAAS,cAAc,MAAM,CAC5C,GAAO,UAAY,sBACnB,GAAO,QAAQ,YAAiB,cAChC,GAAO,YAAc,KAAK,KAAK,UAC/B,KAAK,KAAK,YAAY,GAAO,CAG7B,IAAM,GAAiB,GAAqB,CACtC,EAAE,MAAQ,UACZ,EAAQ,SAAS,EAGrB,KAAK,KAAK,iBAAiB,UAAW,GAAc,CACpD,KAAK,UAAU,SAAW,KAAK,KAAK,oBAAoB,UAAW,GAAc,CAAC,CAElF,EAAU,YAAY,KAAK,KAAK,CAGlC,WAAW,EAA4B,CACrC,IAAM,EAAS,SAAS,cAAc,MAAM,CAc5C,GAZA,EAAO,UAAY,mCADK,EAAQ,OAAS,YAAc,wBAA0B,mBACX,wBAAwB,EAAQ,OACtG,EAAO,QAAQ,YAAiB,EAAQ,OAAS,YAAc,yBAA2B,oBAC1F,EAAO,aAAa,OAAQ,WAAW,CACvC,EAAO,QAAQ,UAAe,EAAQ,GAClC,EAAQ,WACV,EAAO,QAAQ,SAAc,EAAQ,UAGnC,KAAK,oBAAoB,IAAI,EAAQ,GAAG,EAC1C,EAAO,UAAU,IAAI,6BAA6B,CAGhD,EAAQ,WAAY,CACtB,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,gCACpB,IAAM,EAAU,IAAI,gBAAgB,EAAQ,WAAW,CACvD,EAAQ,IAAM,EACd,EAAQ,IAAM,EAAQ,WAAW,KAEjC,EAAQ,iBAAiB,WAAc,IAAI,gBAAgB,EAAQ,CAAE,CAAE,KAAM,GAAM,CAAC,CACpF,EAAQ,iBAAiB,YAAe,IAAI,gBAAgB,EAAQ,CAAE,CAAE,KAAM,GAAM,CAAC,CACrF,EAAO,aAAa,EAAS,EAAO,WAAW,CAGjD,GAAI,EAAQ,QAAS,CACnB,IAAM,EAAO,SAAS,cAAc,MAAM,CAG1C,GAFA,EAAK,UAAY,2BACjB,EAAK,QAAQ,YAAiB,oBAC1B,EAAQ,OAAS,YAGnB,IAFA,EAAK,UAAY,EAAa,EAAQ,QAAQ,CAE1C,KAAK,aAAc,CACrB,IAAM,EAAQ,EAAK,iBAAiB,UAAU,CAC9C,IAAK,IAAM,KAAQ,EACjB,EAAK,iBAAiB,QAAU,GAAM,CACpC,EAAE,gBAAgB,CAClB,IAAM,EAAO,EAAK,aAAa,OAAO,CAClC,GACF,KAAK,eAAe,EAAK,EAE3B,OAIN,EAAK,YAAc,EAAQ,QAE7B,EAAO,YAAY,EAAK,CAI1B,GAAI,EAAQ,OAAS,QAAU,KAAK,YAAa,CAC/C,IAAM,EAAc,SAAS,cAAc,SAAS,CACpD,EAAY,UAAY,kDACxB,EAAY,QAAQ,YAAiB,wBACrC,EAAY,KAAO,SACnB,EAAY,aAAa,aAAc,KAAK,KAAK,kBAAkB,CACnE,EAAY,MAAQ,KAAK,KAAK,kBAC9B,EAAY,UAAY,0OACxB,EAAY,iBAAiB,QAAU,GAAM,CAC3C,EAAE,iBAAiB,CACnB,KAAK,cAAc,EAAQ,GAAG,EAC9B,CACF,EAAO,YAAY,EAAY,CAGjC,KAAK,WAAW,YAAY,EAAO,CAC/B,KAAK,4BACP,KAAK,6BAA6B,CAEpC,KAAK,gBAAgB,EAAQ,OAAS,OAAO,CAI/C,oBAAoB,EAAyB,CAC3C,KAAK,oBAAoB,OAAO,EAAU,CAC1C,KAAK,WAAW,cAAc,qBAAqB,IAAI,OAAO,EAAU,CAAC,IAAI,EAAE,QAAQ,CACnF,KAAK,4BACP,KAAK,6BAA6B,CAItC,oBAAoB,EAA2B,CAC7C,KAAK,uBAAuB,CAC5B,IAAM,EACJ,KAAK,eAAe,OAAS,EACzB,KAAK,eAAe,MAAM,GAAG,CAC7B,EACE,CAAC,EAAW,CACZ,KAAK,KAAK,uBACZ,CAAE,OAAM,WAAY,KAAK,uBAC7B,OACA,EACA,wBACA,sBACD,CACD,EAAK,QAAQ,OAAY,OACzB,KAAK,sBAAwB,EAE7B,KAAK,WAAW,YAAY,EAAK,CACjC,KAAK,gBAAgB,GAAK,CAI5B,gBAAgB,EAAoB,CAClC,IAAM,EAAa,EAAK,MAAM,CACzB,GACD,KAAK,eAAe,KAAK,eAAe,OAAS,KAAO,IAC5D,KAAK,eAAe,KAAK,EAAW,CACpC,KAAK,eAAiB,KAAK,eAAe,MAAM,GAAG,CAC/C,KAAK,uBACP,KAAK,mBAAmB,KAAK,sBAAuB,KAAK,eAAgB,GAAK,CAE5E,KAAK,sBACP,KAAK,mBAAmB,KAAK,qBAAsB,KAAK,eAAgB,GAAK,CAE3E,KAAK,4BACP,KAAK,mBAAmB,KAAK,2BAA4B,KAAK,eAAgB,GAAK,EAIvF,iBAAiB,EAAuB,CACtC,IAAM,EAAa,EAChB,IAAK,GAAS,EAAK,MAAM,CAAC,CAC1B,OAAO,QAAQ,CACf,MAAM,GAAG,CACR,EAAW,SAAW,IAC1B,KAAK,eAAiB,EAClB,KAAK,uBACP,KAAK,mBAAmB,KAAK,sBAAuB,KAAK,eAAgB,GAAK,CAE5E,KAAK,sBACP,KAAK,mBAAmB,KAAK,qBAAsB,KAAK,eAAgB,GAAK,CAE3E,KAAK,4BACP,KAAK,mBAAmB,KAAK,2BAA4B,KAAK,eAAgB,GAAK,EAIvF,uBAA8B,CAC5B,KAAK,uBAAuB,KAAK,sBAAsB,CACvD,KAAK,sBAAwB,KACZ,KAAK,WAAW,cAAc,uBAAuB,EAC5D,QAAQ,CAClB,KAAK,eAAiB,EAAE,CACxB,KAAK,gBAAgB,CAIvB,eAAe,EAA0B,CACvC,KAAK,iBAAmB,EACxB,KAAK,QAAQ,SAAW,GACxB,KAAK,QAAQ,UAAU,IAAI,0BAA2B,oBAAoB,CAC1E,KAAK,QAAQ,UAAU,OAAO,kBAAkB,CAChD,KAAK,QAAQ,aAAa,aAAc,KAAK,KAAK,eAAe,CACjE,KAAK,QAAQ,QAAQ,QAAa,KAAK,KAAK,eAC5C,KAAK,sBAAsB,OAAO,CAIpC,gBAAuB,CACrB,KAAK,iBAAmB,KACxB,KAAK,QAAQ,UAAU,OAAO,0BAA2B,oBAAoB,CAC7E,KAAK,QAAQ,UAAU,IAAI,kBAAkB,CAC7C,KAAK,QAAQ,aAAa,aAAc,KAAK,KAAK,WAAW,CAC7D,KAAK,QAAQ,QAAQ,QAAa,KAAK,KAAK,WAC5C,KAAK,sBAAsB,OAAO,CAClC,KAAK,oBAAoB,CAG3B,UAAU,EAAkB,EAA4B,CACtD,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,qBAClB,EAAM,aAAa,OAAQ,QAAQ,CACnC,IAAM,EAAS,SAAS,cAAc,OAAO,CAI7C,GAHA,EAAO,YAAc,GAAW,KAAK,KAAK,aAC1C,EAAM,YAAY,EAAO,CAErB,EAAS,CACX,IAAM,EAAW,SAAS,cAAc,SAAS,CACjD,EAAS,UAAY,2BACrB,EAAS,YAAc,KAAK,KAAK,aAAe,QAChD,EAAS,iBAAiB,YAAe,CACvC,EAAM,QAAQ,CACd,GAAS,EACT,CACF,EAAM,YAAY,EAAS,CAG7B,KAAK,WAAW,YAAY,EAAM,CAClC,KAAK,gBAAgB,GAAK,CAI5B,sBAAsB,EAAiB,EAAmE,CACxG,KAAK,UAAU,EAAQ,CACvB,KAAK,iBAAiB,EAAQ,CAIhC,sBAAsB,EAAmE,CACvF,KAAK,iBAAiB,EAAQ,CAGhC,iBAAyB,EAAmE,CAC1F,KAAK,SAAS,CACZ,CAAE,MAAO,KAAK,KAAK,eAAgB,SAAU,EAAQ,QAAS,CAC9D,CAAE,MAAO,KAAK,KAAK,uBAAwB,SAAU,EAAQ,cAAe,CAC7E,CAAC,CAGJ,eAAsB,CACpB,IAAM,EAAS,KAAK,mBACpB,IAAK,IAAM,IAAS,CAAC,GAAG,KAAK,WAAW,SAAS,CAC3C,IAAU,GAAQ,EAAM,QAAQ,CAKxC,SACE,EACM,CACN,IAAM,EAAS,KAAK,SAAS,cAAc,6BAA6B,CACxE,GAAI,CAAC,EAAQ,OACb,KAAO,EAAO,YAAY,EAAO,YAAY,EAAO,WAAW,CAE/D,GAAI,EAAM,SAAW,EAAG,CACtB,KAAK,SAAS,MAAM,QAAU,OAC9B,OAGF,KAAK,SAAS,MAAM,QAAU,GAC9B,IAAK,IAAM,KAAQ,EAAO,CACxB,IAAM,EAAM,SAAS,cAAc,SAAS,CAM5C,GALA,EAAI,UAAY,EAAK,MACjB,qEACA,6CACJ,EAAI,KAAO,SAEP,EAAK,KAAM,CACb,IAAM,EAAU,GAAuB,EAAK,OAAA,+LACtC,EAAW,SAAS,cAAc,OAAO,CAC/C,EAAS,UAAY,yBACrB,EAAS,UAAY,EACrB,EAAI,YAAY,EAAS,CAG3B,GAAI,EAAK,OAAS,EAAe,EAAK,MAAM,CAAE,CAC5C,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,UAAY,wBAChB,EAAI,IAAM,EAAK,MACf,EAAI,IAAM,GACV,EAAI,YAAY,EAAI,CAGtB,IAAM,EAAW,SAAS,cAAc,OAAO,CAK/C,GAJA,EAAS,UAAY,yBACrB,EAAS,YAAc,EAAK,MAC5B,EAAI,YAAY,EAAS,CAErB,EAAK,YAAa,CACpB,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,yBACjB,IAAM,EAAS,aAAa,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAG,EAAE,GAClE,EAAK,GAAK,EACV,EAAK,YAAc,EAAK,YACxB,EAAI,YAAY,EAAK,CACrB,EAAI,aAAa,mBAAoB,EAAO,CAG9C,EAAI,iBAAiB,YAAe,EAAK,UAAU,CAAC,CACpD,EAAO,YAAY,EAAI,CAIzB,IAAM,EAAQ,KAAK,SAAS,cAAc,4BAA4B,CAClE,GACF,0BAA4B,CAC1B,EAAM,MAAM,QAAU,EAAO,YAAc,EAAO,YAAc,GAAK,QACrE,CAIN,YAAmB,CACjB,KAAK,QAAQ,OAAO,CAGtB,eAAe,EAAc,EAA6B,CACxD,KAAK,UAAU,UAAY,GAC3B,IAAM,EAAS,GAAiB,CAAE,YAAa,EAAM,YAAW,eAAgB,KAAK,KAAK,eAAgB,CAAC,CAC3G,KAAK,UAAU,YAAY,EAAO,CAGpC,gBAAuB,CACrB,KAAK,UAAU,UAAY,GAI7B,qBAA+B,CAC7B,OAAO,KAAK,UAAU,WAAW,OAAS,EAG5C,YAA0B,CACxB,OAAO,KAAK,KAId,gBAAgB,EAAkB,CAChC,KAAK,mBAAqB,EAC1B,KAAK,aAAa,YAAc,EAAK,KACrC,IAAM,EAAQ,KAAK,cAAc,cAAc,yCAAyC,CACpF,IAEE,EAAM,KAAO,EAAM,IAAI,WAAW,QAAQ,EAC5C,IAAI,gBAAgB,EAAM,IAAI,CAEhC,EAAM,IAAM,IAAI,gBAAgB,EAAK,EAEvC,KAAK,cAAc,UAAU,OAAO,0CAA0C,CAC9E,KAAK,oBAAoB,CAI3B,iBAAwB,CACtB,IAAM,EAAQ,KAAK,cAAc,cAAc,yCAAyC,CACpF,GAAO,MACT,IAAI,gBAAgB,EAAM,IAAI,CAC9B,EAAM,IAAM,IAEd,KAAK,mBAAqB,KAC1B,KAAK,cAAc,UAAU,IAAI,0CAA0C,CAC3E,KAAK,oBAAoB,CAG3B,qBAA6B,EAAkB,CAC7C,GAAI,KAAK,cACP,GAAI,CACF,KAAK,cAAc,EAAK,OACjB,EAAK,CACZ,QAAQ,MAAM,4CAA6C,EAAI,MAGjE,KAAK,gBAAgB,EAAK,CAI9B,kBAAiC,CAC1B,KAAK,gBACV,KAAK,cAAc,aAAa,SAAU,GAAG,CAC7C,KAAK,YAAY,aAAa,gBAAiB,QAAQ,CACnD,KAAK,0BAA4B,OACnC,aAAa,KAAK,wBAAwB,CAC1C,KAAK,wBAA0B,MAEjC,AAEE,KAAK,sBADL,KAAK,oBAAoB,CACC,OAI9B,iBAAgC,CAC9B,GAAI,CAAC,KAAK,cAAe,OACzB,KAAK,cAAc,gBAAgB,SAAS,CAC5C,KAAK,YAAY,aAAa,gBAAiB,OAAO,CACtD,IAAM,EAAgB,GAAwB,CACxC,KAAK,eAAe,SAAS,EAAE,OAAe,EAClD,KAAK,kBAAkB,EAEnB,EAAS,GAA2B,CACpC,EAAE,MAAQ,WACZ,EAAE,iBAAiB,CACnB,KAAK,kBAAkB,GAG3B,KAAK,wBAA0B,OAAO,eAAiB,CACrD,KAAK,wBAA0B,KAC/B,SAAS,iBAAiB,QAAS,EAAc,GAAK,EACrD,EAAE,CACL,SAAS,iBAAiB,UAAW,EAAO,GAAK,CACjD,KAAK,uBAA2B,CAC9B,SAAS,oBAAoB,QAAS,EAAc,GAAK,CACzD,SAAS,oBAAoB,UAAW,EAAO,GAAK,EAIxD,mBAAkC,CAC3B,KAAK,gBACN,KAAK,cAAc,aAAa,SAAS,CAC3C,KAAK,iBAAiB,CAEtB,KAAK,kBAAkB,EAI3B,MAAc,6BAA6B,EAAoD,CAC7F,IAAM,EAAO,MAAM,GAAyB,EAAS,CACrD,GAAI,EAAM,CACR,KAAK,qBAAqB,EAAK,CAC/B,KAAK,kBAAkB,CACvB,OAEF,EAAS,uBAAwB,CAC/B,QAAS,KAAK,KAAK,wBACnB,OAAQ,OACT,CAAC,CACF,KAAK,kBAAkB,CAIzB,sBAAoC,CAClC,OAAO,KAAK,mBAOd,oBACE,EACA,EACM,CACD,QAAK,eAAe,YAGzB,IAFA,KAAK,uBAAuB,KAAK,2BAA2B,CAC5D,KAAK,2BAA6B,KAC9B,IAAU,SAAU,CACtB,KAAK,eAAe,UAAY,GAChC,KAAK,eAAe,aAAa,SAAU,GAAG,CAC9C,OAGF,GADA,KAAK,eAAe,gBAAgB,SAAS,CACzC,IAAU,YAAa,CACzB,KAAK,eAAe,UAAY,GAChC,IAAM,EAAmB,CACvB,GAAS,gBAAkB,KAAK,KAAK,yBACrC,GAAG,KAAK,KAAK,qBACd,CACK,CAAE,OAAM,WAAY,KAAK,uBAC7B,QACA,KAAK,eAAe,OAAS,EAAI,KAAK,eAAe,MAAM,GAAG,CAAG,EACjE,wBACA,mCACD,CACD,KAAK,2BAA6B,EAClC,KAAK,eAAe,YAAY,EAAK,MAC5B,IAAU,WAAa,GAAS,WACzC,KAAK,eAAe,UAAY,GAChC,KAAK,eAAe,YAAY,EAAQ,SAAS,GAIrD,0BAAyC,CACvC,KAAK,eAAiB,SAAS,cAAc,MAAM,CACnD,KAAK,eAAe,UAAY,6BAChC,KAAK,eAAe,aAAa,SAAU,GAAG,CAGhD,oBAAmC,CACjC,KAAK,oBAAoB,CAG3B,4BAAoC,EAA8B,CAChE,IAAM,EAAW,EAAU,cAA2B,kCAAkC,CACxF,GAAI,EAAU,CACZ,IAAM,EAAU,EAAS,cAA2B,wCAAwC,CACtF,EAAY,EAAS,cAA2B,0CAA0C,CAChG,GAAI,GAAS,aAAa,MAAM,CAAE,CAChC,IAAM,EAAe,EAAQ,YAAY,MAAM,CAC/C,EAAU,QAAQ,yBAA8B,EAChD,KAAK,aAAa,SAAS,EAAa,CAEtC,GACF,EAAU,UAAU,IAAI,yCAAyC,CACjE,KAAK,aAAa,WAAW,EAAU,EAEvC,KAAK,aAAa,WAAW,KAAK,CAEpC,EAAS,QAAQ,CACjB,OAEF,KAAK,iCAAiC,EAAU,CAGlD,iCAAyC,EAA8B,CACrE,IAAM,EAAe,EAAU,QAAQ,yBACvC,GAAI,GAAc,MAAM,CAAE,CACxB,KAAK,aAAa,SAAS,EAAa,MAAM,CAAC,CAC/C,OAKF,IAAM,EAHiB,EAAU,cAC/B,wHACD,EACiC,aAAa,MAAM,CACjD,GACF,KAAK,aAAa,SAAS,EAAU,CAKzC,gBAAgB,EAAuB,CACrC,KAAK,uBAAuB,KAAK,qBAAqB,CACtD,KAAK,qBAAuB,KAC5B,KAAK,uBAAuB,KAAK,2BAA2B,CAC5D,KAAK,2BAA6B,KACf,KAAK,eAItB,KAAK,SAAS,UAAU,IAAI,oCAAoC,CAElE,KAAK,SAAS,UAAY,GAC1B,KAAK,0BAA0B,CAC/B,KAAK,SAAS,YAAY,KAAK,aAAa,YAAY,CAAC,CACzD,KAAK,SAAS,YAAY,KAAK,eAAe,CAC9C,KAAK,aAAa,WAAW,KAAK,CAClC,KAAK,SAAS,YAAY,EAAG,CAC7B,KAAK,SAAS,YAAY,KAAK,kBAAkB,YAAY,CAAC,CAC9D,KAAK,SAAS,YAAY,KAAK,iBAAiB,CAChD,KAAK,4BAA4B,EAAG,CACpC,KAAK,WAAW,UAAU,OAAO,qCAAqC,CACjE,KAAK,gBACR,KAAK,cAAgB,GACrB,KAAK,SAAS,UAAU,IAAI,8BAA8B,CAC1D,KAAK,KAAK,UAAU,IAAI,kCAAkC,EAExD,KAAK,iBACP,KAAK,SAAS,UAAU,IAAI,gCAAgC,CAE9D,KAAK,qBAAqB,CAC1B,0BAA4B,CAC1B,KAAK,SAAS,UAAU,OAAO,oCAAoC,CACnE,KAAK,yBAAyB,CAC9B,KAAK,6BAA6B,EAClC,CAEE,KAAK,kBAAiB,KAAK,gBAAgB,MAAM,QAAU,QAC/D,KAAK,oBAAoB,CAI3B,mBAAmB,EAAuB,CACxC,IAAM,EAAQ,KAAK,kBAAkB,YAAY,CAC3C,EAAM,EAAM,gBAAkB,KAAK,SAAW,EAAQ,KAAK,iBACjE,KAAK,SAAS,aAAa,EAAI,EAAI,CACnC,KAAK,4BAA4B,KAAK,wBAAwB,EAAI,EAAG,CACrE,KAAK,WAAW,UAAU,OAAO,qCAAqC,CACjE,KAAK,gBACR,KAAK,cAAgB,GACrB,KAAK,SAAS,UAAU,IAAI,8BAA8B,CAC1D,KAAK,KAAK,UAAU,IAAI,kCAAkC,EAE5D,KAAK,qBAAqB,CAC1B,KAAK,oBAAoB,CAI3B,wBAA6C,CAC3C,IAAM,EAAW,KAAK,SAAS,SAC/B,IAAK,IAAI,EAAI,EAAG,EAAI,EAAS,OAAQ,IAAK,CACxC,IAAM,EAAQ,EAAS,GAErB,OAAM,UAAU,SAAS,4BAA4B,EACrD,EAAM,UAAU,SAAS,6BAA6B,EACtD,EAAM,UAAU,SAAS,iCAAiC,EAC1D,EAAM,UAAU,SAAS,2BAA2B,EAItD,OAAO,EAET,OAAO,KAIT,gBAA0B,CACxB,OAAO,KAAK,cAId,iBAA2B,CACzB,OAAO,KAAK,eAAiB,KAAK,wBAAwB,GAAK,KAIjE,gBAA0B,CACxB,OAAO,KAAK,SAAS,cAAc,+BAA+B,GAAK,KAIzE,iBAAiB,EAA4B,CAC3C,KAAK,uBAAuB,KAAK,qBAAqB,CACtD,KAAK,qBAAuB,KAC5B,KAAK,WAAW,UAAU,OAAO,qCAAqC,CACtE,KAAK,SAAS,UAAY,GAC1B,KAAK,0BAA0B,CAC/B,KAAK,SAAS,YAAY,KAAK,aAAa,YAAY,CAAC,CACzD,KAAK,SAAS,YAAY,KAAK,eAAe,CAC9C,IAAM,EAAW,SAAS,cAAc,MAAM,CAC9C,EAAS,UAAY,8BACrB,IAAM,EACJ,IAAgB,kBAAoB,KAAK,KAAK,0BAA4B,KAAK,KAAK,qBAChF,CAAE,KAAM,EAAa,QAAS,GAAiB,KAAK,uBACxD,QACA,KAAK,eAAe,OAAS,EAAI,KAAK,eAAe,MAAM,GAAG,CAAG,EACjE,uBACA,oCACD,CAGD,OAFA,KAAK,qBAAuB,EAEpB,EAAR,CACE,IAAK,iBAAkB,CACrB,EAAS,YAAY,EAAY,CAEjC,IAAM,EAAW,SAAS,cAAc,MAAM,CAC9C,EAAS,UAAY,6EACrB,EAAS,YAAY,EAAS,CAC9B,IAAK,IAAI,EAAI,EAAG,EAAI,EAAG,IAAK,CAC1B,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,4EACjB,EAAS,YAAY,EAAK,CAE5B,MAEF,IAAK,cACL,IAAK,YAAa,CAChB,EAAS,YAAY,EAAY,CAEjC,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,mCACjB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAG,IAAK,CAC1B,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,4EACjB,EAAK,YAAY,EAAK,CAExB,EAAS,YAAY,EAAK,CAC1B,MAEF,IAAK,kBAAmB,CACtB,EAAS,UAAU,IAAI,0CAA0C,CACjE,EAAS,YAAY,EAAY,CACjC,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,4DACjB,EAAK,aAAa,YAAa,OAAO,CAGtC,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,UAAY,sCAChB,IAAM,EAAW,SAAS,cAAc,MAAM,CAC9C,EAAS,UAAY,sFACrB,EAAI,YAAY,EAAS,CACzB,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,2CACpB,IAAM,EAAS,SAAS,cAAc,MAAM,CAC5C,EAAO,UAAY,oFACnB,EAAO,aAAa,cAAe,OAAO,CAC1C,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,2CACpB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAG,IAAK,CAC1B,IAAM,EAAI,SAAS,cAAc,MAAM,CACvC,EAAE,UAAY,sFACV,IAAM,GAAG,EAAE,UAAU,IAAI,oDAAoD,CACjF,EAAQ,YAAY,EAAE,CAExB,IAAM,EAAW,SAAS,cAAc,MAAM,CAC9C,EAAS,UAAY,sFACrB,EAAQ,YAAY,EAAS,CAC7B,EAAQ,YAAY,EAAO,CAC3B,EAAQ,YAAY,EAAQ,CAC5B,EAAI,YAAY,EAAQ,CACxB,IAAM,EAAK,SAAS,cAAc,MAAM,CACxC,EAAG,UAAY,qCACf,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,qFAClB,EAAG,YAAY,EAAM,CACrB,IAAM,EAAO,SAAS,cAAc,KAAK,CACzC,EAAK,UAAY,2CACjB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAG,IAAK,CAC1B,IAAM,EAAK,SAAS,cAAc,KAAK,CACjC,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,oFACb,IAAM,GAAG,EAAK,UAAU,IAAI,mDAAmD,CAC/E,IAAM,GAAG,EAAK,UAAU,IAAI,kDAAkD,CAClF,EAAG,YAAY,EAAK,CACpB,EAAK,YAAY,EAAG,CAEtB,EAAG,YAAY,EAAK,CACpB,EAAI,YAAY,EAAG,CACnB,EAAK,YAAY,EAAI,CAGrB,IAAM,EAAK,SAAS,cAAc,MAAM,CACxC,EAAG,UAAY,0CACf,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,UAAY,uFAChB,EAAG,YAAY,EAAI,CACnB,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,kDACtB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAG,IAAK,CAC1B,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,oFACjB,EAAU,YAAY,EAAK,CAE7B,EAAG,YAAY,EAAU,CACzB,EAAK,YAAY,EAAG,CAGpB,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,4EACpB,IAAM,EAAe,SAAS,cAAc,MAAM,CAClD,EAAa,UACX,yFACF,EAAQ,YAAY,EAAa,CACjC,EAAK,YAAY,EAAQ,CAGzB,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,8CACtB,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,8CAClB,IAAM,EAAS,SAAS,cAAc,MAAM,CAC5C,EAAO,UAAY,gDACnB,EAAM,YAAY,EAAO,CACzB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAG,IAAK,CAC1B,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,UAAY,6CAChB,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,yFAClB,IAAM,EAAS,SAAS,cAAc,MAAM,CAC5C,EAAO,UAAY,0FACnB,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UACN,2FACF,EAAI,YAAY,EAAM,CACtB,EAAI,YAAY,EAAO,CACvB,EAAI,YAAY,EAAQ,CACxB,EAAM,YAAY,EAAI,CAExB,EAAU,YAAY,EAAM,CAC5B,IAAK,IAAI,EAAI,EAAG,EAAI,EAAG,IAAK,CAC1B,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,UAAY,6CAChB,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,wFACtB,EAAI,YAAY,EAAU,CAC1B,IAAK,IAAI,EAAI,EAAG,EAAI,EAAG,IAAK,CAC1B,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,uFACjB,EAAI,YAAY,EAAK,CAEvB,EAAU,YAAY,EAAI,CAE5B,EAAK,YAAY,EAAU,CAE3B,EAAS,YAAY,EAAK,CAC1B,MAEF,QACE,EAAS,YAAY,EAAY,CAEjC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAG,IAAK,CAC1B,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,oCAClB,EAAS,YAAY,EAAM,CAE7B,MAIJ,KAAK,SAAS,YAAY,EAAS,CACnC,KAAK,SAAS,YAAY,KAAK,kBAAkB,YAAY,CAAC,CAC9D,KAAK,SAAS,YAAY,KAAK,iBAAiB,CAC3C,KAAK,gBACR,KAAK,cAAgB,GACrB,KAAK,SAAS,UAAU,IAAI,8BAA8B,CAC1D,KAAK,KAAK,UAAU,IAAI,kCAAkC,EAE5D,KAAK,qBAAqB,CAC1B,KAAK,oBAAoB,CAI3B,kBAAkB,EAAkB,EAAqB,EAAqB,CAE5E,IAAM,EAAW,KAAK,SAAS,qBAAqB,EAAI,GACxD,KAAK,aAAa,OAAO,EAAW,GAAO,EAAS,EAAY,EAAM,CACtE,IAAM,EAAY,KAAK,wBAAwB,CAC3C,GACF,KAAK,iCAAiC,EAAU,CAIpD,qBAA8B,CAC5B,OAAO,KAAK,aAAa,UAAU,CAIrC,qBAAqB,EAAqB,CACnC,KAAK,cACN,EAAQ,GACV,KAAK,YAAY,YAAc,EAAQ,GAAK,MAAQ,OAAO,EAAM,CACjE,KAAK,YAAY,MAAM,QAAU,IAEjC,KAAK,YAAY,MAAM,QAAU,QAUrC,YAAmB,CACjB,KAAK,uBAAuB,KAAK,qBAAqB,CACtD,KAAK,qBAAuB,KAC5B,KAAK,uBAAuB,KAAK,2BAA2B,CAC5D,KAAK,2BAA6B,KAClC,KAAK,SAAS,UAAY,GAC1B,KAAK,0BAA0B,CAC/B,KAAK,SAAS,YAAY,KAAK,aAAa,YAAY,CAAC,CACzD,KAAK,SAAS,YAAY,KAAK,eAAe,CAC9C,KAAK,SAAS,YAAY,KAAK,kBAAkB,YAAY,CAAC,CAC9D,KAAK,SAAS,YAAY,KAAK,iBAAiB,CAChD,KAAK,aAAa,WAAW,KAAK,CAClC,KAAK,cAAgB,GACrB,KAAK,SAAS,UAAU,OAAO,8BAA+B,gCAAgC,CAC9F,KAAK,KAAK,UAAU,OAAO,kCAAkC,CAC7D,KAAK,WAAW,UAAU,IAAI,qCAAqC,CACnE,KAAK,uBAAyB,GAC9B,KAAK,qBAAqB,CACtB,KAAK,kBAAiB,KAAK,gBAAgB,MAAM,QAAU,QAC/D,KAAK,yBAAyB,KAAK,CACnC,KAAK,oBAAoB,CAO3B,yBAAyB,EAA8B,CACrD,KAAK,sBAAsB,iBAAiB,CACxC,GAAI,KAAK,sBAAsB,YAAY,EAAG,CAOpD,iBAAwB,CACjB,KAAK,gBACV,KAAK,cAAgB,GACrB,KAAK,SAAS,UAAU,OAAO,8BAA8B,CACzD,KAAK,kBAAiB,KAAK,gBAAgB,MAAM,QAAU,QAC/D,KAAK,oBAAoB,EAG3B,yBAAwC,CAClC,KAAK,gBACT,KAAK,cAAgB,GACrB,KAAK,SAAS,UAAU,IAAI,8BAA8B,CACtD,KAAK,kBAAiB,KAAK,gBAAgB,MAAM,QAAU,QAC/D,KAAK,oBAAoB,EAI3B,aAAoB,CAClB,KAAK,gBAAkB,GACvB,KAAK,SAAS,UAAU,OAAO,gCAAgC,CAC1D,KAAK,gBACR,KAAK,cAAgB,GACrB,KAAK,SAAS,UAAU,IAAI,8BAA8B,CAC1D,KAAK,KAAK,UAAU,IAAI,kCAAkC,EAE5D,KAAK,qBAAqB,CAC1B,KAAK,oBAAoB,CAO3B,kBAAyB,CACvB,KAAK,gBAAkB,GACvB,KAAK,SAAS,UAAU,OAAO,gCAAgC,CAE1D,KAAK,gBACR,KAAK,cAAgB,GACrB,KAAK,SAAS,UAAU,IAAI,8BAA8B,CAC1D,KAAK,KAAK,UAAU,IAAI,kCAAkC,EAE5D,KAAK,WAAW,UAAU,OAAO,qCAAqC,CACtE,KAAK,qBAAqB,CAC1B,KAAK,oBAAoB,CAQ3B,6BAA4C,CAC1C,IAAM,EAAQ,KAAK,SAInB,GAFE,OAAO,OAAW,MAAgB,OAAO,aAAa,mCAAmC,EAAE,SAAW,IAEtF,CAChB,EAAM,UAAY,EAClB,OAGF,KAAK,2BAA6B,EAClC,IAAM,EAAQ,KAAK,0BAEnB,0BAA4B,CAC1B,GAAI,IAAU,KAAK,0BAA2B,OAC9C,IAAM,EAAY,KAAK,IAAI,EAAG,EAAM,aAAe,EAAM,aAAa,CACtE,GAAI,GAAa,EAAG,OAEpB,IAAM,EAAW,KAAK,IAAI,IAAK,KAAK,IAAI,GAAI,EAAY,IAAK,CAAC,CAC9D,EAAM,UAAY,EAElB,IAAM,EAAa,KAAK,IAAI,IAAK,KAAK,IAAI,IAAK,IAAM,KAAK,KAAK,EAAS,CAAG,GAAG,CAAC,CACzE,EAAK,YAAY,KAAK,CAEtB,EAAgB,GAAc,GAAK,EAAI,IAAM,EAE7C,EAAQ,GAAgB,CAC5B,GAAI,IAAU,KAAK,0BAA2B,OAC9C,IAAM,EAAU,EAAM,EAChB,EAAS,KAAK,IAAI,EAAG,EAAU,EAAW,CAEhD,EAAM,UAAY,GAAY,EADhB,EAAa,EAAO,EAE9B,EAAS,EACX,sBAAsB,EAAK,CAE3B,EAAM,UAAY,GAGtB,sBAAsB,EAAK,EAC3B,CAIJ,yBAAwC,CACtC,IAAM,EAAQ,KAAK,SACb,EAAW,EAAM,UAAY,EAAM,cAAgB,EAAM,aAAe,GAC9E,EAAM,UAAU,OAAO,iCAAkC,CAAC,GAAY,EAAM,aAAe,EAAM,aAAa,CAC9G,EAAM,UAAU,OAAO,+BAAgC,EAAM,UAAY,GAAG,CAI9E,sBAA8B,EAAuB,CACnD,IAAI,EAAS,EACT,EAAS,EAEP,EAAgB,GAAkB,CACtC,GAAI,EAAE,KAAK,SAAS,qBAAqB,EAAI,OAAO,YAAc,KAAM,OACxE,IAAM,EAAI,EAAE,QAAQ,GACf,IACL,EAAS,EAAE,QACX,EAAS,EAAE,UAGP,EAAc,GAAkB,CACpC,GAAI,EAAE,KAAK,SAAS,qBAAqB,EAAI,OAAO,YAAc,KAAM,OACxE,IAAM,EAAI,EAAE,eAAe,GAC3B,GAAI,CAAC,EAAG,OACR,IAAM,EAAK,EAAE,QAAU,EACjB,EAAK,EAAE,QAAU,EAEnB,KAAK,IAAI,EAAG,CAAG,IAAM,KAAK,IAAI,EAAG,CAAG,KAAK,IAAI,EAAG,CAAG,IACrD,KAAK,aAAa,CAClB,KAAK,kBAAkB,GAI3B,EAAG,iBAAiB,aAAc,EAAc,CAAE,QAAS,GAAM,CAAC,CAClE,EAAG,iBAAiB,WAAY,EAAY,CAAE,QAAS,GAAM,CAAC,CAC9D,KAAK,UAAU,SAAW,CACxB,EAAG,oBAAoB,aAAc,EAAa,CAClD,EAAG,oBAAoB,WAAY,EAAW,EAC9C,CAIJ,aAAoB,CAClB,KAAK,kBAAkB,CAAC,KAAK,gBAAgB,CAI/C,kBAA4B,CAC1B,OAAO,KAAK,gBAId,kBAAkB,EAA0B,CAC1C,KAAK,gBAAkB,EACnB,EACF,KAAK,SAAS,UAAU,IAAI,gCAAgC,CAE5D,KAAK,SAAS,UAAU,OAAO,gCAAgC,CAEjE,IAAM,EAAa,KAAK,WAAW,cAAc,qCAAqC,CAClF,IACF,EAAW,YAAc,EAAY,IAAW,KAElD,KAAK,qBAAqB,CAG5B,yBAAyB,EAAwB,CAC/C,KAAK,uBAAyB,EAC9B,KAAK,qBAAqB,CAI5B,kBAAkB,EAAyB,CACzC,GAAI,CACF,IAAM,EAAM,iBAAiB,IACzB,KAAK,gBACP,eAAe,QAAQ,EAAK,YAAY,CAExC,eAAe,WAAW,EAAI,MAE1B,GAMV,kBAAkB,EAA4B,CAC5C,GAAI,CACF,IAAM,EAAM,iBAAiB,IAC7B,GAAI,eAAe,QAAQ,EAAI,GAAK,YAElC,MADA,MAAK,gBAAkB,GAChB,QAEH,EAGR,MAAO,GAGT,uBACE,EACA,EACA,EACA,EACwD,CACxD,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,GAAG,EAAU,uBAAuB,IAAY,OAAS,2BAA6B,8BACvG,EAAK,QAAQ,YAAiB,EAC9B,EAAK,aAAa,OAAQ,SAAS,CACnC,EAAK,aAAa,YAAa,SAAS,CAExC,IAAM,EAAQ,SAAS,cAAc,OAAO,CAC5C,EAAM,UACJ,IAAY,OACR,8CACA,sDACN,EAAK,YAAY,EAAM,CAEvB,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,IAAY,OAAS,6CAA+C,oBACrF,EAAK,aAAa,cAAe,OAAO,CACxC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAG,IAAK,CAC1B,IAAM,EAAM,SAAS,cAAc,OAAO,CAC1C,EAAI,UAAY,mBAChB,EAAK,YAAY,EAAI,CAEvB,EAAK,YAAY,EAAK,CAEtB,IAAM,EAAkC,CACtC,QAAS,EACT,MAAO,EAAE,CACT,MAAO,EACP,WAAY,KACb,CAED,OADA,KAAK,mBAAmB,EAAS,EAAM,CAChC,CAAE,OAAM,UAAS,CAG1B,mBAA2B,EAAiC,EAAiB,EAAc,GAAa,CACtG,IAAM,EAAa,EAChB,IAAK,GAAM,EAAE,MAAM,CAAC,CACpB,OAAO,QAAQ,CACf,MAAM,GAAG,CACN,EAAW,CAAC,KAAK,KAAK,eAAe,CAC3C,EAAQ,MAAQ,EAAW,OAAS,EAAI,EAAa,EACrD,KAAK,6BAA6B,EAAQ,CAC1C,EAAQ,MAAQ,EAAc,EAAQ,MAAM,OAAS,EAAI,EACzD,EAAQ,QAAQ,YAAc,EAAQ,MAAM,EAAQ,OAEhD,CAAC,GAAe,EAAQ,MAAM,OAAS,IACzC,EAAQ,WAAa,gBAAkB,CACrC,GAAI,EAAQ,OAAS,EAAQ,MAAM,OAAS,EAAG,CAC7C,KAAK,6BAA6B,EAAQ,CAC1C,OAEF,EAAQ,OAAS,EACjB,EAAQ,QAAQ,YAAc,EAAQ,MAAM,EAAQ,OAChD,EAAQ,OAAS,EAAQ,MAAM,OAAS,GAC1C,KAAK,6BAA6B,EAAQ,EAE3C,GAAyB,EAIhC,6BAAqC,EAA8C,CAC7E,GAAS,aACX,cAAc,EAAQ,WAAW,CACjC,EAAQ,WAAa,MAIzB,uBAA+B,EAA8C,CAC3E,KAAK,6BAA6B,EAAQ,CAG5C,oBAAmC,CACjC,GAAI,KAAK,iBAAkB,CACzB,KAAK,QAAQ,SAAW,GACxB,OAEF,IAAM,EAAa,KAAK,QAAQ,MAAM,MAAM,CAAC,OAAS,GAAK,KAAK,qBAAuB,KACvF,KAAK,QAAQ,SAAW,CAAC,EAG3B,sBAA8B,EAA6B,CACzD,KAAK,QAAQ,UACX,IAAS,OACL,uEACA,0HAGR,SAAwB,CACtB,IAAM,EAAO,KAAK,QAAQ,MAAM,MAAM,CAChC,EAAa,KAAK,mBACpB,MAAC,GAAQ,CAAC,GAId,IAAI,KAAK,iBAAkB,CACzB,IAAM,EAAS,KAAK,iBACpB,KAAK,gBAAgB,CACrB,GAAQ,CAEV,KAAK,OAAO,EAAM,GAAc,IAAA,GAAU,CAC1C,KAAK,QAAQ,MAAQ,GACrB,KAAK,QAAQ,MAAM,OAAS,OAC5B,KAAK,iBAAiB,CACtB,KAAK,oBAAoB,EAG3B,cAA6B,CACtB,QAAK,YACV,GAAI,KAAK,YAAY,QAAU,YAAa,CAC1C,IAAM,EAAO,KAAK,YAAY,MAAM,CAChC,EAAK,MAAM,GACb,KAAK,QAAQ,MAAQ,EACrB,KAAK,SAAS,OAGhB,KAAK,QAAQ,MAAQ,GACrB,KAAK,YAAY,OAAO,CAK5B,sBAA6B,CAC3B,KAAK,mBAAqB,KAAK,KAAK,CAAG,IAIzC,gBAAwB,EAAQ,GAAa,CACvC,CAAC,GAAS,KAAK,iBACf,CAAC,GAAS,KAAK,SAAS,cAAc,6BAA6B,EACnE,CAAC,GAAS,KAAK,KAAK,CAAG,KAAK,oBAChC,0BAA4B,CAC1B,KAAK,WAAW,UAAY,KAAK,WAAW,aAC5C,KAAK,gBAAkB,IACvB,CAIJ,wBAA+B,CAC7B,KAAK,gBAAgB,GAAM,CAI7B,iBAAiB,EAAmB,EAAoB,CACtD,IAAM,EAAS,KAAK,WAAW,cAAc,qBAAqB,IAAI,OAAO,EAAU,CAAC,IAAI,CAC5F,GAAI,CAAC,EAAQ,OACb,IAAI,EAAS,EAAO,cAAc,4BAA4B,CACzD,IACH,EAAS,SAAS,cAAc,MAAM,CACtC,EAAO,UAAY,2BACnB,EAAO,YAAY,EAAO,EAE5B,EAAO,UAAY,EAAa,EAAK,CACrC,KAAK,gBAAgB,GAAM,CAI7B,oBAAoB,EAAyB,CAC3C,KAAK,oBAAoB,IAAI,EAAU,CACvC,IAAM,EAAS,KAAK,WAAW,cAAc,qBAAqB,IAAI,OAAO,EAAU,CAAC,IAAI,CACxF,GACF,EAAO,UAAU,IAAI,6BAA6B,CAKtD,oBAA2B,CACzB,IAAM,EAAa,KAAK,WAAW,iBAAiB,mBAAmB,CACvE,GAAI,EAAW,SAAW,EAAG,CAC3B,KAAK,gBAAgB,GAAK,CAC1B,OAEF,IAAM,EAAe,EAAW,EAAW,OAAS,GAAI,aAAa,iBAAiB,CACtF,GAAI,CAAC,EAAc,CACjB,KAAK,gBAAgB,GAAK,CAC1B,OAEF,KAAK,yBAA2B,KAAK,KAAK,CAAG,IAC7C,IAAM,EAAS,KAAK,WAAW,cAAc,oBAAoB,IAAI,OAAO,EAAa,CAAC,IAAI,CAC1F,EACF,0BAA4B,CAC1B,EAAO,eAAe,CAAE,MAAO,QAAS,SAAU,OAAQ,CAAC,CAC3D,KAAK,gBAAkB,IACvB,CAEF,KAAK,gBAAgB,GAAK,CAQ9B,qBAAqB,EAAkB,EAA2B,SAAmB,CACnF,IAAM,EAAU,KAAK,WAAW,iBAAiB,oBAAoB,IAAI,OAAO,EAAS,CAAC,IAAI,CAC1F,EAA6B,KACjC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,OAAQ,IAAK,CACvC,IAAM,EAAK,EAAQ,GACb,gBAAc,aAChB,GAAG,UAAU,SAAS,8CAA8C,CACxE,GAAS,EACT,OAKF,GAHI,CAAC,GAAU,EAAQ,OAAS,GAAK,EAAQ,aAAc,cACzD,EAAS,EAAQ,IAEf,CAAC,EAAQ,MAAO,GAEpB,IAAM,EAAU,KAAK,IAAI,EAAO,UADf,GACqC,EAAE,CAGxD,MAFA,MAAK,yBAA2B,KAAK,KAAK,CAAG,IAC7C,KAAK,kBAAkB,EAAS,EAAS,CAClC,GAIT,2BAA2B,EAA2B,SAAgB,CACpE,KAAK,yBAA2B,KAAK,KAAK,CAAG,IAC7C,0BAA4B,CAC1B,KAAK,kBAAkB,KAAK,WAAW,aAAc,EAAS,CAC9D,KAAK,gBAAkB,IACvB,CAGJ,kBAA0B,EAAa,EAAgC,CACrE,GAAI,OAAO,KAAK,WAAW,UAAa,WAAY,CAClD,KAAK,WAAW,SAAS,CAAE,MAAK,WAAU,CAAC,CAC3C,OAEF,KAAK,WAAW,UAAY,EAI9B,qBAAqB,EAA+B,CAClD,KAAK,2BAA6B,EAClC,KAAK,6BAA6B,CAGpC,+BAA+B,EAAwB,CACjD,KAAK,qBACP,KAAK,mBAAmB,MAAM,QAAU,EAAU,GAAK,QAI3D,6BAA4C,CAC1C,IAAM,EAAQ,KAAK,2BACnB,KAAK,WAAW,iBAA8B,mBAAmB,CAAC,QAAS,GAAO,CAChF,IAAM,EAAM,EAAG,QAAQ,SAClB,IACD,GAAS,IAAQ,EACnB,EAAG,UAAU,IAAI,8CAA8C,CAE/D,EAAG,UAAU,OAAO,8CAA8C,GAEpE,CAIJ,8BAAqC,CAC/B,KAAK,4BACP,KAAK,6BAA6B,CAKtC,kBAAkB,EAA4E,CAE5F,GADA,KAAK,cAAc,UAAY,GAC3B,EAAM,SAAW,EAAG,CACtB,KAAK,cAAc,MAAM,QAAU,OACnC,OAEF,KAAK,cAAc,MAAM,QAAU,GACnC,IAAK,IAAM,KAAQ,EAAO,CACxB,IAAM,EAAM,SAAS,cAAc,SAAS,CAK5C,GAJA,EAAI,UAAY,mCAChB,EAAI,KAAO,SAGP,EAAK,KAAM,CACb,IAAM,EAAU,GAAuB,EAAK,OAAA,+LACtC,EAAW,SAAS,cAAc,OAAO,CAC/C,EAAS,UAAY,+BACrB,EAAS,UAAY,EACrB,EAAI,YAAY,EAAS,CAG3B,IAAM,EAAQ,SAAS,cAAc,OAAO,CAC5C,EAAM,YAAc,EAAK,MACzB,EAAI,YAAY,EAAM,CAEtB,EAAI,iBAAiB,YAAe,EAAK,UAAU,CAAC,CACpD,KAAK,cAAc,YAAY,EAAI,EAKvC,qBAA4B,CAC1B,KAAK,cAAc,UAAY,GAC/B,KAAK,cAAc,MAAM,QAAU,OAGrC,cAAc,EAAiC,CAC7C,KAAK,kBAAkB,WAAW,EAAQ,CACtC,EAAQ,OAAS,EACnB,KAAK,kBAAkB,MAAM,CAE7B,KAAK,kBAAkB,MAAM,CAE/B,KAAK,sBAAsB,EAAQ,CACnC,KAAK,qBAAqB,CAG5B,gBAAuB,CACrB,KAAK,kBAAkB,MAAM,CAC7B,KAAK,sBAAsB,EAAE,CAAC,CAC9B,KAAK,qBAAqB,CAG5B,sBAA8B,EAAiC,CAC7D,KAAK,kBAAkB,UAAY,GAEnC,IAAM,EAAO,IAAI,IACX,EAAmC,EAAE,CAC3C,IAAK,IAAI,EAAI,EAAQ,OAAS,EAAG,GAAK,EAAG,IAAK,CAC5C,IAAM,EAAQ,EAAQ,GAClB,MAAC,GAAS,EAAK,IAAI,EAAM,IAAI,EAAI,CAAC,EAAe,EAAM,SAAS,IACpE,EAAK,IAAI,EAAM,IAAI,CACnB,EAAe,KAAK,EAAM,CACtB,EAAe,QAAU,GAAG,MAElC,EAAe,SAAS,CAExB,IAAK,IAAM,KAAS,EAAgB,CAClC,IAAM,EAAQ,SAAS,cAAc,OAAO,CAC5C,EAAM,UAAY,2CAClB,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,UAAY,yCAChB,EAAI,IAAM,EAAM,SAChB,EAAI,IAAM,GACV,EAAI,MAAQ,GACZ,EAAI,OAAS,GACb,EAAM,YAAY,EAAI,CACtB,KAAK,kBAAkB,YAAY,EAAM,EAI7C,qBAAoC,CAClC,IAAM,EAAa,KAAK,kBAAkB,kBAAoB,EACxD,EACJ,KAAK,wBACL,GACA,KAAK,iBACL,CAAC,KAAK,WAAW,UAAU,SAAS,qCAAqC,CAC3E,KAAK,WAAW,UAAU,OAAO,6CAA8C,EAAS,CAI1F,WAAkB,CAChB,KAAK,0BAA4B,SAAS,cAC1C,KAAK,cAAc,CAEnB,IAAM,EAAW,GAAqB,CACpC,GAAI,EAAE,MAAQ,MAAO,OACrB,IAAM,EAAY,KAAK,KAAK,iBAC1B,4IACD,CACD,GAAI,EAAU,SAAW,EAAG,OAC5B,IAAM,EAAQ,EAAU,GAClB,EAAO,EAAU,EAAU,OAAS,GAGpC,EAAW,KAAK,KAAK,aAAa,CAClC,EAAS,aAAoB,WAAa,EAAS,cAAgB,SAAS,cAE9E,EAAE,UACA,IAAW,GAAS,CAAC,KAAK,KAAK,SAAS,EAAO,IACjD,EAAE,gBAAgB,CAClB,EAAK,OAAO,GAGV,IAAW,GAAQ,CAAC,KAAK,KAAK,SAAS,EAAO,IAChD,EAAE,gBAAgB,CAClB,EAAM,OAAO,GAKnB,KAAK,kBAAoB,EACzB,KAAK,KAAK,iBAAiB,UAAW,EAAQ,CAIhD,cAAqB,CAKnB,GAJA,AAEE,KAAK,qBADL,KAAK,KAAK,oBAAoB,UAAW,KAAK,kBAAkB,CACvC,MAEvB,KAAK,0BAA2B,CAClC,GAAI,CACF,KAAK,0BAA0B,OAAO,MAChC,EAGR,KAAK,0BAA4B,MAKrC,gBAAuB,CAChB,KAAK,WAEV,KAAK,SAAS,UAAU,OAAO,sCAAsC,CAChE,KAAK,SAAS,YACnB,KAAK,SAAS,UAAU,IAAI,sCAAsC,CAClE,KAAK,SAAS,iBACZ,mBACM,CACJ,KAAK,UAAU,UAAU,OAAO,sCAAsC,EAExE,CAAE,KAAM,GAAM,CACf,EAIH,cAAc,EAAuB,CAClB,KAAK,KAAK,cAAc,2BAA2B,EAC1D,QAAQ,CAClB,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,0BAClB,EAAM,aAAa,OAAQ,SAAS,CACpC,EAAM,aAAa,YAAa,SAAS,CACzC,EAAM,YAAc,EACpB,KAAK,KAAK,YAAY,EAAM,CAEvB,EAAM,YACX,EAAM,UAAU,IAAI,mCAAmC,CACvD,eAAiB,CACf,EAAM,UAAU,OAAO,mCAAmC,CAC1D,eAAiB,EAAM,QAAQ,CAAE,IAAI,EACpC,KAAK,CAIV,SAAgB,CACd,GAA0B,KAAK,CAC/B,KAAK,cAAc,CACf,KAAK,eAAiB,OACxB,qBAAqB,KAAK,aAAa,CACvC,KAAK,aAAe,MAEtB,KAAK,uBAAuB,KAAK,sBAAsB,CACvD,KAAK,sBAAwB,KAC7B,KAAK,uBAAuB,KAAK,qBAAqB,CACtD,KAAK,qBAAuB,KAC5B,KAAK,uBAAuB,KAAK,2BAA2B,CAC5D,KAAK,2BAA6B,KAClC,KAAK,kBAAkB,CACvB,IAAK,IAAM,KAAW,KAAK,UAAW,GAAS,CAC/C,KAAK,UAAU,OAAS,EACxB,KAAK,aAAa,SAAS,CAC3B,KAAK,YAAc,OCpiFjB,GAAc;;;;;;;;;;QAYpB,SAAgB,GAAe,EAA4C,CAEzE,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,kCACtB,EAAU,QAAQ,YAAiB,0BAGnC,IAAM,EAAc,SAAS,cAAc,MAAM,CACjD,EAAY,UAAY,qCACxB,EAAY,QAAQ,YAAiB,gCACrC,EAAU,YAAY,EAAY,CAGlC,IAAM,EAAS,SAAS,cAAc,SAAS,CAI/C,GAHA,EAAO,KAAO,SACd,EAAO,aAAa,aAAc,EAAQ,WAAa,YAAY,CAE/D,EAAQ,SAAU,CACpB,EAAO,UAAY,0DACnB,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,IAAM,EAAQ,SAClB,EAAI,IAAM,GACV,EAAI,UAAY,GAChB,EAAO,YAAY,EAAI,MAEvB,EAAO,UAAY,wBACnB,EAAO,UAAY,EAAQ,WAAa,GAI1C,GAFA,EAAO,QAAQ,YAAiB,uBAE5B,EAAQ,UAAY,IAAA,GAAW,CACjC,IAAM,EAAY,SAAS,cAAc,OAAO,CAChD,EAAU,UAAY,gCACtB,EAAU,QAAQ,YAAiB,wBACnC,EAAU,YAAc,EAAQ,QAChC,EAAO,YAAY,EAAU,CAG3B,EAAQ,aACV,EAAU,QAAQ,WAAgB,KAEhC,EAAQ,mBAAqB,IAAA,KAC/B,EAAU,QAAQ,iBAAsB,OAAO,EAAQ,iBAAiB,EAG1E,EAAO,iBAAiB,QAAS,EAAQ,QAAQ,CACjD,EAAU,YAAY,EAAO,CAG7B,IAAM,EAAoB,SAAS,cAAc,MAAM,CAKvD,MAJA,GAAkB,UAAY,4CAC9B,EAAkB,QAAQ,YAAiB,kCAC3C,EAAU,YAAY,EAAkB,CAEjC,CAAE,YAAW,SAAQ,cAAa,oBAAmB,CC7F9D,IAAM,GAAsB,IAAI,IAAI,CAClC,YACA,aACA,YACA,YACA,aACA,YACA,YACD,CAAC,CAGI,GAAsB,IAAI,IAGhC,SAAS,GAAa,EAA+B,CACnD,EAAM,OAAO,CACb,EAAM,gBAAgB,MAAM,CAC5B,EAAM,MAAM,CACZ,GAAoB,OAAO,EAAM,CAcnC,SAAgB,GAAa,EAAgB,EAAc,YAAiC,CAE1F,IAAM,EAAW,EAAY,MAAM,IAAI,CAAC,GAAI,MAAM,CAClD,GAAI,CAAC,GAAoB,IAAI,EAAS,CAAE,OAAO,KAC/C,GAAI,CACF,IAAM,EAAQ,IAAI,MAAM,QAAQ,EAAY,UAAU,IAAS,CAO/D,OANA,GAAoB,IAAI,EAAM,CAC9B,EAAM,iBAAiB,YAAe,GAAa,EAAM,CAAE,CAAE,KAAM,GAAM,CAAC,CAC1E,EAAM,MAAM,CAAC,UAAY,CAEvB,GAAa,EAAM,EACnB,CACK,CACL,SAAY,GAAa,EAAM,CAChC,MACK,CAEN,OAAO,MC1BX,IAAM,GAAgD,CACpD,aAAc,MACd,eAAgB,KAChB,iBAAkB,SAClB,mBAAoB,IACpB,iBAAkB,IAClB,mBAAoB,GACrB,CAeD,SAAgB,EAAY,EAAa,EAAoC,CAC3E,IAAM,EAAM,OAAO,EAAI,CACvB,GAAI,CAAC,OAAO,SAAS,EAAI,EAAI,EAAM,EAAG,OAAO,EAE7C,IAAM,EAAW,CAAE,GAAG,GAAkB,GAAG,EAAQ,CAE7C,EAAgB,KAAK,IAAI,EAAM,EAAE,QAEjC,EADqB,EAAS,oBAAsB,CAAC,EACxB,EAAI,QAAQ,EAAE,CAAG,EAAI,QAAQ,EAAE,CAC5D,EAAS,EAAM,QAAQ,IAAI,CAC3B,EAAU,IAAW,GAAK,EAAQ,EAAM,MAAM,EAAG,EAAO,CACxD,EAAU,IAAW,GAAK,IAAA,GAAY,EAAM,MAAM,EAAS,EAAE,CAG7D,EAAiB,EAAQ,QAAQ,wBAAyB,EAAS,mBAAmB,CAExF,EAcJ,MAbA,CACE,EADE,IAAY,IAAA,GAGF,EAFA,GAAG,IAAiB,EAAS,mBAAmB,IAK1D,EAAS,eACP,EAAS,mBAAqB,SACzB,GAAG,EAAS,iBAAiB,IAE/B,GAAG,EAAU,GAAG,EAAS,iBAG3B,EC1DT,IAAM,GAAiD,CACrD,YAAa,cACb,OAAQ,SACR,iBAAkB,mBAClB,aAAc,eACd,QAAS,UACT,OAAQ,SACR,IAAK,MACL,UAAW,YACX,OAAQ,SACR,WAAY,aACZ,aAAc,eACd,aAAc,eACd,MAAO,QACP,SAAU,WACV,WAAY,aACZ,SAAU,WACV,aAAc,eACd,iBAAkB,mBAClB,iBAAkB,mBAClB,MAAO,QACP,MAAO,QACP,MAAO,QACP,aAAc,eACd,YAAa,cACb,SAAU,WACV,MAAO,QACP,QAAS,UACT,MAAO,QACP,OAAQ,SACR,MAAO,QACR,CAOD,SAAgB,GAAmB,EAAiB,EAAiD,CACnG,OACE,IAAiB,IACjB,GAAuB,IACvB,EAAQ,QAAQ,KAAM,IAAI,CAAC,QAAQ,MAAQ,GAAM,EAAE,aAAa,CAAC,CA8CrE,SAAS,GAAmB,EAAwC,CAClE,GAAI,OAAO,GAAQ,SAAU,MAAO,GACpC,IAAM,EAAM,OAAO,EAAI,CACvB,OAAO,OAAO,SAAS,EAAI,EAAI,EAAM,EAGvC,SAAS,GAAoB,EAA0D,CACrF,IAAM,EAAM,OAAO,GAAQ,SAAW,EAAM,OAAO,GAAQ,SAAW,OAAO,EAAI,CAAG,IACpF,OAAO,OAAO,SAAS,EAAI,EAAI,EAAM,EAGvC,SAAS,GAAkB,EAAmC,CAC5D,IAAM,EAAQ,OAAO,GAAQ,SAAW,EAAM,OAAO,EAAI,CACnD,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,6CAClB,EAAM,UACJ,mRAGF,IAAM,EAAQ,SAAS,cAAc,OAAO,CAI5C,MAHA,GAAM,UAAY,mDAClB,EAAM,YAAc,EAAM,QAAQ,EAAE,CACpC,EAAM,YAAY,EAAM,CACjB,EAGT,SAAgB,GAAsB,EAA8C,CAClF,GAAM,CAAE,cAAa,WAAU,aAAY,aAAY,eAAc,iBAAgB,QAAS,EAExF,EAAY,SAAS,cAAc,MAAM,CAO/C,GANA,EAAU,UAAY,0BACtB,EAAU,QAAQ,YAAiB,oBACnC,EAAU,aAAa,OAAQ,SAAS,CACxC,EAAU,aAAa,aAAc,GAAM,mBAAqB,qBAAqB,CAGjF,EAAa,CACf,IAAM,EAAU,SAAS,cAAc,UAAU,CACjD,EAAQ,UAAY,+CACpB,EAAQ,QAAQ,YAAiB,8BAEjC,IAAM,EAAW,SAAS,cAAc,MAAM,CAC9C,EAAS,UAAY,4CACrB,EAAS,YAAc,GAAM,wBAA0B,qBACvD,EAAQ,YAAY,EAAS,CAE7B,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,2CACpB,EAAQ,UAAU,IAAI,gBAAgB,CACtC,EAAQ,SAAW,EACnB,EAAQ,aAAa,OAAQ,SAAS,CACtC,EAAQ,aAAa,aAAc,EAAY,KAAK,CAEpD,IAAM,EAAQ,SAAS,cAAc,MAAM,CAE3C,GADA,EAAM,UAAY,4CACd,EAAY,UAAY,EAAe,EAAY,SAAS,CAAE,CAChE,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,IAAM,EAAY,SACtB,EAAI,IAAM,EAAY,KACtB,EAAI,QAAU,OACd,EAAI,iBACF,YACM,CACJ,EAAI,MAAM,QAAU,QAEtB,CAAE,KAAM,GAAM,CACf,CACD,EAAM,YAAY,EAAI,KACjB,CACL,IAAM,EAAc,SAAS,cAAc,MAAM,CACjD,EAAY,UAAY,kDACxB,EAAY,aAAa,cAAe,OAAO,CAC/C,EAAM,YAAY,EAAY,CAEhC,EAAQ,YAAY,EAAM,CAE1B,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,2CACjB,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,4CAClB,EAAM,YAAc,EAAY,KAChC,EAAK,YAAY,EAAM,CACvB,IAAM,EAAO,SAAS,cAAc,MAAM,CAK1C,GAJA,EAAK,UAAY,2CACb,GAAoB,EAAY,OAAO,EACzC,EAAK,YAAY,GAAkB,EAAY,OAAQ,CAAC,CAEtD,GAAmB,EAAY,MAAM,CAAE,CACzC,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,4CAClB,EAAM,YAAc,EAAY,EAAY,MAAO,EAAQ,QAAQ,CACnE,EAAK,YAAY,EAAM,CAGzB,GADI,EAAK,kBAAoB,GAAG,EAAK,YAAY,EAAK,CAClD,EAAQ,gBAAiB,CAC3B,IAAM,EAAiB,SAAS,cAAc,IAAI,CAClD,EAAe,UAAY,2CAC3B,EAAe,UAAY,EAAa,EAAQ,gBAAgB,CAChE,EAAK,YAAY,EAAe,CAElC,EAAQ,YAAY,EAAK,CAEzB,IAAM,MAA8B,CAClC,EAAe,CAAE,IAAK,EAAY,IAAK,KAAM,EAAY,KAAM,CAAC,EAYlE,GAVA,EAAQ,iBAAiB,QAAS,EAAgB,CAClD,EAAQ,iBAAiB,UAAY,GAAU,CACzC,EAAM,MAAQ,SAAW,EAAM,MAAQ,MAC3C,EAAM,gBAAgB,CACtB,GAAiB,GACjB,CAEF,EAAQ,YAAY,EAAQ,CAGxB,EAAW,OAAS,EAAG,CACzB,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,qCACtB,EAAU,QAAQ,YAAiB,wBACnC,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,2CACpB,EAAQ,YAAc,GAAM,iBAAmB,iBAC/C,EAAU,YAAY,EAAQ,CAC9B,IAAM,EAAK,SAAS,cAAc,KAAK,CACvC,IAAK,IAAM,KAAM,EAAY,CAC3B,IAAM,EAAK,SAAS,cAAc,KAAK,CACvC,EAAG,YAAc,EACjB,EAAG,YAAY,EAAG,CAEpB,EAAU,YAAY,EAAG,CACzB,EAAQ,YAAY,EAAU,CAGhC,EAAU,YAAY,EAAQ,CAIhC,GAAI,EAAQ,mBAAoB,CAC9B,IAAM,EAAY,SAAS,cAAc,UAAU,CACnD,EAAU,UAAY,0CACtB,EAAU,QAAQ,YAAiB,6BACnC,IAAM,EAAY,SAAS,cAAc,UAAU,CACnD,EAAU,UAAY,kDACtB,IAAM,EAAU,SAAS,cAAc,OAAO,CAC9C,EAAQ,UAAY,wDACpB,EAAQ,YAAc,GAAM,qBAAuB,kBACnD,IAAM,EAAS,SAAS,cAAc,OAAO,CAC7C,EAAO,UAAY,uDACnB,EAAO,YAAc,GAAM,eAAiB,YAC5C,EAAU,YAAY,EAAQ,CAC9B,EAAU,YAAY,EAAO,CAC7B,EAAU,YAAY,EAAU,CAChC,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,kDACtB,EAAU,UAAY,EAAa,GAAqB,EAAQ,mBAAmB,CAAC,CACpF,EAAU,YAAY,EAAU,CAChC,EAAU,YAAY,EAAU,CAIlC,GAAI,GAAgB,EAAa,OAAS,EAAG,CAC3C,IAAM,EAAU,SAAS,cAAc,UAAU,CACjD,EAAQ,UAAY,8EACpB,EAAQ,QAAQ,YAAiB,2BACjC,IAAM,EAAU,SAAS,cAAc,UAAU,CACjD,EAAQ,YAAc,GAAM,mBAAqB,oBACjD,EAAQ,YAAY,EAAQ,CAC5B,IAAM,EAAO,SAAS,cAAc,KAAK,CACzC,IAAK,IAAM,KAAM,EACf,GAA2B,EAAM,EAAG,CAElC,EAAK,kBAAoB,GAC3B,EAAQ,YAAY,EAAK,CAE3B,EAAU,YAAY,EAAQ,CAIhC,GAAI,EAAS,OAAS,GAAK,EAAW,OAAS,EAAG,CAChD,IAAM,EAAQ,SAAS,cAAc,QAAQ,CAC7C,EAAM,UAAY,qDAGlB,IAAM,EAAQ,SAAS,cAAc,QAAQ,CACvC,EAAY,SAAS,cAAc,KAAK,CACxC,EAAU,SAAS,cAAc,KAAK,CAC5C,EAAU,YAAY,EAAQ,CAC9B,IAAK,IAAM,KAAW,EAAU,CAC9B,IAAM,EAAK,SAAS,cAAc,KAAK,CACnC,EAAQ,MAAQ,GAAa,MAC/B,EAAG,UAAY,qEAEjB,IAAM,EAAa,SAAS,cAAc,MAAM,CAChD,EAAW,UACT,+GACF,EAAW,SAAW,EACtB,EAAW,aAAa,OAAQ,SAAS,CACzC,EAAW,aAAa,aAAc,EAAQ,KAAK,CACnD,EAAW,MAAQ,EAAQ,KAC3B,IAAM,MAA0B,CAC9B,EAAe,CAAE,IAAK,EAAQ,IAAK,KAAM,EAAQ,KAAM,CAAC,EAQ1D,GANA,EAAW,iBAAiB,QAAS,EAAY,CACjD,EAAW,iBAAiB,UAAY,GAAU,CAC5C,EAAM,MAAQ,SAAW,EAAM,MAAQ,MAC3C,EAAM,gBAAgB,CACtB,GAAa,GACb,CACE,EAAQ,UAAY,EAAe,EAAQ,SAAS,CAAE,CACxD,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,IAAM,EAAQ,SAClB,EAAI,IAAM,EAAQ,KAClB,EAAI,QAAU,OACd,EAAI,iBACF,YACM,CACJ,EAAI,MAAM,QAAU,QAEtB,CAAE,KAAM,GAAM,CACf,CACD,EAAW,YAAY,EAAI,KACtB,CACL,IAAM,EAAc,SAAS,cAAc,MAAM,CACjD,EAAY,UAAY,uDACxB,EAAY,aAAa,cAAe,OAAO,CAC/C,EAAW,YAAY,EAAY,CAErC,IAAM,EAAO,SAAS,cAAc,MAAM,CAI1C,GAHA,EAAK,UAAY,6CACjB,EAAK,YAAc,EAAQ,KAC3B,EAAW,YAAY,EAAK,CACxB,GAAmB,EAAQ,MAAM,CAAE,CACrC,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,UAAY,sCAChB,EAAI,YAAc,EAAY,EAAQ,MAAO,EAAQ,QAAQ,CAC7D,EAAW,YAAY,EAAI,CAE7B,EAAG,YAAY,EAAW,CAC1B,EAAU,YAAY,EAAG,CAE3B,EAAM,YAAY,EAAU,CAC5B,EAAM,YAAY,EAAM,CAGxB,IAAM,EAAQ,SAAS,cAAc,QAAQ,CAC7C,IAAK,IAAM,KAAQ,EAAY,CAC7B,IAAM,EAAM,SAAS,cAAc,KAAK,CAClC,EAAU,SAAS,cAAc,KAAK,CAC5C,EAAQ,UAAY,gCACpB,EAAQ,YAAc,GAAmB,EAAK,MAAO,GAAM,eAAe,CAC1E,EAAI,YAAY,EAAQ,CACxB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAO,OAAQ,IAAK,CAC3C,IAAM,EAAK,SAAS,cAAc,KAAK,CACnC,EAAS,IAAI,MAAQ,GAAa,MACpC,EAAG,UAAY,qEAEjB,EAAG,YAAc,EAAK,OAAO,IAAM,GACnC,EAAI,YAAY,EAAG,CAErB,EAAM,YAAY,EAAI,CAExB,EAAM,YAAY,EAAM,CAExB,IAAM,EAAe,SAAS,cAAc,MAAM,CAClD,EAAa,UAAY,wCACzB,EAAa,QAAQ,YAAiB,2BACtC,EAAa,YAAY,EAAM,CAC/B,EAAU,YAAY,EAAa,CAqBrC,OAjBA,EAAU,iBAAiB,UAAY,GAAM,CAC3C,GAAI,EAAE,MAAQ,MAAO,OACrB,IAAM,EAAa,EAAU,iBAC3B,yDACD,CACD,GAAI,EAAW,SAAW,EAAG,OAC7B,IAAM,EAAQ,EAAW,GACnB,EAAO,EAAW,EAAW,OAAS,GACxC,EAAE,UAAY,SAAS,gBAAkB,GAC3C,EAAE,gBAAgB,CAClB,EAAK,OAAO,EACH,CAAC,EAAE,UAAY,SAAS,gBAAkB,IACnD,EAAE,gBAAgB,CAClB,EAAM,OAAO,GAEf,CAEK,EAGT,SAAS,GAAqB,EAAsB,CAElD,GAAI,eAAe,KAAK,EAAK,EAAI,YAAY,KAAK,EAAK,CAAE,OAAO,EAChE,IAAM,EAAQ,EAAK,MAAM;EAAK,CAAC,OAAQ,GAAM,EAAE,MAAM,CAAC,CAEtD,OADI,EAAM,QAAU,EAAU,EACvB,OAAS,EAAM,IAAK,GAAM,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,GAAG,CAAG,QAGtE,SAAS,GAA2B,EAAwB,EAAmB,CAC7E,IAAM,EAAY,EAAa,EAAI,CACnC,GAAI,CAAC,EAAW,OAEhB,IAAM,EAAW,SAAS,cAAc,WAAW,CACnD,EAAS,UAAY,EACrB,IAAM,EAAc,MAAM,KAAK,EAAS,QAAQ,iBAAiB,KAAK,CAAC,CACvE,GAAI,EAAY,OAAS,EAAG,CAC1B,IAAK,IAAM,KAAc,EAAa,CACpC,IAAM,EAAK,SAAS,cAAc,KAAK,CACvC,EAAG,UAAY,EAAa,EAAW,UAAU,CACjD,EAAK,YAAY,EAAG,CAEtB,OAGF,IAAM,EAAK,SAAS,cAAc,KAAK,CACnC,GAAc,EAAI,CACpB,EAAG,UAAY,EAEf,EAAG,YAAc,EAEnB,EAAK,YAAY,EAAG,CAGtB,SAAS,GAAc,EAAuB,CAC5C,MAAO,qBAAqB,KAAK,EAAK,CCpaxC,SAAgB,GAAiB,EAAiB,EAAO,GAAgB,CACvE,IAAM,EAAK,6BACL,EAAM,SAAS,gBAAgB,EAAI,MAAM,CAC/C,EAAI,aAAa,QAAS,OAAO,EAAK,CAAC,CACvC,EAAI,aAAa,SAAU,OAAO,EAAK,CAAC,CACxC,EAAI,aAAa,UAAW,YAAY,CACxC,EAAI,aAAa,OAAQ,OAAO,CAChC,EAAI,aAAa,SAAU,eAAe,CAC1C,EAAI,aAAa,eAAgB,IAAI,CACrC,EAAI,aAAa,iBAAkB,QAAQ,CAC3C,EAAI,aAAa,kBAAmB,QAAQ,CAC5C,EAAI,aAAa,cAAe,OAAO,CAEvC,IAAK,IAAM,KAAK,EAAO,CACrB,IAAM,EAAO,SAAS,gBAAgB,EAAI,OAAO,CACjD,EAAK,aAAa,IAAK,EAAE,CACzB,EAAI,YAAY,EAAK,CAGvB,OAAO,ECOT,IAAM,GAAkB,UAClB,GAAoB,IACpB,GAA2B,EAEjC,SAAS,GAAc,EAAwB,CAC7C,GAAI,OAAO,GAAQ,SAAU,MAAO,CAAC,GAAgB,CACrD,IAAM,EAAQ,EACX,MAAM,UAAU,CAChB,IAAK,GAAS,EAAK,MAAM,CAAC,QAAQ,OAAQ,IAAI,CAAC,CAC/C,OAAQ,GAAS,EAAK,OAAS,EAAE,CAEpC,OADI,EAAM,SAAW,EAAU,CAAC,GAAgB,CACzC,MAAM,KAAK,IAAI,IAAI,EAAM,IAAK,GAAS,EAAK,mBAAmB,CAAC,CAAC,CAAC,CAAC,IAAK,GAC5D,EAAM,KAAM,GAAS,EAAK,mBAAmB,GAAK,EAAQ,EACxD,GACnB,CAGJ,SAAS,GAAc,EAA2B,CAEhD,OADI,IAAS,YAAc,IAAS,YAAc,IAAS,UAAkB,EACtE,UAGT,SAAS,GAAiB,EAAuB,CAC/C,GAAI,OAAO,GAAS,SAAU,MAAO,GACrC,IAAM,EAAa,EAAK,MAAM,CAAC,QAAQ,OAAQ,IAAI,CAEnD,OADI,EAAW,QAAU,GAA0B,EAC5C,GAAG,EAAW,MAAM,EAAG,GAAoB,EAAE,CAAC,SAAS,CAAC,KAGjE,SAAS,GAAY,EAAuC,CAG1D,OAFI,EAAQ,SAAW,EAAQ,SAAiB,WAC5C,EAAQ,SAAW,EAAQ,SAAiB,WACzC,UAGT,SAAS,GAAgB,EAA8B,CAOrD,OALS,GADL,IAAS,WACa,CAAC,aAAc,aAAa,CAElD,IAAS,WACa,CAAC,aAAc,aAAa,CAE9B,CAAC,WAAW,CALoB,GAAG,CAQ7D,SAAS,GACP,EACA,EAAwB,sBACxB,EAAsB,oBACd,CACR,OAAO,IAAU,EAAI,EAAgB,GAAG,EAAM,GAAG,IAGnD,SAAgB,GACd,EACA,EAQa,CACb,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,iCAEtB,IAAM,EAAU,EAAQ,OAAQ,QAChC,GAAI,CAAC,MAAM,QAAQ,EAAQ,EAAI,EAAQ,SAAW,EAAG,CACnD,IAAM,EAAQ,SAAS,cAAc,MAAM,CAI3C,MAHA,GAAM,UAAY,4BAClB,EAAM,YAAc,GAAS,qBAAuB,2BACpD,EAAU,YAAY,EAAM,CACrB,EAGT,IAAM,EAAyB,EAAQ,OACpC,GAAkD,OAAO,GAAM,YAA3B,EACtC,CAEK,EAAc,IAAI,IACxB,IAAK,IAAM,KAAQ,EAAU,CAC3B,IAAM,EAAO,GAAc,EAAK,aAAa,CACvC,EAAU,GAAiB,EAAK,YAAY,CAC5C,EAAS,GAAc,EAAK,WAAW,CAE7C,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAM,EAAM,mBAAmB,CACjC,EAAU,EAAY,IAAI,EAAI,CAmBlC,GAlBK,IACH,EAAU,CACR,MACA,QACA,SAAU,EACV,SAAU,EACV,SAAU,EACV,QAAS,EACT,SAAU,EAAE,CACb,CACD,EAAY,IAAI,EAAK,EAAQ,EAG/B,EAAQ,UAAY,EAChB,IAAS,WAAY,EAAQ,UAAY,EACpC,IAAS,WAAY,EAAQ,UAAY,EAC7C,EAAQ,SAAW,EAEpB,EAAQ,OAAS,GAAK,EAAQ,SAAS,OAAS,IAE9C,CADkB,EAAQ,SAAS,KAAM,GAAU,EAAM,OAAS,EAAQ,CAC1D,CAClB,IAAM,EAA8B,CAClC,KAAM,EACN,OACD,CACG,EAAK,gBAAkB,IAAA,IAAa,OAAO,EAAK,cAAc,CAAC,OAAS,IAC1E,EAAa,OAAS,EAAK,eAE7B,EAAQ,SAAS,KAAK,EAAa,GAM3C,IAAM,EAAW,MAAM,KAAK,EAAY,QAAQ,CAAC,CAAC,MAAM,EAAG,IACrD,EAAE,WAAa,EAAE,SACd,EAAE,MAAM,cAAc,EAAE,MAAM,CADC,EAAE,SAAW,EAAE,SAErD,CAEF,GAAI,EAAS,SAAW,EAAG,CACzB,IAAM,EAAQ,SAAS,cAAc,MAAM,CAI3C,MAHA,GAAM,UAAY,4BAClB,EAAM,YAAc,GAAS,qBAAuB,2BACpD,EAAU,YAAY,EAAM,CACrB,EAGT,IAAM,GAAiB,GAAS,sBAAwB,YAAY,aAAa,CAC3E,GAAiB,GAAS,sBAAwB,YAAY,aAAa,CAE3E,EAAiB,SAAS,cAAc,MAAM,CACpD,EAAe,UAAY,uCAC3B,EAAe,YAAc,GAAS,uBAAyB,uBAC/D,EAAU,YAAY,EAAe,CAErC,IAAI,EAAY,EAAS,IAAI,KAAO,KAE9B,EAAa,SAAS,cAAc,MAAM,CAChD,EAAW,UAAY,+DAEvB,IAAM,EAAa,SAAS,cAAc,UAAU,CACpD,EAAW,UAAY,+CAEvB,IAAM,MAA2B,CAC/B,KAAO,EAAW,YAAY,EAAW,YAAY,EAAW,WAAW,CAC3E,GAAI,CAAC,EAAW,OAChB,IAAM,EAAS,EAAS,KAAM,GAAY,EAAQ,MAAQ,EAAU,CACpE,GAAI,CAAC,EAAQ,OAEb,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,kCAEpB,IAAM,EAAW,SAAS,cAAc,OAAO,CAS/C,GARA,EAAS,UAAY,sCACrB,EAAS,YAAc,GAAG,GACxB,EAAO,SACP,GAAS,+BACT,GAAS,6BACV,CAAC,IAAI,EAAO,MAAM,GACnB,EAAQ,YAAY,EAAS,CAEzB,EAAO,SAAW,EAAG,CACvB,IAAM,EAAM,SAAS,cAAc,OAAO,CAC1C,EAAI,UAAY,sCAChB,EAAI,YAAc,GAAG,EAAO,SAAS,GAAG,IACxC,EAAQ,YAAY,EAAI,CAG1B,GAAI,EAAO,SAAW,EAAG,CACvB,IAAM,EAAM,SAAS,cAAc,OAAO,CAC1C,EAAI,UAAY,sCAChB,EAAI,YAAc,GAAG,EAAO,SAAS,GAAG,IACxC,EAAQ,YAAY,EAAI,CAG1B,EAAW,YAAY,EAAQ,CAE/B,IAAM,EAAW,SAAS,cAAc,MAAM,CAC9C,EAAS,UAAY,+BAErB,IAAK,IAAM,KAAW,EAAO,SAAU,CACrC,IAAM,EAAO,SAAS,cAAc,UAAU,CAC9C,EAAK,UAAY,8BACjB,EAAK,QAAQ,KAAU,EAAQ,KAE/B,IAAM,EAAO,SAAS,cAAc,MAAM,CAK1C,GAJA,EAAK,UAAY,mCACjB,EAAK,YAAc,IAAI,EAAQ,KAAK,GACpC,EAAK,YAAY,EAAK,CAElB,EAAQ,SAAW,IAAA,IAAa,OAAO,EAAQ,OAAO,CAAC,OAAS,EAAG,CACrE,IAAM,EAAS,SAAS,cAAc,MAAM,CAC5C,EAAO,UAAY,qCACnB,EAAO,YAAc,UAAU,OAAO,EAAQ,OAAO,GACrD,EAAK,YAAY,EAAO,CAG1B,EAAS,YAAY,EAAK,CAG5B,EAAW,YAAY,EAAS,EAG5B,MAAsC,CAC1C,IAAK,IAAM,KAAQ,EAAW,iBAA8B,+BAA+B,CAAE,CAC3F,IAAM,EAAW,EAAK,QAAQ,aAAkB,EAChD,EAAK,UAAU,OAAO,sCAAuC,EAAS,CACtE,EAAK,UAAU,OAAO,kBAAmB,EAAS,CAClD,EAAK,aAAa,eAAgB,OAAO,EAAS,CAAC,GAIvD,IAAK,IAAM,KAAW,EAAU,CAC9B,IAAM,EAAO,GAAY,EAAQ,CAC3B,EAAO,SAAS,cAAc,SAAS,CAC7C,EAAK,KAAO,SACZ,EAAK,UAAY,uCACjB,EAAK,QAAQ,WAAgB,EAAQ,IACrC,EAAK,QAAQ,KAAU,EAEvB,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,mCACjB,EAAK,YAAY,GAAgB,EAAK,CAAC,CACvC,EAAK,YAAY,EAAK,CAEtB,IAAM,EAAQ,SAAS,cAAc,OAAO,CAC5C,EAAM,UAAY,oCAClB,EAAM,YAAc,EAAQ,MAC5B,EAAK,YAAY,EAAM,CAEvB,IAAM,EAAQ,SAAS,cAAc,OAAO,CAC5C,EAAM,UAAY,oCAClB,EAAM,YAAc,IAAI,EAAQ,SAAS,GACzC,EAAK,YAAY,EAAM,CAEvB,EAAK,iBAAiB,YAAe,CACnC,EAAY,EAAQ,IACpB,GAAyB,CACzB,GAAc,EACd,CAEF,EAAW,YAAY,EAAK,CAO9B,OAJA,EAAU,YAAY,EAAW,CACjC,GAAyB,CACzB,GAAc,CACd,EAAU,YAAY,EAAW,CAC1B,EC7RT,SAAgB,GAAY,EAAuB,CAEjD,OADK,OAAO,SAAS,EAAM,CACpB,KAAK,IAAI,EAAG,KAAK,IAAI,EAAG,EAAM,CAAC,CADF,EAKtC,SAAgB,GAAc,EAAuB,CAEnD,OADK,OAAO,SAAS,EAAM,CACpB,KAAK,IAAI,EAAG,KAAK,IAAI,IAAK,KAAK,MAAM,EAAM,CAAC,CAAC,CADhB,EAgCtC,SAAgB,GAAwB,EAAiC,CACvE,IAAM,EAAU,GAAY,EAAO,CAC7B,EAAO,KAAK,MAAM,EAAQ,CAC1B,EAAU,EAAU,GAAQ,GAC5B,EAAQ,EAAI,EAAQ,IAEpB,EAAY,SAAS,cAAc,OAAO,CAShD,GARA,EAAU,UAAY,sBACtB,EAAU,aAAa,OAAQ,MAAM,CACrC,EAAU,aAAa,aAAc,GAAG,EAAQ,QAAQ,EAAE,CAAC,iBAAiB,CAExE,EAAO,GACT,EAAU,YAAY,SAAS,eAAe,IAAS,OAAO,EAAK,CAAC,CAAC,CAGnE,EAAS,CACX,IAAM,EAAW,SAAS,cAAc,OAAO,CAC/C,EAAS,UAAY,oBACrB,EAAS,YAAc,IACvB,IAAM,EAAS,SAAS,cAAc,OAAO,CAC7C,EAAO,YAAc,IACrB,EAAS,YAAY,EAAO,CAC5B,EAAU,YAAY,EAAS,CAOjC,OAJI,EAAQ,GACV,EAAU,YAAY,SAAS,eAAe,IAAS,OAAO,EAAM,CAAC,CAAC,CAGjE,EAST,SAAgB,GAAqB,EAA6B,CAChE,EAAI,iBACF,YACM,CACJ,EAAI,MAAM,QAAU,QAEtB,CAAE,KAAM,GAAM,CACf,CCxFH,SAAgB,GAA0B,EAAkE,CAC1G,GAAI,CAAC,EAAS,OACd,IAAM,EACJ,EAAQ,gBAAqB,EAAQ,iBAAsB,EAAQ,gBAAqB,EAAQ,gBAClG,GAAI,OAAO,GAAQ,SAAU,OAC7B,IAAM,EAAI,EAAI,MAAM,CACpB,OAAO,EAAE,OAAS,EAAI,EAAI,IAAA,GAG5B,SAAgB,GACd,EACA,EACoB,CAChB,KAAI,gBAAgB,qBAAuB,GAC/C,OAAO,GAA0B,EAAQ,CAO3C,SAAgB,GACd,EACA,EAC2B,CAC3B,IAAM,EAAc,GAAU,oBAAyB,GAAU,qBACjE,GAAI,IAAgB,UAAY,IAAgB,gBAAiB,OAAO,EACxE,IAAM,EAAa,EAAI,gBAAgB,mBAEvC,OADI,IAAe,UAAY,IAAe,gBAAwB,EAC/D,gBAGT,SAAgB,GAA4B,EAA8B,CACxE,IAAM,EAAK,SAAS,cAAc,MAAM,CAGxC,MAFA,GAAG,UAAY,+BACf,EAAG,YAAc,EACV,EAGT,SAAgB,GACd,EACA,EACoB,CAEpB,IAAK,IAAM,IADE,CAAC,wBAAyB,2BAA4B,uBAAwB,0BAA0B,CAC/F,CACpB,IAAM,EAAI,IAAU,GACpB,GAAI,OAAO,GAAM,UAAY,EAAE,MAAM,CAAE,CACrC,IAAM,EAAI,EAAE,MAAM,CAClB,GAAI,EAAe,EAAE,CAAE,OAAO,GAGlC,IAAM,EAAa,EAAI,gBAAgB,qBACvC,GAAI,OAAO,GAAe,UAAY,EAAW,MAAM,CAAE,CACvD,IAAM,EAAI,EAAW,MAAM,CAC3B,GAAI,EAAe,EAAE,CAAE,OAAO,GAQlC,SAAgB,GAAyB,EAIzB,CACd,IAAM,EAAQ,SAAS,cAAc,MAAM,CAI3C,GAHA,EAAM,UAAY,oCAClB,EAAM,QAAQ,YAAiB,uBAE3B,EAAQ,QAAS,CACnB,IAAM,EAAW,SAAS,cAAc,MAAM,CAC9C,EAAS,UAAY,0CACrB,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,IAAM,GACV,EAAI,QAAU,OAEd,EAAI,IAAM,EAAQ,QAClB,GAAqB,EAAI,CACzB,EAAS,YAAY,EAAI,CACzB,EAAM,YAAY,EAAS,CAG7B,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,0CAEjB,IAAM,EAAS,SAAS,cAAc,MAAM,CAC5C,EAAO,UAAY,4CACnB,EAAO,YAAc,EAAQ,WAE7B,IAAM,EAAO,SAAS,cAAc,OAAO,CAO3C,MANA,GAAK,UAAY,0CACjB,EAAK,YAAc,EAAQ,mBAE3B,EAAK,YAAY,EAAO,CACxB,EAAK,YAAY,EAAK,CACtB,EAAM,YAAY,EAAK,CAChB,ECnET,SAAS,GAAiB,EAAoC,CAC5D,IAAM,EAAa,EAAK,QAAQ,IAChC,GAAI,OAAO,GAAe,UAAY,EAAW,OAAS,EAAG,OAAO,EACpE,IAAM,EAAU,EAAK,QAAQ,QAI7B,OAHI,GAAW,OAAO,GAAY,UAAY,QAAS,GAAW,OAAO,EAAQ,KAAQ,SAChF,EAAQ,IAEV,KAGT,IAAM,GAAsC,CAC1C,OAAQ,aACR,WAAY,gBACZ,iBAAkB,sBACnB,CAED,SAAS,GAAa,EAA0B,EAAsD,CACpG,GAAI,CAAC,GAAQ,CAAC,EAAM,OAAO,KAC3B,IAAM,EAAM,GAAY,GAExB,OADK,EACG,EAAgC,IAAQ,EAD/B,EAInB,SAAS,GAAgB,EAAsD,CAC7E,IAAM,EAAM,EAAQ,OACd,EAAM,OAAO,GAAQ,SAAW,EAAM,OAAO,GAAQ,SAAW,WAAW,EAAI,CAAG,IACxF,GAAI,OAAO,MAAM,EAAI,EAAI,GAAO,EAAG,OAAO,KAC1C,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,UAAY,iCAChB,EAAI,QAAQ,YAAiB,qBAC7B,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,sCACjB,EAAK,aAAa,cAAe,OAAO,CACxC,EAAK,UACH,2LACF,IAAM,EAAQ,SAAS,cAAc,OAAO,CAK5C,MAJA,GAAM,UAAY,uCAClB,EAAM,YAAc,EAAI,QAAQ,EAAE,CAClC,EAAI,YAAY,EAAK,CACrB,EAAI,YAAY,EAAM,CACf,EAIT,SAAS,GACP,EACA,EACA,EACA,EACA,EACM,CACN,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,gCAClB,EAAM,QAAQ,YAAiB,oBAE/B,IAAM,EAAU,EAAK,QACf,EAAW,EAAQ,SACzB,GAAI,GAAY,EAAe,EAAS,CAAE,CACxC,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,UAAY,8BAChB,EAAI,QAAQ,YAAiB,oBAC7B,EAAiB,EAAK,MAAO,EAAS,CACtC,EAAI,QAAU,OACd,EAAI,IAAM,EACV,GAAqB,EAAI,CACzB,EAAM,YAAY,EAAI,CAGxB,IAAM,EAAM,GAAiB,EAAK,CAClC,GAAI,GAAO,CAAC,GAAS,mBAAoB,CACvC,IAAM,EAAa,SAAS,cAAc,MAAM,CAGhD,GAFA,EAAW,UAAY,wCAEnB,EAAI,iBAAkB,CACxB,IAAM,EAAQ,SAAS,cAAc,SAAS,CAC9C,EAAM,UAAY,4BAClB,EAAM,KAAO,SACb,EAAM,QAAQ,mBAAqB,EACnC,EAAM,aAAa,aAAc,EAAI,MAAM,qBAAuB,mBAAmB,CACrF,IAAM,EAAQ,EAAI,eAAe,IAAI,EAAI,EAAI,GACzC,GAAO,EAAM,UAAU,IAAI,oCAAoC,CAEnE,EAAM,UAAY,yDADF,EAAQ,eAAiB,OAC0C,qMACnF,EAAM,iBAAiB,QAAU,GAAM,CACrC,EAAE,iBAAiB,CACnB,EAAM,UAAU,OAAO,oCAAoC,CAC3D,EACG,cAAc,MAAM,EACnB,aACA,OACA,EAAM,UAAU,SAAS,oCAAoC,CAAG,eAAiB,OAClF,CACH,EAAI,mBAAmB,EAAK,EAAQ,EACpC,CACF,EAAW,YAAY,EAAM,CAG/B,IAAM,EAAmB,EAAI,MAAM,kBAAoB,eACjD,EAAO,SAAS,cAAc,SAAS,CAC7C,EAAK,UAAY,iCACjB,EAAK,KAAO,SACZ,EAAK,aAAa,aAAc,EAAiB,CACjD,EAAK,QAAQ,QAAa,EAC1B,EAAK,UACH,ySAKF,IAAM,EAAW,SAAS,cAAc,OAAO,CAC/C,EAAS,UAAY,sCACrB,EAAS,YAAc,EACvB,EAAK,YAAY,EAAS,CAC1B,EAAK,iBAAiB,QAAU,GAAM,CACpC,EAAE,iBAAiB,CACnB,EAAI,SAAS,CACX,MAAO,EACP,KAAM,cACN,QAAS,CAAE,MAAK,GAAI,EAAW,CAAE,UAAW,EAAU,CAAG,EAAE,CAAG,CAC/D,CAAC,EACF,CACF,EAAW,YAAY,EAAK,CAE5B,EAAM,YAAY,EAAW,CAG/B,EAAO,YAAY,EAAM,CAG3B,SAAS,GAAe,EAAkC,EAAmB,EAAoC,CAC/G,IAAM,EAAQ,EAAQ,MAChB,EAAgB,EAAQ,cAC9B,GAAI,CAAC,EAAO,OAEZ,IAAM,EAAiB,GAAyB,EAAK,EAAQ,CACvD,EAAY,GAA0B,EAAK,EAAQ,CACnD,EAAc,CAAC,EAAE,GAAiB,IAAkB,GACpD,EAAkB,CAAC,EAAE,GAAkB,GACvC,EAAU,GAA4B,EAAK,EAAQ,CAEnD,EAAW,SAAS,cAAc,MAAM,CAI9C,GAHA,EAAS,UAAY,gCACrB,EAAS,QAAQ,YAAiB,oBAE9B,EAAiB,CACnB,IAAM,EAAQ,GAAyB,CACrC,WAAY,EACZ,mBAAoB,EAAY,EAAO,EAAI,QAAQ,CACnD,GAAI,IAAY,IAAA,GAA0B,EAAE,CAAhB,CAAE,UAAS,CACxC,CAAC,CACF,GAAI,GAAe,IAAc,SAAU,CACzC,EAAS,UAAU,IAAI,wCAAwC,CAC/D,EAAS,YAAY,EAAM,CAC3B,IAAM,EAAM,SAAS,cAAc,OAAO,CAC1C,EAAI,UAAY,oCAChB,EAAI,aAAa,cAAe,OAAO,CACvC,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,yCACjB,EAAK,YAAc,EAAY,EAAgB,EAAI,QAAQ,CAC3D,EAAS,YAAY,EAAI,CACzB,EAAS,YAAY,EAAK,KACrB,CACL,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,yCACjB,EAAK,YAAc,EAAY,EAAgB,EAAI,QAAQ,CAC3D,EAAS,YAAY,EAAK,CAC1B,EAAS,YAAY,SAAS,eAAe,IAAI,CAAC,CAClD,EAAS,YAAY,EAAM,CAE7B,EAAK,YAAY,EAAS,CAC1B,OAGF,GAAI,GAAe,IAAc,SAAU,CACzC,EAAS,UAAU,IAAI,wCAAwC,CAC/D,IAAM,EAAM,SAAS,cAAc,OAAO,CAC1C,EAAI,UAAY,wCAChB,EAAI,YAAc,EAAY,EAAO,EAAI,QAAQ,CACjD,IAAM,EAAM,SAAS,cAAc,OAAO,CAC1C,EAAI,UAAY,oCAChB,EAAI,aAAa,cAAe,OAAO,CACvC,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,yCACjB,EAAK,YAAc,EAAY,EAAgB,EAAI,QAAQ,CAC3D,EAAS,YAAY,EAAI,CACzB,EAAS,YAAY,EAAI,CACzB,EAAS,YAAY,EAAK,SACjB,EAAa,CACtB,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,yCACjB,EAAK,YAAc,EAAY,EAAgB,EAAI,QAAQ,CAC3D,EAAS,YAAY,EAAK,CAC1B,EAAS,YAAY,SAAS,eAAe,IAAI,CAAC,CAClD,IAAM,EAAU,SAAS,cAAc,OAAO,CAC9C,EAAQ,UAAY,wCACpB,EAAQ,YAAc,EAAY,EAAO,EAAI,QAAQ,CACrD,EAAS,YAAY,EAAQ,KACxB,CACL,IAAM,EAAU,SAAS,cAAc,OAAO,CAC9C,EAAQ,UAAY,wCACpB,EAAQ,YAAc,EAAY,EAAO,EAAI,QAAQ,CACrD,EAAS,YAAY,EAAQ,CAG/B,GAAI,EAAgB,CAClB,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,sCAClB,EAAM,YAAY,GAA4B,EAAe,CAAC,CAC9D,EAAM,YAAY,EAAS,CAC3B,EAAK,YAAY,EAAM,MAEvB,EAAK,YAAY,EAAS,CAI9B,SAAS,GAAqB,EAAqB,EAAyB,CAC1E,IAAM,EAAS,OAAO,EAAK,QAAW,SAAW,EAAK,OAAO,MAAM,CAAG,GAChE,EAAkB,OAAO,EAAK,iBAAoB,SAAW,EAAK,gBAAgB,MAAM,CAAG,GAC7F,MAAC,GAAU,CAAC,GAEhB,IAAI,EAAQ,CACV,IAAM,EAAW,SAAS,cAAc,IAAI,CAC5C,EAAS,UAAY,iCACrB,EAAS,QAAQ,YAAiB,qBAClC,EAAS,YAAc,EACvB,EAAK,YAAY,EAAS,CAG5B,GAAI,EAAiB,CACnB,IAAM,EAAW,SAAS,cAAc,MAAM,CAC9C,EAAS,UAAY,iCACrB,EAAS,QAAQ,YAAiB,qBAClC,EAAS,YAAc,EACvB,EAAK,YAAY,EAAS,GAQ9B,SAAS,GAAe,EAAqB,EAA8B,EAAgC,CACzG,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,EACb,6EACA,8EACJ,EAAK,QAAQ,YAAiB,EAAW,0BAA4B,mBACrE,IAAM,EAAU,EAAK,QACf,EAAM,GAAiB,EAAK,CAC5B,EAAO,EAAQ,KAAqB,GACpC,EAAW,EAAQ,SACnB,EAAU,EAAQ,QAClB,EAAU,CAAC,EAAE,GAAO,GAAY,IAAY,IAC5C,EAAS,EAAK,OAChB,GAAO,EAAI,gBACb,EAAK,UAAU,IAAI,gBAAgB,CACnC,EAAK,iBAAiB,QAAU,GAAM,CAC/B,EAAE,OAAuB,QAAQ,+BAA+B,EAChE,EAAE,OAAuB,QAAQ,6BAA6B,EAC9D,EAAE,OAAuB,QAAQ,kCAAkC,EACxE,EAAI,iBAAiB,CAAE,MAAK,MAAK,CAAC,EAClC,EACO,IACT,EAAK,UAAU,IAAI,gBAAgB,CACnC,EAAK,iBAAiB,QAAU,GAAM,CAC/B,EAAE,OAAuB,QAAQ,+BAA+B,EAChE,EAAE,OAAuB,QAAQ,6BAA6B,EAC9D,EAAE,OAAuB,QAAQ,kCAAkC,EACxE,EAAI,SAAS,EAAO,EACpB,EAEJ,IAAM,EAAO,EAAQ,MAAsB,gBACrC,EAAgB,CAAC,GAAY,EAAI,WAAa,GAG9C,EAAY,EACb,GAAa,EAAK,KAAM,EAAI,KAAK,EAAI,EAAI,MAAM,YAAc,YAC9D,GAAa,EAAK,KAAM,EAAI,KAAK,CACrC,GAAI,GAAa,CAAC,EAAe,CAC/B,IAAM,EAAQ,SAAS,cAAc,OAAO,CAC5C,EAAM,UAAY,0CAClB,EAAM,QAAQ,YAAiB,yBAC/B,EAAM,YAAc,EACpB,EAAK,YAAY,EAAM,CAGzB,IAAM,EAAS,SAAS,cAAc,MAAM,CAC5C,EAAO,UAAY,kCACnB,EAAO,QAAQ,YAAiB,sBAChC,GAAmB,EAAM,EAAK,EAAQ,EAAK,CAAE,mBAAoB,EAAe,CAAC,CAEjF,IAAM,EAAO,SAAS,cAAc,MAAM,CAI1C,GAHA,EAAK,UAAY,+BACjB,EAAK,QAAQ,YAAiB,mBAE1B,GAAa,EAAe,CAC9B,IAAM,EAAW,SAAS,cAAc,MAAM,CAC9C,EAAS,UAAY,oCACrB,EAAS,QAAQ,YAAiB,wBAClC,EAAS,YAAc,EACvB,EAAK,YAAY,EAAS,CAG5B,IAAM,EAAO,EAAQ,KACrB,GAAI,EAAM,CACR,IAAM,EAAS,SAAS,cAAc,MAAM,CAC5C,EAAO,UAAY,+BACnB,EAAO,QAAQ,YAAiB,mBAChC,EAAO,YAAc,EACrB,EAAK,YAAY,EAAO,CAG1B,IAAM,EAAY,GAAgB,EAAQ,CACtC,GAAW,EAAK,YAAY,EAAU,CAG1C,GAAe,EAAS,EAAM,EAAI,CAE9B,EAAK,QAAU,EAAK,OAAO,OAAS,GACtC,EAAK,YAAY,GAAqB,EAAK,OAAO,CAAC,CAGrD,EAAO,YAAY,EAAK,CACxB,EAAK,YAAY,EAAO,CAExB,IAAM,EAAS,SAAS,cAAc,MAAM,CAG5C,GAFA,EAAO,UAAY,iCACnB,EAAO,QAAQ,YAAiB,qBAC5B,EACF,GAAqB,EAAM,EAAO,KAC7B,CACL,IAAM,EAAU,OAAO,EAAK,QAAW,SAAW,EAAK,OAAO,MAAM,CAAG,GACvE,GAAI,EAAS,CACX,IAAM,EAAY,SAAS,cAAc,IAAI,CAC7C,EAAU,UAAY,kCACtB,EAAU,QAAQ,YAAiB,sBACnC,EAAU,YAAc,EACxB,EAAO,YAAY,EAAU,EAG7B,EAAO,WAAW,OAAS,GAC7B,EAAK,YAAY,EAAO,CAG1B,IAAM,EAAc,CAAC,EAAE,GAAO,EAAI,qBAAuB,GACnD,IAAW,GAAW,IAAW,CAAC,EAExC,GAAI,GAAe,GAAW,EAAQ,CACpC,IAAM,EAAU,SAAS,cAAc,MAAM,CAM7C,GALA,EAAQ,UAAY,kCACpB,EAAQ,QAAQ,YAAiB,sBACjC,EAAQ,MAAM,QAAU,EAAc,GAAK,OAC3C,EAAK,YAAY,EAAQ,CAErB,GAAS,CACX,IAAM,EAAM,SAAS,cAAc,SAAS,CAC5C,EAAI,UAAY,sDAChB,EAAI,QAAQ,YAAiB,kBAC7B,EAAI,KAAO,SACX,EAAI,YAAc,EACb,EAAI,MAAM,iBAAmB,cAC7B,EAAI,MAAM,aAAe,eAC9B,EAAI,iBAAiB,QAAU,GAAM,CAEnC,GADA,EAAE,iBAAiB,CACf,EAAS,CACX,EAAI,SAAS,CACX,MAAO,EAAI,MAAM,iBAAmB,cACpC,KAAM,YACN,QAAS,CAAO,MAAgB,WAAW,SAAU,EAAG,CACzD,CAAC,CACF,OAEG,KACL,IAAI,EAAO,OAAS,eAAiB,GAAO,EAAI,eAAgB,CAC9D,EAAI,eAAe,CAAE,MAAK,MAAK,CAAC,CAChC,OAEF,EAAI,SAAS,EAAO,GACpB,CACF,EAAK,YAAY,EAAI,EAIzB,OAAO,EAGT,SAAgB,GAAiB,EAAoB,EAA2C,CAC9F,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,4BACtB,EAAU,QAAQ,YAAiB,eAEnC,IAAM,EAAe,EAAQ,OAAQ,aAAkB,EAAE,CACzD,GAAI,EAAY,SAAW,EAAG,OAAO,EAErC,IAAM,EAAQ,SAAS,cAAc,KAAK,CAC1C,EAAM,UAAY,kCAClB,EAAM,QAAQ,YAAiB,qBAC/B,EAAM,YAAc,EAAI,MAAM,iBAAmB,YACjD,EAAU,YAAY,EAAM,CAE5B,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,mCACtB,EAAU,QAAQ,YAAiB,sBAEnC,IAAM,EAAQ,EAAY,GAI1B,GAFA,EAAU,YAAY,GAAe,EAAO,EAAK,GAAK,CAAC,CAEnD,EAAY,OAAS,EAAG,CAC1B,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,iCACjB,EAAK,QAAQ,YAAiB,oBAC9B,IAAK,IAAI,EAAI,EAAG,EAAI,EAAY,OAAQ,IAAK,CAC3C,IAAM,EAAa,EAAY,GACzB,EAAW,EAAW,OAAS,SACrC,EAAK,YAAY,GAAe,EAAY,EAAK,EAAS,CAAC,CAE7D,EAAU,YAAY,EAAK,CAI7B,OADA,EAAU,YAAY,EAAU,CACzB,EAGT,SAAS,GAAqB,EAAuC,CACnE,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,iCACtB,EAAU,QAAQ,YAAiB,qBACnC,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,yCACjB,EAAK,QAAQ,YAAiB,oBAC9B,EAAK,QAAQ,UAAe,EAAM,WAAa,UAC/C,EAAK,YAAc,EAAM,MACzB,EAAU,YAAY,EAAK,CAE7B,OAAO,ECzcT,SAAgB,GAA0B,EAAoB,EAA2C,CACvG,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,kDACtB,EAAU,QAAQ,YAAiB,wBAEnC,IAAM,EAAQ,EAAQ,OAAS,EAAE,CAC3B,EAAQ,EAAM,MACd,EAAc,EAAM,YACpB,EAAS,EAAM,OACf,EAAW,EAAI,MAAM,oBAAsB,eAG3C,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,qCACjB,EAAK,YACH,GAAiB,CAAC,gEAAiE,SAAU,UAAU,CAAC,CACzG,CACD,EAAU,YAAY,EAAK,CAE3B,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,qCACjB,EAAK,QAAQ,YAAiB,wBAG9B,IAAM,EAAU,SAAS,cAAc,MAAM,CAO7C,GANA,EAAQ,UAAY,sCACpB,EAAQ,QAAQ,YAAiB,yBACjC,EAAQ,YAAc,GAAS,EAAI,MAAM,sBAAwB,mBACjE,EAAK,YAAY,EAAQ,CAGrB,EAAa,CACf,IAAM,EAAW,SAAS,cAAc,MAAM,CAC9C,EAAS,UAAY,yCACrB,EAAS,QAAQ,YAAiB,4BAElC,EAAS,aADQ,EAAI,MAAM,yBAA2B,wBACtB,QAAQ,UAAW,EAAY,CAC/D,EAAK,YAAY,EAAS,CAG5B,EAAU,YAAY,EAAK,CAG3B,IAAM,EAAM,SAAS,cAAc,OAAO,CAC1C,EAAI,UAAY,oCAChB,EAAI,QAAQ,YAAiB,uBAC7B,IAAM,EAAa,SAAS,cAAc,OAAO,CACjD,EAAW,YAAc,EACzB,EAAI,YAAY,EAAW,CAC3B,IAAM,EAAU,SAAS,cAAc,OAAO,CAoB9C,MAnBA,GAAQ,UAAY,yCACpB,EAAQ,YAAY,GAAiB,CAAC,WAAY,gBAAgB,CAAE,GAAG,CAAC,CACxE,EAAI,YAAY,EAAQ,CACxB,EAAU,YAAY,EAAI,CAGtB,IACF,EAAU,UAAU,IAAI,gBAAgB,CACxC,EAAU,aAAa,OAAQ,SAAS,CACxC,EAAU,aAAa,WAAY,IAAI,CACvC,EAAU,iBAAiB,YAAe,EAAI,SAAS,EAAO,CAAC,CAC/D,EAAU,iBAAiB,UAAY,GAAqB,EACtD,EAAE,MAAQ,SAAW,EAAE,MAAQ,OACjC,EAAE,gBAAgB,CAClB,EAAI,SAAS,EAAO,GAEtB,EAGG,EC1DT,SAAS,GAAwB,EAAqC,CACpE,GAAI,EAAM,OAAO,OAAS,cAAe,OAAO,EAAM,OACtD,IAAM,EACJ,EAAM,OAAO,SAAW,OAAO,EAAM,OAAO,SAAY,SACnD,EAAM,OAAO,QACd,KACA,EACH,OAAO,GAAU,OAAa,UAAY,EAAQ,MAAS,MAAM,EACjE,OAAO,GAAU,MAAY,UAAY,EAAQ,KAAQ,MAAM,EAChE,EAAM,KAAK,MAAM,CACnB,GAAI,CAAC,EAAM,OAAO,EAAM,OAExB,IAAM,EAA6C,CACjD,OACA,kBAAmB,EACpB,CAID,GAHI,OAAO,GAAU,KAAW,UAAY,EAAQ,IAAO,MAAM,GAC/D,EAAkB,IAAS,EAAQ,KAEjC,MAAM,QAAQ,GAAU,WAAc,CAAE,CAC1C,IAAM,EAAY,EAAQ,WAAc,OAAQ,GAAuB,OAAO,GAAQ,UAAY,EAAI,OAAS,EAAE,CAC7G,EAAU,OAAS,IAAG,EAAkB,WAAgB,GAE9D,MAAO,CACL,MAAO,EAAM,OAAO,MACpB,KAAM,YACN,QAAS,EACV,CAGH,SAAgB,GAAsB,EAAoB,EAA2C,CACnG,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,8BACtB,EAAU,QAAQ,YAAiB,oBAEnC,IAAM,EAAW,EAAQ,OAAQ,SAAc,EAAE,CACjD,GAAI,EAAQ,SAAW,EAAG,OAAO,EAEjC,IAAM,EAAc,EAAQ,OAAQ,aAC9B,EACJ,OAAO,GAAgB,UAAY,EAAY,MAAM,CAAC,OAAS,EAC3D,EAAY,MAAM,CAClB,EAAI,MAAM,wBAChB,GAAI,EAAc,CAChB,IAAM,EAAU,SAAS,cAAc,KAAK,CAC5C,EAAQ,UAAY,sCACpB,EAAQ,YAAc,EACtB,EAAU,YAAY,EAAQ,CAGhC,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,qCACtB,EAAU,QAAQ,YAAiB,2BAEnC,IAAK,IAAM,KAAS,EAAS,CAC3B,IAAM,EAAO,SAAS,cAAc,MAAM,CAO1C,GANA,EAAK,UAAY,sCACjB,EAAK,QAAQ,YAAiB,mBAC9B,EAAK,UAAU,IAAI,gBAAgB,CACnC,EAAK,iBAAiB,YAAe,EAAI,SAAS,GAAwB,EAAM,CAAC,CAAC,CAG9E,EAAM,OAAS,EAAe,EAAM,MAAM,CAAE,CAC9C,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,UAAY,iCAChB,EAAI,QAAQ,YAAiB,yBAC7B,EAAI,IAAM,EAAM,MAChB,EAAI,IAAM,EAAM,KAChB,EAAI,MAAQ,GACZ,EAAI,OAAS,GACb,EAAK,YAAY,EAAI,CAGvB,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,kCACjB,EAAK,QAAQ,YAAiB,wBAE9B,IAAM,EAAS,SAAS,cAAc,OAAO,CAM7C,GALA,EAAO,UAAY,kCACnB,EAAO,QAAQ,YAAiB,wBAChC,EAAO,YAAc,EAAM,KAC3B,EAAK,YAAY,EAAO,CAEpB,EAAM,QAAU,EAAM,OAAO,OAAS,EAAG,CAC3C,IAAM,EAAW,SAAS,cAAc,MAAM,CAC9C,EAAS,UAAY,oCACrB,EAAS,QAAQ,YAAiB,0BAClC,IAAK,IAAM,KAAS,EAAM,OAAO,MAAM,EAAG,EAAE,CAAE,CAC5C,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,4CACjB,EAAK,YAAc,EACnB,EAAS,YAAY,EAAK,CAE5B,EAAK,YAAY,EAAS,CAG5B,EAAK,YAAY,EAAK,CAGtB,IAAM,EAAQ,SAAS,cAAc,OAAO,CAC5C,EAAM,UAAY,mCAClB,EAAM,YAAc,IACpB,EAAK,aAAa,EAAO,EAAK,WAAW,CAEzC,EAAU,YAAY,EAAK,CAK7B,OAFA,EAAU,YAAY,EAAU,CAEzB,ECjHT,SAAgB,GAA6B,EAAoB,EAA2C,CAC1G,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,sCACtB,EAAU,QAAQ,YAAiB,4BAEnC,IAAM,EAAW,EAAQ,OAAQ,SAAc,EAAE,CACjD,GAAI,EAAQ,SAAW,EAAG,OAAO,EAEjC,IAAK,IAAM,KAAS,EAAS,CAC3B,IAAM,EAAO,SAAS,cAAc,MAAM,CAO1C,GANA,EAAK,UAAY,8CACjB,EAAK,QAAQ,YAAiB,2BAC9B,EAAK,UAAU,IAAI,gBAAgB,CACnC,EAAK,iBAAiB,YAAe,EAAI,SAAS,EAAM,OAAO,CAAC,CAG5D,EAAM,OAAS,EAAe,EAAM,MAAM,CAAE,CAC9C,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,UAAY,yCAChB,EAAI,QAAQ,YAAiB,iCAC7B,EAAI,IAAM,EAAM,MAChB,EAAI,IAAM,EAAM,UAChB,EAAI,MAAQ,GACZ,EAAI,OAAS,GACb,EAAK,YAAY,EAAI,CAGvB,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,0CACjB,EAAK,QAAQ,YAAiB,gCAE9B,IAAM,EAAS,SAAS,cAAc,MAAM,CAC5C,EAAO,UAAY,0CACnB,EAAO,QAAQ,YAAiB,gCAChC,EAAO,YAAc,EAAM,UAC3B,EAAK,YAAY,EAAO,CAExB,EAAK,YAAY,EAAK,CACtB,EAAU,YAAY,EAAK,CAG7B,OAAO,ECtDT,SAAgB,GAA+B,EAAwB,EAA2C,CAChH,IAAM,EAAa,EAAa,QAAU,EACpC,EAAQ,EAAI,MAAM,iBAAmB,UACrC,EAAU,EAAI,2BAEd,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,uCACjB,EAAK,QAAQ,YAAiB,kBAE9B,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,2CAEpB,IAAM,EAAQ,SAAS,cAAc,OAAO,CAC5C,EAAM,UAAY,yCAClB,EAAM,YAAc,OAAO,EAAa,OAAO,CAC/C,EAAQ,YAAY,EAAM,CAE1B,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,wCAEjB,IAAM,EAAQ,SAAS,cAAc,MAAM,CAO3C,GANA,EAAM,UAAY,yCAClB,EAAM,YAAc,EAChB,GAAG,EAAM,IAAI,EAAa,OAAO,GAChC,EAAI,MAAM,gBAAkB,6BACjC,EAAK,YAAY,EAAM,CAEnB,EAAS,CACX,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,2CACtB,EAAU,aAAa,OAAQ,SAAS,CACxC,EAAU,aAAa,YAAa,SAAS,CAC7C,EAAU,YAAc,EACxB,EAAK,YAAY,EAAU,CAG7B,EAAQ,YAAY,EAAK,CACzB,EAAK,YAAY,EAAQ,CAEzB,IAAM,EAAS,SAAS,cAAc,SAAS,CAgB/C,GAfA,EAAO,UAAY,kEACnB,EAAO,KAAO,SACd,EAAO,YAAc,EACrB,EAAO,SAAW,CAAC,EACd,GAAY,EAAO,UAAU,IAAI,oDAAoD,CAC1F,EAAO,iBAAiB,YAAe,CAChC,GACL,EAAI,SAAS,CACX,MAAO,EACP,KAAM,qBACN,QAAS,CAAE,SAAU,CAAC,GAAG,EAAa,CAAE,CACzC,CAAC,EACF,CACF,EAAK,YAAY,EAAO,CAEpB,EAAI,sBAAuB,CAC7B,IAAM,EAAQ,SAAS,cAAc,SAAS,CAC9C,EAAM,UAAY,4EAClB,EAAM,QAAQ,YAAiB,wBAC/B,EAAM,KAAO,SACb,EAAM,aAAa,aAAc,EAAI,MAAM,gBAAkB,QAAQ,CACrE,EAAM,UACJ,oMACF,EAAM,iBAAiB,QAAU,GAAM,CACrC,EAAE,gBAAgB,CAClB,EAAE,iBAAiB,CACnB,EAAI,wBAAwB,GAAG,EAC/B,CACF,EAAK,YAAY,EAAM,CAGzB,OAAO,ECzET,SAAgB,GAAkB,EAA2D,CAC3F,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,uCACtB,EAAU,QAAQ,YAAiB,YAEnC,IAAM,EAAc,EAAQ,OAAQ,YACpC,GAAI,EAAa,CACf,IAAM,EAAU,SAAS,cAAc,KAAK,CAC5C,EAAQ,UAAY,iCACpB,EAAQ,YAAc,EACtB,EAAU,YAAY,EAAQ,CAGhC,IAAM,EAAO,EAAQ,OAAQ,KACvB,EAAO,EAAQ,OAAQ,KAE7B,GAAI,GAAQ,EAAK,OAAS,EAAG,CAC3B,IAAM,EAAW,SAAS,cAAc,KAAK,CAC7C,EAAS,UAAY,8BACrB,EAAS,QAAQ,YAAiB,YAClC,IAAK,IAAM,KAAO,EAAM,CACtB,IAAM,EAAK,SAAS,cAAc,KAAK,CACvC,EAAG,UAAY,8BACf,EAAG,QAAQ,YAAiB,YAC5B,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,+DACjB,EAAK,YAAc,IACnB,EAAG,YAAY,EAAK,CACpB,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,YAAc,EACnB,EAAG,YAAY,EAAK,CACpB,EAAS,YAAY,EAAG,CAE1B,EAAU,YAAY,EAAS,CAGjC,GAAI,GAAQ,EAAK,OAAS,EAAG,CAC3B,IAAM,EAAW,SAAS,cAAc,KAAK,CAC7C,EAAS,UAAY,8BACrB,EAAS,QAAQ,YAAiB,YAClC,IAAK,IAAM,KAAO,EAAM,CACtB,IAAM,EAAK,SAAS,cAAc,KAAK,CACvC,EAAG,UAAY,8BACf,EAAG,QAAQ,YAAiB,YAC5B,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,+DACjB,EAAK,YAAc,IACnB,EAAG,YAAY,EAAK,CACpB,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,YAAc,EACnB,EAAG,YAAY,EAAK,CACpB,EAAS,YAAY,EAAG,CAE1B,EAAU,YAAY,EAAS,CAGjC,OAAO,ECnCT,SAAgB,GAA0B,EAAoB,EAA+C,CAC3G,IAAM,EAAU,EAAQ,OAAQ,QAAyC,EAAE,CACrE,EAAc,EAAQ,OAAQ,YAAiD,EAAE,CAEjF,EAAY,SAAS,cAAc,MAAM,CAI/C,GAHA,EAAU,UAAY,0BACtB,EAAU,QAAQ,YAAiB,uBAE/B,EAAO,SAAW,EAAG,OAAO,EAGhC,IAAM,EAAS,SAAS,cAAc,MAAM,CAC5C,EAAO,UAAY,2CACnB,EAAO,QAAQ,YAAiB,qBAChC,EAAO,aAAa,OAAQ,UAAU,CAEtC,IAAM,EAA4B,EAAE,CAC9B,EAAwB,EAAE,CAE1B,EAAe,GAAwB,CAC3C,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAQ,IAAK,CACpC,IAAM,EAAW,IAAM,EACvB,EAAK,GAAI,UAAU,OAAO,sCAAuC,EAAS,CAC1E,EAAK,GAAI,UAAU,OAAO,YAAa,EAAS,CAChD,EAAK,GAAI,aAAa,gBAAiB,OAAO,EAAS,CAAC,CACxD,EAAK,GAAI,SAAW,EAAW,EAAI,GACnC,EAAO,GAAI,MAAM,QAAU,EAAW,GAAK,SAI/C,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,OAAQ,IAAK,CACtC,IAAM,EAAQ,EAAO,GACf,EAAQ,mBAAmB,IAC3B,EAAU,qBAAqB,IAG/B,EAAM,SAAS,cAAc,SAAS,CAC5C,EAAI,UAAY,sCAChB,EAAI,KAAO,SACX,EAAI,QAAQ,YAAiB,iBAC7B,EAAI,GAAK,EACT,EAAI,aAAa,OAAQ,MAAM,CAC/B,EAAI,aAAa,gBAAiB,EAAQ,CAC1C,EAAI,aAAa,gBAAiB,OAAO,IAAM,EAAE,CAAC,CAClD,EAAI,SAAW,IAAM,EAAI,EAAI,GACzB,IAAM,GAAG,EAAI,UAAU,IAAI,sCAAuC,YAAY,CAClF,EAAI,YAAc,EAAM,UAExB,EAAI,iBAAiB,YAAe,EAAY,EAAE,CAAC,CACnD,EAAI,iBAAiB,UAAY,GAAqB,CACpD,IAAI,EAAO,GACP,EAAE,MAAQ,cAAgB,EAAE,MAAQ,YACtC,GAAQ,EAAI,GAAK,EAAO,OACf,EAAE,MAAQ,aAAe,EAAE,MAAQ,UAC5C,GAAQ,EAAI,EAAI,EAAO,QAAU,EAAO,OAC/B,EAAE,MAAQ,OACnB,EAAO,EACE,EAAE,MAAQ,QACnB,EAAO,EAAO,OAAS,GAErB,GAAQ,IACV,EAAE,gBAAgB,CAClB,EAAY,EAAK,CACjB,EAAK,GAAO,OAAO,GAErB,CAEF,EAAK,KAAK,EAAI,CACd,EAAO,YAAY,EAAI,CAGvB,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,+BAClB,EAAM,QAAQ,YAAiB,mBAC/B,EAAM,GAAK,EACX,EAAM,aAAa,OAAQ,WAAW,CACtC,EAAM,aAAa,kBAAmB,EAAM,CACxC,IAAM,IAAG,EAAM,MAAM,QAAU,QAEnC,IAAK,IAAM,KAAW,EAAM,SAAU,CACpC,IAAM,EAAO,GAA0B,EAAS,EAAQ,CACxD,EAAM,YAAY,EAAK,CAGzB,EAAO,KAAK,EAAM,CAGpB,EAAU,YAAY,EAAO,CAC7B,IAAK,IAAM,KAAS,EAAQ,EAAU,YAAY,EAAM,CAGxD,GAAI,EAAW,OAAS,EAAG,CACzB,IAAM,EAAgB,SAAS,cAAc,MAAM,CACnD,EAAc,UAAY,kDAC1B,EAAc,QAAQ,YAAiB,yBAEvC,IAAK,IAAM,KAAO,EAAY,CAC5B,IAAM,EAAS,SAAS,cAAc,SAAS,CAC/C,EAAO,UAAY,8CACnB,EAAO,KAAO,SACd,EAAO,QAAQ,YAAiB,wBAChC,EAAO,YAAc,EAAI,MACrB,EAAI,QACN,EAAO,iBAAiB,YAAe,CACrC,EAAQ,SAAS,EAAI,OAAQ,EAC7B,CAEJ,EAAc,YAAY,EAAO,CAGnC,EAAU,YAAY,EAAc,CAGtC,OAAO,EAGT,SAAS,GAA0B,EAA4B,EAA2C,CACxG,IAAM,EAAO,SAAS,cAAc,MAAM,CAI1C,GAHA,EAAK,UAAY,2EACjB,EAAK,QAAQ,YAAiB,0BAE1B,EAAQ,UAAY,EAAe,EAAQ,SAAS,CAAE,CACxD,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,UAAY,gCAChB,EAAI,IAAM,EAAQ,SAClB,EAAI,IAAM,EAAQ,KAClB,EAAI,QAAU,OACd,GAAqB,EAAI,CACzB,EAAK,YAAY,EAAI,CAGvB,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,iCAEjB,IAAM,EAAS,SAAS,cAAc,MAAM,CAK5C,GAJA,EAAO,UAAY,iCACnB,EAAO,YAAc,EAAQ,KAC7B,EAAK,YAAY,EAAO,CAEpB,EAAQ,MAAO,CACjB,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,kCACpB,EAAQ,YAAc,EAAY,EAAQ,MAAO,EAAI,QAAQ,CAC7D,EAAK,YAAY,EAAQ,CAqB3B,OAlBA,EAAK,YAAY,EAAK,EAGlB,EAAI,iBAAmB,EAAI,YAC7B,EAAK,UAAU,IAAI,gBAAgB,CACnC,EAAK,iBAAiB,YAAe,CACnC,GAAI,EAAQ,IAAK,CACf,EAAI,SAAS,CACX,MAAO,EAAQ,KACf,KAAM,sBACN,QAAS,CAAE,IAAK,EAAQ,IAAK,CAC9B,CAAC,CACF,OAEF,EAAI,kBAAkB,EAA8C,EACpE,EAGG,ECvLT,SAAgB,GACd,EACA,EACa,CACb,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,0EACtB,EAAU,QAAQ,YAAiB,iBACnC,EAAU,aAAa,OAAQ,QAAQ,CAEvC,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,mCACjB,EAAK,YAAc,KACnB,EAAK,aAAa,cAAe,OAAO,CACxC,EAAU,YAAY,EAAK,CAE3B,IAAM,EAAU,SAAS,cAAc,KAAK,CAC5C,EAAQ,UAAY,sCACpB,EAAQ,YAAc,EAAQ,MAAM,gBAAkB,kCACtD,EAAU,YAAY,EAAQ,CAE9B,IAAM,EAAU,EAAQ,OAAQ,QAChC,GAAI,EAAS,CACX,IAAM,EAAY,SAAS,cAAc,IAAI,CAC7C,EAAU,UAAY,sCACtB,EAAU,YAAc,EACxB,EAAU,YAAY,EAAU,CAGlC,OAAO,ECbT,SAAgB,GAAyB,EAAoB,EAA2C,CACtG,IAAM,EAAW,EAAQ,OAAQ,SAAc,EAAQ,MAEjD,EAAO,SAAS,cAAc,MAAM,CAG1C,GAFA,EAAK,UAAY,wCACjB,EAAK,QAAQ,YAAiB,uBAC1B,CAAC,EAAS,OAAO,EAGrB,EAAK,UAAU,IAAI,gBAAgB,CACnC,EAAK,iBAAiB,QAAU,GAAM,CAC/B,EAAE,OAAuB,QAAQ,IAAI,EAC1C,EAAI,kBAAkB,EAAQ,EAC9B,CAGF,IAAM,EAAW,EAAQ,SACzB,GAAI,GAAY,EAAU,EAAS,CAAE,CACnC,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,sCACpB,EAAQ,QAAQ,YAAiB,wBACjC,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,QAAU,OACd,EAAiB,EAAK,MAAO,EAAS,CAEtC,EAAI,IADS,EAAQ,MACH,gBAClB,GAAqB,EAAI,CACzB,EAAQ,YAAY,EAAI,CACxB,EAAK,YAAY,EAAQ,CAI3B,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,wCACpB,EAAQ,QAAQ,YAAiB,0BAGjC,IAAM,EAAQ,EAAQ,MAChB,EAAO,EAAQ,KACrB,GAAI,EAAM,CACR,IAAM,EAAS,SAAS,cAAc,MAAM,CAC5C,EAAO,UAAY,qCACnB,EAAO,QAAQ,YAAiB,uBAGhC,IAAM,EADa,GAAS,CAAC,EAAK,aAAa,CAAC,WAAW,EAAM,aAAa,CAAC,CACjD,GAAG,EAAM,GAAG,IAAS,EACnD,EAAO,YAAc,EACrB,EAAO,MAAQ,EACf,EAAQ,YAAY,EAAO,CAI7B,IAAM,EAAS,EAAQ,OACjB,EAAc,EAAQ,YAC5B,GAAI,OAAO,GAAW,UAAY,OAAO,SAAS,EAAO,EAAI,EAAS,EAAG,CACvE,IAAM,EAAY,SAAS,cAAc,MAAM,CAI/C,GAHA,EAAU,UAAY,uCACtB,EAAU,QAAQ,YAAiB,yBACnC,EAAU,YAAY,GAAwB,EAAO,CAAC,CAClD,OAAO,GAAgB,UAAY,OAAO,SAAS,EAAY,CAAE,CACnE,IAAM,EAAQ,SAAS,cAAc,OAAO,CAC5C,EAAM,UAAY,6CAClB,EAAM,YAAc,KAAK,EAAY,GACrC,EAAU,YAAY,EAAM,CAE9B,EAAQ,YAAY,EAAU,CAIhC,IAAM,EAAQ,EAAQ,MAChB,EAAgB,EAAQ,cAC9B,GAAI,EAAO,CACT,IAAM,EAAiB,GAAyB,EAAK,EAAQ,CACvD,EAAY,GAA0B,EAAK,EAAQ,CACnD,EAAc,CAAC,EAAE,GAAiB,IAAkB,GAEpD,EAAW,SAAS,cAAc,MAAM,CAC9C,EAAS,UAAY,sCACrB,EAAS,QAAQ,YAAiB,wBAElC,IAAM,EAAkB,CAAC,EAAE,GAAkB,GACvC,EAAU,GAA4B,EAAK,EAAQ,CAEzD,GAAI,EAAiB,CACnB,IAAM,EAAQ,GAAyB,CACrC,WAAY,EACZ,mBAAoB,EAAY,EAAO,EAAI,QAAQ,CACnD,GAAI,IAAY,IAAA,GAA0B,EAAE,CAAhB,CAAE,UAAS,CACxC,CAAC,CACF,GAAI,GAAe,IAAc,SAAU,CACzC,EAAS,UAAU,IAAI,8CAA8C,CACrE,EAAS,YAAY,EAAM,CAC3B,IAAM,EAAM,SAAS,cAAc,OAAO,CAC1C,EAAI,UAAY,0CAChB,EAAI,aAAa,cAAe,OAAO,CACvC,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,+CACjB,EAAK,YAAc,EAAY,EAAgB,EAAI,QAAQ,CAC3D,EAAS,YAAY,EAAI,CACzB,EAAS,YAAY,EAAK,KACrB,CACL,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,+CACjB,EAAK,YAAc,EAAY,EAAgB,EAAI,QAAQ,CAC3D,EAAS,YAAY,EAAK,CAC1B,EAAS,YAAY,SAAS,eAAe,IAAI,CAAC,CAClD,EAAS,YAAY,EAAM,CAE7B,EAAQ,YAAY,EAAS,KACxB,CACL,GAAI,GAAe,IAAc,SAAU,CACzC,EAAS,UAAU,IAAI,8CAA8C,CACrE,IAAM,EAAM,SAAS,cAAc,OAAO,CAC1C,EAAI,UAAY,8CAChB,EAAI,YAAc,EAAY,EAAO,EAAI,QAAQ,CACjD,IAAM,EAAM,SAAS,cAAc,OAAO,CAC1C,EAAI,UAAY,0CAChB,EAAI,aAAa,cAAe,OAAO,CACvC,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,+CACjB,EAAK,YAAc,EAAY,EAAgB,EAAI,QAAQ,CAC3D,EAAS,YAAY,EAAI,CACzB,EAAS,YAAY,EAAI,CACzB,EAAS,YAAY,EAAK,SACjB,EAAa,CACtB,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,+CACjB,EAAK,YAAc,EAAY,EAAgB,EAAI,QAAQ,CAC3D,EAAS,YAAY,EAAK,CAC1B,EAAS,YAAY,SAAS,eAAe,IAAI,CAAC,CAClD,IAAM,EAAU,SAAS,cAAc,OAAO,CAC9C,EAAQ,UAAY,8CACpB,EAAQ,YAAc,EAAY,EAAO,EAAI,QAAQ,CACrD,EAAS,YAAY,EAAQ,KACxB,CACL,IAAM,EAAU,SAAS,cAAc,OAAO,CAC9C,EAAQ,UAAY,8CACpB,EAAQ,YAAc,EAAY,EAAO,EAAI,QAAQ,CACrD,EAAS,YAAY,EAAQ,CAE/B,GAAI,EAAgB,CAClB,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,4CAClB,EAAM,YAAY,GAA4B,EAAe,CAAC,CAC9D,EAAM,YAAY,EAAS,CAC3B,EAAQ,YAAY,EAAM,MAE1B,EAAQ,YAAY,EAAS,EAKnC,EAAK,YAAY,EAAQ,CAGzB,IAAM,EAAM,EAAQ,IACpB,GAAI,GAAO,EAAU,EAAI,CAAE,CACzB,IAAM,EAAM,SAAS,cAAc,IAAI,CACvC,EAAI,UAAY,6DAChB,EAAI,QAAQ,YAAiB,sBAC7B,EAAiB,EAAK,OAAQ,EAAI,CAClC,EAAiB,EAAK,SAAU,SAAS,CACzC,EAAiB,EAAK,MAAO,sBAAsB,CACnD,EAAI,YAAc,EAAI,MAAM,iBAAmB,OAC/C,EAAK,YAAY,EAAI,CAGvB,OAAO,EC9IT,SAAS,IAA4B,CACnC,OAAO,OAAO,WAAa,IAG7B,IAAM,GAAoD,CACxD,eAAgB,CAAE,UAAS,aAAc,GAAoB,EAAS,EAAQ,CAC9E,cAAe,CAAE,UAAS,aAAc,GAAmB,EAAS,EAAQ,CAC5E,aAAc,CAAE,UAAS,aAAc,GAAkB,EAAS,EAAQ,CAC1E,qBAAsB,CAAE,UAAS,aAAc,GAA0B,EAAS,EAAQ,CAC1F,aAAc,CAAE,UAAS,OAAM,gBAAe,aAAc,GAAkB,EAAS,EAAM,EAAe,EAAQ,CACpH,kBAAmB,CAAE,UAAS,aAC5B,GAAgC,EAAS,CACvC,oBAAqB,EAAQ,MAAM,oBACnC,qBAAsB,EAAQ,MAAM,qBACpC,qBAAsB,EAAQ,MAAM,qBACpC,+BAAgC,EAAQ,MAAM,+BAC9C,6BAA8B,EAAQ,MAAM,6BAC5C,sBAAuB,EAAQ,MAAM,sBACtC,CAAC,CACJ,iBAAkB,CAAE,UAAS,aAAc,GAA6B,EAAS,EAAQ,CACzF,YAAa,CAAE,UAAS,aAAc,GAAiB,EAAS,EAAQ,CACxE,qBAAsB,CAAE,UAAS,aAAc,GAA0B,EAAS,EAAQ,CAC1F,iBAAkB,CAAE,UAAS,aAAc,GAAsB,EAAS,EAAQ,CAClF,wBAAyB,CAAE,UAAS,aAAc,GAA6B,EAAS,EAAQ,CAChG,aAAc,CAAE,aAAc,GAAkB,EAAQ,CACxD,qBAAsB,CAAE,UAAS,aAAc,GAA0B,EAAS,EAAQ,CAC1F,eAAgB,CAAE,UAAS,aAAc,GAAoB,EAAS,EAAQ,CAC9E,oBAAqB,CAAE,UAAS,aAAc,GAAyB,EAAS,EAAQ,CACxF,SAAU,CAAE,aAAc,GAAc,EAAQ,CACjD,CAEY,IAAmF,CAC9F,UACA,mBACI,CAIJ,GAAI,CAAC,EAAQ,UAAY,EAAQ,SAAS,SAAW,EACnD,OAAO,KAET,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,IAAK,IAAM,KAAW,EAAQ,SAAU,CACtC,IAAM,EAAW,EAAc,EAAQ,CACnC,GAAU,EAAQ,YAAY,EAAS,CAE7C,OAAO,GAGT,SAAgB,IAAsD,CACpE,MAAO,CAAE,GAAG,GAA+B,CAG7C,SAAgB,GACd,EACA,EACA,EAAW,GACX,EAAiE,GACpD,CACb,OAAO,EAAyB,CAC9B,OACA,QAAS,EACT,WACA,mBAAoB,sBACpB,kBACD,CAAC,CAGJ,SAAS,GAAoB,EAAoB,EAAuC,CACtF,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,8BAEtB,IAAM,EAAU,EAAQ,OAAQ,QAEhC,GAAI,EACF,IAAK,IAAM,KAAO,EAAS,CACzB,IAAM,EAAS,SAAS,cAAc,SAAS,CAC/C,EAAO,UAAY,0BACnB,EAAO,YAAc,EAAI,MACzB,EAAO,iBAAiB,YAAe,EAAI,SAAS,EAAI,OAAO,CAAC,CAChE,EAAU,YAAY,EAAO,CAIjC,OAAO,EAGT,SAAS,GAAmB,EAAoB,EAAuC,CACrF,IAAM,EAAS,SAAS,cAAc,SAAS,CAC/C,EAAO,UAAY,0BACnB,IAAM,EAAQ,EAAQ,OAAQ,MAC1B,OAAO,GAAU,WAAU,EAAO,YAAc,GACpD,IAAM,EAAS,EAAQ,OAAQ,OAI/B,OAHI,GACF,EAAO,iBAAiB,YAAe,EAAI,SAAS,EAAO,CAAC,CAEvD,EAGT,SAAS,GACP,EACA,EACA,EACA,EACA,EACM,CACN,EAAW,iBAAiB,CAC5B,IAAM,EAAQ,GAA0B,EAAK,EAAQ,CAC/C,EAAc,CAAC,EAAE,GAAiB,GAAS,IAAkB,GAC7D,EAAiB,GAAyB,EAAK,EAAQ,CACvD,EAAkB,CAAC,EAAE,GAAkB,GACvC,EAAU,GAA4B,EAAK,EAAQ,CACzD,GAAI,CAAC,GAAS,WAAW,EAAM,EAAI,EAAG,OAEtC,GAAI,EAAiB,CACnB,IAAM,EAAQ,GAAyB,CACrC,WAAY,EACZ,mBAAoB,EAAY,EAAO,EAAI,QAAQ,CACnD,GAAI,IAAY,IAAA,GAA0B,EAAE,CAAhB,CAAE,UAAS,CACxC,CAAC,CACF,GAAI,GAAe,IAAU,SAAU,CACrC,EAAW,UAAU,IAAI,gDAAgD,CACzE,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,UAAY,sCAChB,EAAI,YAAY,EAAM,CACtB,IAAM,EAAM,SAAS,cAAc,OAAO,CAC1C,EAAI,UAAY,sCAChB,EAAI,aAAa,cAAe,OAAO,CACvC,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,2CACjB,EAAK,YAAc,EAAY,EAAgB,EAAI,QAAQ,CAC3D,EAAI,YAAY,EAAI,CACpB,EAAI,YAAY,EAAK,CACrB,EAAW,YAAY,EAAI,CAC3B,OAIF,GAFA,EAAW,UAAU,OAAO,gDAAgD,CAC5E,EAAW,YAAY,EAAM,CACzB,EAAa,CACf,EAAW,YAAY,SAAS,eAAe,IAAI,CAAC,CACpD,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,2CACjB,EAAK,YAAc,EAAY,EAAgB,EAAI,QAAQ,CAC3D,EAAW,YAAY,EAAK,CAE9B,OAGF,GAAI,GAAe,IAAU,SAAU,CACrC,EAAW,UAAU,IAAI,gDAAgD,CACzE,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,UAAY,sCAChB,IAAM,EAAM,SAAS,cAAc,OAAO,CAC1C,EAAI,UAAY,0CAChB,EAAI,YAAc,EAAY,EAAO,EAAI,QAAQ,CACjD,IAAM,EAAM,SAAS,cAAc,OAAO,CAC1C,EAAI,UAAY,sCAChB,EAAI,aAAa,cAAe,OAAO,CACvC,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,2CACjB,EAAK,YAAc,EAAY,EAAe,EAAI,QAAQ,CAC1D,EAAI,YAAY,EAAI,CACpB,EAAI,YAAY,EAAI,CACpB,EAAI,YAAY,EAAK,CACrB,EAAW,YAAY,EAAI,CAC3B,OAGF,EAAW,UAAU,OAAO,gDAAgD,CAC5E,IAAM,EAAU,SAAS,cAAc,OAAO,CAI9C,GAHA,EAAQ,UAAY,0CACpB,EAAQ,YAAc,EAAY,EAAO,EAAI,QAAQ,CACrD,EAAW,YAAY,EAAQ,CAC3B,EAAa,CACf,EAAW,YAAY,SAAS,eAAe,IAAI,CAAC,CACpD,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,2CACjB,EAAK,YAAc,EAAY,EAAgB,EAAI,QAAQ,CAC3D,EAAW,YAAY,EAAK,EAIhC,SAAS,GACP,EACA,EACA,EACA,EACA,EACA,EACA,EACM,CACN,EAAS,UAAU,OAAO,6CAA6C,CACvE,EAAS,iBAAiB,CAC1B,EAAS,UAAY,qCACrB,IAAM,EAAiB,GAAyB,EAAK,EAAQ,CACvD,EAAkB,CAAC,EAAE,GAAkB,GACvC,EAAU,GAA4B,EAAK,EAAQ,CACzD,GAAI,EAAiB,CACnB,IAAM,EAAQ,GAAyB,CACrC,WAAY,EACZ,mBAAoB,EAAY,EAAO,EAAI,QAAQ,CACnD,GAAI,IAAY,IAAA,GAA0B,EAAE,CAAhB,CAAE,UAAS,CACxC,CAAC,CACF,GAAI,GAAe,IAAU,SAAU,CACrC,EAAS,UAAU,IAAI,6CAA6C,CACpE,EAAS,YAAY,EAAM,CAC3B,IAAM,EAAM,SAAS,cAAc,OAAO,CAC1C,EAAI,UAAY,yCAChB,EAAI,aAAa,cAAe,OAAO,CACvC,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,8CACjB,EAAK,YAAc,EAAY,EAAgB,EAAI,QAAQ,CAC3D,EAAS,YAAY,EAAI,CACzB,EAAS,YAAY,EAAK,KACrB,CACL,GAAI,EAAa,CACf,IAAM,EAAW,SAAS,cAAc,OAAO,CAC/C,EAAS,UAAY,8CACrB,EAAS,YAAc,EAAY,EAAgB,EAAI,QAAQ,CAC/D,EAAS,YAAY,EAAS,CAC9B,EAAS,YAAY,SAAS,eAAe,IAAI,CAAC,CAEpD,EAAS,YAAY,EAAM,UAEpB,GAAe,IAAU,SAAU,CAC5C,EAAS,UAAU,IAAI,6CAA6C,CACpE,IAAM,EAAM,SAAS,cAAc,OAAO,CAC1C,EAAI,UAAY,6CAChB,EAAI,YAAc,EAAY,EAAO,EAAI,QAAQ,CACjD,IAAM,EAAM,SAAS,cAAc,OAAO,CAC1C,EAAI,UAAY,yCAChB,EAAI,aAAa,cAAe,OAAO,CACvC,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,8CACjB,EAAK,YAAc,EAAY,EAAgB,EAAI,QAAQ,CAC3D,EAAS,YAAY,EAAI,CACzB,EAAS,YAAY,EAAI,CACzB,EAAS,YAAY,EAAK,KACrB,CACL,GAAI,EAAa,CACf,IAAM,EAAW,SAAS,cAAc,OAAO,CAC/C,EAAS,UAAY,8CACrB,EAAS,YAAc,EAAY,EAAgB,EAAI,QAAQ,CAC/D,EAAS,YAAY,EAAS,CAC9B,EAAS,YAAY,SAAS,eAAe,IAAI,CAAC,CAEpD,IAAM,EAAe,SAAS,cAAc,OAAO,CACnD,EAAa,UAAY,6CACzB,EAAa,YAAc,EAAY,EAAO,EAAI,QAAQ,CAC1D,EAAS,YAAY,EAAa,CAGpC,IAAM,EAAkB,GAAc,EAAS,kBAAmB,sBAAsB,CACxF,GAAI,OAAO,GAAoB,UAAY,EAAkB,EAAG,CAC9D,IAAM,EAAgB,SAAS,cAAc,OAAO,CACpD,EAAc,UAAY,8CAC1B,EAAc,YAAc,IAAI,GAAc,EAAgB,GAC9D,EAAS,YAAY,EAAc,EAIvC,SAAS,GAAkB,EAAoB,EAAuC,CACpF,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,2EAGjB,IAAM,EAAW,EAAQ,OAAQ,SAAc,EAAQ,MACvD,GAAI,CAAC,EAAS,OAAO,EAGrB,IAAM,EAAa,EAAQ,IACvB,IAAY,EAAK,QAAQ,IAAS,GACtC,IAAM,EAAS,EAAQ,OAAQ,QAG3B,EAAI,iBAAmB,KACzB,EAAK,UAAU,IAAI,gBAAgB,CACnC,EAAK,iBAAiB,QAAU,GAAM,CAEhC,MAAK,eAAe,UAAU,SAAS,yCAAyC,EAC/E,GAAE,OAAuB,QAAQ,iCAAiC,EAClE,GAAE,OAAuB,QAAQ,iCAAiC,CACvE,IAAI,EAAQ,CACV,EAAI,SAAS,EAAO,CACpB,OAEF,EAAI,kBAAkB,EAAQ,GAC9B,EAGJ,IAAM,EAAW,EAAQ,SACzB,GAAI,GAAY,EAAU,EAAS,CAAE,CACnC,IAAM,EAAa,SAAS,cAAc,MAAM,CAChD,EAAW,UAAY,wCAEvB,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,UAAY,gCAChB,EAAI,QAAU,OACd,EAAiB,EAAK,MAAO,EAAS,CACtC,IAAM,EAAO,EAAQ,KACjB,IAAM,EAAI,IAAM,GACpB,GAAqB,EAAI,CACzB,EAAW,YAAY,EAAI,CAG3B,IAAM,EAAkB,EAAQ,gBAChC,GAAI,OAAO,GAAoB,UAAY,EAAkB,EAAG,CAC9D,IAAM,EAAQ,SAAS,cAAc,OAAO,CAC5C,EAAM,UAAY,2CAClB,EAAM,YAAc,IAAI,GAAc,EAAgB,GACtD,EAAW,YAAY,EAAM,CAG/B,IAAM,EAAa,SAAS,cAAc,MAAM,CAChD,EAAW,UAAY,wCAGvB,IAAM,EAAS,EAAQ,IACvB,GAAI,GAAU,EAAI,iBAAkB,CAClC,IAAM,EAAQ,SAAS,cAAc,SAAS,CAC9C,EAAM,UAAY,4BAClB,EAAM,KAAO,SACb,EAAM,QAAQ,mBAAqB,EACnC,EAAM,aAAa,aAAc,EAAI,MAAM,qBAAuB,mBAAmB,CACrF,IAAM,EAAQ,EAAI,eAAe,IAAI,EAAO,EAAI,GAC5C,GAAO,EAAM,UAAU,IAAI,oCAAoC,CAEnE,EAAM,UAAY,yDADF,EAAQ,eAAiB,OAC0C,qMACnF,EAAM,iBAAiB,QAAU,GAAM,CACrC,EAAE,iBAAiB,CACnB,EAAM,UAAU,OAAO,oCAAoC,CAC3D,IAAM,EAAM,EAAM,cAAc,MAAM,CAClC,GACF,EAAI,aACF,OACA,EAAM,UAAU,SAAS,oCAAoC,CAAG,eAAiB,OAClF,CAEH,EAAI,iBAAkB,EAAQ,EAAQ,EACtC,CACF,EAAW,YAAY,EAAM,CAI/B,IAAM,EAAiB,EAAQ,IACzB,EAAmB,EAAI,MAAM,kBAAoB,eACvD,GAAI,EAAgB,CAClB,IAAM,EAAO,SAAS,cAAc,SAAS,CAC7C,EAAK,UAAY,iCACjB,EAAK,KAAO,SACZ,EAAK,aAAa,aAAc,EAAiB,CACjD,EAAK,QAAQ,QAAa,EAC1B,EAAK,UACH,ySAKF,IAAM,EAAW,SAAS,cAAc,OAAO,CAC/C,EAAS,UAAY,sCACrB,EAAS,YAAc,EACvB,EAAK,YAAY,EAAS,CAC1B,EAAK,iBAAiB,QAAU,GAAM,CACpC,EAAE,iBAAiB,CACnB,EAAI,SAAS,CACX,MAAO,EACP,KAAM,cACN,QAAS,CAAE,IAAK,EAAgB,GAAI,EAAW,CAAE,UAAW,EAAU,CAAG,EAAE,CAAG,CAC/E,CAAC,EACF,CACF,EAAW,YAAY,EAAK,CAG1B,EAAW,kBAAoB,GACjC,EAAW,YAAY,EAAW,CAGpC,EAAK,YAAY,EAAW,CAG9B,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,iCAEjB,IAAM,EAAQ,EAAQ,MACtB,GAAI,EAAO,CACT,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,kCACpB,EAAQ,YAAc,EACtB,EAAK,YAAY,EAAQ,CAG3B,IAAM,EAAS,EAAQ,OACjB,EAAc,EAAQ,YACtB,EAAQ,EAAQ,MAChB,EAAgB,EAAQ,cACxB,EAAa,EAAQ,YACrB,EAAiB,GAAyB,EAAK,EAAQ,CACvD,EAAa,GAA0B,EAAK,EAAQ,CAEpD,EAAoB,CAAC,EAAE,GADF,GAAiB,GAAS,IAAkB,GAAS,WAAW,EAAM,CAAG,GACjC,IAAe,UAE5E,EAAmB,OAAO,GAAW,UAAY,OAAO,SAAS,EAAO,EAAI,EAAS,EACrF,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,qCAEpB,IAAM,EAAa,SAAS,cAAc,MAAM,CAEhD,EAAW,UAAY,wEAEvB,IAAM,GAA0B,OACrB,CACL,IAAM,EAAQ,SAAS,cAAc,MAAM,CAI3C,MAHA,GAAM,UAAY,wCACd,GAAgB,EAAM,YAAY,GAA4B,EAAe,CAAC,CAClF,EAAM,YAAY,EAAW,CACtB,KACL,CACJ,EAEJ,GAAI,IAAe,GAAM,CACvB,IAAM,EAAW,SAAS,cAAc,OAAO,CAC/C,EAAS,UAAY,8BACrB,EAAW,YAAY,EAAS,CAChC,eAAiB,CACV,EAAS,gBACV,GAAS,WAAW,EAAM,CAAG,EAC/B,GAA0B,EAAY,EAAK,EAAS,EAAO,EAAQ,cAAuC,CAE1G,EAAS,QAAQ,GAElB,IAAI,MACE,GAAS,WAAW,EAAM,CAAG,GACtC,GAA0B,EAAY,EAAK,EAAS,EAAO,EAAc,CAO3E,IAJI,EAAW,kBAAoB,GAAK,IAAe,KACrD,EAAQ,YAAY,GAAW,CAG7B,EAAkB,CACpB,IAAM,EAAgB,SAAS,cAAc,MAAM,CACnD,EAAc,UAAY,4EAC1B,IAAM,EAAK,GAAY,EAAO,CACxB,EAAa,CAAC,GAAG,EAAG,QAAQ,EAAE,GAAI,iBAAiB,CACrD,OAAO,GAAgB,UAAY,OAAO,SAAS,EAAY,EACjE,EAAW,KAAK,IAAI,EAAY,WAAW,CAE7C,EAAc,aAAa,aAAc,EAAW,KAAK,IAAI,CAAC,CAC9D,IAAM,EAAS,SAAS,cAAc,OAAO,CAC7C,EAAO,UAAY,gDACnB,EAAO,aAAa,cAAe,OAAO,CAC1C,EAAO,UACL,2LACF,IAAM,EAAQ,SAAS,cAAc,OAAO,CAC5C,EAAM,UAAY,iDAClB,EAAM,YAAc,EAAG,QAAQ,EAAE,CACjC,EAAc,YAAY,EAAO,CACjC,EAAc,YAAY,EAAM,CAChC,EAAQ,YAAY,EAAc,CAGhC,EAAQ,oBAAsB,IAChC,EAAQ,UAAU,IAAI,4CAA4C,CAClE,EAAQ,aAAa,cAAe,OAAO,EAE7C,EAAK,YAAY,EAAQ,CAEzB,IAAM,EAAO,EAAQ,KACrB,GAAI,EAAM,CACR,IAAM,EAAS,SAAS,cAAc,MAAM,CAC5C,EAAO,UAAY,iCACnB,EAAO,YAAc,EACrB,EAAO,MAAQ,EACf,EAAK,YAAY,EAAO,CAI1B,IAAM,EAAU,EAAQ,QACxB,GAAI,OAAO,GAAY,UAAW,CAChC,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,mCAAmC,EAAU,cAAgB,oBAC/E,EAAM,YAAc,EACf,EAAI,MAAM,cAAgB,WAC1B,EAAI,MAAM,iBAAmB,eAClC,EAAK,YAAY,EAAM,CAGzB,EAAK,YAAY,EAAK,CAEtB,IAAM,EAAM,EAAQ,IACd,EAAM,EAAQ,IACd,EAAW,EAAQ,SAEnB,EAAU,CAAC,EAAE,GAAY,GAAO,IAAY,IAC5C,EAAW,EAAI,MAAM,iBAAmB,OAE9C,GAAI,EAAS,CACX,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,uCAEtB,IAAM,EAAU,SAAS,cAAc,SAAS,CAChD,EAAQ,KAAO,SACf,EAAQ,UAAY,wCACpB,EAAQ,YAAc,EACtB,EAAQ,iBAAiB,QAAU,GAAM,CACvC,EAAE,iBAAiB,CACnB,EAAI,SAAS,CACX,MAAO,EAAI,MAAM,iBAAmB,EACpC,KAAM,YACN,QAAS,CAAO,MAAgB,WAAW,SAAU,EAAG,CACzD,CAAC,EACF,CAEF,EAAU,YAAY,EAAQ,CAC9B,EAAK,YAAY,EAAU,SAClB,EAAQ,CACjB,IAAM,EAAM,SAAS,cAAc,SAAS,CAC5C,EAAI,UAAY,gCAChB,EAAI,KAAO,SACX,EAAI,YAAc,EAAO,OAAS,sBAAwB,EAAW,EAAO,OAAS,EACrF,EAAI,iBAAiB,QAAU,GAAM,CACnC,GAAI,EAAK,eAAe,UAAU,SAAS,yCAAyC,CAAE,CACpF,EAAE,iBAAiB,CACnB,OAEF,EAAI,SAAS,EAAO,EACpB,CACF,EAAK,YAAY,EAAI,SACZ,GAAO,EAAU,EAAI,CAAE,CAChC,IAAM,EAAM,SAAS,cAAc,IAAI,CACvC,EAAI,UAAY,gCAChB,EAAiB,EAAK,OAAQ,EAAI,CAClC,EAAiB,EAAK,SAAU,SAAS,CACzC,EAAiB,EAAK,MAAO,sBAAsB,CACnD,EAAI,YAAc,EAClB,EAAI,iBAAiB,QAAU,GAAM,CACnC,GAAI,EAAK,eAAe,UAAU,SAAS,yCAAyC,CAAE,CACpF,EAAE,gBAAgB,CAClB,EAAE,iBAAiB,CACnB,OAEE,EAAI,gBAAkB,IACxB,EAAE,gBAAgB,CAClB,EAAI,eAAe,CAAE,MAAK,MAAK,CAAC,GAElC,CACF,EAAK,YAAY,EAAI,CAIvB,GAAI,EAAI,sBAAwB,GAAO,EAAI,sBAAuB,CAChE,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,yCACpB,IAAM,EAAa,EAAI,wBAAwB,SAAS,EAAI,EAAI,GAC5D,GAAY,EAAQ,UAAU,IAAI,mDAAmD,CAEzF,IAAM,EAAe,EAAQ,MAAkC,EACzD,EACJ,EAAI,MAAM,0BAA4B,gEACxC,EAAQ,aAAa,OAAQ,QAAQ,CACrC,EAAQ,aAAa,aAAc,GAAG,OAAO,EAAY,CAAC,IAAI,IAAW,CAEzE,IAAM,EAAS,SAAS,cAAc,SAAS,CAC/C,EAAO,KAAO,SACd,EAAO,UAAY,mCACnB,EAAO,QAAQ,SAAc,EAAa,OAAS,QACnD,EAAO,aAAa,eAAgB,EAAa,OAAS,QAAQ,CAClE,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,wCACjB,EAAK,UAAY,EACb,wLACA,6DACJ,IAAM,EAAQ,SAAS,cAAc,OAAO,CAC5C,EAAM,UAAY,yCAClB,EAAM,YAAc,EACf,EAAI,MAAM,yBAA2B,WACrC,EAAI,MAAM,uBAAyB,oBACxC,EAAO,YAAY,EAAK,CACxB,EAAO,YAAY,EAAM,CACzB,EAAO,iBAAiB,QAAU,GAAM,CACtC,EAAE,iBAAiB,CACnB,EAAI,wBAAwB,EAAI,EAChC,CAEF,IAAM,EAAO,SAAS,cAAc,MAAM,CAiB1C,MAhBA,GAAK,UAAY,oCACjB,EAAK,aAAa,cAAe,OAAO,CACxC,EAAK,YAAc,EAKnB,EAAQ,iBAAiB,QAAU,GAAM,CAClC,EAAE,OAAuB,QAAQ,oCAAoC,GAC1E,EAAE,iBAAiB,CACnB,EAAI,wBAAwB,EAAI,GAChC,CAEF,EAAQ,YAAY,EAAO,CAC3B,EAAQ,YAAY,EAAK,CACzB,EAAQ,YAAY,EAAK,CAClB,EAGT,OAAO,EAST,IAAM,GAAsB,IAAI,IAAI,CAAC,QAAS,SAAU,OAAQ,YAAa,aAAa,CAAC,CACrF,GAAqB,IAAI,IAAI,CAAC,OAAQ,QAAS,QAAQ,CAAC,CACxD,GAAuB,IAAI,IAAI,CAAC,SAAU,QAAS,SAAS,CAAC,CAC7D,GAAqB,CACzB,WACA,iBACA,kBACA,kBACA,mBACA,UACD,CACK,GAAmC,IAAI,IAAI,CAC/C,KACA,KACA,KACA,IACA,KACA,KACA,KACA,KACA,SACA,IACA,KACA,IACD,CAAC,CACI,GAAmC,IAAI,IAAI,CAAC,SAAU,QAAS,WAAY,SAAU,SAAU,QAAQ,CAAC,CAE9G,SAAS,EAAc,EAAkC,GAAG,EAAoC,CAC9F,IAAK,IAAM,KAAO,EAAM,CACtB,IAAM,EAAQ,EAAQ,GACtB,GAAI,OAAO,GAAU,SAAU,CAC7B,IAAM,EAAU,EAAM,MAAM,CAC5B,GAAI,EAAQ,OAAS,EAAG,OAAO,IAMrC,SAAS,GAAc,EAAkC,GAAG,EAAoC,CAC9F,IAAK,IAAM,KAAO,EAAM,CACtB,IAAM,EAAQ,EAAQ,GACtB,GAAI,OAAO,GAAU,UAAY,OAAO,SAAS,EAAM,CAAE,OAAO,EAChE,GAAI,OAAO,GAAU,SAAU,CAC7B,IAAM,EAAS,OAAO,EAAM,QAAQ,IAAK,IAAI,CAAC,CAC9C,GAAI,OAAO,SAAS,EAAO,CAAE,OAAO,IAM1C,SAAS,GAAe,EAAkC,GAAG,EAAqC,CAChG,IAAK,IAAM,KAAO,EAAM,CACtB,IAAM,EAAQ,EAAQ,GACtB,GAAI,OAAO,GAAU,UAAW,OAAO,GAK3C,SAAS,GAAc,EAAkC,GAAG,EAAqD,CAC/G,IAAK,IAAM,KAAO,EAAM,CACtB,IAAM,EAAQ,EAAQ,GACtB,GAAI,GAAS,OAAO,GAAU,UAAY,CAAC,MAAM,QAAQ,EAAM,CAC7D,OAAO,GAMb,SAAS,GAAmB,EAA0B,CAEpD,OADK,MAAM,QAAQ,EAAM,CAClB,EAAM,OAAQ,GAAyB,OAAO,GAAS,UAAY,EAAK,MAAM,CAAC,OAAS,EAAE,CAD/D,EAAE,CAItC,SAAS,GAAiB,EAA4C,CACpE,IAAM,EAAO,CACX,GAAG,GAAmB,EAAQ,OAAU,CACxC,EAAc,EAAS,WAAY,YAAa,QAAQ,CACzD,CAAC,OAAQ,GAAuB,CAAC,CAAC,GAAO,EAAU,EAAI,CAAC,CACzD,OAAO,MAAM,KAAK,IAAI,IAAI,EAAK,CAAC,CAGlC,SAAS,GAAgB,EAAsB,CAK7C,OAJI,OAAO,UAAc,KACX,IAAI,WAAW,CAAC,gBAAgB,EAAM,YAAY,CAClD,KAAK,aAAe,IAAI,QAAQ,OAAQ,IAAI,CAAC,MAAM,CAE1D,EACJ,QAAQ,WAAY,IAAI,CACxB,QAAQ,OAAQ,IAAI,CACpB,MAAM,CAGX,SAAS,GAAmB,EAAyE,CACnG,IAAM,EAAO,EAAc,EAAS,mBAAoB,kBAAkB,CAC1E,GAAI,EAAM,CACR,IAAM,EAAO,GAAgB,EAAK,CAClC,GAAI,EAAM,MAAO,CAAE,OAAM,OAAM,CAGjC,IAAM,EAAc,EAAc,EAAS,cAAc,CACzD,OAAO,EAAc,CAAE,KAAM,EAAa,CAAG,IAAA,GAG/C,SAAS,GAAyB,EAAuC,CA+BvE,OA9BI,MAAM,QAAQ,EAAM,CACf,EACJ,IAAK,GAAsC,CAC1C,GAAI,CAAC,GAAS,OAAO,GAAU,SAAU,OAAO,KAChD,IAAM,EAAS,EACT,EAAM,EAAc,EAAQ,MAAO,OAAQ,QAAS,QAAQ,CAC5D,EAAW,EAAO,MAClB,EACJ,OAAO,GAAa,UAAY,OAAO,GAAa,UAAY,OAAO,GAAa,UAChF,OAAO,EAAS,CAAC,MAAM,CACvB,IAAA,GAEN,MADI,CAAC,GAAO,CAAC,EAAY,KAClB,CAAE,MAAK,MAAO,EAAK,EAC1B,CACD,OAAQ,GAAwC,IAAU,KAAK,CAGhE,GAAS,OAAO,GAAU,SACrB,OAAO,QAAQ,EAAiC,CACpD,KAAK,CAAC,EAAK,KAA0C,CACpD,IAAM,EACJ,OAAO,GAAa,UAAY,OAAO,GAAa,UAAY,OAAO,GAAa,UAChF,OAAO,EAAS,CAAC,MAAM,CACvB,IAAA,GAEN,MADI,CAAC,GAAO,CAAC,EAAY,KAClB,CAAE,MAAK,MAAO,EAAK,EAC1B,CACD,OAAQ,GAAwC,IAAU,KAAK,CAG7D,EAAE,CAGX,SAAS,GAAsB,EAAyD,CACtF,IAAM,EAAW,GAAyB,EAAQ,SAAY,CAE9D,OADI,EAAS,OAAS,EAAU,EACzB,GAAyB,EAAQ,eAAkB,CAG5D,SAAS,GACP,EAC4E,CAC5E,IAAM,EAAW,EAAQ,eACnB,EAAkB,GAAyB,EAAS,CAC1D,GAAI,EAAgB,OAAS,EAC3B,OAAO,MAAM,QAAQ,EAAS,CAC1B,EACA,OAAO,YAAY,EAAgB,IAAK,GAAS,CAAC,EAAK,IAAK,EAAK,MAAM,CAAC,CAAC,CAG/E,IAAM,EAAiB,GAAyB,EAAQ,SAAY,CACpE,OAAO,EAAe,OAAS,EAAI,EAAiB,IAAA,GAGtD,SAAS,GAAc,EAAkC,GAAG,EAAoC,CAC9F,OAAO,EAAc,EAAS,GAAG,EAAK,CAGxC,SAAS,GAAc,EAAkC,GAAG,EAAoC,CAC9F,OAAO,GAAc,EAAS,GAAG,EAAK,CAGxC,SAAS,GAAoB,EAAsD,CACjF,OAAO,GACL,EACA,QACA,eACA,kBACA,QACA,QACA,OACA,eACA,MACD,CAGH,SAAS,GAAgB,EAAsD,CAG7E,OAFqB,GAAc,EAAS,OAAQ,YAAa,cAAe,iBAAiB,GAE1F,GAAc,EAAS,QAAQ,CAAG,GAAc,EAAS,OAAQ,eAAe,CAAG,IAAA,IAG5F,SAAS,GAAe,EAA2C,CACjE,IAAM,EAAW,GAAgB,EAAQ,EAAE,aAAa,CACxD,MAAO,CAAC,EACN,EAAc,EAAS,QAAS,SAAU,YAAa,MAAO,SAAU,cAAc,EACrF,GAAY,GAAoB,IAAI,EAAS,EAIlD,SAAS,GAAa,EAA+C,CACnE,GAAI,CAAC,EAAO,OACZ,IAAM,EAAU,EAAM,MAAM,CACxB,MAAC,GAAW,EAAQ,SAAS,IAAI,IACjC,OAAO,IAAQ,KAAe,IAAI,WAAW,QAAS,EAAQ,EAC9D,8CAA8C,KAAK,EAAQ,EAAE,OAAO,EAI1E,SAAS,GAAmB,EAAsD,CAChF,IAAM,EAAW,GAAa,GAAc,EAAS,cAAe,SAAU,YAAa,MAAO,QAAS,SAAS,CAAC,CACrH,GAAI,EAAU,OAAO,EAChB,MAAe,EAAQ,CAC5B,OAAO,GAAa,GAAoB,EAAQ,CAAC,CAGnD,SAAS,GAAa,EAAsD,CAC1E,OAAO,GAAc,EAAS,QAAS,WAAY,YAAa,cAAe,eAAe,CAGhG,SAAS,GAAa,EAA+D,CACnF,OACE,GAAc,EAAS,mBAAoB,kBAAkB,EAC7D,GAAc,EAAS,mBAAoB,kBAAkB,EAC7D,GAAc,EAAS,QAAQ,EAC/B,GAAc,EAAS,QAAQ,CAInC,SAAS,GAAmB,EAAsB,CAChD,IAAM,EAAa,EAAI,MAAM,CAAC,aAAa,CAC3C,OACE,GAAoB,IAAI,EAAW,EAAI,GAAmB,IAAI,EAAW,EAAI,GAAqB,IAAI,EAAW,CAIrH,SAAS,GAA0B,EAAqB,CACtD,IAAM,EAAa,EAAI,MAAM,CAAC,aAAa,CAG3C,OAFI,IAAe,YAAoB,OACnC,IAAe,aAAqB,QACjC,EAAI,MAAM,CAGnB,SAAS,GAAoB,EAA8E,CACzG,IAAK,IAAM,KAAO,GAAoB,CACpC,IAAM,EAAQ,EAAQ,GACtB,GAAI,CAAC,MAAM,QAAQ,EAAM,CAAE,SAC3B,IAAM,EAAW,EAAM,OACpB,GAA0C,CAAC,CAAC,GAAQ,OAAO,GAAS,UAAY,CAAC,MAAM,QAAQ,EAAK,CACtG,CACD,GAAI,EAAS,OAAS,EAAG,OAAO,GAKpC,SAAS,GAAgB,EAAkE,CACzF,IAAM,EAAmB,GAAoB,EAAQ,CACrD,GAAI,EAAkB,OAAO,EAE7B,IAAM,EAAM,EAAc,EAAS,MAAM,CACnC,EAAU,GAAe,EAAS,UAAW,WAAW,CACxD,EAAmD,EAAE,CACrD,EAAO,IAAI,IAEX,GAAc,EAAa,IAAmB,CAElD,GADI,CAAC,GAAmB,EAAI,EACxB,OAAO,GAAU,UAAY,OAAO,GAAU,UAAY,OAAO,GAAU,UAAW,OAC1F,IAAM,EAAe,OAAO,EAAM,CAAC,MAAM,CACzC,GAAI,CAAC,EAAc,OACnB,IAAM,EAAc,GAA0B,EAAI,CAC5C,EAAY,GAAG,EAAY,aAAa,CAAC,GAAG,EAAa,aAAa,GACxE,EAAK,IAAI,EAAU,GACvB,EAAK,IAAI,EAAU,CACnB,EAAiB,KAAK,CACpB,KAAM,EACN,MAAO,EACP,MACA,UACD,CAAC,GAGE,EAAY,GAAc,EAAS,YAAa,aAAa,CACnE,GAAI,EACF,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,EAAU,CAClD,EAAW,EAAK,EAAM,CAI1B,IAAK,IAAM,KAAW,GAAsB,EAAQ,CAClD,EAAW,EAAQ,IAAK,EAAQ,MAAM,CAGxC,OAAO,EAGT,SAAS,GAAoB,EAA0C,EAAkC,CACvG,IAAM,EAAQ,MAAM,KAAK,IAAI,IAAI,EAAS,IAAI,GAAgB,CAAC,OAAQ,GAA2B,CAAC,CAAC,EAAM,CAAC,CAAC,CAE5G,OADI,EAAM,SAAW,EAAU,GAAG,EAAS,OAAO,GAAG,EAAM,KACpD,EAAI,MAAM,eAAiB,WAGpC,SAAS,GAA0B,EAAoB,EAAuC,CAC5F,IAAM,EAAQ,SAAS,cAAc,UAAU,CAC/C,EAAM,UAAY,qCAElB,IAAM,EAAW,EAAQ,OAAQ,SAAc,EAAQ,MACvD,GAAI,CAAC,EAAS,OAAO,EAErB,IAAM,EAAO,EAAc,EAAS,OAAO,CACrC,EAAQ,EAAc,EAAS,QAAQ,CACvC,EAAM,EAAc,EAAS,MAAM,CACnC,EAAW,EAAc,EAAS,WAAY,YAAY,CAC1D,EAAQ,EAAc,EAAS,QAAQ,CACvC,EAAgB,EAAc,EAAS,gBAAiB,iBAAiB,CACzE,EAAa,GAAe,EAAS,cAAc,CACnD,EAAiB,GAAyB,EAAK,EAAQ,CACvD,EAAoB,GAA0B,EAAK,EAAQ,CAC3D,EAAqB,CAAC,EAAE,GAAiB,GAAS,IAAkB,GACpE,EAAU,GAAe,EAAS,UAAW,WAAW,CACxD,EAAc,GAAc,EAAS,cAAe,eAAe,CACnE,EAAS,GAAc,EAAS,SAAS,CACzC,EAAa,GAAiB,EAAQ,CACtC,EAAiB,GAAsB,EAAQ,CAAC,MAAM,EAAG,EAAE,CAGjE,GAAI,EAAW,OAAS,EAAG,CAEzB,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UACJ,gHAEF,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,mCACpB,EAAiB,EAAS,MAAO,EAAW,GAAI,CAChD,EAAQ,IAAM,GAAQ,gBACtB,GAAqB,EAAQ,CAC7B,EAAM,YAAY,EAAQ,CAE1B,IAAM,EAAa,SAAS,cAAc,MAAM,CAChD,EAAW,UAAY,sCAEvB,IACI,EAAkC,KAClC,EAAiB,EAEf,EAAO,EAAI,KACX,EAAY,GAAM,sBAAwB,iBAC1C,EAAY,GAAM,sBAAwB,aAE1C,EAAU,GACd,IAAQ,OACJ,kNACA,iNAEA,EAAU,SAAS,cAAc,SAAS,CAChD,EAAQ,KAAO,SACf,EAAQ,UACN,6GACF,EAAQ,aAAa,aAAc,EAAU,CAC7C,EAAQ,UAAY,EAAO,OAAO,CAElC,IAAM,EAAU,SAAS,cAAc,SAAS,CAChD,EAAQ,KAAO,SACf,EAAQ,UACN,6GACF,EAAQ,aAAa,aAAc,EAAU,CAC7C,EAAQ,UAAY,EAAO,OAAO,CAElC,IAAM,MAAgC,CACpC,EAAQ,SAAW,GAAkB,EACrC,EAAQ,SAAW,GAAkB,EAAW,OAAS,GAGrD,EAAa,GAA0B,CAC3C,GAAI,EAAU,GAAK,GAAW,EAAW,QAAU,IAAY,EAAgB,OAC/E,IAAM,EAAU,EAAW,GAC3B,GAAI,CAAC,EAAS,OACd,EAAiB,EAAS,MAAO,EAAQ,CACzC,IAAM,EAAW,EAAW,iBAAiB,sCAAsC,CAC/E,GAAa,EAAY,UAAU,OAAO,6CAA6C,CACvF,EAAU,GAA0B,EAAS,IAC9C,EAAS,GAAyB,UAAU,IAAI,6CAA6C,CAC9F,EAAc,EAAS,IAEvB,EAAc,KAEhB,EAAiB,EACjB,GAAmB,EAGrB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAW,OAAQ,IAAK,CAC1C,IAAM,EAAS,EAAW,GAC1B,GAAI,GAAK,EAAwB,MACjC,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,qCACd,IAAM,IACR,EAAM,UAAU,IAAI,6CAA6C,CACjE,EAAc,GAEhB,EAAiB,EAAO,MAAO,EAAO,CACtC,EAAM,IAAM,GAAG,GAAQ,UAAU,GAAG,EAAI,IACxC,EAAM,MAAQ,GACd,EAAM,OAAS,GACf,GAAqB,EAAM,CAC3B,EAAM,iBAAiB,YAAe,CACpC,EAAU,EAAE,EACZ,CACF,EAAW,YAAY,EAAM,CAI/B,GAAI,EAAW,OAAS,EAAwB,CAC9C,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,0CACjB,EAAK,YAAc,IAAI,EAAW,OAAS,IAC3C,EAAW,YAAY,EAAK,CAG9B,EAAQ,iBAAiB,QAAU,GAAM,CACvC,EAAE,iBAAiB,CACnB,EAAU,EAAiB,EAAE,EAC7B,CACF,EAAQ,iBAAiB,QAAU,GAAM,CACvC,EAAE,iBAAiB,CACnB,EAAU,EAAiB,EAAE,EAC7B,CACF,GAAmB,CAGnB,IAAI,EAAc,EAGlB,EAAQ,iBACN,aACC,GAAkB,CACjB,EAAc,EAAE,eAAe,GAAI,SAErC,CAAE,QAAS,GAAM,CAClB,CAED,EAAQ,iBAAiB,WAAa,GAAkB,CACtD,IAAM,EAAY,EAAE,eAAe,GAAI,QACjC,EAAO,EAAc,EACvB,KAAK,IAAI,EAAK,CAAG,IAOrB,EAJE,EAAO,EACH,KAAK,IAAI,EAAiB,EAAG,EAAW,OAAS,EAAE,CACnD,KAAK,IAAI,EAAiB,EAAG,EAAE,CAEnB,EAClB,CAEF,EAAM,YAAY,EAAQ,CAC1B,EAAM,YAAY,EAAQ,CAC1B,EAAM,YAAY,EAAW,CAE7B,EAAM,YAAY,EAAM,SACf,EAAW,SAAW,EAAG,CAElC,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,2EAClB,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,UAAY,mCAChB,EAAI,QAAU,OACd,EAAiB,EAAK,MAAO,EAAW,GAAI,CAC5C,GAAqB,EAAI,CACzB,EAAI,IAAM,GAAQ,gBAClB,EAAM,YAAY,EAAI,CAEtB,EAAM,YAAY,EAAM,CAG1B,IAAM,EAAU,SAAS,cAAc,MAAM,CAG7C,GAFA,EAAQ,UAAY,uCAEhB,IAAU,CAAC,GAAQ,CAAC,EAAK,aAAa,CAAC,WAAW,EAAM,aAAa,CAAC,EAAG,CAC3E,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,qCACpB,EAAQ,YAAc,EACtB,EAAQ,YAAY,EAAQ,CAG9B,GAAI,EAAM,CACR,IAAM,EAAQ,SAAS,cAAc,KAAK,CAC1C,EAAM,UAAY,qCAClB,EAAM,YAAc,EACpB,EAAM,MAAQ,EACd,EAAQ,YAAY,EAAM,CAG5B,GAAI,OAAO,GAAW,UAAY,OAAO,SAAS,EAAO,EAAI,EAAS,EAAG,CACvE,IAAM,EAAY,SAAS,cAAc,EAAM,SAAW,MAAM,CAChE,EAAU,UAAY,sCAClB,IACD,EAAgC,KAAO,SACxC,EAAU,UAAU,IAAI,iDAAiD,CACzE,EAAU,aAAa,aAAc,EAAI,MAAM,oBAAsB,eAAe,CACpF,EAAU,iBAAiB,YAAe,CACxC,EAAI,SAAS,CACX,MAAO,EAAI,MAAM,sBAAwB,mBACzC,KAAM,gBACN,QAAS,CAAE,MAAK,CACjB,CAAC,EACF,EAEJ,EAAU,YAAY,GAAwB,EAAO,CAAC,CACtD,IAAM,EAAc,SAAS,cAAc,OAAO,CAIlD,GAHA,EAAY,UAAY,4CACxB,EAAY,YAAc,GAAY,EAAO,CAAC,QAAQ,EAAE,CACxD,EAAU,YAAY,EAAY,CAC9B,OAAO,GAAgB,UAAY,OAAO,SAAS,EAAY,CAAE,CACnE,IAAM,EAAQ,SAAS,cAAc,OAAO,CAC5C,EAAM,UAAY,4CAClB,EAAM,YAAc,KAAK,EAAY,GACrC,EAAU,YAAY,EAAM,CAE9B,EAAQ,YAAY,EAAU,CAGhC,CACE,IAAI,EAAiC,EACrC,GAAI,EAAgB,CAClB,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,2CAClB,EAAM,YAAY,GAA4B,EAAe,CAAC,CAC9D,EAAQ,YAAY,EAAM,CAC1B,EAAoB,EAGtB,IAAM,EAAW,SAAS,cAAc,MAAM,CAG9C,GAFA,EAAS,UAAY,qCAEjB,IAAe,GAAM,CACvB,IAAM,EAAW,SAAS,cAAc,OAAO,CAC/C,EAAS,UAAY,8BACrB,EAAS,YAAY,EAAS,CAC9B,EAAkB,YAAY,EAAS,CACvC,eAAiB,CACV,KAAS,cACd,GAAI,GAAS,WAAW,EAAM,CAAG,EAC/B,GACE,EACA,EACA,EACA,EACA,EACA,EACA,EACD,KACI,CACL,IAAM,EAAO,EAAS,cACtB,EAAS,QAAQ,CACb,GAAM,UAAU,SAAS,2CAA2C,EACtE,EAAK,QAAQ,GAGhB,IAAI,SACE,GAAS,WAAW,EAAM,CAAG,EACtC,GAA2B,EAAU,EAAK,EAAS,EAAO,EAAe,EAAmB,EAAmB,CAC/G,EAAkB,YAAY,EAAS,SAC9B,EAAgB,CACzB,IAAM,EAAO,EACT,EAAK,UAAU,SAAS,2CAA2C,EACrE,EAAK,QAAQ,EAKnB,GAAI,OAAO,GAAY,UAAW,CAChC,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,sCAAsC,EAAU,cAAgB,oBAClF,EAAM,YAAc,EACf,EAAI,MAAM,cAAgB,WAC1B,EAAI,MAAM,iBAAmB,eAClC,EAAQ,YAAY,EAAM,CAI5B,IAAM,GAAa,EAAQ,WAC3B,GAAI,IAAc,GAAW,OAAS,EAAG,CACvC,IAAM,EAAc,SAAS,cAAc,MAAM,CACjD,EAAY,UAAY,sCACxB,IAAK,IAAM,KAAS,GAAW,MAAM,EAAG,EAAE,CAAE,CAC1C,GAAI,CAAC,GAAS,gBAAgB,KAAK,EAAM,CAAE,SAC3C,IAAM,EAAQ,SAAS,cAAc,OAAO,CAC5C,EAAM,UAAY,2CAClB,EAAM,YAAc,EACpB,EAAM,MAAQ,EACd,EAAY,YAAY,EAAM,CAE5B,EAAY,kBAAoB,GAAG,EAAQ,YAAY,EAAY,CAGzE,GAAI,EAAe,OAAS,EAAG,CAC7B,IAAM,EAAQ,SAAS,cAAc,KAAK,CAC1C,EAAM,UAAY,qCAClB,IAAK,IAAM,KAAW,EAAgB,CACpC,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,oCACjB,IAAM,EAAM,SAAS,cAAc,KAAK,CACxC,EAAI,YAAc,EAAQ,IAC1B,IAAM,EAAM,SAAS,cAAc,KAAK,CACxC,EAAI,YAAc,EAAQ,MAC1B,EAAK,YAAY,EAAI,CACrB,EAAK,YAAY,EAAI,CACrB,EAAM,YAAY,EAAK,CAEzB,EAAQ,YAAY,EAAM,CAI5B,IAAM,EAAW,GAAgB,EAAQ,CACzC,GAAI,EAAS,OAAS,EAAG,CACvB,IAAM,EAAiB,SAAS,cAAc,MAAM,CACpD,EAAe,UAAY,gCAE3B,IAAM,EAAiB,SAAS,cAAc,MAAM,CACpD,EAAe,UAAY,sCAC3B,EAAe,YAAc,GAAoB,EAAU,EAAI,CAC/D,EAAe,YAAY,EAAe,CAE1C,IAAM,EAAc,SAAS,cAAc,MAAM,CACjD,EAAY,UAAY,qCAExB,IAAK,IAAM,KAAW,EAAU,CAC9B,IAAM,EAAc,GAAoB,EAAQ,CAC1C,EAAa,GAAc,EAAS,MAAM,CAChD,GAAI,CAAC,GAAe,CAAC,EAAY,SAEjC,IAAM,EAAM,SAAS,cAAc,SAAS,CAC5C,EAAI,UAAY,4CAChB,EAAI,KAAO,SACX,IAAM,EAAY,GAAe,GAAc,GAC/C,EAAI,MAAQ,EACZ,IAAM,EAAiB,GAAe,EAAS,WAAY,UAAU,CACjE,GAAc,GAAO,IAAe,GACtC,EAAI,UAAU,IAAI,2CAA2C,CAC7D,EAAI,aAAa,eAAgB,OAAO,EAExC,EAAI,aAAa,eAAgB,QAAQ,CAEvC,IAAmB,KACrB,EAAI,UAAU,IAAI,wCAAwC,CAC1D,EAAI,SAAW,IAGjB,IAAM,EAAc,GAAa,EAAQ,CACnC,EAAc,GAAmB,EAAQ,CAC/C,GAAI,GAAe,EAAU,EAAY,CAAE,CACzC,IAAM,EAAS,SAAS,cAAc,MAAM,CAC5C,EAAO,UAAY,iFACnB,EAAiB,EAAQ,MAAO,EAAY,CAC5C,EAAO,IAAM,GACb,EAAO,aAAa,cAAe,OAAO,CAC1C,GAAqB,EAAO,CAC5B,EAAI,YAAY,EAAO,SACd,EAAa,CACtB,IAAM,EAAS,SAAS,cAAc,OAAO,CAC7C,EAAO,UAAY,sCACnB,EAAO,aAAa,cAAe,OAAO,CAC1C,EAAO,MAAM,gBAAkB,EAC/B,EAAI,YAAY,EAAO,CAGzB,IAAM,EAAQ,SAAS,cAAc,OAAO,CAC5C,EAAM,UAAY,qCAClB,EAAM,YAAc,EACpB,EAAI,YAAY,EAAM,CAEtB,IAAM,EAAmB,GAAa,EAAQ,CAC9C,GAAI,GAAoB,OAAO,EAAiB,GAAK,OAAO,EAAM,CAAE,CAClE,IAAM,EAAU,SAAS,cAAc,OAAO,CAC9C,EAAQ,UAAY,qCACpB,EAAQ,YAAc,EAAY,OAAO,EAAiB,CAAE,EAAI,QAAQ,CACxE,EAAI,YAAY,EAAQ,CAG1B,GAAI,GAAc,IAAe,EAAK,CACpC,IAAM,EAAc,GAAQ,GACtB,GACH,OAAO,EAAQ,OAAa,SAAW,EAAQ,MAAS,MAAM,CAAG,MACjE,OAAO,EAAQ,MAAY,SAAW,EAAQ,KAAQ,MAAM,CAAG,MAC/D,OAAO,EAAQ,cAAoB,SAAW,EAAQ,aAAgB,MAAM,CAAG,KAChF,GACI,EACJ,EAAY,OAAS,EACjB,EAAa,OAAS,GAAK,IAAiB,EAC1C,GAAG,EAAY,IAAI,EAAa,GAChC,EACF,EAEN,EAAI,iBAAiB,YAAe,CAClC,EAAI,SAAS,CACX,MAAO,EACP,KAAM,gBACN,QAAS,CAAE,IAAK,EAAY,CAC7B,CAAC,EACF,CAEJ,EAAY,YAAY,EAAI,CAG1B,EAAY,kBAAoB,IAClC,EAAe,YAAY,EAAY,CACvC,EAAQ,YAAY,EAAe,EAIvC,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,uCAEtB,IAAM,EAAS,EAAQ,OAAQ,OAC/B,GAAI,EAAQ,CACV,IAAM,EAAY,SAAS,cAAc,SAAS,CAClD,EAAU,UAAY,2DACtB,EAAU,KAAO,SACjB,EAAU,YAAc,EAAO,OAAS,EAAI,MAAM,iBAAmB,OACrE,EAAU,iBAAiB,YAAe,EAAI,SAAS,EAAO,CAAC,CAC/D,EAAU,YAAY,EAAU,CAIlC,GAAI,GAAY,GAAO,IAAY,GAAO,CACxC,IAAM,EAAe,SAAS,cAAc,SAAS,CACrD,EAAa,UAAY,2DACzB,EAAa,KAAO,SACpB,EAAa,YAAc,EAAI,MAAM,iBAAmB,cACxD,EAAa,iBAAiB,YAAe,CAC3C,EAAI,SAAS,CACX,MAAO,EAAI,MAAM,iBAAmB,cACpC,KAAM,YACN,QAAS,CAAE,MAAK,WAAU,SAAU,EAAG,CACxC,CAAC,EACF,CACF,EAAU,YAAY,EAAa,CAGrC,IAAM,EAAM,EAAc,EAAS,MAAM,CACzC,GAAI,CAAC,GAAU,GAAO,EAAU,EAAI,CAAE,CACpC,IAAM,EAAM,SAAS,cAAc,IAAI,CACvC,EAAI,UAAY,6DAChB,EAAiB,EAAK,OAAQ,EAAI,CAClC,EAAiB,EAAK,SAAU,SAAS,CACzC,EAAiB,EAAK,MAAO,sBAAsB,CACnD,EAAI,YAAc,EAAI,MAAM,iBAAmB,EAAI,MAAM,iBAAmB,eAC5E,EAAI,iBAAiB,QAAU,GAAM,CAC/B,EAAI,gBAAkB,IACxB,EAAE,gBAAgB,CAClB,EAAI,eAAe,CAAE,MAAK,MAAK,CAAC,GAElC,CACF,EAAU,YAAY,EAAI,CAI5B,IAAM,EAAW,EACjB,GAAI,GAAY,EAAU,EAAS,CAAE,CACnC,IAAM,EAAW,SAAS,cAAc,SAAS,CACjD,EAAS,UAAY,wEACrB,EAAS,KAAO,SAChB,IAAM,EAAa,EAAI,MAAM,aAAe,QAC5C,EAAS,MAAQ,EACjB,EAAS,aAAa,aAAc,EAAW,CAC/C,IAAM,EAAQ,6BACR,EAAM,SAAS,gBAAgB,EAAO,MAAM,CAClD,EAAI,aAAa,QAAS,KAAK,CAC/B,EAAI,aAAa,SAAU,KAAK,CAChC,EAAI,aAAa,UAAW,YAAY,CACxC,EAAI,aAAa,OAAQ,OAAO,CAChC,EAAI,aAAa,SAAU,eAAe,CAC1C,EAAI,aAAa,eAAgB,IAAI,CACrC,EAAI,aAAa,iBAAkB,QAAQ,CAC3C,EAAI,aAAa,kBAAmB,QAAQ,CAC5C,SAAS,EAAU,EAAY,EAAkB,CAC/C,IAAM,EAAI,SAAS,gBAAgB,EAAO,SAAS,CACnD,EAAE,aAAa,KAAM,EAAG,CACxB,EAAE,aAAa,KAAM,EAAG,CACxB,EAAE,aAAa,IAAK,IAAI,CACxB,EAAI,YAAY,EAAE,CAEpB,SAAS,EAAQ,EAAY,EAAY,EAAY,EAAkB,CACrE,IAAM,EAAI,SAAS,gBAAgB,EAAO,OAAO,CACjD,EAAE,aAAa,KAAM,EAAG,CACxB,EAAE,aAAa,KAAM,EAAG,CACxB,EAAE,aAAa,KAAM,EAAG,CACxB,EAAE,aAAa,KAAM,EAAG,CACxB,EAAI,YAAY,EAAE,CAEpB,EAAU,KAAM,IAAI,CACpB,EAAU,IAAK,KAAK,CACpB,EAAU,KAAM,KAAK,CACrB,EAAQ,OAAQ,QAAS,QAAS,QAAQ,CAC1C,EAAQ,QAAS,OAAQ,OAAQ,QAAQ,CACzC,EAAS,YAAY,EAAI,CACzB,EAAS,iBAAiB,QAAS,SAAY,CAC7C,GAAI,CACE,UAAU,MACZ,MAAM,UAAU,MAAM,CAAE,MAAO,GAAQ,GAAI,IAAK,EAAU,CAAC,CAClD,UAAU,YACnB,MAAM,UAAU,UAAU,UAAU,EAAS,CAC7C,EAAS,UAAU,IAAI,6CAA6C,CACpE,eAAiB,EAAS,UAAU,OAAO,6CAA6C,CAAE,KAAK,OAE3F,IAGR,CACF,EAAU,YAAY,EAAS,CAG7B,EAAU,kBAAoB,GAChC,EAAQ,YAAY,EAAU,CAGhC,EAAM,YAAY,EAAQ,CAG1B,IAAM,EAAc,GAAmB,EAAQ,CACzC,EAAiB,GAAsB,EAAQ,CAKrD,OAJI,GAAe,IACjB,EAAM,YAAY,GAAwB,EAAa,EAAgB,EAAI,CAAC,CAGvE,EAGT,SAAS,GAA+B,EAAyB,CAC/D,GAAI,EAAK,WAAa,KAAK,UACzB,OAAO,SAAS,eAAe,EAAK,aAAe,GAAG,CAGxD,GAAI,EAAK,WAAa,KAAK,aAAc,OAAO,KAEhD,IAAM,EAAU,EACV,EAAU,EAAQ,QAAQ,aAAa,CAC7C,GAAI,GAAiC,IAAI,EAAQ,CAAE,OAAO,KAE1D,GAAI,CAAC,GAAiC,IAAI,EAAQ,CAAE,CAClD,IAAM,EAAW,SAAS,wBAAwB,CAClD,IAAK,IAAM,KAAS,MAAM,KAAK,EAAQ,WAAW,CAAE,CAClD,IAAM,EAAY,GAA+B,EAAM,CACnD,GAAW,EAAS,YAAY,EAAU,CAEhD,OAAO,EAGT,IAAM,EAAmB,SAAS,cAAc,EAAQ,aAAa,CAAC,CACtE,IAAK,IAAM,KAAS,MAAM,KAAK,EAAQ,WAAW,CAAE,CAClD,IAAM,EAAY,GAA+B,EAAM,CACnD,GAAW,EAAiB,YAAY,EAAU,CAExD,OAAO,EAGT,SAAS,GAA8B,EAAqB,EAAoB,CAC9E,IAAM,EAAa,EAChB,MAAM,SAAS,CACf,IAAK,GAAc,EAAU,QAAQ,OAAQ,IAAI,CAAC,MAAM,CAAC,CACzD,OAAO,QAAQ,CAEd,KAAW,SAAW,EAE1B,IAAK,IAAM,KAAa,EAAY,CAClC,IAAM,EAAI,SAAS,cAAc,IAAI,CACrC,EAAE,YAAc,EAChB,EAAO,YAAY,EAAE,EAIzB,SAAS,GAAyB,EAAqB,EAA8C,CAGnG,GAFA,EAAO,UAAU,IAAI,mCAAmC,CAEpD,EAAY,MAAQ,OAAO,UAAc,IAAa,CACxD,IAAM,EAAM,IAAI,WAAW,CAAC,gBAAgB,EAAY,KAAM,YAAY,CACpE,EAAQ,MAAM,KAAK,EAAI,KAAK,WAAW,CAC1C,IAAK,GAAS,GAA+B,EAAK,CAAC,CACnD,OAAQ,GAAuB,CAAC,CAAC,IAAS,EAAK,WAAa,KAAK,cAAgB,CAAC,CAAC,EAAK,aAAa,MAAM,EAAE,CAEhH,GAAI,EAAM,OAAS,EAAG,CACpB,IAAK,IAAM,KAAQ,EAAO,EAAO,YAAY,EAAK,CAClD,QAIJ,GAA8B,EAAQ,EAAY,KAAK,CAGzD,SAAS,GACP,EACA,EACA,EACa,CACb,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,mCAEtB,IAAM,EAAS,SAAS,cAAc,MAAM,CAC5C,EAAO,UAAY,kDAEnB,IAAM,EAA2B,EAAE,CAGnC,GAAI,EAAa,CACf,IAAM,EAAM,SAAS,cAAc,SAAS,CAC5C,EAAI,UAAY,4FAChB,EAAI,KAAO,SACX,EAAI,aAAa,gBAAiB,OAAO,CACzC,EAAI,YAAc,EAAI,MAAM,gBAAkB,eAC9C,EAAO,YAAY,EAAI,CAEvB,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,wCAClB,GAAyB,EAAO,EAAY,CAC5C,EAAU,KAAK,EAAM,CAIvB,GAAI,EAAgB,CAClB,IAAM,EAAM,SAAS,cAAc,SAAS,CAC5C,EAAI,UAAY,0CAA2C,EAAqE,GAAvD,uDACzE,EAAI,KAAO,SACX,EAAI,aAAa,gBAAiB,EAAc,QAAU,OAAO,CACjE,EAAI,YAAc,EAAI,MAAM,mBAAqB,iBACjD,EAAO,YAAY,EAAI,CAEvB,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,wCACd,IACF,EAAM,MAAM,QAAU,QAGxB,IAAM,EAAQ,SAAS,cAAc,QAAQ,CAC7C,EAAM,UAAY,mCAClB,IAAM,EAAU,MAAM,QAAQ,EAAe,CACzC,EACA,OAAO,QAAQ,EAAe,CAAC,KAAK,CAAC,EAAK,MAAY,CAAE,MAAK,QAAO,EAAE,CAC1E,IAAK,IAAM,KAAS,EAAS,CAC3B,IAAM,EAAM,SAAS,cAAc,KAAK,CAClC,EAAU,SAAS,cAAc,KAAK,CAC5C,EAAQ,UAAY,iCACpB,EAAQ,YAAc,EAAM,IAC5B,IAAM,EAAU,SAAS,cAAc,KAAK,CAC5C,EAAQ,UAAY,mCACpB,EAAQ,YAAc,EAAM,MAC5B,EAAI,YAAY,EAAQ,CACxB,EAAI,YAAY,EAAQ,CACxB,EAAM,YAAY,EAAI,CAExB,EAAM,YAAY,EAAM,CACxB,EAAU,KAAK,EAAM,CAIvB,IAAM,EAAO,EAAO,iBAAiB,mCAAmC,CACxE,EAAK,SAAS,EAAO,IAAQ,CAC3B,EAAM,iBAAiB,YAAe,CACpC,EAAK,QAAS,GAAM,CAClB,EAAE,UAAU,OAAO,0CAA2C,YAAY,CAC1E,EAAE,aAAa,gBAAiB,QAAQ,EACxC,CACF,EAAM,UAAU,IAAI,0CAA2C,YAAY,CAC3E,EAAM,aAAa,gBAAiB,OAAO,CAC3C,EAAU,SAAS,EAAG,IAAS,CAC7B,EAAE,MAAM,QAAU,IAAS,EAAM,GAAK,QACtC,EACF,EACF,CAEF,EAAU,YAAY,EAAO,CAC7B,IAAK,IAAM,KAAK,EAAW,EAAU,YAAY,EAAE,CACnD,OAAO,EAMT,SAAS,GAAuB,EAAmC,CACjE,IAAM,EACJ,+IACF,OAAQ,EAAR,CACE,IAAK,UACH,MAAO,QAAQ,EAAE,uEACnB,IAAK,WACH,MAAO,QAAQ,EAAE,iHACnB,IAAK,YACH,MAAO,QAAQ,EAAE,kHACnB,QACE,MAAO,IAIb,SAAS,IAAoC,CAC3C,MAAO,6PAGT,SAAS,IAAkC,CACzC,MAAO,2PAIT,SAAS,IAAsC,CAG7C,MAAO,uQAGT,SAAS,GAAuB,EAAqB,EAA8B,CACjF,OAAO,EAAE,OAAS,EAAE,MAAQ,EAAE,YAAc,EAAE,UAGhD,SAAS,GAAkB,EAAoB,EAAc,EAAmC,CAC9F,GAAI,CAAC,GAAQ,EAAK,OAAS,UAAW,OAAO,EAE7C,IAAM,EAAY,EAAS,IAAK,GAAO,CAErC,IAAM,EADK,EAAK,SAAS,IACL,OAAQ,QACtB,EAAQ,EAAU,OAAO,EAAQ,MAAS,CAAG,IACnD,MAAO,CAAE,KAAI,MAAO,OAAO,SAAS,EAAM,CAAG,EAAQ,IAAU,EAC/D,CASF,OAPA,EAAU,MAAM,EAAG,IACb,EAAE,QAAU,KAAY,EAAE,QAAU,IAAiB,EACrD,EAAE,QAAU,IAAiB,EAC7B,EAAE,QAAU,IAAiB,GAC1B,EAAK,YAAc,OAAS,EAAE,MAAQ,EAAE,MAAQ,EAAE,MAAQ,EAAE,MACnE,CAEK,EAAU,IAAK,GAAM,EAAE,GAAG,CAGnC,SAAS,GAAW,EAAmB,EAAoB,EAAc,EAA8B,CACrG,IAAM,EAAS,GAAkB,EAAU,EAAM,EAAK,CAEhD,EAAW,IAAI,IACrB,IAAK,IAAM,KAAS,MAAM,KAAK,EAAK,SAAS,CAAmB,CAC9D,IAAM,EAAO,EAAM,QAAQ,UACvB,GAAM,EAAS,IAAI,EAAM,EAAM,CAGrC,IAAK,IAAM,KAAM,EAAQ,CACvB,IAAM,EAAK,EAAS,IAAI,EAAG,CACvB,GAAI,EAAK,YAAY,EAAG,EAIhC,SAAS,GACP,EACA,EACA,EACA,EACa,CACb,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,oCAEpB,IAAM,EAAW,EAAQ,UAAY,EAAE,CACjC,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,4BAEjB,IAAM,EAAa,GAAK,wBAIxB,GAHuB,EAAS,OAAS,GAAK,GAAK,aAG/B,CAClB,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,oCACpB,EAAQ,aAAa,OAAQ,UAAU,CACvC,EAAQ,aAAa,aAAc,EAAI,MAAM,sBAAwB,gBAAgB,CAErF,IAAI,EAAgC,EAAI,aAAe,CAAE,KAAM,UAAW,CAEpE,EAAgG,CACpG,CAAE,MAAO,EAAI,MAAM,aAAe,UAAW,UAAW,CAAE,KAAM,UAAW,CAAE,KAAM,UAAW,CAC9F,CACE,MAAO,EAAI,MAAM,cAAgB,UACjC,UAAW,CAAE,KAAM,QAAS,UAAW,MAAO,CAC9C,KAAM,WACP,CACD,CACE,MAAO,EAAI,MAAM,eAAiB,UAClC,UAAW,CAAE,KAAM,QAAS,UAAW,OAAQ,CAC/C,KAAM,YACP,CACF,CAEK,EAAW,SAAS,cAAc,MAAM,CAC9C,EAAS,UAAY,qCAErB,IAAM,EAAU,SAAS,cAAc,SAAS,CAChD,EAAQ,KAAO,SACf,EAAQ,UAAY,0DACpB,EAAQ,aAAa,gBAAiB,UAAU,CAChD,EAAQ,aAAa,gBAAiB,QAAQ,CAC9C,IAAM,EAAW,EAAI,MAAM,sBAAwB,gBAE7C,EAAc,SAAS,cAAc,OAAO,CAClD,EAAY,UAAY,yCACxB,IAAM,EAAe,SAAS,cAAc,OAAO,CACnD,EAAa,UAAY,0CAEzB,IAAM,EAAuB,GAA8B,CACzD,IAAM,EAAM,EAAY,KAAM,GAAM,GAAuB,EAAE,UAAW,EAAE,CAAC,EAAI,EAAY,GAC3F,EAAa,YAAc,EAAI,MAC/B,EAAY,UAAY,GAAuB,EAAI,KAAK,CACxD,EAAS,QAAQ,SAAc,EAAI,KACnC,EAAQ,aAAa,aAAc,GAAG,EAAS,IAAI,EAAI,QAAQ,CAC/D,EAAQ,MAAQ,EAAI,OAEtB,EAAoB,EAAY,CAEhC,IAAM,EAAW,SAAS,cAAc,OAAO,CAC/C,EAAS,UAAY,4CACrB,EAAS,UAAY,IAA2B,CAEhD,EAAQ,YAAY,EAAY,CAChC,EAAQ,YAAY,EAAa,CACjC,EAAQ,YAAY,EAAS,CAE7B,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,0CACjB,EAAK,OAAS,GACd,EAAK,aAAa,OAAQ,UAAU,CACpC,EAAK,aAAa,aAAc,EAAS,CAEzC,IAAM,EAAM,EAAQ,cAChB,EAA2C,KAEzC,MAA4B,CAChC,EAAK,OAAS,GACd,EAAS,UAAU,OAAO,2CAA2C,CACrE,EAAQ,aAAa,gBAAiB,QAAQ,CAC9C,GAAkB,OAAO,CACzB,EAAmB,MAIf,GAA0B,GAAwB,CACjD,EAAS,UAAU,SAAS,2CAA2C,GACxE,EAAS,SAAS,EAAE,OAAe,EACvC,GAAe,GAGX,EAAoB,GAA2B,CAC/C,EAAE,MAAQ,WACZ,EAAE,gBAAgB,CAClB,GAAe,GAIb,MAA2B,CAC/B,EAAK,OAAS,GACd,EAAS,UAAU,IAAI,2CAA2C,CAClE,EAAQ,aAAa,gBAAiB,OAAO,CAC7C,EAAmB,IAAI,gBACvB,GAAM,CAAE,UAAW,EACnB,EAAI,iBAAiB,QAAS,GAAwB,CAAE,SAAQ,CAAC,CACjE,EAAI,iBAAiB,UAAW,EAAkB,CAAE,QAAS,GAAM,SAAQ,CAAC,EAG9E,EAAQ,iBAAiB,QAAU,GAAM,CACvC,EAAE,iBAAiB,CACf,EAAS,UAAU,SAAS,2CAA2C,CACzE,GAAe,CAEf,GAAc,EAEhB,CAEF,IAAK,IAAM,KAAO,EAAa,CAC7B,IAAM,EAAY,SAAS,cAAc,SAAS,CAClD,EAAU,KAAO,SACjB,EAAU,UAAY,mDACtB,EAAU,aAAa,OAAQ,SAAS,CACxC,IAAM,EAAW,GAAuB,EAAa,EAAI,UAAU,CACnE,EAAU,aAAa,gBAAiB,EAAW,OAAS,QAAQ,CAChE,GAAU,EAAU,UAAU,IAAI,2CAA4C,yBAAyB,CAC3G,IAAM,EAAU,EAAI,UAAU,OAAS,UAAY,UAAY,SAAS,EAAI,UAAU,WAAa,KACnG,EAAU,QAAQ,QAAa,EAE/B,IAAM,EAAQ,SAAS,cAAc,OAAO,CAC5C,EAAM,UAAY,wCAClB,EAAM,UAAY,GAAuB,EAAI,KAAK,CAElD,IAAM,EAAS,SAAS,cAAc,OAAO,CAC7C,EAAO,UAAY,yCACnB,EAAO,YAAc,EAAI,MAEzB,IAAM,EAAS,SAAS,cAAc,OAAO,CAC7C,EAAO,UAAY,yCACnB,EAAO,UAAY,IAAyB,CAC5C,EAAO,aAAa,cAAe,OAAO,CACrC,GAAU,EAAO,UAAU,IAAI,iDAAiD,CAErF,EAAU,YAAY,EAAM,CAC5B,EAAU,YAAY,EAAO,CAC7B,EAAU,YAAY,EAAO,CAE7B,EAAU,iBAAiB,YAAe,CACxC,EAAc,EAAI,UAClB,EAAI,eAAe,EAAI,UAAU,CACjC,GAAW,EAAM,EAAU,EAAM,EAAI,UAAU,CAC/C,EAAK,iBAAiB,oCAAoC,CAAC,QAAS,GAAO,CACzE,IAAM,EAAM,EACN,EAAS,EAAI,QAAQ,UAAe,EAC1C,EAAI,UAAU,OAAO,2CAA4C,EAAO,CACxE,EAAI,UAAU,OAAO,yBAA0B,EAAO,CACtD,EAAI,aAAa,gBAAiB,EAAS,OAAS,QAAQ,CAC9C,EAAI,cAAc,0CAA0C,EACnE,UAAU,OAAO,iDAAkD,CAAC,EAAO,EAClF,CACF,EAAoB,EAAI,UAAU,CAClC,GAAe,EACf,CAEF,EAAK,YAAY,EAAU,CAO7B,GAJA,EAAS,YAAY,EAAQ,CAC7B,EAAS,YAAY,EAAK,CAC1B,EAAQ,YAAY,EAAS,CAEzB,EAAI,sBAAuB,CAC7B,IAAM,EAAa,SAAS,cAAc,SAAS,CACnD,EAAW,UAAY,2DACvB,EAAW,KAAO,SACd,EAAI,sBACN,EAAW,UAAU,IAAI,6CAA6C,CAEpE,EAAI,aACN,EAAW,UAAU,IAAI,6CAA6C,CAExE,IAAM,EAAc,SAAS,cAAc,OAAO,CAClD,EAAY,UAAY,sCACxB,EAAY,UAAY,IAA6B,CACrD,IAAM,EAAe,SAAS,cAAc,OAAO,CACnD,EAAa,UAAY,uCACzB,IAAM,EAAc,EAAI,MAAM,iBAAmB,UACjD,EAAa,YAAc,EAC3B,EAAW,aAAa,aAAc,EAAY,CAClD,EAAW,MAAQ,EACnB,EAAW,YAAY,EAAY,CACnC,EAAW,YAAY,EAAa,CACpC,EAAW,iBAAiB,YAAe,CACzC,EAAI,wBAAwB,GAAG,EAC/B,CACF,EAAQ,YAAY,EAAW,CAGjC,GAAI,EAAY,CACd,EAAQ,UAAU,IAAI,4CAA4C,CAClE,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,iCACjB,IAAM,EAAU,SAAS,cAAc,OAAO,CAC9C,EAAQ,UAAY,uCACpB,EAAQ,YAAc,EACtB,EAAK,YAAY,EAAQ,CACzB,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,yCACpB,EAAQ,YAAY,EAAQ,CAC5B,EAAK,YAAY,EAAQ,CACzB,EAAQ,YAAY,EAAK,MAEzB,EAAQ,YAAY,EAAQ,SAErB,EAAY,CACrB,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,iCACjB,IAAM,EAAU,SAAS,cAAc,OAAO,CAC9C,EAAQ,UAAY,uCACpB,EAAQ,YAAc,EACtB,EAAK,YAAY,EAAQ,CACzB,EAAQ,YAAY,EAAK,CAG3B,IAAM,EAAY,GAAkB,EAAU,EAAM,GAAK,YAAY,CACrE,IAAK,IAAM,KAAW,EAAW,CAC/B,GAAI,CAAC,EAAK,SAAS,GAAU,SAC7B,IAAM,EAAW,EAAc,EAAQ,CACnC,IACF,EAAS,QAAQ,UAAe,EAChC,EAAK,YAAY,EAAS,EAa9B,IARI,GAAK,UAAY,IAAkB,GACrC,EAAK,UAAU,IAAI,oCAAoC,CAGzD,EAAQ,YAAY,EAAK,CAGP,EAAQ,OAAQ,YAChB,IAAQ,EAAS,OAAS,EAAG,CAC7C,IAAM,EAAgB,GAAK,MAAM,eAAiB,YAC5C,EAAc,SAAS,cAAc,SAAS,CACpD,EAAY,UAAY,sCACxB,EAAY,KAAO,SACnB,EAAY,YAAc,EAC1B,EAAY,iBAAiB,YAAe,CAC1C,GAAK,SAAS,CAAE,MAAO,EAAe,KAAM,kBAAmB,QAAS,EAAE,CAAE,CAAC,EAC7E,CACF,EAAQ,YAAY,EAAY,CAIlC,IAAM,EAAe,GAAK,UAAY,IAAkB,CACxD,GAAI,GAAK,sBAAwB,EAAI,wBAA0B,CAAC,EAAc,CAC5E,IAAM,EAAc,GAA+B,EAAI,uBAAwB,EAAI,CACnF,EAAQ,YAAY,EAAY,CAGlC,OAAO,EAGT,SAAS,GAA6B,EAAoB,EAAuC,CAC/F,IAAM,EAAQ,EAAQ,OAAS,EAAE,CAC3B,EAAqB,EAAM,mBAC3B,EAAc,EAAM,YACpB,EAAY,EAAM,UAAmD,EAAE,CACvE,EAAc,EAAM,YAAuD,EAAE,CAC7E,EAAc,EAAM,YAA0C,EAAE,CAChE,EAAe,EAAM,aACrB,EAAkB,EAAM,gBACxB,EAAa,EAAM,WACnB,EAAiB,EAAM,eAI7B,GAAI,CAAC,EAEH,OADiB,SAAS,cAAc,MAAM,CAIhD,IAAM,EAAiE,CACrE,cACA,WACA,aACA,aACA,eACA,gBAAiB,CAAE,MAAK,UAAW,CACjC,EAAI,iBAAiB,CAAE,MAAK,IAAK,GAAI,OAAM,CAAC,EAE9C,QAAS,EAAI,QACd,CACG,IAAoB,IAAA,KAAW,EAAQ,gBAAkB,GACzD,IAAe,IAAA,KAAW,EAAQ,WAAa,GAC/C,IAAmB,IAAA,KAAW,EAAQ,eAAiB,GACvD,IAAuB,IAAA,KAAW,EAAQ,mBAAqB,GAC/D,EAAI,OACN,EAAQ,KAAO,CACb,kBAAmB,EAAI,KAAK,4BAC5B,uBAAwB,EAAI,KAAK,uBACjC,gBAAiB,EAAI,KAAK,gBAC1B,oBAAqB,EAAI,KAAK,oBAC9B,cAAe,EAAI,KAAK,cACxB,kBAAmB,EAAI,KAAK,kBAC5B,gBAAiB,EAAI,KAAK,gBAC3B,EAGH,IAAM,EAAK,GAAsB,EAAQ,CAOzC,OAJI,EAAI,UAAY,IAAkB,GACpC,EAAG,UAAU,IAAI,kCAAkC,CAG9C,EAGT,SAAS,GAAc,EAAiC,CACtD,IAAM,EAAK,SAAS,cAAc,KAAK,CACvC,EAAG,UAAY,uBACf,IAAM,EAAQ,EAAQ,OAAQ,MAC9B,GAAI,EAAO,CACT,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,+BACpB,IAAM,EAAU,SAAS,cAAc,OAAO,CAC9C,EAAQ,UAAY,6BACpB,EAAQ,YAAc,EACtB,EAAQ,YAAY,EAAG,CACvB,EAAQ,YAAY,EAAQ,CAC5B,IAAM,EAAM,SAAS,cAAc,KAAK,CAGxC,MAFA,GAAI,UAAY,uBAChB,EAAQ,YAAY,EAAI,CACjB,EAET,OAAO,ECh+DT,IAAM,GAAiB,IAAI,IAAI,CAC7B,IACA,MACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,aACA,MACA,QACA,UACA,KACA,SACA,aACA,KACA,KACA,KACD,CAAC,CAOF,SAAS,GAAgB,EAA2B,CAClD,IAAM,EAAmB,EAAE,CACvB,EAAwB,EAAE,CAE9B,IAAK,IAAM,KAAQ,EACb,EAAK,WAAa,KAAK,cAAgB,GAAe,IAAK,EAAiB,QAAQ,EAElF,EAAc,OAAS,IACzB,EAAO,KAAK,EAAc,CAC1B,EAAgB,EAAE,EAEpB,EAAO,KAAK,CAAC,EAAK,CAAC,EAEnB,EAAc,KAAK,EAAK,CAQ5B,OAJI,EAAc,OAAS,GACzB,EAAO,KAAK,EAAc,CAGrB,EAGT,SAAS,GAAc,EAA0B,CAC/C,IAAK,IAAM,KAAS,EAClB,IAAK,IAAM,KAAQ,EACjB,GACE,EAAK,WAAa,KAAK,eACrB,EAAiB,UAAY,SAAY,EAAiB,gBAAgB,QAAQ,EAEpF,MAAO,GAIb,MAAO,GAGT,SAAS,IAAgC,CAEvC,OADI,OAAO,OAAW,KAAe,OAAO,OAAO,YAAe,WAAmB,GAC9E,OAAO,WAAW,mCAAmC,CAAC,QAG/D,SAAgB,GAAc,EAA8C,CAC1E,GAAM,CAAE,YAAW,OAAM,UAAU,GAAI,SAAQ,cAAe,EAGxD,EAAW,SAAS,cAAc,WAAW,CACnD,EAAS,UAAY,EACrB,IAAM,EAAS,GAAgB,EAAS,QAAQ,WAAW,CAG3D,GAAI,IAAsB,EAAI,EAAO,QAAU,GAAK,GAAc,EAAO,CAGvE,MAFA,GAAU,UAAY,EACtB,KAAc,CACP,CAAE,UAAW,GAAI,QAAS,GAAI,UAAW,GAAO,CAIzD,EAAU,UAAY,GACtB,IAAI,EAAe,EACf,EAAgD,KAChD,EAAU,GAEd,SAAS,GAAmB,CAC1B,GAAI,CAAC,GAAW,GAAgB,EAAO,OAAQ,CAC7C,EAAU,GACV,KAAc,CACd,OAGF,IAAM,EAAQ,EAAO,GACf,EAAU,SAAS,cAAc,OAAO,CAC9C,EAAQ,UAAY,gCACpB,IAAK,IAAM,KAAQ,EACjB,EAAQ,YAAY,EAAK,UAAU,GAAK,CAAC,CAE3C,EAAU,YAAY,EAAQ,CAE9B,IACA,KAAU,CAEN,EAAe,EAAO,OACxB,EAAU,WAAW,EAAY,EAAQ,EAEzC,EAAU,GACV,KAAc,EAOlB,OAFA,GAAY,CAEL,CACL,UAAW,CACJ,IACD,IAAY,MAAM,aAAa,EAAQ,CAC3C,EAAU,GACV,EAAU,UAAY,EACtB,KAAc,GAEhB,QAAS,CACH,IAAY,MAAM,aAAa,EAAQ,CAC3C,EAAU,IAEZ,IAAI,WAAY,CACd,OAAO,GAEV,CC9IH,SAAS,GAAW,EAAmC,CACrD,OAAO,IAAS,IAAA,IAAa,iBAAiB,KAAK,EAAK,CAS1D,SAAgB,GAAoB,EAA4C,CAC9E,GAAM,CAAE,YAAW,WAAU,kBAAmB,EAChD,GAAI,EAAS,SAAW,EAAG,OAG3B,IAAM,EAAa,IAAI,IACvB,IAAK,IAAM,KAAK,EACV,EAAE,WAAW,SAAW,GAC5B,EAAW,IAAI,EAAE,WAAW,aAAa,CAAE,EAAE,CAG3C,KAAW,OAAS,EAIxB,IAAK,GAAM,CAAC,EAAW,KAAY,EAAY,CAC7C,IAAM,EAAS,SAAS,iBAAiB,EAAW,WAAW,UAAU,CACrE,EAAO,EAAO,UAAU,CACxB,EAAQ,GAEZ,KAAO,GAAQ,CAAC,GAAO,CACrB,IAAM,EAAO,EAAK,aAAe,GAC3B,EAAM,EAAK,aAAa,CAAC,QAAQ,EAAU,CACjD,GAAI,IAAQ,GAAI,CACd,EAAO,EAAO,UAAU,CACxB,SAGF,IAAM,EAAW,EAAM,EAAI,EAAK,EAAM,GAAK,IAAA,GACrC,EAAW,EAAK,EAAM,EAAQ,WAAW,QAC/C,GAAI,GAAW,EAAS,EAAI,GAAW,EAAS,CAAE,CAChD,EAAO,EAAO,UAAU,CACxB,SAGF,IAAM,EAAS,EAAK,MAAM,EAAG,EAAI,CAC3B,EAAQ,EAAK,MAAM,EAAK,EAAM,EAAQ,WAAW,OAAO,CACxD,EAAQ,EAAK,MAAM,EAAM,EAAQ,WAAW,OAAO,CAEnD,EAAS,EAAK,WACpB,GAAI,CAAC,EAAQ,CACX,EAAO,EAAO,UAAU,CACxB,SAGF,IAAM,EAAO,SAAS,cAAc,IAAI,CACxC,EAAK,UAAY,0BACjB,EAAK,YAAc,EACnB,EAAK,KAAO,IACZ,EAAK,iBAAiB,QAAU,GAAM,CACpC,EAAE,gBAAgB,CAClB,EAAe,EAAQ,IAAI,EAC3B,CAEE,GAAQ,EAAO,aAAa,SAAS,eAAe,EAAO,CAAE,EAAK,CACtE,EAAO,aAAa,EAAM,EAAK,CAC3B,GAAO,EAAO,aAAa,SAAS,eAAe,EAAM,CAAE,EAAK,CACpE,EAAO,YAAY,EAAK,CAExB,EAAQ,KCnFd,IAAM,GAAmB,IAAI,IAAI,CAAC,SAAU,OAAQ,SAAU,UAAU,CAAC,CACnE,GAAmB,IAAI,IAAI,CAC/B,cACA,gBACA,kBACA,kBACA,oBACD,CAAC,CAEF,SAAgB,GAAkB,EAA6D,CAG7F,MADA,GADI,EAAI,MAAQ,GAAiB,IAAI,EAAI,KAAK,EAC1C,EAAI,QAAQ,MAAQ,GAAiB,IAAI,EAAI,OAAO,KAAK,ECX/D,IAAM,GAAsB,oCAEtB,GAA4B,2CAYlC,SAAgB,GAAqB,EAA6C,CAChF,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,wCACjB,EAAK,QAAQ,YAAiB,kBAE9B,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,oCAEjB,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,uCACtB,EAAU,YAAc,EAAQ,QAChC,EAAK,YAAY,EAAU,CAE3B,IAAM,EAAe,SAAS,cAAc,MAAM,CAClD,EAAa,UAAY,0CACzB,EAAa,YAAc,EAAQ,WACnC,EAAK,YAAY,EAAa,CAE9B,EAAK,YAAY,EAAK,CAEtB,IAAM,EAAM,SAAS,cAAc,SAAS,CAC5C,EAAI,KAAO,SACX,EAAI,UAAY,2DAChB,EAAI,QAAQ,YAAiB,sBAC7B,EAAI,YAAc,EAAQ,SAC1B,EAAI,iBAAiB,YAAe,CAClC,GAAc,EAAQ,SAAS,CAC/B,EAAK,QAAQ,CACb,EAAQ,YAAY,EACpB,CACF,EAAK,YAAY,EAAI,CAErB,IAAM,EAAU,SAAS,cAAc,SAAS,CAahD,MAZA,GAAQ,KAAO,SACf,EAAQ,UAAY,uCACpB,EAAQ,QAAQ,YAAiB,0BACjC,EAAQ,YAAc,IACtB,EAAQ,aAAa,aAAc,EAAQ,kBAAoB,UAAU,CACzE,EAAQ,iBAAiB,YAAe,CACtC,GAAc,EAAQ,SAAS,CAC/B,EAAK,QAAQ,CACb,EAAQ,aAAa,EACrB,CACF,EAAK,YAAY,EAAQ,CAElB,EAIT,SAAgB,IAAwC,CACtD,GAAI,CACF,eAAe,WAAW,GAAoB,CAC9C,eAAe,WAAW,GAA0B,MAC9C,GAKV,SAAgB,GAA0B,EAA2B,CACnE,GAAI,CACF,IAAM,EAAM,eAAe,QAAQ,GAAoB,CAGvD,OAFK,EACuB,KAAK,MAAM,EAAI,CAC1B,SAAS,EAAS,CAFlB,QAGX,CACN,MAAO,IAIX,SAAS,GAAc,EAAwB,CAC7C,GAAI,CACF,IAAM,EAAM,eAAe,QAAQ,GAAoB,CACjD,EAAsB,EAAO,KAAK,MAAM,EAAI,CAAgB,EAAE,CAC/D,EAAU,SAAS,EAAS,EAC/B,EAAU,KAAK,EAAS,CAE1B,eAAe,QAAQ,GAAqB,KAAK,UAAU,EAAU,CAAC,MAChE,GAMV,SAAgB,GAAuC,EAAwB,CACxE,GACL,GAAc,EAAS,CC5CzB,IAAM,GAAU,oBACV,GAAa,EAEb,GAAiB,WACjB,GAAgB,UAChB,GAAgB,UAChB,GAAkB,YAKxB,SAAS,GAAoB,EAAoC,CAC/D,OAAO,IAAI,SAAS,EAAS,IAAW,CACtC,EAAQ,cAAkB,EAAQ,EAAQ,OAAO,CACjD,EAAQ,YAAgB,EAAO,EAAQ,MAAM,EAC7C,CAMJ,SAAS,GAAoB,EAAmC,CAC9D,OAAO,IAAI,SAAS,EAAS,IAAW,CACtC,EAAG,eAAmB,GAAS,CAC/B,EAAG,YAAgB,EAAO,EAAG,MAAM,CACnC,EAAG,YAAgB,EAAO,EAAG,OAAS,IAAI,aAAa,sBAAsB,CAAC,EAC9E,CAGJ,IAAa,GAAb,KAA8B,CAK5B,YAAY,EAAiB,GAAS,EAAkB,GAAY,UAJlC,KAKhC,KAAK,QAAU,EACf,KAAK,SAAW,EAOlB,MAAM,MAA6B,CAGjC,OAFI,KAAK,IAAY,KAAK,IAEnB,IAAI,SAAS,EAAS,IAAW,CACtC,IAAM,EAAU,UAAU,KAAK,KAAK,QAAS,KAAK,SAAS,CAE3D,EAAQ,gBAAmB,GAAU,CACnC,IAAM,EAAK,EAAQ,OACb,EAAc,EAAgC,WAEhD,EAAa,IAEf,EAAG,kBAAkB,GAAgB,CAAE,QAAS,CAAC,SAAU,QAAS,YAAY,CAAE,CAAC,CACnF,EAAG,kBAAkB,GAAe,CAAE,QAAS,CAAC,YAAa,WAAW,CAAE,CAAC,CACtD,EAAG,kBAAkB,GAAe,CACvD,QAAS,CAAC,WAAY,YAAY,CACnC,CAAC,CACW,YAAY,WAAY,WAAY,CAAE,OAAQ,GAAO,CAAC,CACnE,EAAG,kBAAkB,GAAiB,CAAE,QAAS,CAAC,SAAU,QAAS,MAAM,CAAE,CAAC,EAG5E,GAAc,GAAK,EAAa,IAE9B,EAAG,iBAAiB,SAAS,GAAe,EAAE,EAAG,kBAAkB,GAAe,CAClF,EAAG,iBAAiB,SAAS,GAAc,EAAE,EAAG,kBAAkB,GAAc,CACpF,EAAG,kBAAkB,GAAgB,CAAE,QAAS,CAAC,SAAU,QAAS,YAAY,CAAE,CAAC,CAC9D,EAAG,kBAAkB,GAAe,CACvD,QAAS,CAAC,WAAY,YAAY,CACnC,CAAC,CACW,YAAY,WAAY,WAAY,CAAE,OAAQ,GAAO,CAAC,CAE9D,EAAG,iBAAiB,SAAS,GAAc,EAC9C,EAAG,kBAAkB,GAAe,CAAE,QAAS,CAAC,YAAa,WAAW,CAAE,CAAC,EAI3E,EAAa,IACV,EAAG,iBAAiB,SAAS,GAAgB,EAChD,EAAG,kBAAkB,GAAiB,CAAE,QAAS,CAAC,SAAU,QAAS,MAAM,CAAE,CAAC,GAKpF,EAAQ,cAAkB,CACxB,KAAK,IAAM,EAAQ,OACnB,EAAQ,KAAK,IAAI,EAGnB,EAAQ,YAAgB,EAAO,EAAQ,MAAM,EAC7C,CAGJ,OAAc,CACZ,KAAK,KAAK,OAAO,CACjB,KAAK,IAAM,KAOb,MAAM,YAAY,EAAkC,CAElD,IAAM,EADK,KAAK,YAAY,CACd,YAAY,GAAgB,YAAY,CACtD,EAAG,YAAY,GAAe,CAAC,IAAI,EAAK,CACxC,MAAM,GAAoB,EAAG,CAG/B,MAAM,YAAY,EAAgB,EAAe,EAAgD,CAI/F,OADe,MAAM,GAFV,KAAK,YAAY,CACd,YAAY,GAAgB,WAAW,CACZ,YAAY,GAAe,CAAC,IAAI,CAAC,EAAQ,EAAO,EAAU,CAAC,CAAC,EACvD,KAOhD,MAAM,YAAY,EAAkC,CAElD,IAAM,EADK,KAAK,YAAY,CACd,YAAY,GAAe,YAAY,CACrD,EAAG,YAAY,GAAc,CAAC,IAAI,EAAK,CACvC,MAAM,GAAoB,EAAG,CAG/B,MAAM,YAAY,EAAmB,EAA+C,CAIlF,OADe,MAAM,GAFV,KAAK,YAAY,CACd,YAAY,GAAe,WAAW,CACX,YAAY,GAAc,CAAC,IAAI,CAAC,EAAW,EAAS,CAAC,CAAC,EACjD,KAUhD,MAAM,0BAA0B,EAAmB,EAAiC,CAElF,IAAM,EADK,KAAK,YAAY,CACd,YAAY,GAAe,YAAY,CAI/C,EAHQ,EAAG,YAAY,GAAc,CAGrB,YAAY,CAElC,MAAM,IAAI,SAAe,EAAS,IAAW,CAC3C,EAAQ,cAAkB,CACxB,IAAM,EAAS,EAAQ,OACvB,GAAI,CAAC,EAAQ,CACX,GAAS,CACT,OAEF,IAAM,EAAQ,EAAO,MACrB,GAAI,EAAM,YAAc,GAAa,EAAM,SAAW,EACpD,GAAI,CACF,EAAO,QAAQ,MACT,EAIV,EAAO,UAAU,EAEnB,EAAQ,YAAgB,EAAO,EAAQ,MAAM,EAC7C,CAEF,MAAM,GAAoB,EAAG,CAO/B,MAAM,kBAAkB,EAAgD,CAGtE,IAAM,EAFK,KAAK,YAAY,CACd,YAAY,GAAe,WAAW,CACnC,YAAY,GAAc,CAGrC,EAAQ,YAAY,MAAM,CAAC,EAAW,GAAG,CAAE,CAAC,EAAW,IAAS,CAAC,CAEvE,OAAO,IAAI,SAAS,EAAS,IAAW,CACtC,IAAM,EAAU,EAAM,WAAW,EAAO,OAAO,CAC/C,EAAQ,cAAkB,CACxB,IAAM,EAAS,EAAQ,OACvB,EAAQ,EAAU,EAAO,MAAwB,KAAK,EAExD,EAAQ,YAAgB,EAAO,EAAQ,MAAM,EAC7C,CAOJ,MAAM,YAAY,EAAkC,CAElD,IAAM,EADK,KAAK,YAAY,CACd,YAAY,GAAe,YAAY,CACrD,EAAG,YAAY,GAAc,CAAC,IAAI,EAAK,CACvC,MAAM,GAAoB,EAAG,CAG/B,MAAM,YAAY,EAAkB,EAAgD,CAIlF,OADe,MAAM,GAFV,KAAK,YAAY,CACd,YAAY,GAAe,WAAW,CACX,YAAY,GAAc,CAAC,IAAI,CAAC,EAAU,EAAU,CAAC,CAAC,EACjD,KAGhD,MAAM,qBAAqB,EAA0C,CAGnE,IAAM,EAFK,KAAK,YAAY,CACd,YAAY,GAAe,WAAW,CACnC,YAAY,GAAc,CAAC,MAAM,WAAW,CACvD,EAAyB,EAAE,CAEjC,OAAO,IAAI,SAAS,EAAS,IAAW,CACtC,IAAM,EAAU,EAAM,WAAW,YAAY,KAAK,EAAS,CAAC,CAC5D,EAAQ,cAAkB,CACxB,IAAM,EAAS,EAAQ,OACvB,GAAI,CAAC,EAAQ,CACX,EAAQ,EAAQ,CAChB,OAEF,EAAQ,KAAK,EAAO,MAAqB,CACzC,EAAO,UAAU,EAEnB,EAAQ,YAAgB,EAAO,EAAQ,MAAM,EAC7C,CAOJ,MAAM,aAAa,EAAmC,CAEpD,IAAM,EADK,KAAK,YAAY,CACd,YAAY,GAAiB,YAAY,CACvD,EAAG,YAAY,GAAgB,CAAC,IAAI,EAAK,CACzC,MAAM,GAAoB,EAAG,CAG/B,MAAM,eAAe,EAAgB,EAAe,EAA4B,CAE9E,IAAM,EADK,KAAK,YAAY,CACd,YAAY,GAAiB,YAAY,CACvD,EAAG,YAAY,GAAgB,CAAC,OAAO,CAAC,EAAQ,EAAO,EAAI,CAAC,CAC5D,MAAM,GAAoB,EAAG,CAG/B,MAAM,cAAc,EAAgB,EAAwC,CAI1E,OADY,MAAM,GAFP,KAAK,YAAY,CACd,YAAY,GAAiB,WAAW,CAChB,YAAY,GAAgB,CAAC,QAAQ,CAAC,EAC7C,OAAQ,GAAM,EAAE,SAAW,GAAU,EAAE,QAAU,EAAM,CAGxF,MAAM,WAAW,EAAgB,EAAe,EAA+B,CAI7E,OADe,MAAM,GAFV,KAAK,YAAY,CACd,YAAY,GAAiB,WAAW,CACb,YAAY,GAAgB,CAAC,IAAI,CAAC,EAAQ,EAAO,EAAI,CAAC,CAAC,GAC9E,IAAA,GAOpB,YAAkC,CAChC,GAAI,CAAC,KAAK,IACR,MAAU,MAAM,0DAA0D,CAE5E,OAAO,KAAK,MC7TV,GAAwD,CAC5D,kBACA,YACA,yBACA,cACD,CAQY,GAAb,KAAiC,CAU/B,YAAY,EAAqC,iBAT5B,qBACG,8BACiC,qBACpC,yBACM,sBACH,GAKtB,KAAK,UAAY,EAAQ,SAEzB,IAAM,EAAQ,IAAI,IAAY,GAAyB,CACnD,EAAQ,uBACV,EAAM,IAAI,iBAAiB,CAE7B,KAAK,mBAAqB,EAG5B,IAAI,YAAsB,CACxB,OACE,KAAK,aAAe,GACpB,CAAC,KAAK,eACN,CAAC,KAAK,kBACN,KAAK,wBAA0B,MAC/B,KAAK,mBAAmB,IAAI,KAAK,sBAAsB,EACvD,KAAK,WAIT,QAAe,CACT,KAAK,WAAa,GACpB,KAAK,aAEP,KAAK,mBAAmB,CAG1B,MAAa,CACX,KAAK,aACL,KAAK,mBAAmB,CAG1B,gBAAgB,EAAuB,CACrC,KAAK,cAAgB,EACrB,KAAK,mBAAmB,CAG1B,aAAa,EAAsB,CACjC,KAAK,WAAa,EAClB,KAAK,mBAAmB,CAG1B,iBAAiB,EAAoB,CACnC,KAAK,iBAAmB,EACxB,KAAK,mBAAmB,CAG1B,oBAAoB,EAAqC,CACvD,KAAK,sBAAwB,EAC7B,KAAK,mBAAmB,CAG1B,mBAAkC,CAChC,IAAM,EAAU,KAAK,WACjB,IAAY,KAAK,gBACnB,KAAK,cAAgB,EACrB,KAAK,UAAU,EAAQ,IC1EhB,GAAb,KAA0B,CAgBxB,YAAY,EAAyC,CAAxB,KAAA,KAAA,iBAdR,IAAI,6BAEc,IAAI,uBAElB,IAAI,8BAES,sBAET,kBAET,EAAE,qBAEU,KAShC,mBAAmB,EAAmB,EAAiD,CACrF,IAAM,EAAS,KAAK,KAAK,QAAQ,CAGjC,GAFI,CAAC,GAAQ,iBAAiB,EAE1B,EAAO,gBAAgB,CAAE,OAC7B,IAAM,EAAY,EAAO,wBAAwB,CACjD,GAAI,CAAC,EAAW,OAChB,IAAM,EAAQ,EAAU,UAAU,GAAK,CACvC,KAAK,UAAU,IAAI,EAAW,EAAM,CAChC,GAAS,KAAK,oBAAoB,IAAI,EAAW,EAAQ,CAEzD,KAAK,aACP,KAAK,cAAc,IAAI,EAAW,KAAK,YAAY,CAUvD,kBAAkB,EAA4B,CAE5C,IAAM,EAAU,KAAK,oBAAoB,IAAI,EAAU,CACjD,EAAW,KAAK,UAAU,IAAI,EAAU,CAC9C,GAAI,CAAC,GAAW,CAAC,EAAU,MAAO,GAElC,IAAM,EAAS,KAAK,KAAK,QAAQ,CAC3B,EAAS,KAAK,KAAK,QAAQ,CAG7B,KAAK,uBACM,GAAQ,cAAc,qBAAqB,IAAI,OAAO,KAAK,qBAAqB,CAAC,IAAI,GAC5F,UAAU,OAAO,8BAA8B,EAIvC,GAAQ,cAAc,qBAAqB,IAAI,OAAO,EAAU,CAAC,IAAI,GAC5E,UAAU,IAAI,8BAA8B,CACrD,KAAK,qBAAuB,EAG5B,IAAM,EAAK,EAAU,GAAS,CAAI,EAAU,UAAU,GAAK,CAC3D,GAAQ,gBAAgB,EAAG,CAG3B,IAAM,EAAe,KAAK,cAAc,IAAI,EAAU,CAEtD,GADA,GAAQ,yBAAyB,IAAiB,cAAc,CAC5D,EAGF,GAFA,KAAK,YAAc,EACI,EAAG,cAAc,wCAAwC,EAC1D,IAAiB,cAAe,CACpD,IAAM,EAAkB,KAAK,KAAK,iBAAiB,CACnD,GAAI,EAAiB,CACnB,IAAM,EAAM,KAAK,QAAQ,QAAQ,EAAgB,CAC3C,EAAU,EAAM,EAChB,EAAa,GAAO,GAAK,EAAM,KAAK,QAAQ,OAAS,EAC3D,KAAK,KAAK,QAAQ,EAAE,kBAAkB,EAAS,EAAY,GAAG,OAGhE,KAAK,aAAa,EAAa,CAGnC,MAAO,GAOT,gBAAgB,EAAyB,CACvC,IAAM,EAAS,KAAK,KAAK,QAAQ,CAC7B,EACF,eAAiB,GAAQ,KAAK,eAAgB,EAAE,CAAC,CAAE,IAAI,CAEvD,eAAiB,GAAQ,KAAK,aAAc,EAAE,CAAC,CAAE,IAAI,CAQzD,kBAAkB,EAAuB,EAA+B,CACtE,GAAI,EAAc,OAAO,EACzB,IAAM,EAAO,KAAK,KAAK,MAAM,CAC7B,OAAQ,EAAR,CACE,IAAK,sBACH,OAAO,EAAK,yBACd,IAAK,cAEH,OAAO,GAAuB,KAAK,eAAe,CAC9C,EAAK,wBACL,EAAK,0BACX,IAAK,kBACH,OAAO,EAAK,4BACd,IAAK,kBACH,OAAO,EAAK,qBACd,QACE,MAAO,IASb,aAAa,EAAuB,EAAuB,EAA6B,CACtF,IAAM,EAAkB,KAAK,KAAK,iBAAiB,CACnD,GAAI,CAAC,EAAiB,OACtB,IAAM,EAAM,KAAK,QAAQ,QAAQ,EAAgB,CAC3C,EAAU,EAAM,EAChB,EAAa,GAAO,GAAK,EAAM,KAAK,QAAQ,OAAS,EACrD,EAAQ,IAAiB,IAAA,GAA2B,KAAK,kBAAkB,EAAe,EAAa,CAAlE,EAC3C,KAAK,KAAK,QAAQ,EAAE,kBAAkB,EAAS,EAAY,EAAM,CAOnE,uBAAuB,EAA2B,CAChD,IAAM,EAAO,KAAK,KAAK,MAAM,CASvB,EAR0C,CAC9C,eAAgB,EAAK,yBACrB,YAAa,GAAuB,KAAK,eAAe,CACpD,EAAK,wBACL,EAAK,0BACT,gBAAiB,EAAK,4BACtB,UAAW,EAAK,qBACjB,CAC6B,IAAgB,GAC9C,GAAI,EAAO,CACT,IAAM,EAAkB,KAAK,KAAK,iBAAiB,CAC7C,EAAM,EAAkB,KAAK,QAAQ,QAAQ,EAAgB,CAAG,GAChE,EAAU,EAAM,EAChB,EAAa,GAAO,GAAK,EAAM,KAAK,QAAQ,OAAS,EAC3D,KAAK,KAAK,QAAQ,EAAE,kBAAkB,EAAS,EAAY,EAAM,EAOrE,mBAAmB,EAA6B,CAO9C,IAAM,EAN4C,CAChD,gBAAiB,kBACjB,gBAAiB,YACjB,oBAAqB,iBACrB,YAAa,cACd,CACyB,IAAkB,KAC5C,KAAK,KAAK,qBAAqB,EAAE,oBAAoB,EAAU,CAIjE,cAAqB,CACnB,IAAM,EAAkB,KAAK,KAAK,iBAAiB,CACnD,GAAI,CAAC,EAAiB,OACtB,IAAM,EAAM,KAAK,QAAQ,QAAQ,EAAgB,CACjD,GAAI,EAAM,EAAG,CACX,IAAM,EAAS,KAAK,QAAQ,EAAM,GAC9B,GAAQ,KAAK,KAAK,iBAAiB,EAAO,EAKlD,iBAAwB,CACtB,IAAM,EAAkB,KAAK,KAAK,iBAAiB,CACnD,GAAI,CAAC,EAAiB,OACtB,IAAM,EAAM,KAAK,QAAQ,QAAQ,EAAgB,CACjD,GAAI,GAAO,GAAK,EAAM,KAAK,QAAQ,OAAS,EAAG,CAC7C,IAAM,EAAS,KAAK,QAAQ,EAAM,GAC9B,GAAQ,KAAK,KAAK,iBAAiB,EAAO,EASlD,YAAY,EAAsB,CAChC,IAAM,EAAO,EAAK,SAAS,EAAK,MAChC,GAAI,CAAC,GAAQ,EAAK,OAAS,cAAe,OAAO,EAEjD,IAAM,EAAuB,CAC3B,GAAG,EACH,KAAM,sBACP,CAED,MAAO,CACL,KAAM,EAAK,KACX,SAAU,CACR,GAAG,EAAK,UACP,EAAK,MAAO,EACd,CACF,CAGH,SAAgB,CACd,KAAK,UAAU,OAAO,CACtB,KAAK,oBAAoB,OAAO,CAChC,KAAK,cAAc,OAAO,CAC1B,KAAK,qBAAuB,KAC5B,KAAK,YAAc,KACnB,KAAK,QAAU,EAAE,GAIrB,SAAS,GAAuB,EAAoC,CAClE,OAAO,IAAe,gBAAkB,IAAe,YAYzD,SAAgB,GAA2B,EAOrB,CAuBpB,OApBE,EAAK,gBACL,EAAK,mBAAqB,uBAC1B,EAAK,iBACL,CAAC,EAAK,eAEC,iBAOP,EAAK,gBAAkB,eACvB,CAAC,EAAK,6BACN,EAAK,iBACL,CAAC,EAAK,eAEC,SAGF,UC/QT,IAAa,GAAb,KAAgC,CAS9B,YAAY,EAA6B,oBANH,IAAI,yBAEW,IAAI,sBAEnB,QAAQ,SAAS,CAGrD,KAAK,IAAM,EAGb,IAAI,IAA8B,CAChC,OAAO,KAAK,IAGd,IAAI,GAAG,EAAgC,CACrC,KAAK,IAAM,EAOb,MAAM,QAAQ,EAA6C,CACzD,GAAI,CAAC,KAAK,IAAK,OAEf,IAAM,EAAO,KAAK,aACd,EACJ,KAAK,aAAe,IAAI,QAAe,GAAM,CAC3C,EAAS,GACT,CACF,MAAM,EACN,GAAI,CACF,MAAM,KAAK,aAAa,EAAO,QACvB,CACR,GAAS,EAIb,MAAc,aAAa,EAA6C,CACtE,GAAI,CAAC,KAAK,IAAK,OAEf,IAAM,EAAkD,EAAO,SAAS,IAAK,GAAM,CACjF,IAAM,EAA8B,CAClC,GAAI,EAAE,GACN,KAAM,EAAE,KACR,UAAW,EAAE,UACb,OAAQ,EAAE,SAAW,YAAc,OAAS,EAAE,OAC/C,CAID,OAHI,EAAE,WAAa,IAAA,KAAW,EAAG,SAAW,EAAE,UAC1C,EAAE,UAAY,IAAA,KAAW,EAAG,QAAU,EAAE,SACxC,EAAE,SAAQ,EAAG,OAAS,IACnB,GACP,CAGI,EAA4C,EAAE,CACpD,IAAK,GAAM,CAAC,EAAO,KAAO,EAAO,eAC3B,EAAG,cAAc,+BAA+B,GACpD,EAAkB,GAAS,EAAG,WAGhC,MAAM,KAAK,IAAI,YAAY,CACzB,OAAQ,EAAO,OACf,MAAO,EAAO,MACd,UAAW,EAAO,UAClB,SAAU,EACV,gBAAiB,EAAO,gBACxB,aAAc,EAAO,aACrB,UAAW,EAAO,cAClB,aAAc,EAAO,aAAa,OAAS,EAAI,EAAO,aAAe,IAAA,GACrE,iBAAkB,EAAO,iBAAiB,OAAS,EAAI,EAAO,iBAAmB,IAAA,GACjF,kBAAmB,OAAO,KAAK,EAAkB,CAAC,OAAS,EAAI,EAAoB,IAAA,GACnF,IAAK,EAAO,IACb,CAAC,CAGE,EAAO,oBAAsB,EAAO,iBACtC,MAAM,KAAK,IAAI,YAAY,CACzB,UAAW,EAAO,UAClB,SAAU,EAAO,gBACjB,QAAS,EAAO,mBACjB,CAAC,CAMJ,IAAK,IAAM,KAAK,EAAO,SACjB,EAAE,QAAU,EAAE,UAChB,MAAM,KAAK,IAAI,YAAY,CACzB,SAAU,EAAE,SACZ,UAAW,EAAE,GACb,OAAQ,EAAE,OACX,CAAC,CAcR,MAAM,eAAe,EAAa,EAAgC,EAAmD,CACnH,GAAI,CACF,MAAM,GAAW,MACX,EASR,GAAQ,KAAK,kBAAmB,CAAE,MAAK,CAAC,CACpC,EAAU,EAAI,GAChB,OAAO,SAAS,KAAO,GAQ3B,MAAM,YAAY,EAAkB,EAA2C,CAC7E,GAAI,CAAC,KAAK,IAAK,OAAO,KAEtB,IAAK,IAAI,EAAU,EAAG,EAAU,EAAG,IAAW,CAC5C,GAAI,CACF,IAAM,EAAU,MAAM,KAAK,IAAI,YAAY,EAAU,EAAU,CAC/D,GAAI,EAAS,OAAO,EAAQ,YACtB,EAGJ,EAAU,GACZ,MAAM,IAAI,QAAS,GAAM,WAAW,EAAG,IAAI,CAAC,CAGhD,OAAO,KAMT,MAAM,cAAc,EAAgB,EAA8B,CAC3D,QAAK,IACV,GAAI,CACF,IAAM,EAAO,MAAM,KAAK,IAAI,cAAc,EAAQ,EAAM,CACxD,IAAK,IAAM,KAAK,EACd,KAAK,cAAc,IAAI,EAAE,IAAI,CAC7B,KAAK,gBAAgB,IAAI,EAAE,IAAK,EAAE,MAE9B,GAMV,qBAAsC,CACpC,MAAO,CAAC,GAAG,KAAK,gBAAgB,QAAQ,CAAC,CAAC,MAAM,EAAG,KACzC,EAAE,SAAW,IAAI,cAAc,EAAE,SAAW,GAAG,CACvD,CAMJ,MAAM,eAAe,EAAgB,EAAe,EAAa,EAAiD,CAEhH,GAAI,KAAK,cAAc,IAAI,EAAI,CAC7B,KAAK,cAAc,OAAO,EAAI,CAC9B,KAAK,gBAAgB,OAAO,EAAI,CAC5B,KAAK,KAAK,MAAM,KAAK,IAAI,eAAe,EAAQ,EAAO,EAAI,KAC1D,CACL,IAAM,EAAqB,CACzB,SACA,QACA,MACA,KAAM,EAAQ,KACd,SAAU,EAAQ,SAClB,MAAO,EAAQ,MACf,QAAS,IAAI,MAAM,CAAC,aAAa,CAClC,CACD,KAAK,cAAc,IAAI,EAAI,CAC3B,KAAK,gBAAgB,IAAI,EAAK,EAAK,CAC/B,KAAK,KAAK,MAAM,KAAK,IAAI,aAAa,EAAK,EAInD,OAAc,CACZ,KAAK,KAAK,OAAO,CACjB,KAAK,IAAM,OClNT,GAAoB,GAAqB,GAAG,EAAS,YAE9C,GAAb,KAAmC,2BAEe,EAAE,qBACjC,wBACC,wBAEe,gBAEzB,gCACiC,wBACH,wBACd,EAExB,SAAS,EAAsB,CAC7B,KAAK,MAAQ,EACR,IACH,KAAK,gBAAkB,GACvB,KAAK,eAAiB,GACtB,KAAK,gBAAkB,KACvB,KAAK,cAAgB,MAIzB,OAAc,CACZ,KAAK,OAAS,EAAE,CAChB,KAAK,eAAiB,GACtB,KAAK,gBAAkB,GACvB,KAAK,gBAAkB,KACvB,KAAK,wBAA0B,KAC/B,KAAK,cAAgB,KAGvB,0BAA0B,EAAwB,CAChD,IAAM,EAAK,GAAiB,EAAS,CAC/B,EAAW,KAAK,OAAO,GACvB,EACJ,KAAK,OAAS,KAAK,gBAAkB,CAAC,KAAK,gBAAkB,gBAAkB,SAEjF,KAAK,OAAO,GAAM,CAChB,KACA,WACA,UAAW,GAAU,YAAc,OAAS,EAAY,GAAU,WAAa,EAC/E,OAAQ,YACR,UAAW,KAAK,KAAK,CACtB,CACG,GAAU,YAAc,QAAU,KAAK,0BAA4B,IACrE,KAAK,wBAA0B,MAInC,uBAAuB,EAAwB,CAC7C,IAAM,EAAK,GAAiB,EAAS,CAC/B,EAAW,KAAK,OAAO,GACxB,IACL,EAAS,OAAS,WAClB,EAAS,UAAY,KAAK,KAAK,EAGjC,mBAAmB,EAAsE,CACvF,IAAM,EAAI,KAAK,KAAK,CACpB,IAAK,GAAM,CAAE,UAAS,eAAe,EAAS,CAC5C,IAAM,EAAI,KAAK,OAAO,GAClB,CAAC,GAAK,EAAE,YAAc,IAC1B,EAAE,UAAY,EACd,EAAE,UAAY,IAIlB,mBAAmB,EAAkB,EAA2B,SAAgB,CAC9E,KAAK,gBAAkB,EACvB,KAAK,cAAgB,CACnB,GAAI,KAAK,gBACT,KAAM,SACN,WACA,WACD,CAGH,sBAAsB,EAA2B,SAAgB,CAC/D,KAAK,cAAgB,CACnB,GAAI,KAAK,gBACT,KAAM,SACN,WACD,CAGH,qBAAqB,EAAkB,CACjC,KAAK,eAAe,KAAO,IAC7B,KAAK,cAAgB,MAIzB,sBAA6B,CAC3B,KAAK,gBAAkB,KAIzB,mBAAmB,EAA+B,CAChD,KAAK,gBAAkB,EAGzB,sBAAsB,EAAuB,CAC3C,KAAK,wBAA0B,EAIjC,6BAAuC,CACrC,OAAO,KAAK,OAAS,KAAK,iBAAmB,CAAC,KAAK,eAGrD,sBAAsB,EAA8C,CAClE,OAAO,KAAK,OAAO,GAAiB,EAAS,GAAG,YAOpD,SAAgB,GACd,EACA,EACe,CACf,IAAK,IAAI,EAAI,EAAiB,OAAS,EAAG,GAAK,EAAG,IAAK,CACrD,IAAM,EAAM,EAAiB,GACvB,EAAK,EAAM,sBAAsB,EAAI,CAC3C,GAAI,IAAO,IAAA,IAAa,IAAO,OAC7B,OAAO,EAGX,OAAO,KCpGT,SAAgB,GAAiB,EAA+C,CAC9E,GAAM,CACJ,QACA,YACA,eACA,iBAAiB,UACjB,aAAa,UACb,iBAAiB,8BACjB,UAAU,8BACV,mBAAmB,IACnB,eAAe,QACf,gBAAgB,OAChB,WAAW,QACT,EAEE,EAAc,GAAG,SAAS,EAAc,GAAG,CAAG,GAAG,IACjD,EAAe,GAAG,SAAS,EAAe,GAAG,CAAG,EAAE,IAClD,EAAiB,GAAG,SAAS,EAAU,GAAG,CAAG,EAAE,IAE/C,EAAM;;oBAEM,EAAa;sBACX,EAAe;iBACpB,EAAW;;;;;WAKjB,EAAa;eACT,EAAa;eACb,EAAa;YAChB,EAAc;gBACV,EAAc;;;;;;;;;;;;;;;;;;;;;;;;GAwB3B,EAAe;;;;;;;;;;WAUP,EAAS;YACR,EAAS;cACP,EAAS;;;;;;;;;qBASF,EAAiB;;aAEzB,EAAY;iBACR,EAAY;iBACZ,EAAY;cACf,EAAa;kBACT,EAAa;;;;;KAK1B,EAAe;;;;;aAKP,EAAe;cACd,EAAe;kBACX,EAAe;;;EAG/B,MAAM,CAEA,MAA2C,CAC/C,IAAK,IAAM,KAAM,SAAS,iBAAiB,wBAAwB,CAAE,CACnE,GAAI,EAAE,aAAc,aAAc,SAClC,IAAM,EAAK,EAAG,WACT,OACD,EAAG,cAAc,6CAA6C,EAAI,EAAG,cAAc,qBAAqB,EAC1G,OAAO,EAGX,OAAO,MAGH,EAAe,GAA2B,CAC9C,GAAI,EAAK,eAAe,EAAQ,CAAE,OAClC,IAAM,EAAQ,SAAS,cAAc,QAAQ,CAC7C,EAAM,GAAK,EACX,EAAM,YAAc,EACpB,EAAK,YAAY,EAAM,EAGnB,EAAmB,IAAI,OAAO,EAAe,CAE7C,EAAa,GAA8B,CAC/C,EAAY,EAAK,CAEjB,IAAM,EAAW,EAAK,cAAc,6CAA6C,CACjF,GAAI,EAAE,aAAoB,mBAAoB,MAAO,GAKrD,IAAM,EAAe,EAAK,cAAc,2CAA2C,CACnF,GAAI,aAAwB,iBAAkB,CAC5C,IAAM,EAAc,EAAK,cAAgC,iDAAiD,EACtG,CAAC,GAAe,EAAa,MAAQ,EAAY,MACnD,EAAa,UAAU,OAAO,mCAAmC,CAIrE,GAAI,EAAS,cAAc,IAAI,IAAmB,CAAE,MAAO,GAE3D,EAAS,aAAa,aAAc,EAAM,CAC1C,IAAM,EAAU,SAAS,cAAc,OAAO,CAI9C,MAHA,GAAQ,UAAY,EACpB,EAAQ,YAAc,EACtB,EAAS,YAAY,EAAQ,CACtB,IAkCT,MAAO,CAAE,iBAAkB,EAAW,MA/BxB,KAAO,IAA6C,CAIhE,GAAI,EAAc,CAChB,EAAY,EAAa,CACzB,MAAM,QAAQ,SAAS,CACvB,IAAK,IAAI,EAAQ,EAAG,EAAQ,GAAI,IAAS,CACvC,GAAI,EAAU,EAAa,CAAE,OAC7B,MAAM,IAAI,QAAe,GAAY,0BAA4B,GAAS,CAAC,CAAC,CAE9E,OAIF,IAAM,EAAY,GAAgB,CAC9B,GAAW,YAAY,EAAY,EAAU,WAAW,CAE5D,MAAM,QAAQ,SAAS,CAEvB,IAAK,IAAI,EAAQ,EAAG,EAAQ,GAAI,IAAS,CAEvC,IAAM,EADO,GAAgB,EACV,YAAc,KACjC,GAAI,IACF,EAAY,EAAK,CACb,EAAU,EAAK,EAAE,OAEvB,MAAM,IAAI,QAAe,GAAY,0BAA4B,GAAS,CAAC,CAAC,GAInC,CCvO/C,IAAM,GAAqB,mBAErB,GAAyB,CAC7B,yBACA,2BACA,gBACA,cACA,aACA,kBACA,aACA,qBACA,6BACA,yBACD,CAEK,GAA0B,CAC9B,kBACA,kBACA,qBACA,kBACA,iCACA,eACA,oBACA,qBACA,eACA,kBACA,6BACD,CAGK,GAA+B,IAOrC,SAAgB,GAAgC,EAAY,EAA8B,CACxF,GAAI,GAA0B,EAAI,CAAE,MAAO,GAE3C,IAAM,EAAM,EAAI,QAAQ,MAAM,CAE9B,GADI,GAAmB,KAAK,EAAI,EAC5B,GAAuB,KAAM,GAAM,EAAE,KAAK,EAAI,CAAC,CAAE,MAAO,GAE5D,IAAM,EAAO,EAAY,MAAM,CAC/B,GAAI,EAAK,OAAS,GAA8B,MAAO,GAEvD,IAAM,EAAQ,EAAK,MAAM,KAAK,CAAC,OAK/B,MAFA,GAFI,GAAS,GACT,GAAwB,KAAM,GAAM,EAAE,KAAK,EAAK,CAAC,EACjD,GAAS,GAAK,UAAU,KAAK,EAAK,EC1CxC,IAAM,GAAmB,qBACnB,GAAoB,CAAC,OAAQ,eAAgB,eAAe,CAC5D,GAAqB,WAE3B,SAAgB,GAAa,EAAuB,CAClD,IAAM,EAAQ,EAAK,aAAa,CAChC,OAAO,GAAkB,KAAM,GAAM,EAAM,SAAS,EAAE,CAAC,EAAI,GAAmB,KAAK,EAAM,CAG3F,SAAgB,GAAY,EAA4B,CACtD,GAAI,CACF,OAAO,aAAa,QAAQ,GAAG,GAAiB,GAAG,IAAY,GAAK,SAC9D,CACN,MAAO,IAIX,SAAgB,GAAc,EAAyB,CACrD,GAAI,CACF,aAAa,QAAQ,GAAG,GAAiB,GAAG,IAAa,IAAI,MACvD,GAUV,SAAgB,GAAe,EAAsB,CAEnD,IAAM,EADM,IAAI,WAAW,CAAC,gBAAgB,EAAM,YAAY,CAC7C,KACX,EAAW,MAAM,KAAK,EAAK,SAAS,CAC1C,IAAK,IAAM,KAAS,EAClB,GAAI,GAAa,EAAM,aAAe,GAAG,CAAE,CACzC,EAAM,QAAQ,CACd,MAGJ,OAAO,EAAK,UAAU,MAAM,CAG9B,SAAgB,GAAiB,EAA6B,CAC5D,IAAM,EAAM,IAAI,WAAW,CAAC,gBAAgB,EAAM,YAAY,CAC9D,IAAK,IAAM,KAAS,MAAM,KAAK,EAAI,KAAK,SAAS,CAC/C,GAAI,GAAa,EAAM,aAAe,GAAG,CACvC,OAAO,EAAM,UAGjB,OAAO,KAGT,IAAM,GAA6C,CACjD,GAAI,UACJ,GAAI,UACJ,GAAI,SACJ,GAAI,SACL,CAED,SAAgB,GAAuB,EAAyB,CAE9D,OADK,EACE,GAAmB,EAAO,aAAa,CAAC,MAAM,EAAG,EAAE,GAAK,UAD3C,w+oKEvCtB,SAAS,IAAyB,CAChC,OAAO,OAAO,OAAW,KAAe,MAAM,QAAQ,OAAO,UAAU,CAOzE,SAAS,EAAU,EAAmB,EAAwC,CAC5E,IAAM,EAA0B,CAC9B,MAAO,EACP,GAAG,EACJ,CAEG,IAAe,EACjB,OAAO,UAAW,KAAK,EAAQ,CAUnC,SAAgB,GAAU,EAAsB,CAC9C,EAAU,kBAAmB,CAAE,eAAgB,EAAQ,CAAC,CAI1D,SAAgB,GAAU,EAAsB,CAC9C,EAAU,eAAgB,CAAE,eAAgB,EAAQ,CAAC,CASvD,SAAgB,GAAuB,EAAe,EAAoB,CACxE,EAAU,6BAA8B,CACtC,uBAAwB,EACxB,oBAAqB,EACtB,CAAC,CAIJ,SAAgB,GAAkB,EAAmB,CACnD,EAAU,wBAAyB,CAAE,YAAa,EAAK,CAAC,CAI1D,SAAgB,GAAyB,EAAmB,CAC1D,EAAU,+BAAgC,CAAE,YAAa,EAAK,CAAC,CAIjE,SAAgB,GAAqB,EAAsB,CACzD,EAAU,2BAA4B,CACpC,aAAc,EACd,sBAAuB,EAAK,OAC7B,CAAC,CAIJ,SAAgB,IAA0B,CACxC,EAAU,wBAAwB,CAIpC,SAAgB,GAAqB,EAA4B,CAC/D,EAAU,2BAA4B,CACpC,sBAAuB,EACxB,CAAC,CAIJ,SAAgB,GAAiB,EAAmB,CAClD,EAAU,uBAAwB,CAAE,YAAa,EAAK,CAAC,CAIzD,SAAgB,IAAsB,CACpC,EAAU,oBAAoB,CAIhC,SAAgB,GAAY,EAAgB,EAA4B,CACtE,EAAU,iBAAkB,CAC1B,qBAAsB,EACtB,qBAAsB,EACvB,CAAC,CAIJ,SAAgB,GAAmB,EAAa,EAAqB,CACnE,EAAU,yBAA0B,CAClC,YAAa,EACb,qBAAsB,EACvB,CAAC,CAIJ,SAAgB,GAAa,EAAa,EAAwB,CAChE,EAAU,mBAAoB,CAC5B,YAAa,EACb,iBAAkB,EACnB,CAAC,CAIJ,SAAgB,IAAyB,CACvC,EAAU,uBAAuB,CAInC,SAAgB,IAA6B,CAC3C,EAAU,2BAA2B,CAIvC,SAAgB,IAA+B,CAC7C,EAAU,6BAA6B,CAIzC,SAAgB,IAAwB,CACtC,EAAU,sBAAsB,CC/Jb,OAAO,OAAO,CAC/B,OAAQ,UACX,CAAC,CACF,SAAyC,EAAa,EAAM,EAAa,EAAQ,CAC7E,SAAS,EAAK,EAAM,EAAK,CAWrB,GAVK,EAAK,MACN,OAAO,eAAe,EAAM,OAAQ,CAChC,MAAO,CACH,MACA,OAAQ,EACR,OAAQ,IAAI,IACf,CACD,WAAY,GACf,CAAC,CAEF,EAAK,KAAK,OAAO,IAAI,EAAK,CAC1B,OAEJ,EAAK,KAAK,OAAO,IAAI,EAAK,CAC1B,EAAY,EAAM,EAAI,CAEtB,IAAM,EAAQ,EAAE,UACV,EAAO,OAAO,KAAK,EAAM,CAC/B,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAQ,IAAK,CAClC,IAAM,EAAI,EAAK,GACT,KAAK,IACP,EAAK,GAAK,EAAM,GAAG,KAAK,EAAK,GAKzC,IAAM,EAAS,GAAQ,QAAU,OACjC,MAAM,UAAmB,CAAO,EAEhC,OAAO,eAAe,EAAY,OAAQ,CAAE,MAAO,EAAM,CAAC,CAC1D,SAAS,EAAE,EAAK,CACZ,IAAI,EACJ,IAAM,EAAO,GAAQ,OAAS,IAAI,EAAe,KACjD,EAAK,EAAM,EAAI,EACd,EAAK,EAAK,MAAM,WAAa,EAAG,SAAW,EAAE,EAC9C,IAAK,IAAM,KAAM,EAAK,KAAK,SACvB,GAAI,CAER,OAAO,EAWX,OATA,OAAO,eAAe,EAAG,OAAQ,CAAE,MAAO,EAAM,CAAC,CACjD,OAAO,eAAe,EAAG,OAAO,YAAa,CACzC,MAAQ,GACA,GAAQ,QAAU,aAAgB,EAAO,OAClC,GACJ,GAAM,MAAM,QAAQ,IAAI,EAAK,CAE3C,CAAC,CACF,OAAO,eAAe,EAAG,OAAQ,CAAE,MAAO,EAAM,CAAC,CAC1C,EAIX,IAAa,GAAb,cAAoC,KAAM,CACtC,aAAc,CACV,MAAM,2EAA2E,GAG5E,GAAb,cAAqC,KAAM,CACvC,YAAY,EAAM,CACd,MAAM,uDAAuD,IAAO,CACpE,KAAK,KAAO,mBAGP,GAAe,EAAE,CAC9B,SAAgB,GAAO,EAAW,CAG9B,OAFI,GACA,OAAO,OAAO,GAAc,EAAU,CACnC,GC9DX,SAAgB,GAAc,EAAS,CACnC,IAAM,EAAgB,OAAO,OAAO,EAAQ,CAAC,OAAQ,GAAM,OAAO,GAAM,SAAS,CAIjF,OAHe,OAAO,QAAQ,EAAQ,CACjC,QAAQ,CAAC,EAAG,KAAO,EAAc,QAAQ,CAAC,EAAE,GAAK,GAAG,CACpD,KAAK,CAAC,EAAG,KAAO,EAAE,CAM3B,SAAgB,GAAsB,EAAG,EAAO,CAG5C,OAFI,OAAO,GAAU,SACV,EAAM,UAAU,CACpB,EAEX,SAAgB,GAAO,EAAQ,CAE3B,MAAO,CACH,IAAI,OAAQ,CACE,CACN,IAAM,EAAQ,GAAQ,CAEtB,OADA,OAAO,eAAe,KAAM,QAAS,CAAE,QAAO,CAAC,CACxC,EAEX,MAAU,MAAM,2BAA2B,EAElD,CAEL,SAAgB,GAAQ,EAAO,CAC3B,OAAO,GAAU,KAErB,SAAgB,GAAW,EAAQ,CAC/B,IAAM,EAAQ,KAAO,WAAW,IAAI,CAC9B,EAAM,EAAO,SAAS,IAAI,CAAG,EAAO,OAAS,EAAI,EAAO,OAC9D,OAAO,EAAO,MAAM,EAAO,EAAI,CAEnC,SAAgB,GAAmB,EAAK,EAAM,CAC1C,IAAM,GAAe,EAAI,UAAU,CAAC,MAAM,IAAI,CAAC,IAAM,IAAI,OACnD,EAAa,EAAK,UAAU,CAC9B,GAAgB,EAAW,MAAM,IAAI,CAAC,IAAM,IAAI,OACpD,GAAI,IAAiB,GAAK,WAAW,KAAK,EAAW,CAAE,CACnD,IAAM,EAAQ,EAAW,MAAM,aAAa,CACxC,IAAQ,KACR,EAAe,OAAO,SAAS,EAAM,GAAG,EAGhD,IAAM,EAAW,EAAc,EAAe,EAAc,EAG5D,OAFe,OAAO,SAAS,EAAI,QAAQ,EAAS,CAAC,QAAQ,IAAK,GAAG,CAAC,CACtD,OAAO,SAAS,EAAK,QAAQ,EAAS,CAAC,QAAQ,IAAK,GAAG,CAAC,CAC5C,IAAM,EAEtC,IAAM,GAAa,OAAO,aAAa,CACvC,SAAgB,EAAW,EAAQ,EAAK,EAAQ,CAC5C,IAAI,EACJ,OAAO,eAAe,EAAQ,EAAK,CAC/B,KAAM,CACE,OAAU,GAQd,OAJI,IAAU,IAAA,KACV,EAAQ,GACR,EAAQ,GAAQ,EAEb,GAEX,IAAI,EAAG,CACH,OAAO,eAAe,EAAQ,EAAK,CAC/B,MAAO,EAEV,CAAC,EAGN,aAAc,GACjB,CAAC,CAKN,SAAgB,GAAW,EAAQ,EAAM,EAAO,CAC5C,OAAO,eAAe,EAAQ,EAAM,CAChC,QACA,SAAU,GACV,WAAY,GACZ,aAAc,GACjB,CAAC,CAEN,SAAgB,GAAU,GAAG,EAAM,CAC/B,IAAM,EAAoB,EAAE,CAC5B,IAAK,IAAM,KAAO,EAEd,OAAO,OAAO,EADM,OAAO,0BAA0B,EAAI,CACZ,CAEjD,OAAO,OAAO,iBAAiB,EAAE,CAAE,EAAkB,CA6BzD,SAAgB,GAAI,EAAK,CACrB,OAAO,KAAK,UAAU,EAAI,CAE9B,SAAgB,GAAQ,EAAO,CAC3B,OAAO,EACF,aAAa,CACb,MAAM,CACN,QAAQ,YAAa,GAAG,CACxB,QAAQ,WAAY,IAAI,CACxB,QAAQ,WAAY,GAAG,CAEhC,IAAa,GAAqB,sBAAuB,MAAQ,MAAM,mBAAqB,GAAG,IAAU,GACzG,SAAgB,GAAS,EAAM,CAC3B,OAAO,OAAO,GAAS,YAAY,GAAiB,CAAC,MAAM,QAAQ,EAAK,CAE5E,IAAa,GAAa,OAAa,CAEnC,GAAI,OAAO,UAAc,KAAe,WAAW,WAAW,SAAS,aAAa,CAChF,MAAO,GAEX,GAAI,CAGA,OAFU,SACJ,GAAG,CACF,QAED,CACN,MAAO,KAEb,CACF,SAAgB,GAAc,EAAG,CAC7B,GAAI,GAAS,EAAE,GAAK,GAChB,MAAO,GAEX,IAAM,EAAO,EAAE,YAGf,GAFI,IAAS,IAAA,IAET,OAAO,GAAS,WAChB,MAAO,GAEX,IAAM,EAAO,EAAK,UAOlB,MAHA,EAHI,GAAS,EAAK,GAAK,IAGnB,OAAO,UAAU,eAAe,KAAK,EAAM,gBAAgB,GAAK,IAKxE,SAAgB,GAAa,EAAG,CAK5B,OAJI,GAAc,EAAE,CACT,CAAE,GAAG,EAAG,CACf,MAAM,QAAQ,EAAE,CACT,CAAC,GAAG,EAAE,CACV,EAwDX,IAAa,GAAmB,IAAI,IAAI,CAAC,SAAU,SAAU,SAAS,CAAC,CAEvE,SAAgB,GAAY,EAAK,CAC7B,OAAO,EAAI,QAAQ,sBAAuB,OAAO,CAGrD,SAAgB,GAAM,EAAM,EAAK,EAAQ,CACrC,IAAM,EAAK,IAAI,EAAK,KAAK,OAAO,GAAO,EAAK,KAAK,IAAI,CAGrD,OAFI,CAAC,GAAO,GAAQ,UAChB,EAAG,KAAK,OAAS,GACd,EAEX,SAAgB,EAAgB,EAAS,CACrC,IAAM,EAAS,EACf,GAAI,CAAC,EACD,MAAO,EAAE,CACb,GAAI,OAAO,GAAW,SAClB,MAAO,CAAE,UAAa,EAAQ,CAClC,GAAI,GAAQ,UAAY,IAAA,GAAW,CAC/B,GAAI,GAAQ,QAAU,IAAA,GAClB,MAAU,MAAM,mDAAmD,CACvE,EAAO,MAAQ,EAAO,QAK1B,OAHA,OAAO,EAAO,QACV,OAAO,EAAO,OAAU,SACjB,CAAE,GAAG,EAAQ,UAAa,EAAO,MAAO,CAC5C,EA0CX,SAAgB,GAAa,EAAO,CAChC,OAAO,OAAO,KAAK,EAAM,CAAC,OAAQ,GACvB,EAAM,GAAG,KAAK,QAAU,YAAc,EAAM,GAAG,KAAK,SAAW,WACxE,CAEN,IAAa,GAAuB,CAChC,QAAS,CAAC,mBAAiD,CAC3D,MAAO,CAAC,YAAa,WAAW,CAChC,OAAQ,CAAC,EAAG,WAAW,CACvB,QAAS,CAAC,sBAAwB,qBAAsB,CACxD,QAAS,CAAC,CAAC,OAAO,UAAW,OAAO,UAAU,CACjD,CAKD,SAAgB,GAAK,EAAQ,EAAM,CAC/B,IAAM,EAAU,EAAO,KAAK,IACtB,EAAS,EAAQ,OAEvB,GADkB,GAAU,EAAO,OAAS,EAExC,MAAU,MAAM,kEAAkE,CAkBtF,OAAO,GAAM,EAhBD,GAAU,EAAO,KAAK,IAAK,CACnC,IAAI,OAAQ,CACR,IAAM,EAAW,EAAE,CACnB,IAAK,IAAM,KAAO,EAAM,CACpB,GAAI,EAAE,KAAO,EAAQ,OACjB,MAAU,MAAM,sBAAsB,EAAI,GAAG,CAE5C,EAAK,KAEV,EAAS,GAAO,EAAQ,MAAM,IAGlC,OADA,GAAW,KAAM,QAAS,EAAS,CAC5B,GAEX,OAAQ,EAAE,CACb,CAAC,CACuB,CAE7B,SAAgB,GAAK,EAAQ,EAAM,CAC/B,IAAM,EAAU,EAAO,KAAK,IACtB,EAAS,EAAQ,OAEvB,GADkB,GAAU,EAAO,OAAS,EAExC,MAAU,MAAM,kEAAkE,CAkBtF,OAAO,GAAM,EAhBD,GAAU,EAAO,KAAK,IAAK,CACnC,IAAI,OAAQ,CACR,IAAM,EAAW,CAAE,GAAG,EAAO,KAAK,IAAI,MAAO,CAC7C,IAAK,IAAM,KAAO,EAAM,CACpB,GAAI,EAAE,KAAO,EAAQ,OACjB,MAAU,MAAM,sBAAsB,EAAI,GAAG,CAE5C,EAAK,IAEV,OAAO,EAAS,GAGpB,OADA,GAAW,KAAM,QAAS,EAAS,CAC5B,GAEX,OAAQ,EAAE,CACb,CAAC,CACuB,CAE7B,SAAgB,GAAO,EAAQ,EAAO,CAClC,GAAI,CAAC,GAAc,EAAM,CACrB,MAAU,MAAM,mDAAmD,CAEvE,IAAM,EAAS,EAAO,KAAK,IAAI,OAE/B,GADkB,GAAU,EAAO,OAAS,EAC7B,CAGX,IAAM,EAAgB,EAAO,KAAK,IAAI,MACtC,IAAK,IAAM,KAAO,EACd,GAAI,OAAO,yBAAyB,EAAe,EAAI,GAAK,IAAA,GACxD,MAAU,MAAM,+FAA+F,CAW3H,OAAO,GAAM,EAPD,GAAU,EAAO,KAAK,IAAK,CACnC,IAAI,OAAQ,CACR,IAAM,EAAS,CAAE,GAAG,EAAO,KAAK,IAAI,MAAO,GAAG,EAAO,CAErD,OADA,GAAW,KAAM,QAAS,EAAO,CAC1B,GAEd,CAAC,CACuB,CAE7B,SAAgB,GAAW,EAAQ,EAAO,CACtC,GAAI,CAAC,GAAc,EAAM,CACrB,MAAU,MAAM,uDAAuD,CAS3E,OAAO,GAAM,EAPD,GAAU,EAAO,KAAK,IAAK,CACnC,IAAI,OAAQ,CACR,IAAM,EAAS,CAAE,GAAG,EAAO,KAAK,IAAI,MAAO,GAAG,EAAO,CAErD,OADA,GAAW,KAAM,QAAS,EAAO,CAC1B,GAEd,CAAC,CACuB,CAE7B,SAAgB,GAAM,EAAG,EAAG,CAYxB,OAAO,GAAM,EAXD,GAAU,EAAE,KAAK,IAAK,CAC9B,IAAI,OAAQ,CACR,IAAM,EAAS,CAAE,GAAG,EAAE,KAAK,IAAI,MAAO,GAAG,EAAE,KAAK,IAAI,MAAO,CAE3D,OADA,GAAW,KAAM,QAAS,EAAO,CAC1B,GAEX,IAAI,UAAW,CACX,OAAO,EAAE,KAAK,IAAI,UAEtB,OAAQ,EAAE,CACb,CAAC,CACkB,CAExB,SAAgB,GAAQ,EAAO,EAAQ,EAAM,CAEzC,IAAM,EADU,EAAO,KAAK,IACL,OAEvB,GADkB,GAAU,EAAO,OAAS,EAExC,MAAU,MAAM,qEAAqE,CAsCzF,OAAO,GAAM,EApCD,GAAU,EAAO,KAAK,IAAK,CACnC,IAAI,OAAQ,CACR,IAAM,EAAW,EAAO,KAAK,IAAI,MAC3B,EAAQ,CAAE,GAAG,EAAU,CAC7B,GAAI,EACA,IAAK,IAAM,KAAO,EAAM,CACpB,GAAI,EAAE,KAAO,GACT,MAAU,MAAM,sBAAsB,EAAI,GAAG,CAE5C,EAAK,KAGV,EAAM,GAAO,EACP,IAAI,EAAM,CACR,KAAM,WACN,UAAW,EAAS,GACvB,CAAC,CACA,EAAS,SAInB,IAAK,IAAM,KAAO,EAEd,EAAM,GAAO,EACP,IAAI,EAAM,CACR,KAAM,WACN,UAAW,EAAS,GACvB,CAAC,CACA,EAAS,GAIvB,OADA,GAAW,KAAM,QAAS,EAAM,CACzB,GAEX,OAAQ,EAAE,CACb,CAAC,CACuB,CAE7B,SAAgB,GAAS,EAAO,EAAQ,EAAM,CAgC1C,OAAO,GAAM,EA/BD,GAAU,EAAO,KAAK,IAAK,CACnC,IAAI,OAAQ,CACR,IAAM,EAAW,EAAO,KAAK,IAAI,MAC3B,EAAQ,CAAE,GAAG,EAAU,CAC7B,GAAI,EACA,IAAK,IAAM,KAAO,EAAM,CACpB,GAAI,EAAE,KAAO,GACT,MAAU,MAAM,sBAAsB,EAAI,GAAG,CAE5C,EAAK,KAGV,EAAM,GAAO,IAAI,EAAM,CACnB,KAAM,cACN,UAAW,EAAS,GACvB,CAAC,OAIN,IAAK,IAAM,KAAO,EAEd,EAAM,GAAO,IAAI,EAAM,CACnB,KAAM,cACN,UAAW,EAAS,GACvB,CAAC,CAIV,OADA,GAAW,KAAM,QAAS,EAAM,CACzB,GAEd,CAAC,CACuB,CAG7B,SAAgB,GAAQ,EAAG,EAAa,EAAG,CACvC,GAAI,EAAE,UAAY,GACd,MAAO,GACX,IAAK,IAAI,EAAI,EAAY,EAAI,EAAE,OAAO,OAAQ,IAC1C,GAAI,EAAE,OAAO,IAAI,WAAa,GAC1B,MAAO,GAGf,MAAO,GAEX,SAAgB,GAAa,EAAM,EAAQ,CACvC,OAAO,EAAO,IAAK,GAAQ,CACvB,IAAI,EAGJ,OAFC,EAAK,GAAK,OAAS,EAAG,KAAO,EAAE,EAChC,EAAI,KAAK,QAAQ,EAAK,CACf,GACT,CAEN,SAAgB,GAAc,EAAS,CACnC,OAAO,OAAO,GAAY,SAAW,EAAU,GAAS,QAE5D,SAAgB,GAAc,EAAK,EAAK,EAAQ,CAC5C,IAAM,EAAO,CAAE,GAAG,EAAK,KAAM,EAAI,MAAQ,EAAE,CAAE,CAgB7C,OAdK,EAAI,UAML,EAAK,QALW,GAAc,EAAI,MAAM,KAAK,KAAK,QAAQ,EAAI,CAAC,EAC3D,GAAc,GAAK,QAAQ,EAAI,CAAC,EAChC,GAAc,EAAO,cAAc,EAAI,CAAC,EACxC,GAAc,EAAO,cAAc,EAAI,CAAC,EACxC,iBAIR,OAAO,EAAK,KACZ,OAAO,EAAK,SACP,GAAK,aACN,OAAO,EAAK,MAET,EAYX,SAAgB,GAAoB,EAAO,CAKvC,OAJI,MAAM,QAAQ,EAAM,CACb,QACP,OAAO,GAAU,SACV,SACJ,UAuBX,SAAgB,GAAM,GAAG,EAAM,CAC3B,GAAM,CAAC,EAAK,EAAO,GAAQ,EAS3B,OARI,OAAO,GAAQ,SACR,CACH,QAAS,EACT,KAAM,SACN,QACA,OACH,CAEE,CAAE,GAAG,EAAK,CCnlBrB,IAAMA,IAAe,EAAM,IAAQ,CAC/B,EAAK,KAAO,YACZ,OAAO,eAAe,EAAM,OAAQ,CAChC,MAAO,EAAK,KACZ,WAAY,GACf,CAAC,CACF,OAAO,eAAe,EAAM,SAAU,CAClC,MAAO,EACP,WAAY,GACf,CAAC,CACF,EAAK,QAAU,KAAK,UAAU,EAAKC,GAA4B,EAAE,CACjE,OAAO,eAAe,EAAM,WAAY,CACpC,UAAa,EAAK,QAClB,WAAY,GACf,CAAC,EAEO,GAAY,EAAa,YAAaD,GAAY,CAClD,GAAgB,EAAa,YAAaA,GAAa,CAAE,OAAQ,MAAO,CAAC,CACtF,SAAgB,GAAa,EAAO,EAAU,GAAU,EAAM,QAAS,CACnE,IAAM,EAAc,EAAE,CAChB,EAAa,EAAE,CACrB,IAAK,IAAM,KAAO,EAAM,OAChB,EAAI,KAAK,OAAS,GAClB,EAAY,EAAI,KAAK,IAAM,EAAY,EAAI,KAAK,KAAO,EAAE,CACzD,EAAY,EAAI,KAAK,IAAI,KAAK,EAAO,EAAI,CAAC,EAG1C,EAAW,KAAK,EAAO,EAAI,CAAC,CAGpC,MAAO,CAAE,aAAY,cAAa,CAEtC,SAAgB,GAAY,EAAO,EAAU,GAAU,EAAM,QAAS,CAClE,IAAM,EAAc,CAAE,QAAS,EAAE,CAAE,CAC7B,EAAgB,GAAU,CAC5B,IAAK,IAAM,KAAS,EAAM,OACtB,GAAI,EAAM,OAAS,iBAAmB,EAAM,OAAO,OAC/C,EAAM,OAAO,IAAK,GAAW,EAAa,CAAE,SAAQ,CAAC,CAAC,SAEjD,EAAM,OAAS,cACpB,EAAa,CAAE,OAAQ,EAAM,OAAQ,CAAC,SAEjC,EAAM,OAAS,kBACpB,EAAa,CAAE,OAAQ,EAAM,OAAQ,CAAC,SAEjC,EAAM,KAAK,SAAW,EAC3B,EAAY,QAAQ,KAAK,EAAO,EAAM,CAAC,KAEtC,CACD,IAAI,EAAO,EACP,EAAI,EACR,KAAO,EAAI,EAAM,KAAK,QAAQ,CAC1B,IAAM,EAAK,EAAM,KAAK,GACL,IAAM,EAAM,KAAK,OAAS,GAKvC,EAAK,GAAM,EAAK,IAAO,CAAE,QAAS,EAAE,CAAE,CACtC,EAAK,GAAI,QAAQ,KAAK,EAAO,EAAM,CAAC,EAJpC,EAAK,GAAM,EAAK,IAAO,CAAE,QAAS,EAAE,CAAE,CAM1C,EAAO,EAAK,GACZ,OAMhB,OADA,EAAa,EAAM,CACZ,ECnEX,IAAa,GAAU,IAAU,EAAQ,EAAO,EAAM,IAAY,CAC9D,IAAM,EAAM,EAAO,OAAO,OAAO,EAAM,CAAE,MAAO,GAAO,CAAC,CAAG,CAAE,MAAO,GAAO,CACrE,EAAS,EAAO,KAAK,IAAI,CAAE,QAAO,OAAQ,EAAE,CAAE,CAAE,EAAI,CAC1D,GAAI,aAAkB,QAClB,MAAM,IAAIE,GAEd,GAAI,EAAO,OAAO,OAAQ,CACtB,IAAM,EAAI,IAAK,GAAS,KAAO,GAAM,EAAO,OAAO,IAAK,GAAQC,GAAmB,EAAK,EAAKC,IAAa,CAAC,CAAC,CAAC,CAE7G,MADA,GAAuB,EAAG,GAAS,OAAO,CACpC,EAEV,OAAO,EAAO,OAGL,GAAe,GAAS,MAAO,EAAQ,EAAO,EAAM,IAAW,CACxE,IAAM,EAAM,EAAO,OAAO,OAAO,EAAM,CAAE,MAAO,GAAM,CAAC,CAAG,CAAE,MAAO,GAAM,CACrE,EAAS,EAAO,KAAK,IAAI,CAAE,QAAO,OAAQ,EAAE,CAAE,CAAE,EAAI,CAGxD,GAFI,aAAkB,UAClB,EAAS,MAAM,GACf,EAAO,OAAO,OAAQ,CACtB,IAAM,EAAI,IAAK,GAAQ,KAAO,GAAM,EAAO,OAAO,IAAK,GAAQD,GAAmB,EAAK,EAAKC,IAAa,CAAC,CAAC,CAAC,CAE5G,MADA,GAAuB,EAAG,GAAQ,OAAO,CACnC,EAEV,OAAO,EAAO,OAGL,GAAc,IAAU,EAAQ,EAAO,IAAS,CACzD,IAAM,EAAM,EAAO,CAAE,GAAG,EAAM,MAAO,GAAO,CAAG,CAAE,MAAO,GAAO,CACzD,EAAS,EAAO,KAAK,IAAI,CAAE,QAAO,OAAQ,EAAE,CAAE,CAAE,EAAI,CAC1D,GAAI,aAAkB,QAClB,MAAM,IAAIF,GAEd,OAAO,EAAO,OAAO,OACf,CACE,QAAS,GACT,MAAO,IAAK,GAAQM,IAAkB,EAAO,OAAO,IAAK,GAAQL,GAAmB,EAAK,EAAKC,IAAa,CAAC,CAAC,CAAC,CACjH,CACC,CAAE,QAAS,GAAM,KAAM,EAAO,MAAO,EAElCK,GAA2B,GAAWH,GAAqB,CAC3D,GAAmB,GAAS,MAAO,EAAQ,EAAO,IAAS,CACpE,IAAM,EAAM,EAAO,OAAO,OAAO,EAAM,CAAE,MAAO,GAAM,CAAC,CAAG,CAAE,MAAO,GAAM,CACrE,EAAS,EAAO,KAAK,IAAI,CAAE,QAAO,OAAQ,EAAE,CAAE,CAAE,EAAI,CAGxD,OAFI,aAAkB,UAClB,EAAS,MAAM,GACZ,EAAO,OAAO,OACf,CACE,QAAS,GACT,MAAO,IAAI,EAAK,EAAO,OAAO,IAAK,GAAQH,GAAmB,EAAK,EAAKC,IAAa,CAAC,CAAC,CAAC,CAC3F,CACC,CAAE,QAAS,GAAM,KAAM,EAAO,MAAO,EAElCM,GAAgC,GAAgBJ,GAAqB,CACrE,GAAW,IAAU,EAAQ,EAAO,IAAS,CACtD,IAAM,EAAM,EAAO,OAAO,OAAO,EAAM,CAAE,UAAW,WAAY,CAAC,CAAG,CAAE,UAAW,WAAY,CAC7F,OAAO,GAAO,EAAK,CAAC,EAAQ,EAAO,EAAI,EAG9B,GAAW,IAAU,EAAQ,EAAO,IACtC,GAAO,EAAK,CAAC,EAAQ,EAAO,EAAK,CAG/B,GAAgB,GAAS,MAAO,EAAQ,EAAO,IAAS,CACjE,IAAM,EAAM,EAAO,OAAO,OAAO,EAAM,CAAE,UAAW,WAAY,CAAC,CAAG,CAAE,UAAW,WAAY,CAC7F,OAAO,GAAY,EAAK,CAAC,EAAQ,EAAO,EAAI,EAGnC,GAAgB,GAAS,MAAO,EAAQ,EAAO,IACjD,GAAY,EAAK,CAAC,EAAQ,EAAO,EAAK,CAGpC,GAAe,IAAU,EAAQ,EAAO,IAAS,CAC1D,IAAM,EAAM,EAAO,OAAO,OAAO,EAAM,CAAE,UAAW,WAAY,CAAC,CAAG,CAAE,UAAW,WAAY,CAC7F,OAAO,GAAW,EAAK,CAAC,EAAQ,EAAO,EAAI,EAGlC,GAAe,IAAU,EAAQ,EAAO,IAC1C,GAAW,EAAK,CAAC,EAAQ,EAAO,EAAK,CAGnC,GAAoB,GAAS,MAAO,EAAQ,EAAO,IAAS,CACrE,IAAM,EAAM,EAAO,OAAO,OAAO,EAAM,CAAE,UAAW,WAAY,CAAC,CAAG,CAAE,UAAW,WAAY,CAC7F,OAAO,GAAgB,EAAK,CAAC,EAAQ,EAAO,EAAI,EAGvC,GAAoB,GAAS,MAAO,EAAQ,EAAO,IACrD,GAAgB,EAAK,CAAC,EAAQ,EAAO,EAAK,CCzFxC,GAAO,mBACP,GAAQ,cACR,GAAO,wCACP,GAAM,oBACN,GAAQ,oBACR,GAAS,sBAETa,GAAW,gGAIX,GAAO,kFAIP,GAAQ,GACZ,EAEM,OAAO,mCAAmC,EAAQ,yDAAyD,CAD3G,yKAOF,GAAQ,mGAUfC,GAAS,uDACf,SAAgB,IAAQ,CACpB,OAAO,IAAI,OAAOA,GAAQ,IAAI,CAElC,IAAa,GAAO,sHACP,GAAO,+XAKP,GAAS,2IACT,GAAS,iIAET,GAAS,8EACT,GAAY,mBAOZ,GAAO,oBAEd,GAAa,sNACNC,GAAyB,OAAO,IAAI,GAAW,GAAG,CAC/D,SAAS,GAAW,EAAM,CACtB,IAAM,EAAO,8BAQb,OAPc,OAAO,EAAK,WAAc,SAClC,EAAK,YAAc,GACf,GAAG,IACH,EAAK,YAAc,EACf,GAAG,EAAK,WACR,GAAG,EAAK,kBAAkB,EAAK,UAAU,GACjD,GAAG,EAAK,4BAGlB,SAAgBC,GAAK,EAAM,CACvB,OAAW,OAAO,IAAI,GAAW,EAAK,CAAC,GAAG,CAG9C,SAAgBC,GAAS,EAAM,CAC3B,IAAM,EAAO,GAAW,CAAE,UAAW,EAAK,UAAW,CAAC,CAChD,EAAO,CAAC,IAAI,CACd,EAAK,OACL,EAAK,KAAK,GAAG,CAEb,EAAK,QACL,EAAK,KAAK,oCAAoC,CAClD,IAAM,EAAY,GAAG,EAAK,KAAK,EAAK,KAAK,IAAI,CAAC,GAC9C,OAAW,OAAO,IAAI,GAAW,MAAM,EAAU,IAAI,CAEzD,IAAaC,GAAU,GAAW,CAC9B,IAAM,EAAQ,EAAS,YAAY,GAAQ,SAAW,EAAE,GAAG,GAAQ,SAAW,GAAG,GAAK,YACtF,OAAW,OAAO,IAAI,EAAM,GAAG,EAGtB,GAAU,UACVC,GAAS,oBACTC,GAAU,oBAMV,GAAY,YAEZ,GAAY,YCjGZ,EAA0B,EAAkB,aAAc,EAAM,IAAQ,CACjF,IAAI,EACJ,AAAc,EAAK,OAAO,EAAE,CAC5B,EAAK,KAAK,IAAM,GACf,EAAK,EAAK,MAAM,WAAa,EAAG,SAAW,EAAE,GAChD,CACI,GAAmB,CACrB,OAAQ,SACR,OAAQ,SACR,OAAQ,OACX,CACY,GAAkC,EAAkB,qBAAsB,EAAM,IAAQ,CACjG,EAAU,KAAK,EAAM,EAAI,CACzB,IAAM,EAAS,GAAiB,OAAO,EAAI,OAC3C,EAAK,KAAK,SAAS,KAAM,GAAS,CAC9B,IAAM,EAAM,EAAK,KAAK,IAChB,GAAQ,EAAI,UAAY,EAAI,QAAU,EAAI,mBAAqB,IACjE,EAAI,MAAQ,IACR,EAAI,UACJ,EAAI,QAAU,EAAI,MAElB,EAAI,iBAAmB,EAAI,QAErC,CACF,EAAK,KAAK,MAAS,GAAY,EACvB,EAAI,UAAY,EAAQ,OAAS,EAAI,MAAQ,EAAQ,MAAQ,EAAI,QAGrE,EAAQ,OAAO,KAAK,CAChB,SACA,KAAM,UACN,QAAS,OAAO,EAAI,OAAU,SAAW,EAAI,MAAM,SAAS,CAAG,EAAI,MACnE,MAAO,EAAQ,MACf,UAAW,EAAI,UACf,OACA,SAAU,CAAC,EAAI,MAClB,CAAC,GAER,CACW,GAAqC,EAAkB,wBAAyB,EAAM,IAAQ,CACvG,EAAU,KAAK,EAAM,EAAI,CACzB,IAAM,EAAS,GAAiB,OAAO,EAAI,OAC3C,EAAK,KAAK,SAAS,KAAM,GAAS,CAC9B,IAAM,EAAM,EAAK,KAAK,IAChB,GAAQ,EAAI,UAAY,EAAI,QAAU,EAAI,mBAAqB,KACjE,EAAI,MAAQ,IACR,EAAI,UACJ,EAAI,QAAU,EAAI,MAElB,EAAI,iBAAmB,EAAI,QAErC,CACF,EAAK,KAAK,MAAS,GAAY,EACvB,EAAI,UAAY,EAAQ,OAAS,EAAI,MAAQ,EAAQ,MAAQ,EAAI,QAGrE,EAAQ,OAAO,KAAK,CAChB,SACA,KAAM,YACN,QAAS,OAAO,EAAI,OAAU,SAAW,EAAI,MAAM,SAAS,CAAG,EAAI,MACnE,MAAO,EAAQ,MACf,UAAW,EAAI,UACf,OACA,SAAU,CAAC,EAAI,MAClB,CAAC,GAER,CACW,GACC,EAAkB,uBAAwB,EAAM,IAAQ,CAClE,EAAU,KAAK,EAAM,EAAI,CACzB,EAAK,KAAK,SAAS,KAAM,GAAS,CAC9B,IAAI,GACH,EAAK,EAAK,KAAK,KAAK,aAAe,EAAG,WAAa,EAAI,QAC1D,CACF,EAAK,KAAK,MAAS,GAAY,CAC3B,GAAI,OAAO,EAAQ,OAAU,OAAO,EAAI,MACpC,MAAU,MAAM,qDAAqD,EACtD,OAAO,EAAQ,OAAU,SACtC,EAAQ,MAAQ,EAAI,QAAU,OAAO,EAAE,CACvCC,GAAwB,EAAQ,MAAO,EAAI,MAAM,GAAK,IAG5D,EAAQ,OAAO,KAAK,CAChB,OAAQ,OAAO,EAAQ,MACvB,KAAM,kBACN,QAAS,EAAI,MACb,MAAO,EAAQ,MACf,OACA,SAAU,CAAC,EAAI,MAClB,CAAC,GAER,CACW,GAAsC,EAAkB,yBAA0B,EAAM,IAAQ,CACzG,EAAU,KAAK,EAAM,EAAI,CACzB,EAAI,OAAS,EAAI,QAAU,UAC3B,IAAM,EAAQ,EAAI,QAAQ,SAAS,MAAM,CACnC,EAAS,EAAQ,MAAQ,SACzB,CAAC,EAAS,GAAWC,GAA0B,EAAI,QACzD,EAAK,KAAK,SAAS,KAAM,GAAS,CAC9B,IAAM,EAAM,EAAK,KAAK,IACtB,EAAI,OAAS,EAAI,OACjB,EAAI,QAAU,EACd,EAAI,QAAU,EACV,IACA,EAAI,QAAUC,KACpB,CACF,EAAK,KAAK,MAAS,GAAY,CAC3B,IAAM,EAAQ,EAAQ,MACtB,GAAI,EAAO,CACP,GAAI,CAAC,OAAO,UAAU,EAAM,CAAE,CAU1B,EAAQ,OAAO,KAAK,CAChB,SAAU,EACV,OAAQ,EAAI,OACZ,KAAM,eACN,SAAU,GACV,QACA,OACH,CAAC,CACF,OAUJ,GAAI,CAAC,OAAO,cAAc,EAAM,CAAE,CAC1B,EAAQ,EAER,EAAQ,OAAO,KAAK,CAChB,QACA,KAAM,UACN,gBACA,KAAM,kDACN,OACA,SACA,UAAW,GACX,SAAU,CAAC,EAAI,MAClB,CAAC,CAIF,EAAQ,OAAO,KAAK,CAChB,QACA,KAAM,YACN,QAAS,WACT,KAAM,kDACN,OACA,SACA,UAAW,GACX,SAAU,CAAC,EAAI,MAClB,CAAC,CAEN,QAGJ,EAAQ,GACR,EAAQ,OAAO,KAAK,CAChB,OAAQ,SACR,QACA,KAAM,YACN,UACA,UAAW,GACX,OACA,SAAU,CAAC,EAAI,MAClB,CAAC,CAEF,EAAQ,GACR,EAAQ,OAAO,KAAK,CAChB,OAAQ,SACR,QACA,KAAM,UACN,UACA,UAAW,GACX,OACA,SAAU,CAAC,EAAI,MAClB,CAAC,GAGZ,CA0HW,GAAmC,EAAkB,sBAAuB,EAAM,IAAQ,CACnG,IAAI,EACJ,EAAU,KAAK,EAAM,EAAI,EACxB,EAAK,EAAK,KAAK,KAAK,OAAS,EAAG,KAAQ,GAAY,CACjD,IAAM,EAAM,EAAQ,MACpB,MAAO,CAACC,GAAa,EAAI,EAAI,EAAI,SAAW,IAAA,KAEhD,EAAK,KAAK,SAAS,KAAM,GAAS,CAC9B,IAAM,EAAQ,EAAK,KAAK,IAAI,SAAW,IACnC,EAAI,QAAU,IACd,EAAK,KAAK,IAAI,QAAU,EAAI,UAClC,CACF,EAAK,KAAK,MAAS,GAAY,CAC3B,IAAM,EAAQ,EAAQ,MAEtB,GADe,EAAM,QACP,EAAI,QACd,OACJ,IAAM,EAASC,GAAyB,EAAM,CAC9C,EAAQ,OAAO,KAAK,CAChB,SACA,KAAM,UACN,QAAS,EAAI,QACb,UAAW,GACX,QACA,OACA,SAAU,CAAC,EAAI,MAClB,CAAC,GAER,CACW,GAAmC,EAAkB,sBAAuB,EAAM,IAAQ,CACnG,IAAI,EACJ,EAAU,KAAK,EAAM,EAAI,EACxB,EAAK,EAAK,KAAK,KAAK,OAAS,EAAG,KAAQ,GAAY,CACjD,IAAM,EAAM,EAAQ,MACpB,MAAO,CAACD,GAAa,EAAI,EAAI,EAAI,SAAW,IAAA,KAEhD,EAAK,KAAK,SAAS,KAAM,GAAS,CAC9B,IAAM,EAAQ,EAAK,KAAK,IAAI,SAAW,KACnC,EAAI,QAAU,IACd,EAAK,KAAK,IAAI,QAAU,EAAI,UAClC,CACF,EAAK,KAAK,MAAS,GAAY,CAC3B,IAAM,EAAQ,EAAQ,MAEtB,GADe,EAAM,QACP,EAAI,QACd,OACJ,IAAM,EAASC,GAAyB,EAAM,CAC9C,EAAQ,OAAO,KAAK,CAChB,SACA,KAAM,YACN,QAAS,EAAI,QACb,UAAW,GACX,QACA,OACA,SAAU,CAAC,EAAI,MAClB,CAAC,GAER,CACW,GAAsC,EAAkB,yBAA0B,EAAM,IAAQ,CACzG,IAAI,EACJ,EAAU,KAAK,EAAM,EAAI,EACxB,EAAK,EAAK,KAAK,KAAK,OAAS,EAAG,KAAQ,GAAY,CACjD,IAAM,EAAM,EAAQ,MACpB,MAAO,CAACD,GAAa,EAAI,EAAI,EAAI,SAAW,IAAA,KAEhD,EAAK,KAAK,SAAS,KAAM,GAAS,CAC9B,IAAM,EAAM,EAAK,KAAK,IACtB,EAAI,QAAU,EAAI,OAClB,EAAI,QAAU,EAAI,OAClB,EAAI,OAAS,EAAI,QACnB,CACF,EAAK,KAAK,MAAS,GAAY,CAC3B,IAAM,EAAQ,EAAQ,MAChB,EAAS,EAAM,OACrB,GAAI,IAAW,EAAI,OACf,OACJ,IAAM,EAASC,GAAyB,EAAM,CACxC,EAAS,EAAS,EAAI,OAC5B,EAAQ,OAAO,KAAK,CAChB,SACA,GAAI,EAAS,CAAE,KAAM,UAAW,QAAS,EAAI,OAAQ,CAAG,CAAE,KAAM,YAAa,QAAS,EAAI,OAAQ,CAClG,UAAW,GACX,MAAO,GACP,MAAO,EAAQ,MACf,OACA,SAAU,CAAC,EAAI,MAClB,CAAC,GAER,CACW,GAAsC,EAAkB,yBAA0B,EAAM,IAAQ,CACzG,IAAI,EAAI,EACR,EAAU,KAAK,EAAM,EAAI,CACzB,EAAK,KAAK,SAAS,KAAM,GAAS,CAC9B,IAAM,EAAM,EAAK,KAAK,IACtB,EAAI,OAAS,EAAI,OACb,EAAI,UACJ,AAAiB,EAAI,WAAW,IAAI,IACpC,EAAI,SAAS,IAAI,EAAI,QAAQ,GAEnC,CACE,EAAI,SACH,EAAK,EAAK,MAAM,QAAU,EAAG,MAAS,GAAY,CAC/C,EAAI,QAAQ,UAAY,EACpB,GAAI,QAAQ,KAAK,EAAQ,MAAM,EAEnC,EAAQ,OAAO,KAAK,CAChB,OAAQ,SACR,KAAM,iBACN,OAAQ,EAAI,OACZ,MAAO,EAAQ,MACf,GAAI,EAAI,QAAU,CAAE,QAAS,EAAI,QAAQ,UAAU,CAAE,CAAG,EAAE,CAC1D,OACA,SAAU,CAAC,EAAI,MAClB,CAAC,IAGL,EAAK,EAAK,MAAM,QAAU,EAAG,UAAc,KAClD,CACW,GAA+B,EAAkB,kBAAmB,EAAM,IAAQ,CAC3F,GAAsB,KAAK,EAAM,EAAI,CACrC,EAAK,KAAK,MAAS,GAAY,CAC3B,EAAI,QAAQ,UAAY,EACpB,GAAI,QAAQ,KAAK,EAAQ,MAAM,EAEnC,EAAQ,OAAO,KAAK,CAChB,OAAQ,SACR,KAAM,iBACN,OAAQ,QACR,MAAO,EAAQ,MACf,QAAS,EAAI,QAAQ,UAAU,CAC/B,OACA,SAAU,CAAC,EAAI,MAClB,CAAC,GAER,CACW,GAAmC,EAAkB,sBAAuB,EAAM,IAAQ,CACnG,AAAgB,EAAI,UAAUC,GAC9B,GAAsB,KAAK,EAAM,EAAI,EACvC,CACW,GAAmC,EAAkB,sBAAuB,EAAM,IAAQ,CACnG,AAAgB,EAAI,UAAUC,GAC9B,GAAsB,KAAK,EAAM,EAAI,EACvC,CACW,GAAkC,EAAkB,qBAAsB,EAAM,IAAQ,CACjG,EAAU,KAAK,EAAM,EAAI,CACzB,IAAM,EAAeC,GAAiB,EAAI,SAAS,CAC7C,EAAU,IAAI,OAAO,OAAO,EAAI,UAAa,SAAW,MAAM,EAAI,SAAS,GAAG,IAAiB,EAAa,CAClH,EAAI,QAAU,EACd,EAAK,KAAK,SAAS,KAAM,GAAS,CAC9B,IAAM,EAAM,EAAK,KAAK,IACtB,AAAiB,EAAI,WAAW,IAAI,IACpC,EAAI,SAAS,IAAI,EAAQ,EAC3B,CACF,EAAK,KAAK,MAAS,GAAY,CACvB,EAAQ,MAAM,SAAS,EAAI,SAAU,EAAI,SAAS,EAEtD,EAAQ,OAAO,KAAK,CAChB,OAAQ,SACR,KAAM,iBACN,OAAQ,WACR,SAAU,EAAI,SACd,MAAO,EAAQ,MACf,OACA,SAAU,CAAC,EAAI,MAClB,CAAC,GAER,CACW,GAAoC,EAAkB,uBAAwB,EAAM,IAAQ,CACrG,EAAU,KAAK,EAAM,EAAI,CACzB,IAAM,EAAc,OAAO,IAAIA,GAAiB,EAAI,OAAO,CAAC,IAAI,CAChE,AAAgB,EAAI,UAAU,EAC9B,EAAK,KAAK,SAAS,KAAM,GAAS,CAC9B,IAAM,EAAM,EAAK,KAAK,IACtB,AAAiB,EAAI,WAAW,IAAI,IACpC,EAAI,SAAS,IAAI,EAAQ,EAC3B,CACF,EAAK,KAAK,MAAS,GAAY,CACvB,EAAQ,MAAM,WAAW,EAAI,OAAO,EAExC,EAAQ,OAAO,KAAK,CAChB,OAAQ,SACR,KAAM,iBACN,OAAQ,cACR,OAAQ,EAAI,OACZ,MAAO,EAAQ,MACf,OACA,SAAU,CAAC,EAAI,MAClB,CAAC,GAER,CACW,GAAkC,EAAkB,qBAAsB,EAAM,IAAQ,CACjG,EAAU,KAAK,EAAM,EAAI,CACzB,IAAM,EAAc,OAAO,KAAKA,GAAiB,EAAI,OAAO,CAAC,GAAG,CAChE,AAAgB,EAAI,UAAU,EAC9B,EAAK,KAAK,SAAS,KAAM,GAAS,CAC9B,IAAM,EAAM,EAAK,KAAK,IACtB,AAAiB,EAAI,WAAW,IAAI,IACpC,EAAI,SAAS,IAAI,EAAQ,EAC3B,CACF,EAAK,KAAK,MAAS,GAAY,CACvB,EAAQ,MAAM,SAAS,EAAI,OAAO,EAEtC,EAAQ,OAAO,KAAK,CAChB,OAAQ,SACR,KAAM,iBACN,OAAQ,YACR,OAAQ,EAAI,OACZ,MAAO,EAAQ,MACf,OACA,SAAU,CAAC,EAAI,MAClB,CAAC,GAER,CAyCW,GAAmC,EAAkB,sBAAuB,EAAM,IAAQ,CACnG,EAAU,KAAK,EAAM,EAAI,CACzB,EAAK,KAAK,MAAS,GAAY,CAC3B,EAAQ,MAAQ,EAAI,GAAG,EAAQ,MAAM,GAE3C,CC9jBW,GAAb,KAAiB,CACb,YAAY,EAAO,EAAE,CAAE,CACnB,KAAK,QAAU,EAAE,CACjB,KAAK,OAAS,EACV,OACA,KAAK,KAAO,GAEpB,SAAS,EAAI,CACT,KAAK,QAAU,EACf,EAAG,KAAK,CACR,OAAK,OAET,MAAM,EAAK,CACP,GAAI,OAAO,GAAQ,WAAY,CAC3B,EAAI,KAAM,CAAE,UAAW,OAAQ,CAAC,CAChC,EAAI,KAAM,CAAE,UAAW,QAAS,CAAC,CACjC,OAGJ,IAAM,EADU,EACM,MAAM;EAAK,CAAC,OAAQ,GAAM,EAAE,CAC5C,EAAY,KAAK,IAAI,GAAG,EAAM,IAAK,GAAM,EAAE,OAAS,EAAE,WAAW,CAAC,OAAO,CAAC,CAC1E,EAAW,EAAM,IAAK,GAAM,EAAE,MAAM,EAAU,CAAC,CAAC,IAAK,GAAM,IAAI,OAAO,KAAK,OAAS,EAAE,CAAG,EAAE,CACjG,IAAK,IAAM,KAAQ,EACf,KAAK,QAAQ,KAAK,EAAK,CAG/B,SAAU,CACN,IAAM,EAAI,SACJ,EAAO,MAAM,KAEb,EAAQ,CAAC,IADC,MAAM,SAAW,CAAC,GAAG,EACX,IAAK,GAAM,KAAK,IAAI,CAAC,CAE/C,OAAO,IAAI,EAAE,GAAG,EAAM,EAAM,KAAK;EAAK,CAAC,GChClC,GAAU,CACnB,MAAO,EACP,MAAO,EACP,MAAO,EACV,CCGY,EAAyB,EAAkB,YAAa,EAAM,IAAQ,CAC/E,IAAI,EACJ,AAAS,IAAO,EAAE,CAClB,EAAK,KAAK,IAAM,EAChB,EAAK,KAAK,IAAM,EAAK,KAAK,KAAO,EAAE,CACnC,EAAK,KAAK,QAAU,GACpB,IAAM,EAAS,CAAC,GAAI,EAAK,KAAK,IAAI,QAAU,EAAE,CAAE,CAE5C,EAAK,KAAK,OAAO,IAAI,YAAY,EACjC,EAAO,QAAQ,EAAK,CAExB,IAAK,IAAM,KAAM,EACb,IAAK,IAAM,KAAM,EAAG,KAAK,SACrB,EAAG,EAAK,CAGhB,GAAI,EAAO,SAAW,GAGjB,EAAK,EAAK,MAAM,WAAa,EAAG,SAAW,EAAE,EAC9C,EAAK,KAAK,UAAU,SAAW,CAC3B,EAAK,KAAK,IAAM,EAAK,KAAK,OAC5B,KAED,CACD,IAAM,GAAa,EAAS,EAAQ,IAAQ,CACxC,IAAI,EAAYC,GAAa,EAAQ,CACjC,EACJ,IAAK,IAAM,KAAM,EAAQ,CACrB,GAAI,EAAG,KAAK,IAAI,SAER,CADc,EAAG,KAAK,IAAI,KAAK,EAAQ,CAEvC,iBAEC,EACL,SAEJ,IAAM,EAAU,EAAQ,OAAO,OACzB,EAAI,EAAG,KAAK,MAAM,EAAQ,CAChC,GAAI,aAAa,SAAW,GAAK,QAAU,GACvC,MAAM,IAAIC,GAEd,GAAI,GAAe,aAAa,QAC5B,GAAe,GAAe,QAAQ,SAAS,EAAE,KAAK,SAAY,CAC9D,MAAM,EACU,EAAQ,OAAO,SACf,IAEhB,AACI,IAAYD,GAAa,EAAS,EAAQ,GAChD,KAED,CAED,GADgB,EAAQ,OAAO,SACf,EACZ,SACJ,AACI,IAAYA,GAAa,EAAS,EAAQ,EAQtD,OALI,EACO,EAAY,SACR,EACT,CAEC,GAEL,GAAsB,EAAQ,EAAS,IAAQ,CAEjD,GAAIA,GAAa,EAAO,CAEpB,MADA,GAAO,QAAU,GACV,EAGX,IAAM,EAAc,EAAU,EAAS,EAAQ,EAAI,CACnD,GAAI,aAAuB,QAAS,CAChC,GAAI,EAAI,QAAU,GACd,MAAM,IAAIC,GACd,OAAO,EAAY,KAAM,GAAgB,EAAK,KAAK,MAAM,EAAa,EAAI,CAAC,CAE/E,OAAO,EAAK,KAAK,MAAM,EAAa,EAAI,EAE5C,EAAK,KAAK,KAAO,EAAS,IAAQ,CAC9B,GAAI,EAAI,WACJ,OAAO,EAAK,KAAK,MAAM,EAAS,EAAI,CAExC,GAAI,EAAI,YAAc,WAAY,CAG9B,IAAM,EAAS,EAAK,KAAK,MAAM,CAAE,MAAO,EAAQ,MAAO,OAAQ,EAAE,CAAE,CAAE,CAAE,GAAG,EAAK,WAAY,GAAM,CAAC,CAMlG,OALI,aAAkB,QACX,EAAO,KAAM,GACT,EAAmB,EAAQ,EAAS,EAAI,CACjD,CAEC,EAAmB,EAAQ,EAAS,EAAI,CAGnD,IAAM,EAAS,EAAK,KAAK,MAAM,EAAS,EAAI,CAC5C,GAAI,aAAkB,QAAS,CAC3B,GAAI,EAAI,QAAU,GACd,MAAM,IAAIA,GACd,OAAO,EAAO,KAAM,GAAW,EAAU,EAAQ,EAAQ,EAAI,CAAC,CAElE,OAAO,EAAU,EAAQ,EAAQ,EAAI,EAI7C,EAAgB,EAAM,iBAAoB,CACtC,SAAW,GAAU,CACjB,GAAI,CACA,IAAM,EAAIC,GAAU,EAAM,EAAM,CAChC,OAAO,EAAE,QAAU,CAAE,MAAO,EAAE,KAAM,CAAG,CAAE,OAAQ,EAAE,OAAO,OAAQ,MAE5D,CACN,OAAOC,GAAe,EAAM,EAAM,CAAC,KAAM,GAAO,EAAE,QAAU,CAAE,MAAO,EAAE,KAAM,CAAG,CAAE,OAAQ,EAAE,OAAO,OAAQ,CAAE,GAGrH,OAAQ,MACR,QAAS,EACZ,EAAE,EACL,CAEW,GAA2B,EAAkB,cAAe,EAAM,IAAQ,CACnF,EAAS,KAAK,EAAM,EAAI,CACxB,EAAK,KAAK,QAAU,CAAC,GAAI,GAAM,KAAK,KAAK,UAAY,EAAE,CAAE,CAAC,KAAK,EAAIC,GAAe,EAAK,KAAK,IAAI,CAChG,EAAK,KAAK,OAAS,EAAS,IAAM,CAC9B,GAAI,EAAI,OACJ,GAAI,CACA,EAAQ,MAAQ,OAAO,EAAQ,MAAM,MAE/B,EASd,OARI,OAAO,EAAQ,OAAU,UAE7B,EAAQ,OAAO,KAAK,CAChB,SAAU,SACV,KAAM,eACN,MAAO,EAAQ,MACf,OACH,CAAC,CANS,IASjB,CACW,EAAiC,EAAkB,oBAAqB,EAAM,IAAQ,CAE/F,GAA6B,KAAK,EAAM,EAAI,CAC5C,GAAW,KAAK,EAAM,EAAI,EAC5B,CACW,GAAyB,EAAkB,YAAa,EAAM,IAAQ,CAC/E,AAAgB,EAAI,UAAUC,GAC9B,EAAiB,KAAK,EAAM,EAAI,EAClC,CACW,GAAyB,EAAkB,YAAa,EAAM,IAAQ,CAC/E,GAAI,EAAI,QAAS,CAWb,IAAM,EAVa,CACf,GAAI,EACJ,GAAI,EACJ,GAAI,EACJ,GAAI,EACJ,GAAI,EACJ,GAAI,EACJ,GAAI,EACJ,GAAI,EACP,CACoB,EAAI,SACzB,GAAI,IAAM,IAAA,GACN,MAAU,MAAM,0BAA0B,EAAI,QAAQ,GAAG,CAC7D,AAAgB,EAAI,UAAUC,GAAa,EAAE,MAG7C,AAAgB,EAAI,UAAUA,IAAc,CAChD,EAAiB,KAAK,EAAM,EAAI,EAClC,CACW,GAA0B,EAAkB,aAAc,EAAM,IAAQ,CACjF,AAAgB,EAAI,UAAUC,GAC9B,EAAiB,KAAK,EAAM,EAAI,EAClC,CACW,GAAwB,EAAkB,WAAY,EAAM,IAAQ,CAC7E,EAAiB,KAAK,EAAM,EAAI,CAChC,EAAK,KAAK,MAAS,GAAY,CAC3B,GAAI,CAEA,IAAM,EAAU,EAAQ,MAAM,MAAM,CAE9B,EAAM,IAAI,IAAI,EAAQ,CACxB,EAAI,WACJ,EAAI,SAAS,UAAY,EACpB,EAAI,SAAS,KAAK,EAAI,SAAS,EAChC,EAAQ,OAAO,KAAK,CAChB,KAAM,iBACN,OAAQ,MACR,KAAM,mBACN,QAAS,EAAI,SAAS,OACtB,MAAO,EAAQ,MACf,OACA,SAAU,CAAC,EAAI,MAClB,CAAC,EAGN,EAAI,WACJ,EAAI,SAAS,UAAY,EACpB,EAAI,SAAS,KAAK,EAAI,SAAS,SAAS,IAAI,CAAG,EAAI,SAAS,MAAM,EAAG,GAAG,CAAG,EAAI,SAAS,EACzF,EAAQ,OAAO,KAAK,CAChB,KAAM,iBACN,OAAQ,MACR,KAAM,mBACN,QAAS,EAAI,SAAS,OACtB,MAAO,EAAQ,MACf,OACA,SAAU,CAAC,EAAI,MAClB,CAAC,EAIN,EAAI,UAEJ,EAAQ,MAAQ,EAAI,KAIpB,EAAQ,MAAQ,EAEpB,YAEM,CACN,EAAQ,OAAO,KAAK,CAChB,KAAM,iBACN,OAAQ,MACR,MAAO,EAAQ,MACf,OACA,SAAU,CAAC,EAAI,MAClB,CAAC,IAGZ,CACW,GAA0B,EAAkB,aAAc,EAAM,IAAQ,CACjF,AAAgB,EAAI,UAAUC,IAAe,CAC7C,EAAiB,KAAK,EAAM,EAAI,EAClC,CACW,GAA2B,EAAkB,cAAe,EAAM,IAAQ,CACnF,AAAgB,EAAI,UAAUC,GAC9B,EAAiB,KAAK,EAAM,EAAI,EAClC,CACW,GAAyB,EAAkB,YAAa,EAAM,IAAQ,CAC/E,AAAgB,EAAI,UAAUC,GAC9B,EAAiB,KAAK,EAAM,EAAI,EAClC,CACW,GAA0B,EAAkB,aAAc,EAAM,IAAQ,CACjF,AAAgB,EAAI,UAAUC,GAC9B,EAAiB,KAAK,EAAM,EAAI,EAClC,CACW,GAAyB,EAAkB,YAAa,EAAM,IAAQ,CAC/E,AAAgB,EAAI,UAAUC,GAC9B,EAAiB,KAAK,EAAM,EAAI,EAClC,CACW,GAAwB,EAAkB,WAAY,EAAM,IAAQ,CAC7E,AAAgB,EAAI,UAAUC,GAC9B,EAAiB,KAAK,EAAM,EAAI,EAClC,CACW,GAA0B,EAAkB,aAAc,EAAM,IAAQ,CACjF,AAAgB,EAAI,UAAUC,GAC9B,EAAiB,KAAK,EAAM,EAAI,EAClC,CACW,GAAgC,EAAkB,mBAAoB,EAAM,IAAQ,CAC7F,AAAgB,EAAI,UAAUC,GAAiB,EAAI,CACnD,EAAiB,KAAK,EAAM,EAAI,EAClC,CACW,GAA4B,EAAkB,eAAgB,EAAM,IAAQ,CACrF,AAAgB,EAAI,UAAUC,GAC9B,EAAiB,KAAK,EAAM,EAAI,EAClC,CACW,GAA4B,EAAkB,eAAgB,EAAM,IAAQ,CACrF,AAAgB,EAAI,UAAUC,GAAa,EAAI,CAC/C,EAAiB,KAAK,EAAM,EAAI,EAClC,CACW,GAAgC,EAAkB,mBAAoB,EAAM,IAAQ,CAC7F,AAAgB,EAAI,UAAUC,GAC9B,EAAiB,KAAK,EAAM,EAAI,EAClC,CACW,GAAyB,EAAkB,YAAa,EAAM,IAAQ,CAC/E,AAAgB,EAAI,UAAUC,GAC9B,EAAiB,KAAK,EAAM,EAAI,CAChC,EAAK,KAAK,IAAI,OAAS,QACzB,CACW,GAAyB,EAAkB,YAAa,EAAM,IAAQ,CAC/E,AAAgB,EAAI,UAAUC,GAC9B,EAAiB,KAAK,EAAM,EAAI,CAChC,EAAK,KAAK,IAAI,OAAS,OACvB,EAAK,KAAK,MAAS,GAAY,CAC3B,GAAI,CAEA,IAAI,IAAI,WAAW,EAAQ,MAAM,GAAG,MAGlC,CACF,EAAQ,OAAO,KAAK,CAChB,KAAM,iBACN,OAAQ,OACR,MAAO,EAAQ,MACf,OACA,SAAU,CAAC,EAAI,MAClB,CAAC,IAGZ,CAMW,GAA2B,EAAkB,cAAe,EAAM,IAAQ,CACnF,AAAgB,EAAI,UAAUC,GAC9B,EAAiB,KAAK,EAAM,EAAI,EAClC,CACW,GAA2B,EAAkB,cAAe,EAAM,IAAQ,CACnF,AAAgB,EAAI,UAAUC,GAC9B,EAAiB,KAAK,EAAM,EAAI,CAChC,EAAK,KAAK,MAAS,GAAY,CAC3B,IAAM,EAAQ,EAAQ,MAAM,MAAM,IAAI,CACtC,GAAI,CACA,GAAI,EAAM,SAAW,EACjB,MAAU,OAAO,CACrB,GAAM,CAAC,EAAS,GAAU,EAC1B,GAAI,CAAC,EACD,MAAU,OAAO,CACrB,IAAM,EAAY,OAAO,EAAO,CAGhC,GAFI,GAAG,MAAgB,GAEnB,EAAY,GAAK,EAAY,IAC7B,MAAU,OAAO,CAErB,IAAI,IAAI,WAAW,EAAQ,GAAG,MAE5B,CACF,EAAQ,OAAO,KAAK,CAChB,KAAM,iBACN,OAAQ,SACR,MAAO,EAAQ,MACf,OACA,SAAU,CAAC,EAAI,MAClB,CAAC,IAGZ,CAEF,SAAgB,GAAc,EAAM,CAChC,GAAI,IAAS,GACT,MAAO,GACX,GAAI,EAAK,OAAS,GAAM,EACpB,MAAO,GACX,GAAI,CAGA,OADA,KAAK,EAAK,CACH,QAEL,CACF,MAAO,IAGf,IAAa,GAA2B,EAAkB,cAAe,EAAM,IAAQ,CACnF,AAAgB,EAAI,UAAUC,GAC9B,EAAiB,KAAK,EAAM,EAAI,CAChC,EAAK,KAAK,IAAI,gBAAkB,SAChC,EAAK,KAAK,MAAS,GAAY,CACvB,GAAc,EAAQ,MAAM,EAEhC,EAAQ,OAAO,KAAK,CAChB,KAAM,iBACN,OAAQ,SACR,MAAO,EAAQ,MACf,OACA,SAAU,CAAC,EAAI,MAClB,CAAC,GAER,CAEF,SAAgB,GAAiB,EAAM,CACnC,GAAI,CAAA,GAAmB,KAAK,EAAK,CAC7B,MAAO,GACX,IAAM,EAAS,EAAK,QAAQ,QAAU,GAAO,IAAM,IAAM,IAAM,IAAK,CAEpE,OAAO,GADQ,EAAO,OAAO,KAAK,KAAK,EAAO,OAAS,EAAE,CAAG,EAAG,IAAI,CACvC,CAEhC,IAAa,GAA8B,EAAkB,iBAAkB,EAAM,IAAQ,CACzF,AAAgB,EAAI,UAAUC,GAC9B,EAAiB,KAAK,EAAM,EAAI,CAChC,EAAK,KAAK,IAAI,gBAAkB,YAChC,EAAK,KAAK,MAAS,GAAY,CACvB,GAAiB,EAAQ,MAAM,EAEnC,EAAQ,OAAO,KAAK,CAChB,KAAM,iBACN,OAAQ,YACR,MAAO,EAAQ,MACf,OACA,SAAU,CAAC,EAAI,MAClB,CAAC,GAER,CACW,GAAyB,EAAkB,YAAa,EAAM,IAAQ,CAC/E,AAAgB,EAAI,UAAUC,GAC9B,EAAiB,KAAK,EAAM,EAAI,EAClC,CAEF,SAAgB,GAAW,EAAO,EAAY,KAAM,CAChD,GAAI,CACA,IAAM,EAAc,EAAM,MAAM,IAAI,CACpC,GAAI,EAAY,SAAW,EACvB,MAAO,GACX,GAAM,CAAC,GAAU,EACjB,GAAI,CAAC,EACD,MAAO,GAEX,IAAM,EAAe,KAAK,MAAM,KAAK,EAAO,CAAC,CAO7C,MAFA,EAJI,QAAS,GAAgB,GAAc,MAAQ,OAE/C,CAAC,EAAa,KAEd,IAAc,EAAE,QAAS,IAAiB,EAAa,MAAQ,SAIjE,CACF,MAAO,IAGf,IAAa,GAAwB,EAAkB,WAAY,EAAM,IAAQ,CAC7E,EAAiB,KAAK,EAAM,EAAI,CAChC,EAAK,KAAK,MAAS,GAAY,CACvB,GAAW,EAAQ,MAAO,EAAI,IAAI,EAEtC,EAAQ,OAAO,KAAK,CAChB,KAAM,iBACN,OAAQ,MACR,MAAO,EAAQ,MACf,OACA,SAAU,CAAC,EAAI,MAClB,CAAC,GAER,CAeW,GAA2B,EAAkB,cAAe,EAAM,IAAQ,CACnF,EAAS,KAAK,EAAM,EAAI,CACxB,EAAK,KAAK,QAAU,EAAK,KAAK,IAAI,SAAWC,GAC7C,EAAK,KAAK,OAAS,EAAS,IAAS,CACjC,GAAI,EAAI,OACJ,GAAI,CACA,EAAQ,MAAQ,OAAO,EAAQ,MAAM,MAE/B,EACd,IAAM,EAAQ,EAAQ,MACtB,GAAI,OAAO,GAAU,UAAY,CAAC,OAAO,MAAM,EAAM,EAAI,OAAO,SAAS,EAAM,CAC3E,OAAO,EAEX,IAAM,EAAW,OAAO,GAAU,SAC5B,OAAO,MAAM,EAAM,CACf,MACC,OAAO,SAAS,EAAM,CAEnB,IAAA,GADA,WAER,IAAA,GAQN,OAPA,EAAQ,OAAO,KAAK,CAChB,SAAU,SACV,KAAM,eACN,QACA,OACA,GAAI,EAAW,CAAE,WAAU,CAAG,EAAE,CACnC,CAAC,CACK,IAEb,CACW,GAAiC,EAAkB,oBAAqB,EAAM,IAAQ,CAC/F,GAA6B,KAAK,EAAM,EAAI,CAC5C,GAAW,KAAK,EAAM,EAAI,EAC5B,CACW,GAA4B,EAAkB,eAAgB,EAAM,IAAQ,CACrF,EAAS,KAAK,EAAM,EAAI,CACxB,EAAK,KAAK,QAAUC,GACpB,EAAK,KAAK,OAAS,EAAS,IAAS,CACjC,GAAI,EAAI,OACJ,GAAI,CACA,EAAQ,MAAQ,EAAQ,EAAQ,WAE1B,EACd,IAAM,EAAQ,EAAQ,MAStB,OARI,OAAO,GAAU,WAErB,EAAQ,OAAO,KAAK,CAChB,SAAU,UACV,KAAM,eACN,QACA,OACH,CAAC,CANS,IASjB,CAgFW,GAA4B,EAAkB,eAAgB,EAAM,IAAQ,CACrF,EAAS,KAAK,EAAM,EAAI,CACxB,EAAK,KAAK,MAAS,GAAY,GACjC,CACW,GAA0B,EAAkB,aAAc,EAAM,IAAQ,CACjF,EAAS,KAAK,EAAM,EAAI,CACxB,EAAK,KAAK,OAAS,EAAS,KACxB,EAAQ,OAAO,KAAK,CAChB,SAAU,QACV,KAAM,eACN,MAAO,EAAQ,MACf,OACH,CAAC,CACK,IAEb,CAwCF,SAAS,GAAkB,EAAQ,EAAO,EAAO,CACzC,EAAO,OAAO,QACd,EAAM,OAAO,KAAK,GAAGC,GAAkB,EAAO,EAAO,OAAO,CAAC,CAEjE,EAAM,MAAM,GAAS,EAAO,MAEhC,IAAa,GAA0B,EAAkB,aAAc,EAAM,IAAQ,CACjF,EAAS,KAAK,EAAM,EAAI,CACxB,EAAK,KAAK,OAAS,EAAS,IAAQ,CAChC,IAAM,EAAQ,EAAQ,MACtB,GAAI,CAAC,MAAM,QAAQ,EAAM,CAOrB,OANA,EAAQ,OAAO,KAAK,CAChB,SAAU,QACV,KAAM,eACN,QACA,OACH,CAAC,CACK,EAEX,EAAQ,MAAQ,MAAM,EAAM,OAAO,CACnC,IAAM,EAAQ,EAAE,CAChB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,OAAQ,IAAK,CACnC,IAAM,EAAO,EAAM,GACb,EAAS,EAAI,QAAQ,KAAK,IAAI,CAChC,MAAO,EACP,OAAQ,EAAE,CACb,CAAE,EAAI,CACH,aAAkB,QAClB,EAAM,KAAK,EAAO,KAAM,GAAW,GAAkB,EAAQ,EAAS,EAAE,CAAC,CAAC,CAG1E,GAAkB,EAAQ,EAAS,EAAE,CAM7C,OAHI,EAAM,OACC,QAAQ,IAAI,EAAM,CAAC,SAAW,EAAQ,CAE1C,IAEb,CACF,SAAS,GAAqB,EAAQ,EAAO,EAAK,EAAO,EAAe,CACpE,GAAI,EAAO,OAAO,OAAQ,CAEtB,GAAI,GAAiB,EAAE,KAAO,GAC1B,OAEJ,EAAM,OAAO,KAAK,GAAGA,GAAkB,EAAK,EAAO,OAAO,CAAC,CAE3D,EAAO,QAAU,IAAA,GACb,KAAO,IACP,EAAM,MAAM,GAAO,IAAA,IAIvB,EAAM,MAAM,GAAO,EAAO,MAGlC,SAAS,GAAa,EAAK,CACvB,IAAM,EAAO,OAAO,KAAK,EAAI,MAAM,CACnC,IAAK,IAAM,KAAK,EACZ,GAAI,CAAC,EAAI,QAAQ,IAAI,MAAM,QAAQ,IAAI,WAAW,CAC9C,MAAU,MAAM,2BAA2B,EAAE,0BAA0B,CAG/E,IAAM,EAAQC,GAAkB,EAAI,MAAM,CAC1C,MAAO,CACH,GAAG,EACH,OACA,OAAQ,IAAI,IAAI,EAAK,CACrB,QAAS,EAAK,OACd,aAAc,IAAI,IAAI,EAAM,CAC/B,CAEL,SAAS,GAAe,EAAO,EAAO,EAAS,EAAK,EAAK,EAAM,CAC3D,IAAM,EAAe,EAAE,CAEjB,EAAS,EAAI,OACb,EAAY,EAAI,SAAS,KACzB,EAAI,EAAU,IAAI,KAClB,EAAgB,EAAU,SAAW,WAC3C,IAAK,IAAM,KAAO,EAAO,CACrB,GAAI,EAAO,IAAI,EAAI,CACf,SACJ,GAAI,IAAM,QAAS,CACf,EAAa,KAAK,EAAI,CACtB,SAEJ,IAAM,EAAI,EAAU,IAAI,CAAE,MAAO,EAAM,GAAM,OAAQ,EAAE,CAAE,CAAE,EAAI,CAC3D,aAAa,QACb,EAAM,KAAK,EAAE,KAAM,GAAM,GAAqB,EAAG,EAAS,EAAK,EAAO,EAAc,CAAC,CAAC,CAGtF,GAAqB,EAAG,EAAS,EAAK,EAAO,EAAc,CAanE,OAVI,EAAa,QACb,EAAQ,OAAO,KAAK,CAChB,KAAM,oBACN,KAAM,EACN,QACA,OACH,CAAC,CAED,EAAM,OAEJ,QAAQ,IAAI,EAAM,CAAC,SACf,EACT,CAHS,EAKf,IAAa,GAA2B,EAAkB,cAAe,EAAM,IAAQ,CAKnF,GAHA,EAAS,KAAK,EAAM,EAAI,CAGpB,CADS,OAAO,yBAAyB,EAAK,QAAQ,EAC/C,IAAK,CACZ,IAAM,EAAK,EAAI,MACf,OAAO,eAAe,EAAK,QAAS,CAChC,QAAW,CACP,IAAM,EAAQ,CAAE,GAAG,EAAI,CAIvB,OAHA,OAAO,eAAe,EAAK,QAAS,CAChC,MAAO,EACV,CAAC,CACK,GAEd,CAAC,CAEN,IAAM,EAAcC,OAAkB,GAAa,EAAI,CAAC,CACxD,EAAgB,EAAK,KAAM,iBAAoB,CAC3C,IAAM,EAAQ,EAAI,MACZ,EAAa,EAAE,CACrB,IAAK,IAAM,KAAO,EAAO,CACrB,IAAM,EAAQ,EAAM,GAAK,KACzB,GAAI,EAAM,OAAQ,CACd,EAAW,KAAS,EAAW,GAAO,IAAI,KAC1C,IAAK,IAAM,KAAK,EAAM,OAClB,EAAW,GAAK,IAAI,EAAE,EAGlC,OAAO,GACT,CACF,IAAMC,EAAWC,GACX,EAAW,EAAI,SACjB,EACJ,EAAK,KAAK,OAAS,EAAS,IAAQ,CAChC,AAAU,IAAQ,EAAY,MAC9B,IAAM,EAAQ,EAAQ,MACtB,GAAI,CAACD,EAAS,EAAM,CAOhB,OANA,EAAQ,OAAO,KAAK,CAChB,SAAU,SACV,KAAM,eACN,QACA,OACH,CAAC,CACK,EAEX,EAAQ,MAAQ,EAAE,CAClB,IAAM,EAAQ,EAAE,CACV,EAAQ,EAAM,MACpB,IAAK,IAAM,KAAO,EAAM,KAAM,CAC1B,IAAM,EAAK,EAAM,GACX,EAAgB,EAAG,KAAK,SAAW,WACnC,EAAI,EAAG,KAAK,IAAI,CAAE,MAAO,EAAM,GAAM,OAAQ,EAAE,CAAE,CAAE,EAAI,CACzD,aAAa,QACb,EAAM,KAAK,EAAE,KAAM,GAAM,GAAqB,EAAG,EAAS,EAAK,EAAO,EAAc,CAAC,CAAC,CAGtF,GAAqB,EAAG,EAAS,EAAK,EAAO,EAAc,CAMnE,OAHK,EAGE,GAAe,EAAO,EAAO,EAAS,EAAK,EAAY,MAAO,EAAK,CAF/D,EAAM,OAAS,QAAQ,IAAI,EAAM,CAAC,SAAW,EAAQ,CAAG,IAIzE,CACW,GAA8B,EAAkB,iBAAkB,EAAM,IAAQ,CAEzF,GAAW,KAAK,EAAM,EAAI,CAC1B,IAAM,EAAa,EAAK,KAAK,MACvB,EAAcD,OAAkB,GAAa,EAAI,CAAC,CAClD,EAAoB,GAAU,CAChC,IAAM,EAAM,IAAI,GAAI,CAAC,QAAS,UAAW,MAAM,CAAC,CAC1C,EAAa,EAAY,MACzB,EAAY,GAAQ,CACtB,IAAM,EAAIG,GAAS,EAAI,CACvB,MAAO,SAAS,EAAE,4BAA4B,EAAE,wBAEpD,EAAI,MAAM,+BAA+B,CACzC,IAAM,EAAM,OAAO,OAAO,KAAK,CAC3B,EAAU,EACd,IAAK,IAAM,KAAO,EAAW,KACzB,EAAI,GAAO,OAAO,MAGtB,EAAI,MAAM,wBAAwB,CAClC,IAAK,IAAM,KAAO,EAAW,KAAM,CAC/B,IAAM,EAAK,EAAI,GACT,EAAIA,GAAS,EAAI,CAEjB,EADS,EAAM,IACS,MAAM,SAAW,WAC/C,EAAI,MAAM,SAAS,EAAG,KAAK,EAAS,EAAI,CAAC,GAAG,CACxC,EAEA,EAAI,MAAM;cACZ,EAAG;gBACD,EAAE;qDACmC,EAAG;;kCAEtB,EAAE,oBAAoB,EAAE;;;;;cAK5C,EAAG;gBACD,EAAE;wBACM,EAAE;;;sBAGJ,EAAE,MAAM,EAAG;;;QAGzB,CAGQ,EAAI,MAAM;cACZ,EAAG;mDACkC,EAAG;;gCAEtB,EAAE,oBAAoB,EAAE;;;;cAI1C,EAAG;gBACD,EAAE;wBACM,EAAE;;;sBAGJ,EAAE,MAAM,EAAG;;;QAGzB,CAGA,EAAI,MAAM,6BAA6B,CACvC,EAAI,MAAM,kBAAkB,CAC5B,IAAM,EAAK,EAAI,SAAS,CACxB,OAAQ,EAAS,IAAQ,EAAG,EAAO,EAAS,EAAI,EAEhD,EACEF,EAAWC,GACX,EAAM,CAAA,GAAmB,QAEzB,EAAc,GADDG,GACmB,MAChC,EAAW,EAAI,SACjB,EACJ,EAAK,KAAK,OAAS,EAAS,IAAQ,CAChC,AAAU,IAAQ,EAAY,MAC9B,IAAM,EAAQ,EAAQ,MAmBtB,OAlBKJ,EAAS,EAAM,CAShB,GAAO,GAAe,GAAK,QAAU,IAAS,EAAI,UAAY,IAE9D,AACI,IAAW,EAAiB,EAAI,MAAM,CAC1C,EAAU,EAAS,EAAS,EAAI,CAC3B,EAEE,GAAe,EAAE,CAAE,EAAO,EAAS,EAAK,EAAO,EAAK,CADhD,GAGR,EAAW,EAAS,EAAI,EAjB3B,EAAQ,OAAO,KAAK,CAChB,SAAU,SACV,KAAM,eACN,QACA,OACH,CAAC,CACK,KAajB,CACF,SAAS,GAAmB,EAAS,EAAO,EAAM,EAAK,CACnD,IAAK,IAAM,KAAU,EACjB,GAAI,EAAO,OAAO,SAAW,EAEzB,MADA,GAAM,MAAQ,EAAO,MACd,EAGf,IAAM,EAAa,EAAQ,OAAQ,GAAM,CAAC/B,GAAa,EAAE,CAAC,CAW1D,OAVI,EAAW,SAAW,GACtB,EAAM,MAAQ,EAAW,GAAG,MACrB,EAAW,KAEtB,EAAM,OAAO,KAAK,CACd,KAAM,gBACN,MAAO,EAAM,MACb,OACA,OAAQ,EAAQ,IAAK,GAAW,EAAO,OAAO,IAAK,GAAQoC,GAAmB,EAAK,EAAKC,IAAa,CAAC,CAAC,CAAC,CAC3G,CAAC,CACK,GAEX,IAAa,GAA0B,EAAkB,aAAc,EAAM,IAAQ,CACjF,EAAS,KAAK,EAAM,EAAI,CACxB,EAAgB,EAAK,KAAM,YAAe,EAAI,QAAQ,KAAM,GAAM,EAAE,KAAK,QAAU,WAAW,CAAG,WAAa,IAAA,GAAU,CACxH,EAAgB,EAAK,KAAM,aAAgB,EAAI,QAAQ,KAAM,GAAM,EAAE,KAAK,SAAW,WAAW,CAAG,WAAa,IAAA,GAAU,CAC1H,EAAgB,EAAK,KAAM,aAAgB,CACvC,GAAI,EAAI,QAAQ,MAAO,GAAM,EAAE,KAAK,OAAO,CACvC,OAAO,IAAI,IAAI,EAAI,QAAQ,QAAS,GAAW,MAAM,KAAK,EAAO,KAAK,OAAO,CAAC,CAAC,EAGrF,CACF,EAAgB,EAAK,KAAM,cAAiB,CACxC,GAAI,EAAI,QAAQ,MAAO,GAAM,EAAE,KAAK,QAAQ,CAAE,CAC1C,IAAM,EAAW,EAAI,QAAQ,IAAK,GAAM,EAAE,KAAK,QAAQ,CACvD,OAAW,OAAO,KAAK,EAAS,IAAK,GAAMC,GAAgB,EAAE,OAAO,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,GAG1F,CACF,IAAM,EAAS,EAAI,QAAQ,SAAW,EAChC,EAAQ,EAAI,QAAQ,GAAG,KAAK,IAClC,EAAK,KAAK,OAAS,EAAS,IAAQ,CAChC,GAAI,EACA,OAAO,EAAM,EAAS,EAAI,CAE9B,IAAI,EAAQ,GACN,EAAU,EAAE,CAClB,IAAK,IAAM,KAAU,EAAI,QAAS,CAC9B,IAAM,EAAS,EAAO,KAAK,IAAI,CAC3B,MAAO,EAAQ,MACf,OAAQ,EAAE,CACb,CAAE,EAAI,CACP,GAAI,aAAkB,QAClB,EAAQ,KAAK,EAAO,CACpB,EAAQ,OAEP,CACD,GAAI,EAAO,OAAO,SAAW,EACzB,OAAO,EACX,EAAQ,KAAK,EAAO,EAK5B,OAFK,EAEE,QAAQ,IAAI,EAAQ,CAAC,KAAM,GACvB,GAAmB,EAAS,EAAS,EAAM,EAAI,CACxD,CAHS,GAAmB,EAAS,EAAS,EAAM,EAAI,GAKhE,CAgIW,GAAiC,EAAkB,oBAAqB,EAAM,IAAQ,CAC/F,EAAS,KAAK,EAAM,EAAI,CACxB,EAAK,KAAK,OAAS,EAAS,IAAQ,CAChC,IAAM,EAAQ,EAAQ,MAChB,EAAO,EAAI,KAAK,KAAK,IAAI,CAAE,MAAO,EAAO,OAAQ,EAAE,CAAE,CAAE,EAAI,CAC3D,EAAQ,EAAI,MAAM,KAAK,IAAI,CAAE,MAAO,EAAO,OAAQ,EAAE,CAAE,CAAE,EAAI,CAOnE,OANc,aAAgB,SAAW,aAAiB,QAE/C,QAAQ,IAAI,CAAC,EAAM,EAAM,CAAC,CAAC,MAAM,CAAC,EAAM,KACpC,GAA0B,EAAS,EAAM,EAAM,CACxD,CAEC,GAA0B,EAAS,EAAM,EAAM,GAE5D,CACF,SAAS,GAAY,EAAG,EAAG,CAMvB,GAHI,IAAM,GAGN,aAAa,MAAQ,aAAa,MAAQ,CAAC,GAAM,CAAC,EAClD,MAAO,CAAE,MAAO,GAAM,KAAM,EAAG,CAEnC,GAAIC,GAAmB,EAAE,EAAIA,GAAmB,EAAE,CAAE,CAChD,IAAM,EAAQ,OAAO,KAAK,EAAE,CACtB,EAAa,OAAO,KAAK,EAAE,CAAC,OAAQ,GAAQ,EAAM,QAAQ,EAAI,GAAK,GAAG,CACtE,EAAS,CAAE,GAAG,EAAG,GAAG,EAAG,CAC7B,IAAK,IAAM,KAAO,EAAY,CAC1B,IAAM,EAAc,GAAY,EAAE,GAAM,EAAE,GAAK,CAC/C,GAAI,CAAC,EAAY,MACb,MAAO,CACH,MAAO,GACP,eAAgB,CAAC,EAAK,GAAG,EAAY,eAAe,CACvD,CAEL,EAAO,GAAO,EAAY,KAE9B,MAAO,CAAE,MAAO,GAAM,KAAM,EAAQ,CAExC,GAAI,MAAM,QAAQ,EAAE,EAAI,MAAM,QAAQ,EAAE,CAAE,CACtC,GAAI,EAAE,SAAW,EAAE,OACf,MAAO,CAAE,MAAO,GAAO,eAAgB,EAAE,CAAE,CAE/C,IAAM,EAAW,EAAE,CACnB,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAE,OAAQ,IAAS,CAC3C,IAAM,EAAQ,EAAE,GACV,EAAQ,EAAE,GACV,EAAc,GAAY,EAAO,EAAM,CAC7C,GAAI,CAAC,EAAY,MACb,MAAO,CACH,MAAO,GACP,eAAgB,CAAC,EAAO,GAAG,EAAY,eAAe,CACzD,CAEL,EAAS,KAAK,EAAY,KAAK,CAEnC,MAAO,CAAE,MAAO,GAAM,KAAM,EAAU,CAE1C,MAAO,CAAE,MAAO,GAAO,eAAgB,EAAE,CAAE,CAE/C,SAAS,GAA0B,EAAQ,EAAM,EAAO,CAEpD,IAAM,EAAY,IAAI,IAClB,EACJ,IAAK,IAAM,KAAO,EAAK,OACnB,GAAI,EAAI,OAAS,oBAAqB,CAClC,AAAe,IAAa,EAC5B,IAAK,IAAM,KAAK,EAAI,KACX,EAAU,IAAI,EAAE,EACjB,EAAU,IAAI,EAAG,EAAE,CAAC,CACxB,EAAU,IAAI,EAAE,CAAC,EAAI,QAIzB,EAAO,OAAO,KAAK,EAAI,CAG/B,IAAK,IAAM,KAAO,EAAM,OACpB,GAAI,EAAI,OAAS,oBACb,IAAK,IAAM,KAAK,EAAI,KACX,EAAU,IAAI,EAAE,EACjB,EAAU,IAAI,EAAG,EAAE,CAAC,CACxB,EAAU,IAAI,EAAE,CAAC,EAAI,QAIzB,EAAO,OAAO,KAAK,EAAI,CAI/B,IAAM,EAAW,CAAC,GAAG,EAAU,CAAC,QAAQ,EAAG,KAAO,EAAE,GAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAO,EAAE,CAI7E,GAHI,EAAS,QAAU,GACnB,EAAO,OAAO,KAAK,CAAE,GAAG,EAAY,KAAM,EAAU,CAAC,CAErDvC,GAAa,EAAO,CACpB,OAAO,EACX,IAAM,EAAS,GAAY,EAAK,MAAO,EAAM,MAAM,CACnD,GAAI,CAAC,EAAO,MACR,MAAU,MAAM,wCAA6C,KAAK,UAAU,EAAO,eAAe,GAAG,CAGzG,MADA,GAAO,MAAQ,EAAO,KACf,EA+EX,IAAa,GAA2B,EAAkB,cAAe,EAAM,IAAQ,CACnF,EAAS,KAAK,EAAM,EAAI,CACxB,EAAK,KAAK,OAAS,EAAS,IAAQ,CAChC,IAAM,EAAQ,EAAQ,MACtB,GAAI,CAACuC,GAAmB,EAAM,CAO1B,OANA,EAAQ,OAAO,KAAK,CAChB,SAAU,SACV,KAAM,eACN,QACA,OACH,CAAC,CACK,EAEX,IAAM,EAAQ,EAAE,CACV,EAAS,EAAI,QAAQ,KAAK,OAChC,GAAI,EAAQ,CACR,EAAQ,MAAQ,EAAE,CAClB,IAAM,EAAa,IAAI,IACvB,IAAK,IAAM,KAAO,EACd,GAAI,OAAO,GAAQ,UAAY,OAAO,GAAQ,UAAY,OAAO,GAAQ,SAAU,CAC/E,EAAW,IAAI,OAAO,GAAQ,SAAW,EAAI,UAAU,CAAG,EAAI,CAC9D,IAAM,EAAS,EAAI,UAAU,KAAK,IAAI,CAAE,MAAO,EAAM,GAAM,OAAQ,EAAE,CAAE,CAAE,EAAI,CACzE,aAAkB,QAClB,EAAM,KAAK,EAAO,KAAM,GAAW,CAC3B,EAAO,OAAO,QACd,EAAQ,OAAO,KAAK,GAAGX,GAAkB,EAAK,EAAO,OAAO,CAAC,CAEjE,EAAQ,MAAM,GAAO,EAAO,OAC9B,CAAC,EAGC,EAAO,OAAO,QACd,EAAQ,OAAO,KAAK,GAAGA,GAAkB,EAAK,EAAO,OAAO,CAAC,CAEjE,EAAQ,MAAM,GAAO,EAAO,OAIxC,IAAI,EACJ,IAAK,IAAM,KAAO,EACT,EAAW,IAAI,EAAI,GACpB,IAA+B,EAAE,CACjC,EAAa,KAAK,EAAI,EAG1B,GAAgB,EAAa,OAAS,GACtC,EAAQ,OAAO,KAAK,CAChB,KAAM,oBACN,QACA,OACA,KAAM,EACT,CAAC,KAGL,CACD,EAAQ,MAAQ,EAAE,CAClB,IAAK,IAAM,KAAO,QAAQ,QAAQ,EAAM,CAAE,CACtC,GAAI,IAAQ,YACR,SACJ,IAAI,EAAY,EAAI,QAAQ,KAAK,IAAI,CAAE,MAAO,EAAK,OAAQ,EAAE,CAAE,CAAE,EAAI,CACrE,GAAI,aAAqB,QACrB,MAAU,MAAM,uDAAuD,CAK3E,GADwB,OAAO,GAAQ,UAAA,GAA2B,KAAK,EAAI,EAAI,EAAU,OAAO,OAC3E,CACjB,IAAM,EAAc,EAAI,QAAQ,KAAK,IAAI,CAAE,MAAO,OAAO,EAAI,CAAE,OAAQ,EAAE,CAAE,CAAE,EAAI,CACjF,GAAI,aAAuB,QACvB,MAAU,MAAM,uDAAuD,CAEvE,EAAY,OAAO,SAAW,IAC9B,EAAY,GAGpB,GAAI,EAAU,OAAO,OAAQ,CACrB,EAAI,OAAS,QAEb,EAAQ,MAAM,GAAO,EAAM,GAI3B,EAAQ,OAAO,KAAK,CAChB,KAAM,cACN,OAAQ,SACR,OAAQ,EAAU,OAAO,IAAK,GAAQQ,GAAmB,EAAK,EAAKC,IAAa,CAAC,CAAC,CAClF,MAAO,EACP,KAAM,CAAC,EAAI,CACX,OACH,CAAC,CAEN,SAEJ,IAAM,EAAS,EAAI,UAAU,KAAK,IAAI,CAAE,MAAO,EAAM,GAAM,OAAQ,EAAE,CAAE,CAAE,EAAI,CACzE,aAAkB,QAClB,EAAM,KAAK,EAAO,KAAM,GAAW,CAC3B,EAAO,OAAO,QACd,EAAQ,OAAO,KAAK,GAAGT,GAAkB,EAAK,EAAO,OAAO,CAAC,CAEjE,EAAQ,MAAM,EAAU,OAAS,EAAO,OAC1C,CAAC,EAGC,EAAO,OAAO,QACd,EAAQ,OAAO,KAAK,GAAGA,GAAkB,EAAK,EAAO,OAAO,CAAC,CAEjE,EAAQ,MAAM,EAAU,OAAS,EAAO,QAOpD,OAHI,EAAM,OACC,QAAQ,IAAI,EAAM,CAAC,SAAW,EAAQ,CAE1C,IAEb,CAmGW,GAAyB,EAAkB,YAAa,EAAM,IAAQ,CAC/E,EAAS,KAAK,EAAM,EAAI,CACxB,IAAM,EAASY,GAAmB,EAAI,QAAQ,CACxC,EAAY,IAAI,IAAI,EAAO,CACjC,EAAK,KAAK,OAAS,EACnB,EAAK,KAAK,QAAc,OAAO,KAAK,EAC/B,OAAQ,GAAA,GAA4B,IAAI,OAAO,EAAE,CAAC,CAClD,IAAK,GAAO,OAAO,GAAM,SAAWC,GAAiB,EAAE,CAAG,EAAE,UAAU,CAAE,CACxE,KAAK,IAAI,CAAC,IAAI,CACnB,EAAK,KAAK,OAAS,EAAS,IAAS,CACjC,IAAM,EAAQ,EAAQ,MAUtB,OATI,EAAU,IAAI,EAAM,EAGxB,EAAQ,OAAO,KAAK,CAChB,KAAM,gBACN,SACA,QACA,OACH,CAAC,CAPS,IAUjB,CAyCW,GAA8B,EAAkB,iBAAkB,EAAM,IAAQ,CACzF,EAAS,KAAK,EAAM,EAAI,CACxB,EAAK,KAAK,OAAS,EAAS,IAAQ,CAChC,GAAI,EAAI,YAAc,WAClB,MAAM,IAAIC,GAAqB,EAAK,YAAY,KAAK,CAEzD,IAAM,EAAO,EAAI,UAAU,EAAQ,MAAO,EAAQ,CAClD,GAAI,EAAI,MAEJ,OADe,aAAgB,QAAU,EAAO,QAAQ,QAAQ,EAAK,EACvD,KAAM,IAChB,EAAQ,MAAQ,EACT,GACT,CAEN,GAAI,aAAgB,QAChB,MAAM,IAAIzC,GAGd,MADA,GAAQ,MAAQ,EACT,IAEb,CACF,SAAS,GAAqB,EAAQ,EAAO,CAIzC,OAHI,EAAO,OAAO,QAAU,IAAU,IAAA,GAC3B,CAAE,OAAQ,EAAE,CAAE,MAAO,IAAA,GAAW,CAEpC,EAEX,IAAa,GAA6B,EAAkB,gBAAiB,EAAM,IAAQ,CACvF,EAAS,KAAK,EAAM,EAAI,CACxB,EAAK,KAAK,MAAQ,WAClB,EAAK,KAAK,OAAS,WACnB,EAAgB,EAAK,KAAM,aAChB,EAAI,UAAU,KAAK,OAAS,IAAI,IAAI,CAAC,GAAG,EAAI,UAAU,KAAK,OAAQ,IAAA,GAAU,CAAC,CAAG,IAAA,GAC1F,CACF,EAAgB,EAAK,KAAM,cAAiB,CACxC,IAAM,EAAU,EAAI,UAAU,KAAK,QACnC,OAAO,EAAc,OAAO,KAAKqC,GAAgB,EAAQ,OAAO,CAAC,KAAK,CAAG,IAAA,IAC3E,CACF,EAAK,KAAK,OAAS,EAAS,IAAQ,CAChC,GAAI,EAAI,UAAU,KAAK,QAAU,WAAY,CACzC,IAAM,EAAS,EAAI,UAAU,KAAK,IAAI,EAAS,EAAI,CAGnD,OAFI,aAAkB,QACX,EAAO,KAAM,GAAM,GAAqB,EAAG,EAAQ,MAAM,CAAC,CAC9D,GAAqB,EAAQ,EAAQ,MAAM,CAKtD,OAHI,EAAQ,QAAU,IAAA,GACX,EAEJ,EAAI,UAAU,KAAK,IAAI,EAAS,EAAI,GAEjD,CACW,GAAkC,EAAkB,qBAAsB,EAAM,IAAQ,CAEjG,GAAa,KAAK,EAAM,EAAI,CAE5B,EAAgB,EAAK,KAAM,aAAgB,EAAI,UAAU,KAAK,OAAO,CACrE,EAAgB,EAAK,KAAM,cAAiB,EAAI,UAAU,KAAK,QAAQ,CAEvE,EAAK,KAAK,OAAS,EAAS,IACjB,EAAI,UAAU,KAAK,IAAI,EAAS,EAAI,EAEjD,CACW,GAA6B,EAAkB,gBAAiB,EAAM,IAAQ,CACvF,EAAS,KAAK,EAAM,EAAI,CACxB,EAAgB,EAAK,KAAM,YAAe,EAAI,UAAU,KAAK,MAAM,CACnE,EAAgB,EAAK,KAAM,aAAgB,EAAI,UAAU,KAAK,OAAO,CACrE,EAAgB,EAAK,KAAM,cAAiB,CACxC,IAAM,EAAU,EAAI,UAAU,KAAK,QACnC,OAAO,EAAc,OAAO,KAAKA,GAAgB,EAAQ,OAAO,CAAC,SAAS,CAAG,IAAA,IAC/E,CACF,EAAgB,EAAK,KAAM,aAChB,EAAI,UAAU,KAAK,OAAS,IAAI,IAAI,CAAC,GAAG,EAAI,UAAU,KAAK,OAAQ,KAAK,CAAC,CAAG,IAAA,GACrF,CACF,EAAK,KAAK,OAAS,EAAS,IAEpB,EAAQ,QAAU,KACX,EACJ,EAAI,UAAU,KAAK,IAAI,EAAS,EAAI,EAEjD,CACW,GAA4B,EAAkB,eAAgB,EAAM,IAAQ,CACrF,EAAS,KAAK,EAAM,EAAI,CAExB,EAAK,KAAK,MAAQ,WAClB,EAAgB,EAAK,KAAM,aAAgB,EAAI,UAAU,KAAK,OAAO,CACrE,EAAK,KAAK,OAAS,EAAS,IAAQ,CAChC,GAAI,EAAI,YAAc,WAClB,OAAO,EAAI,UAAU,KAAK,IAAI,EAAS,EAAI,CAG/C,GAAI,EAAQ,QAAU,IAAA,GAKlB,MAJA,GAAQ,MAAQ,EAAI,aAIb,EAGX,IAAM,EAAS,EAAI,UAAU,KAAK,IAAI,EAAS,EAAI,CAInD,OAHI,aAAkB,QACX,EAAO,KAAM,GAAW,GAAoB,EAAQ,EAAI,CAAC,CAE7D,GAAoB,EAAQ,EAAI,GAE7C,CACF,SAAS,GAAoB,EAAS,EAAK,CAIvC,OAHI,EAAQ,QAAU,IAAA,KAClB,EAAQ,MAAQ,EAAI,cAEjB,EAEX,IAAa,GAA6B,EAAkB,gBAAiB,EAAM,IAAQ,CACvF,EAAS,KAAK,EAAM,EAAI,CACxB,EAAK,KAAK,MAAQ,WAClB,EAAgB,EAAK,KAAM,aAAgB,EAAI,UAAU,KAAK,OAAO,CACrE,EAAK,KAAK,OAAS,EAAS,KACpB,EAAI,YAAc,YAIlB,EAAQ,QAAU,IAAA,KAClB,EAAQ,MAAQ,EAAI,cAJb,EAAI,UAAU,KAAK,IAAI,EAAS,EAAI,GAQrD,CACW,GAAgC,EAAkB,mBAAoB,EAAM,IAAQ,CAC7F,EAAS,KAAK,EAAM,EAAI,CACxB,EAAgB,EAAK,KAAM,aAAgB,CACvC,IAAM,EAAI,EAAI,UAAU,KAAK,OAC7B,OAAO,EAAI,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,OAAQ,GAAM,IAAM,IAAA,GAAU,CAAC,CAAG,IAAA,IAC9D,CACF,EAAK,KAAK,OAAS,EAAS,IAAQ,CAChC,IAAM,EAAS,EAAI,UAAU,KAAK,IAAI,EAAS,EAAI,CAInD,OAHI,aAAkB,QACX,EAAO,KAAM,GAAW,GAAwB,EAAQ,EAAK,CAAC,CAElE,GAAwB,EAAQ,EAAK,GAElD,CACF,SAAS,GAAwB,EAAS,EAAM,CAS5C,MARI,CAAC,EAAQ,OAAO,QAAU,EAAQ,QAAU,IAAA,IAC5C,EAAQ,OAAO,KAAK,CAChB,KAAM,eACN,SAAU,cACV,MAAO,EAAQ,MACf,OACH,CAAC,CAEC,EAmBX,IAAa,GAA0B,EAAkB,aAAc,EAAM,IAAQ,CACjF,EAAS,KAAK,EAAM,EAAI,CACxB,EAAgB,EAAK,KAAM,YAAe,EAAI,UAAU,KAAK,MAAM,CACnE,EAAgB,EAAK,KAAM,aAAgB,EAAI,UAAU,KAAK,OAAO,CACrE,EAAgB,EAAK,KAAM,aAAgB,EAAI,UAAU,KAAK,OAAO,CACrE,EAAK,KAAK,OAAS,EAAS,IAAQ,CAChC,GAAI,EAAI,YAAc,WAClB,OAAO,EAAI,UAAU,KAAK,IAAI,EAAS,EAAI,CAG/C,IAAM,EAAS,EAAI,UAAU,KAAK,IAAI,EAAS,EAAI,CA4BnD,OA3BI,aAAkB,QACX,EAAO,KAAM,IAChB,EAAQ,MAAQ,EAAO,MACnB,EAAO,OAAO,SACd,EAAQ,MAAQ,EAAI,WAAW,CAC3B,GAAG,EACH,MAAO,CACH,OAAQ,EAAO,OAAO,IAAK,GAAQF,GAAmB,EAAK,EAAKC,IAAa,CAAC,CAAC,CAClF,CACD,MAAO,EAAQ,MAClB,CAAC,CACF,EAAQ,OAAS,EAAE,EAEhB,GACT,EAEN,EAAQ,MAAQ,EAAO,MACnB,EAAO,OAAO,SACd,EAAQ,MAAQ,EAAI,WAAW,CAC3B,GAAG,EACH,MAAO,CACH,OAAQ,EAAO,OAAO,IAAK,GAAQD,GAAmB,EAAK,EAAKC,IAAa,CAAC,CAAC,CAClF,CACD,MAAO,EAAQ,MAClB,CAAC,CACF,EAAQ,OAAS,EAAE,EAEhB,KAEb,CAgBW,GAAyB,EAAkB,YAAa,EAAM,IAAQ,CAC/E,EAAS,KAAK,EAAM,EAAI,CACxB,EAAgB,EAAK,KAAM,aAAgB,EAAI,GAAG,KAAK,OAAO,CAC9D,EAAgB,EAAK,KAAM,YAAe,EAAI,GAAG,KAAK,MAAM,CAC5D,EAAgB,EAAK,KAAM,aAAgB,EAAI,IAAI,KAAK,OAAO,CAC/D,EAAgB,EAAK,KAAM,iBAAoB,EAAI,GAAG,KAAK,WAAW,CACtE,EAAK,KAAK,OAAS,EAAS,IAAQ,CAChC,GAAI,EAAI,YAAc,WAAY,CAC9B,IAAM,EAAQ,EAAI,IAAI,KAAK,IAAI,EAAS,EAAI,CAI5C,OAHI,aAAiB,QACV,EAAM,KAAM,GAAU,GAAiB,EAAO,EAAI,GAAI,EAAI,CAAC,CAE/D,GAAiB,EAAO,EAAI,GAAI,EAAI,CAE/C,IAAM,EAAO,EAAI,GAAG,KAAK,IAAI,EAAS,EAAI,CAI1C,OAHI,aAAgB,QACT,EAAK,KAAM,GAAS,GAAiB,EAAM,EAAI,IAAK,EAAI,CAAC,CAE7D,GAAiB,EAAM,EAAI,IAAK,EAAI,GAEjD,CACF,SAAS,GAAiB,EAAM,EAAM,EAAK,CAMvC,OALI,EAAK,OAAO,QAEZ,EAAK,QAAU,GACR,GAEJ,EAAK,KAAK,IAAI,CAAE,MAAO,EAAK,MAAO,OAAQ,EAAK,OAAQ,CAAE,EAAI,CAwDzE,IAAa,GAA6B,EAAkB,gBAAiB,EAAM,IAAQ,CACvF,EAAS,KAAK,EAAM,EAAI,CACxB,EAAgB,EAAK,KAAM,iBAAoB,EAAI,UAAU,KAAK,WAAW,CAC7E,EAAgB,EAAK,KAAM,aAAgB,EAAI,UAAU,KAAK,OAAO,CACrE,EAAgB,EAAK,KAAM,YAAe,EAAI,WAAW,MAAM,MAAM,CACrE,EAAgB,EAAK,KAAM,aAAgB,EAAI,WAAW,MAAM,OAAO,CACvE,EAAK,KAAK,OAAS,EAAS,IAAQ,CAChC,GAAI,EAAI,YAAc,WAClB,OAAO,EAAI,UAAU,KAAK,IAAI,EAAS,EAAI,CAE/C,IAAM,EAAS,EAAI,UAAU,KAAK,IAAI,EAAS,EAAI,CAInD,OAHI,aAAkB,QACX,EAAO,KAAK,GAAqB,CAErC,GAAqB,EAAO,GAEzC,CACF,SAAS,GAAqB,EAAS,CAEnC,MADA,GAAQ,MAAQ,OAAO,OAAO,EAAQ,MAAM,CACrC,EA2JX,IAAa,GAA2B,EAAkB,cAAe,EAAM,IAAQ,CACnF,EAAiB,KAAK,EAAM,EAAI,CAChC,EAAS,KAAK,EAAM,EAAI,CACxB,EAAK,KAAK,OAAS,EAAS,IACjB,EAEX,EAAK,KAAK,MAAS,GAAY,CAC3B,IAAM,EAAQ,EAAQ,MAChB,EAAI,EAAI,GAAG,EAAM,CACvB,GAAI,aAAa,QACb,OAAO,EAAE,KAAM,GAAM,GAAmB,EAAG,EAAS,EAAO,EAAK,CAAC,CAErE,GAAmB,EAAG,EAAS,EAAO,EAAK,GAGjD,CACF,SAAS,GAAmB,EAAQ,EAAS,EAAO,EAAM,CACtD,GAAI,CAAC,EAAQ,CACT,IAAM,EAAO,CACT,KAAM,SACN,QACA,OACA,KAAM,CAAC,GAAI,EAAK,KAAK,IAAI,MAAQ,EAAE,CAAE,CACrC,SAAU,CAAC,EAAK,KAAK,IAAI,MAE5B,CACG,EAAK,KAAK,IAAI,SACd,EAAK,OAAS,EAAK,KAAK,IAAI,QAChC,EAAQ,OAAO,KAAKM,GAAW,EAAK,CAAC,EC1iE7C,IAAI,GAGS,GAAb,KAA0B,CACtB,aAAc,CACV,KAAK,KAAO,IAAI,QAChB,KAAK,OAAS,IAAI,IAEtB,IAAI,EAAQ,GAAG,EAAO,CAClB,IAAM,EAAO,EAAM,GAKnB,OAJA,KAAK,KAAK,IAAI,EAAQ,EAAK,CACvB,GAAQ,OAAO,GAAS,UAAY,OAAQ,GAC5C,KAAK,OAAO,IAAI,EAAK,GAAI,EAAO,CAE7B,KAEX,OAAQ,CAGJ,MAFA,MAAK,KAAO,IAAI,QAChB,KAAK,OAAS,IAAI,IACX,KAEX,OAAO,EAAQ,CACX,IAAM,EAAO,KAAK,KAAK,IAAI,EAAO,CAKlC,OAJI,GAAQ,OAAO,GAAS,UAAY,OAAQ,GAC5C,KAAK,OAAO,OAAO,EAAK,GAAG,CAE/B,KAAK,KAAK,OAAO,EAAO,CACjB,KAEX,IAAI,EAAQ,CAGR,IAAM,EAAI,EAAO,KAAK,OACtB,GAAI,EAAG,CACH,IAAM,EAAK,CAAE,GAAI,KAAK,IAAI,EAAE,EAAI,EAAE,CAAG,CACrC,OAAO,EAAG,GACV,IAAM,EAAI,CAAE,GAAG,EAAI,GAAG,KAAK,KAAK,IAAI,EAAO,CAAE,CAC7C,OAAO,OAAO,KAAK,EAAE,CAAC,OAAS,EAAI,IAAA,GAEvC,OAAO,KAAK,KAAK,IAAI,EAAO,CAEhC,IAAI,EAAQ,CACR,OAAO,KAAK,KAAK,IAAI,EAAO,GAIpC,SAAgB,IAAW,CACvB,OAAO,IAAI,IAEd,GAAK,YAAY,uBAAyB,GAAG,qBAAuB,IAAU,EAC/E,IAAa,GAAiB,WAAW,qBC7CzC,SAAgB,GAAQ,EAAO,EAAQ,CACnC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,GAAGC,EAAqB,EAAO,CAClC,CAAC,CAWN,SAAgB,GAAO,EAAO,EAAQ,CAClC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,QACR,MAAO,gBACP,MAAO,GACP,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAM,EAAO,EAAQ,CACjC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,OACR,MAAO,gBACP,MAAO,GACP,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAM,EAAO,EAAQ,CACjC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,OACR,MAAO,gBACP,MAAO,GACP,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAQ,EAAO,EAAQ,CACnC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,OACR,MAAO,gBACP,MAAO,GACP,QAAS,KACT,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAQ,EAAO,EAAQ,CACnC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,OACR,MAAO,gBACP,MAAO,GACP,QAAS,KACT,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAQ,EAAO,EAAQ,CACnC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,OACR,MAAO,gBACP,MAAO,GACP,QAAS,KACT,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAK,EAAO,EAAQ,CAChC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,MACR,MAAO,gBACP,MAAO,GACP,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAO,EAAO,EAAQ,CAClC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,QACR,MAAO,gBACP,MAAO,GACP,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAQ,EAAO,EAAQ,CACnC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,SACR,MAAO,gBACP,MAAO,GACP,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAM,EAAO,EAAQ,CACjC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,OACR,MAAO,gBACP,MAAO,GACP,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAO,EAAO,EAAQ,CAClC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,QACR,MAAO,gBACP,MAAO,GACP,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAM,EAAO,EAAQ,CACjC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,OACR,MAAO,gBACP,MAAO,GACP,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAK,EAAO,EAAQ,CAChC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,MACR,MAAO,gBACP,MAAO,GACP,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAO,EAAO,EAAQ,CAClC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,QACR,MAAO,gBACP,MAAO,GACP,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAM,EAAO,EAAQ,CACjC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,OACR,MAAO,gBACP,MAAO,GACP,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAM,EAAO,EAAQ,CACjC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,OACR,MAAO,gBACP,MAAO,GACP,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAaN,SAAgB,GAAQ,EAAO,EAAQ,CACnC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,SACR,MAAO,gBACP,MAAO,GACP,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAQ,EAAO,EAAQ,CACnC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,SACR,MAAO,gBACP,MAAO,GACP,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAQ,EAAO,EAAQ,CACnC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,SACR,MAAO,gBACP,MAAO,GACP,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAW,EAAO,EAAQ,CACtC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,YACR,MAAO,gBACP,MAAO,GACP,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAM,EAAO,EAAQ,CACjC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,OACR,MAAO,gBACP,MAAO,GACP,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAK,EAAO,EAAQ,CAChC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,MACR,MAAO,gBACP,MAAO,GACP,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAUN,SAAgB,GAAa,EAAO,EAAQ,CACxC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,WACR,MAAO,gBACP,OAAQ,GACR,MAAO,GACP,UAAW,KACX,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAS,EAAO,EAAQ,CACpC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,OACR,MAAO,gBACP,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAS,EAAO,EAAQ,CACpC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,OACR,MAAO,gBACP,UAAW,KACX,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAa,EAAO,EAAQ,CACxC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,WACR,MAAO,gBACP,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAQ,EAAO,EAAQ,CACnC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,OAAQ,EAAE,CACV,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAYN,SAAgB,GAAK,EAAO,EAAQ,CAChC,OAAO,IAAI,EAAM,CACb,KAAM,SACN,MAAO,gBACP,MAAO,GACP,OAAQ,UACR,GAAGA,EAAqB,EAAO,CAClC,CAAC,CA2CN,SAAgB,GAAS,EAAO,EAAQ,CACpC,OAAO,IAAI,EAAM,CACb,KAAM,UACN,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAyEN,SAAgB,GAAS,EAAO,CAC5B,OAAO,IAAI,EAAM,CACb,KAAM,UACT,CAAC,CAGN,SAAgB,GAAO,EAAO,EAAQ,CAClC,OAAO,IAAI,EAAM,CACb,KAAM,QACN,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAgCN,SAAgB,GAAI,EAAO,EAAQ,CAC/B,OAAO,IAAIC,GAAyB,CAChC,MAAO,YACP,GAAGD,EAAqB,EAAO,CAC/B,QACA,UAAW,GACd,CAAC,CAGN,SAAgB,GAAK,EAAO,EAAQ,CAChC,OAAO,IAAIC,GAAyB,CAChC,MAAO,YACP,GAAGD,EAAqB,EAAO,CAC/B,QACA,UAAW,GACd,CAAC,CAMN,SAAgB,GAAI,EAAO,EAAQ,CAC/B,OAAO,IAAIE,GAA4B,CACnC,MAAO,eACP,GAAGF,EAAqB,EAAO,CAC/B,QACA,UAAW,GACd,CAAC,CAGN,SAAgB,GAAK,EAAO,EAAQ,CAChC,OAAO,IAAIE,GAA4B,CACnC,MAAO,eACP,GAAGF,EAAqB,EAAO,CAC/B,QACA,UAAW,GACd,CAAC,CAyBN,SAAgB,GAAY,EAAO,EAAQ,CACvC,OAAO,IAAIG,GAA2B,CAClC,MAAO,cACP,GAAGH,EAAqB,EAAO,CAC/B,QACH,CAAC,CA2BN,SAAgB,GAAW,EAAS,EAAQ,CAMxC,OALW,IAAII,GAA0B,CACrC,MAAO,aACP,GAAGJ,EAAqB,EAAO,CAC/B,UACH,CAAC,CAIN,SAAgB,GAAW,EAAS,EAAQ,CACxC,OAAO,IAAIK,GAA0B,CACjC,MAAO,aACP,GAAGL,EAAqB,EAAO,CAC/B,UACH,CAAC,CAGN,SAAgB,GAAQ,EAAQ,EAAQ,CACpC,OAAO,IAAIM,GAA6B,CACpC,MAAO,gBACP,GAAGN,EAAqB,EAAO,CAC/B,SACH,CAAC,CAGN,SAAgB,GAAO,EAAS,EAAQ,CACpC,OAAO,IAAIO,GAAsB,CAC7B,MAAO,gBACP,OAAQ,QACR,GAAGP,EAAqB,EAAO,CAC/B,UACH,CAAC,CAGN,SAAgB,GAAW,EAAQ,CAC/B,OAAO,IAAIQ,GAA0B,CACjC,MAAO,gBACP,OAAQ,YACR,GAAGR,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAW,EAAQ,CAC/B,OAAO,IAAIS,GAA0B,CACjC,MAAO,gBACP,OAAQ,YACR,GAAGT,EAAqB,EAAO,CAClC,CAAC,CAGN,SAAgB,GAAU,EAAU,EAAQ,CACxC,OAAO,IAAIU,GAAyB,CAChC,MAAO,gBACP,OAAQ,WACR,GAAGV,EAAqB,EAAO,CAC/B,WACH,CAAC,CAGN,SAAgB,GAAY,EAAQ,EAAQ,CACxC,OAAO,IAAIW,GAA2B,CAClC,MAAO,gBACP,OAAQ,cACR,GAAGX,EAAqB,EAAO,CAC/B,SACH,CAAC,CAGN,SAAgB,GAAU,EAAQ,EAAQ,CACtC,OAAO,IAAIY,GAAyB,CAChC,MAAO,gBACP,OAAQ,YACR,GAAGZ,EAAqB,EAAO,CAC/B,SACH,CAAC,CAoBN,SAAgB,GAAW,EAAI,CAC3B,OAAO,IAAIa,GAA0B,CACjC,MAAO,YACP,KACH,CAAC,CAIN,SAAgB,GAAW,EAAM,CAC7B,OAAO,GAAY,GAAU,EAAM,UAAU,EAAK,CAAC,CAIvD,SAAgB,IAAQ,CACpB,OAAO,GAAY,GAAU,EAAM,MAAM,CAAC,CAI9C,SAAgB,IAAe,CAC3B,OAAO,GAAY,GAAU,EAAM,aAAa,CAAC,CAIrD,SAAgB,IAAe,CAC3B,OAAO,GAAY,GAAU,EAAM,aAAa,CAAC,CAIrD,SAAgB,IAAW,CACvB,OAAO,GAAY,GAAUC,GAAa,EAAM,CAAC,CAGrD,SAAgB,GAAO,EAAO,EAAS,EAAQ,CAC3C,OAAO,IAAI,EAAM,CACb,KAAM,QACN,UAIA,GAAGd,EAAqB,EAAO,CAClC,CAAC,CAyON,SAAgB,GAAQ,EAAO,EAAI,EAAS,CAOxC,OANe,IAAI,EAAM,CACrB,KAAM,SACN,MAAO,SACH,KACJ,GAAGA,EAAqB,EAAQ,CACnC,CAAC,CAIN,SAAgB,GAAa,EAAI,CAC7B,IAAM,EAAK,GAAQ,IACf,EAAQ,SAAY,GAAU,CAC1B,GAAI,OAAOe,GAAU,SACjB,EAAQ,OAAO,KAAKC,GAAWD,EAAO,EAAQ,MAAO,EAAG,KAAK,IAAI,CAAC,KAEjE,CAED,IAAM,EAASA,EACX,EAAO,QACP,EAAO,SAAW,IACtB,AAAgB,EAAO,OAAO,SAC9B,AAAiB,EAAO,QAAQ,EAAQ,MACxC,AAAgB,EAAO,OAAO,EAC9B,AAAoB,EAAO,WAAW,CAAC,EAAG,KAAK,IAAI,MACnD,EAAQ,OAAO,KAAKC,GAAW,EAAO,CAAC,GAGxC,EAAG,EAAQ,MAAO,EAAQ,EACnC,CACF,OAAO,EAGX,SAAgB,GAAO,EAAI,EAAQ,CAC/B,IAAM,EAAK,IAAIC,EAAiB,CAC5B,MAAO,SACP,GAAGjB,EAAqB,EAAO,CAClC,CAAC,CAEF,MADA,GAAG,KAAK,MAAQ,EACT,ECh9BX,SAAgB,GAAkB,EAAQ,CAEtC,IAAI,EAAS,GAAQ,QAAU,gBAK/B,OAJI,IAAW,YACX,EAAS,YACT,IAAW,YACX,EAAS,YACN,CACH,WAAY,EAAO,YAAc,EAAE,CACnC,iBAAkB,GAAQ,UAAY,GACtC,SACA,gBAAiB,GAAQ,iBAAmB,QAC5C,SAAU,GAAQ,eAAmB,IACrC,GAAI,GAAQ,IAAM,SAClB,QAAS,EACT,KAAM,IAAI,IACV,OAAQ,GAAQ,QAAU,MAC1B,OAAQ,GAAQ,QAAU,SAC1B,SAAU,GAAQ,UAAY,IAAA,GACjC,CAEL,SAAgBoB,EAAQ,EAAQ,EAAK,EAAU,CAAE,KAAM,EAAE,CAAE,WAAY,EAAE,CAAE,CAAE,CACzE,IAAI,EACJ,IAAM,EAAM,EAAO,KAAK,IAElB,EAAO,EAAI,KAAK,IAAI,EAAO,CACjC,GAAI,EAOA,MANA,GAAK,QAEW,EAAQ,WAAW,SAAS,EAAO,GAE/C,EAAK,MAAQ,EAAQ,MAElB,EAAK,OAGhB,IAAM,EAAS,CAAE,OAAQ,EAAE,CAAE,MAAO,EAAG,MAAO,IAAA,GAAW,KAAM,EAAQ,KAAM,CAC7E,EAAI,KAAK,IAAI,EAAQ,EAAO,CAE5B,IAAM,EAAiB,EAAO,KAAK,gBAAgB,CACnD,GAAI,EACA,EAAO,OAAS,MAEf,CACD,IAAM,EAAS,CACX,GAAG,EACH,WAAY,CAAC,GAAG,EAAQ,WAAY,EAAO,CAC3C,KAAM,EAAQ,KACjB,CACD,GAAI,EAAO,KAAK,kBACZ,EAAO,KAAK,kBAAkB,EAAK,EAAO,OAAQ,EAAO,KAExD,CACD,IAAM,EAAQ,EAAO,OACf,EAAY,EAAI,WAAW,EAAI,MACrC,GAAI,CAAC,EACD,MAAU,MAAM,uDAAuD,EAAI,OAAO,CAEtF,EAAU,EAAQ,EAAK,EAAO,EAAO,CAEzC,IAAM,EAAS,EAAO,KAAK,OACvB,IAEA,AACI,EAAO,MAAM,EACjB,EAAQ,EAAQ,EAAK,EAAO,CAC5B,EAAI,KAAK,IAAI,EAAO,CAAC,SAAW,IAIxC,IAAM,EAAO,EAAI,iBAAiB,IAAI,EAAO,CAc7C,OAbI,GACA,OAAO,OAAO,EAAO,OAAQ,EAAK,CAClC,EAAI,KAAO,SAAW,EAAe,EAAO,GAE5C,OAAO,EAAO,OAAO,SACrB,OAAO,EAAO,OAAO,SAGrB,EAAI,KAAO,SAAW,EAAO,OAAO,aACnC,EAAK,EAAO,QAAQ,UAAY,EAAG,QAAU,EAAO,OAAO,YAChE,OAAO,EAAO,OAAO,UAEL,EAAI,KAAK,IAAI,EAAO,CACrB,OAEnB,SAAgB,GAAY,EAAK,EAE/B,CAEE,IAAM,EAAO,EAAI,KAAK,IAAI,EAAO,CACjC,GAAI,CAAC,EACD,MAAU,MAAM,4CAA4C,CAEhE,IAAM,EAAa,IAAI,IACvB,IAAK,IAAM,KAAS,EAAI,KAAK,SAAS,CAAE,CACpC,IAAM,EAAK,EAAI,iBAAiB,IAAI,EAAM,GAAG,EAAE,GAC/C,GAAI,EAAI,CACJ,IAAM,EAAW,EAAW,IAAI,EAAG,CACnC,GAAI,GAAY,IAAa,EAAM,GAC/B,MAAU,MAAM,wBAAwB,EAAG,mHAAmH,CAElK,EAAW,IAAI,EAAI,EAAM,GAAG,EAKpC,IAAM,EAAW,GAAU,CAKvB,IAAM,EAAc,EAAI,SAAW,gBAAkB,QAAU,cAC/D,GAAI,EAAI,SAAU,CACd,IAAM,EAAa,EAAI,SAAS,SAAS,IAAI,EAAM,GAAG,EAAE,GAElD,EAAe,EAAI,SAAS,MAAS,GAAO,GAClD,GAAI,EACA,MAAO,CAAE,IAAK,EAAa,EAAW,CAAE,CAG5C,IAAM,EAAK,EAAM,GAAG,OAAS,EAAM,GAAG,OAAO,IAAM,SAAS,EAAI,YAEhE,MADA,GAAM,GAAG,MAAQ,EACV,CAAE,MAAO,EAAI,IAAK,GAAG,EAAa,WAAW,CAAC,IAAI,EAAY,GAAG,IAAM,CAElF,GAAI,EAAM,KAAO,EACb,MAAO,CAAE,IAAK,IAAK,CAIvB,IAAM,EAAe,KAAgB,EAAY,GAC3C,EAAQ,EAAM,GAAG,OAAO,IAAM,WAAW,EAAI,YACnD,MAAO,CAAE,QAAO,IAAK,EAAe,EAAO,EAIzC,EAAgB,GAAU,CAE5B,GAAI,EAAM,GAAG,OAAO,KAChB,OAEJ,IAAM,EAAO,EAAM,GACb,CAAE,MAAK,SAAU,EAAQ,EAAM,CACrC,EAAK,IAAM,CAAE,GAAG,EAAK,OAAQ,CAGzB,IACA,EAAK,MAAQ,GAEjB,IAAM,EAAS,EAAK,OACpB,IAAK,IAAM,KAAO,EACd,OAAO,EAAO,GAElB,EAAO,KAAO,GAIlB,GAAI,EAAI,SAAW,QACf,IAAK,IAAM,KAAS,EAAI,KAAK,SAAS,CAAE,CACpC,IAAM,EAAO,EAAM,GACnB,GAAI,EAAK,MACL,MAAU,MAAM,qBACP,EAAK,OAAO,KAAK,IAAI,CAAC;;kFACwD,CAKnG,IAAK,IAAM,KAAS,EAAI,KAAK,SAAS,CAAE,CACpC,IAAM,EAAO,EAAM,GAEnB,GAAI,IAAW,EAAM,GAAI,CACrB,EAAa,EAAM,CACnB,SAGJ,GAAI,EAAI,SAAU,CACd,IAAM,EAAM,EAAI,SAAS,SAAS,IAAI,EAAM,GAAG,EAAE,GACjD,GAAI,IAAW,EAAM,IAAM,EAAK,CAC5B,EAAa,EAAM,CACnB,UAKR,GADW,EAAI,iBAAiB,IAAI,EAAM,GAAG,EAAE,GACvC,CACJ,EAAa,EAAM,CACnB,SAGJ,GAAI,EAAK,MAAO,CAEZ,EAAa,EAAM,CACnB,SAGJ,GAAI,EAAK,MAAQ,GACT,EAAI,SAAW,MAAO,CACtB,EAAa,EAAM,CAEnB,WAKhB,SAAgB,GAAS,EAAK,EAAQ,CAClC,IAAM,EAAO,EAAI,KAAK,IAAI,EAAO,CACjC,GAAI,CAAC,EACD,MAAU,MAAM,4CAA4C,CAEhE,IAAM,EAAc,GAAc,CAC9B,IAAM,EAAO,EAAI,KAAK,IAAI,EAAU,CAEpC,GAAI,EAAK,MAAQ,KACb,OACJ,IAAM,EAAS,EAAK,KAAO,EAAK,OAC1B,EAAU,CAAE,GAAG,EAAQ,CACvB,EAAM,EAAK,IAEjB,GADA,EAAK,IAAM,KACP,EAAK,CACL,EAAW,EAAI,CACf,IAAM,EAAU,EAAI,KAAK,IAAI,EAAI,CAC3B,EAAY,EAAQ,OAc1B,GAZI,EAAU,OAAS,EAAI,SAAW,YAAc,EAAI,SAAW,YAAc,EAAI,SAAW,gBAE5F,EAAO,MAAQ,EAAO,OAAS,EAAE,CACjC,EAAO,MAAM,KAAK,EAAU,EAG5B,OAAO,OAAO,EAAQ,EAAU,CAGpC,OAAO,OAAO,EAAQ,EAAQ,CACV,EAAU,KAAK,SAAW,EAG1C,IAAK,IAAM,KAAO,EACV,IAAQ,QAAU,IAAQ,SAExB,KAAO,GACT,OAAO,EAAO,GAK1B,GAAI,EAAU,MAAQ,EAAQ,IAC1B,IAAK,IAAM,KAAO,EACV,IAAQ,QAAU,IAAQ,SAE1B,KAAO,EAAQ,KAAO,KAAK,UAAU,EAAO,GAAK,GAAK,KAAK,UAAU,EAAQ,IAAI,GAAK,EACtF,OAAO,EAAO,GAQ9B,IAAM,EAAS,EAAU,KAAK,OAC9B,GAAI,GAAU,IAAW,EAAK,CAE1B,EAAW,EAAO,CAClB,IAAM,EAAa,EAAI,KAAK,IAAI,EAAO,CACvC,GAAI,GAAY,OAAO,OACnB,EAAO,KAAO,EAAW,OAAO,KAE5B,EAAW,KACX,IAAK,IAAM,KAAO,EACV,IAAQ,QAAU,IAAQ,SAE1B,KAAO,EAAW,KAAO,KAAK,UAAU,EAAO,GAAK,GAAK,KAAK,UAAU,EAAW,IAAI,GAAK,EAC5F,OAAO,EAAO,GAOlC,EAAI,SAAS,CACE,YACX,WAAY,EACZ,KAAM,EAAK,MAAQ,EAAE,CACxB,CAAC,EAEN,IAAK,IAAM,IAAS,CAAC,GAAG,EAAI,KAAK,SAAS,CAAC,CAAC,SAAS,CACjD,EAAW,EAAM,GAAG,CAExB,IAAM,EAAS,EAAE,CAgBjB,GAfI,EAAI,SAAW,gBACf,EAAO,QAAU,+CAEZ,EAAI,SAAW,WACpB,EAAO,QAAU,0CAEZ,EAAI,SAAW,WACpB,EAAO,QAAU,0CAEZ,EAAI,OAMT,EAAI,UAAU,IAAK,CACnB,IAAM,EAAK,EAAI,SAAS,SAAS,IAAI,EAAO,EAAE,GAC9C,GAAI,CAAC,EACD,MAAU,MAAM,qCAAqC,CACzD,EAAO,IAAM,EAAI,SAAS,IAAI,EAAG,CAErC,OAAO,OAAO,EAAQ,EAAK,KAAO,EAAK,OAAO,CAE9C,IAAM,EAAO,EAAI,UAAU,MAAQ,EAAE,CACrC,IAAK,IAAM,KAAS,EAAI,KAAK,SAAS,CAAE,CACpC,IAAM,EAAO,EAAM,GACf,EAAK,KAAO,EAAK,QACjB,EAAK,EAAK,OAAS,EAAK,KAI5B,EAAI,UAGA,OAAO,KAAK,EAAK,CAAC,OAAS,IACvB,EAAI,SAAW,gBACf,EAAO,MAAQ,EAGf,EAAO,YAAc,GAIjC,GAAI,CAIA,IAAM,EAAY,KAAK,MAAM,KAAK,UAAU,EAAO,CAAC,CAYpD,OAXA,OAAO,eAAe,EAAW,YAAa,CAC1C,MAAO,CACH,GAAG,EAAO,aACV,WAAY,CACR,MAAO,GAA+B,EAAQ,QAAS,EAAI,WAAW,CACtE,OAAQ,GAA+B,EAAQ,SAAU,EAAI,WAAW,CAC3E,CACJ,CACD,WAAY,GACZ,SAAU,GACb,CAAC,CACK,OAEE,CACT,MAAU,MAAM,mCAAmC,EAG3D,SAAS,EAAe,EAAS,EAAM,CACnC,IAAM,EAAM,GAAQ,CAAE,KAAM,IAAI,IAAO,CACvC,GAAI,EAAI,KAAK,IAAI,EAAQ,CACrB,MAAO,GACX,EAAI,KAAK,IAAI,EAAQ,CACrB,IAAM,EAAM,EAAQ,KAAK,IACzB,GAAI,EAAI,OAAS,YACb,MAAO,GACX,GAAI,EAAI,OAAS,QACb,OAAO,EAAe,EAAI,QAAS,EAAI,CAC3C,GAAI,EAAI,OAAS,MACb,OAAO,EAAe,EAAI,UAAW,EAAI,CAC7C,GAAI,EAAI,OAAS,OACb,OAAO,EAAe,EAAI,QAAQ,CAAE,EAAI,CAC5C,GAAI,EAAI,OAAS,WACb,EAAI,OAAS,YACb,EAAI,OAAS,eACb,EAAI,OAAS,YACb,EAAI,OAAS,YACb,EAAI,OAAS,WACb,EAAI,OAAS,WACb,OAAO,EAAe,EAAI,UAAW,EAAI,CAE7C,GAAI,EAAI,OAAS,eACb,OAAO,EAAe,EAAI,KAAM,EAAI,EAAI,EAAe,EAAI,MAAO,EAAI,CAE1E,GAAI,EAAI,OAAS,UAAY,EAAI,OAAS,MACtC,OAAO,EAAe,EAAI,QAAS,EAAI,EAAI,EAAe,EAAI,UAAW,EAAI,CAEjF,GAAI,EAAI,OAAS,OACb,OAAO,EAAe,EAAI,GAAI,EAAI,EAAI,EAAe,EAAI,IAAK,EAAI,CAEtE,GAAI,EAAI,OAAS,SAAU,CACvB,IAAK,IAAM,KAAO,EAAI,MAClB,GAAI,EAAe,EAAI,MAAM,GAAM,EAAI,CACnC,MAAO,GAEf,MAAO,GAEX,GAAI,EAAI,OAAS,QAAS,CACtB,IAAK,IAAM,KAAU,EAAI,QACrB,GAAI,EAAe,EAAQ,EAAI,CAC3B,MAAO,GAEf,MAAO,GAEX,GAAI,EAAI,OAAS,QAAS,CACtB,IAAK,IAAM,KAAQ,EAAI,MACnB,GAAI,EAAe,EAAM,EAAI,CACzB,MAAO,GAIf,MAFA,GAAI,EAAI,MAAQ,EAAe,EAAI,KAAM,EAAI,EAIjD,MAAO,GAMX,IAAa,IAA4B,EAAQ,EAAa,EAAE,GAAM,GAAW,CAC7E,IAAM,EAAM,GAAkB,CAAE,GAAG,EAAQ,aAAY,CAAC,CAGxD,OAFA,EAAQ,EAAQ,EAAI,CACpB,GAAY,EAAK,EAAO,CACjB,GAAS,EAAK,EAAO,EAEnB,IAAkC,EAAQ,EAAI,EAAa,EAAE,GAAM,GAAW,CACvF,GAAM,CAAE,iBAAgB,UAAW,GAAU,EAAE,CACzC,EAAM,GAAkB,CAAE,GAAI,GAAkB,EAAE,CAAG,SAAQ,KAAI,aAAY,CAAC,CAGpF,OAFA,EAAQ,EAAQ,EAAI,CACpB,GAAY,EAAK,EAAO,CACjB,GAAS,EAAK,EAAO,ECjb1B,GAAY,CACd,KAAM,OACN,IAAK,MACL,SAAU,YACV,YAAa,cACb,MAAO,GACV,CAEY,IAAmB,EAAQ,EAAK,EAAO,IAAY,CAC5D,IAAM,EAAO,EACb,EAAK,KAAO,SACZ,GAAM,CAAE,UAAS,UAAS,SAAQ,WAAU,mBAAoB,EAAO,KAClE,IAkBL,GAjBI,OAAO,GAAY,WACnB,EAAK,UAAY,GACjB,OAAO,GAAY,WACnB,EAAK,UAAY,GAEjB,IACA,EAAK,OAAS,GAAU,IAAW,EAC/B,EAAK,SAAW,IAChB,OAAO,EAAK,OAGZ,IAAW,QACX,OAAO,EAAK,QAGhB,IACA,EAAK,gBAAkB,GACvB,GAAY,EAAS,KAAO,EAAG,CAC/B,IAAM,EAAU,CAAC,GAAG,EAAS,CACzB,EAAQ,SAAW,EACnB,EAAK,QAAU,EAAQ,GAAG,OACrB,EAAQ,OAAS,IACtB,EAAK,MAAQ,CACT,GAAG,EAAQ,IAAK,IAAW,CACvB,GAAI,EAAI,SAAW,YAAc,EAAI,SAAW,YAAc,EAAI,SAAW,cACvE,CAAE,KAAM,SAAU,CAClB,EAAE,CACR,QAAS,EAAM,OAClB,EAAE,CACN,IAIA,IAAmB,EAAQ,EAAK,EAAO,IAAY,CAC5D,IAAM,EAAO,EACP,CAAE,UAAS,UAAS,SAAQ,aAAY,mBAAkB,oBAAqB,EAAO,KAAK,IAC7F,OAAO,GAAW,UAAY,EAAO,SAAS,MAAM,CACpD,EAAK,KAAO,UAEZ,EAAK,KAAO,SACZ,OAAO,GAAqB,WACxB,EAAI,SAAW,YAAc,EAAI,SAAW,eAC5C,EAAK,QAAU,EACf,EAAK,iBAAmB,IAGxB,EAAK,iBAAmB,GAG5B,OAAO,GAAY,WACnB,EAAK,QAAU,EACX,OAAO,GAAqB,UAAY,EAAI,SAAW,aACnD,GAAoB,EACpB,OAAO,EAAK,QAEZ,OAAO,EAAK,mBAGpB,OAAO,GAAqB,WACxB,EAAI,SAAW,YAAc,EAAI,SAAW,eAC5C,EAAK,QAAU,EACf,EAAK,iBAAmB,IAGxB,EAAK,iBAAmB,GAG5B,OAAO,GAAY,WACnB,EAAK,QAAU,EACX,OAAO,GAAqB,UAAY,EAAI,SAAW,aACnD,GAAoB,EACpB,OAAO,EAAK,QAEZ,OAAO,EAAK,mBAGpB,OAAO,GAAe,WACtB,EAAK,WAAa,IAEb,IAAoB,EAAS,EAAM,EAAM,IAAY,CAC9D,EAAK,KAAO,WAgCH,IAAkB,EAAS,EAAM,EAAM,IAAY,CAC5D,EAAK,IAAM,EAAE,EAaJ,IAAiB,EAAQ,EAAM,EAAM,IAAY,CAC1D,IAAM,EAAM,EAAO,KAAK,IAClB,EAAS,GAAc,EAAI,QAAQ,CAErC,EAAO,MAAO,GAAM,OAAO,GAAM,SAAS,GAC1C,EAAK,KAAO,UACZ,EAAO,MAAO,GAAM,OAAO,GAAM,SAAS,GAC1C,EAAK,KAAO,UAChB,EAAK,KAAO,GA6FH,IAAmB,EAAS,EAAK,EAAO,IAAY,CAC7D,GAAI,EAAI,kBAAoB,QACxB,MAAU,MAAM,oDAAoD,EAQ/D,IAAsB,EAAS,EAAK,EAAO,IAAY,CAChE,GAAI,EAAI,kBAAoB,QACxB,MAAU,MAAM,kDAAkD,EAc7D,IAAkB,EAAQ,EAAK,EAAO,IAAW,CAC1D,IAAM,EAAO,EACP,EAAM,EAAO,KAAK,IAClB,CAAE,UAAS,WAAY,EAAO,KAAK,IACrC,OAAO,GAAY,WACnB,EAAK,SAAW,GAChB,OAAO,GAAY,WACnB,EAAK,SAAW,GACpB,EAAK,KAAO,QACZ,EAAK,MAAQC,EAAQ,EAAI,QAAS,EAAK,CAAE,GAAG,EAAQ,KAAM,CAAC,GAAG,EAAO,KAAM,QAAQ,CAAE,CAAC,EAE7E,IAAmB,EAAQ,EAAK,EAAO,IAAW,CAC3D,IAAM,EAAO,EACP,EAAM,EAAO,KAAK,IACxB,EAAK,KAAO,SACZ,EAAK,WAAa,EAAE,CACpB,IAAM,EAAQ,EAAI,MAClB,IAAK,IAAM,KAAO,EACd,EAAK,WAAW,GAAOA,EAAQ,EAAM,GAAM,EAAK,CAC5C,GAAG,EACH,KAAM,CAAC,GAAG,EAAO,KAAM,aAAc,EAAI,CAC5C,CAAC,CAGN,IAAM,EAAU,IAAI,IAAI,OAAO,KAAK,EAAM,CAAC,CACrC,EAAe,IAAI,IAAI,CAAC,GAAG,EAAQ,CAAC,OAAQ,GAAQ,CACtD,IAAM,EAAI,EAAI,MAAM,GAAK,KAKrB,OAJA,EAAI,KAAO,QACJ,EAAE,QAAU,IAAA,GAGZ,EAAE,SAAW,IAAA,IAE1B,CAAC,CACC,EAAa,KAAO,IACpB,EAAK,SAAW,MAAM,KAAK,EAAa,EAGxC,EAAI,UAAU,KAAK,IAAI,OAAS,QAEhC,EAAK,qBAAuB,GAEtB,EAAI,SAKL,EAAI,WACT,EAAK,qBAAuBA,EAAQ,EAAI,SAAU,EAAK,CACnD,GAAG,EACH,KAAM,CAAC,GAAG,EAAO,KAAM,uBAAuB,CACjD,CAAC,EAPE,EAAI,KAAO,WACX,EAAK,qBAAuB,KAS3B,IAAkB,EAAQ,EAAK,EAAM,IAAW,CACzD,IAAM,EAAM,EAAO,KAAK,IAGlB,EAAc,EAAI,YAAc,GAChC,EAAU,EAAI,QAAQ,KAAK,EAAG,IAAMA,EAAQ,EAAG,EAAK,CACtD,GAAG,EACH,KAAM,CAAC,GAAG,EAAO,KAAM,EAAc,QAAU,QAAS,EAAE,CAC7D,CAAC,CAAC,CACC,EACA,EAAK,MAAQ,EAGb,EAAK,MAAQ,GAGR,IAAyB,EAAQ,EAAK,EAAM,IAAW,CAChE,IAAM,EAAM,EAAO,KAAK,IAClB,EAAIA,EAAQ,EAAI,KAAM,EAAK,CAC7B,GAAG,EACH,KAAM,CAAC,GAAG,EAAO,KAAM,QAAS,EAAE,CACrC,CAAC,CACI,EAAIA,EAAQ,EAAI,MAAO,EAAK,CAC9B,GAAG,EACH,KAAM,CAAC,GAAG,EAAO,KAAM,QAAS,EAAE,CACrC,CAAC,CACI,EAAwB,GAAQ,UAAW,GAAO,OAAO,KAAK,EAAI,CAAC,SAAW,EAKpF,EAAK,MAJS,CACV,GAAI,EAAqB,EAAE,CAAG,EAAE,MAAQ,CAAC,EAAE,CAC3C,GAAI,EAAqB,EAAE,CAAG,EAAE,MAAQ,CAAC,EAAE,CAC9C,EAkDQ,IAAmB,EAAQ,EAAK,EAAO,IAAW,CAC3D,IAAM,EAAO,EACP,EAAM,EAAO,KAAK,IACxB,EAAK,KAAO,SAIZ,IAAM,EAAU,EAAI,QAEd,EADS,EAAQ,KAAK,KACH,SACzB,GAAI,EAAI,OAAS,SAAW,GAAY,EAAS,KAAO,EAAG,CAEvD,IAAM,EAAcA,EAAQ,EAAI,UAAW,EAAK,CAC5C,GAAG,EACH,KAAM,CAAC,GAAG,EAAO,KAAM,oBAAqB,IAAI,CACnD,CAAC,CACF,EAAK,kBAAoB,EAAE,CAC3B,IAAK,IAAM,KAAW,EAClB,EAAK,kBAAkB,EAAQ,QAAU,QAKzC,EAAI,SAAW,YAAc,EAAI,SAAW,mBAC5C,EAAK,cAAgBA,EAAQ,EAAI,QAAS,EAAK,CAC3C,GAAG,EACH,KAAM,CAAC,GAAG,EAAO,KAAM,gBAAgB,CAC1C,CAAC,EAEN,EAAK,qBAAuBA,EAAQ,EAAI,UAAW,EAAK,CACpD,GAAG,EACH,KAAM,CAAC,GAAG,EAAO,KAAM,uBAAuB,CACjD,CAAC,CAGN,IAAM,EAAY,EAAQ,KAAK,OAC/B,GAAI,EAAW,CACX,IAAM,EAAiB,CAAC,GAAG,EAAU,CAAC,OAAQ,GAAM,OAAO,GAAM,UAAY,OAAO,GAAM,SAAS,CAC/F,EAAe,OAAS,IACxB,EAAK,SAAW,KAIf,IAAqB,EAAQ,EAAK,EAAM,IAAW,CAC5D,IAAM,EAAM,EAAO,KAAK,IAClB,EAAQA,EAAQ,EAAI,UAAW,EAAK,EAAO,CAC3C,EAAO,EAAI,KAAK,IAAI,EAAO,CAC7B,EAAI,SAAW,eACf,EAAK,IAAM,EAAI,UACf,EAAK,SAAW,IAGhB,EAAK,MAAQ,CAAC,EAAO,CAAE,KAAM,OAAQ,CAAC,EAGjC,IAAwB,EAAQ,EAAK,EAAO,IAAW,CAChE,IAAM,EAAM,EAAO,KAAK,IACxB,EAAQ,EAAI,UAAW,EAAK,EAAO,CACnC,IAAM,EAAO,EAAI,KAAK,IAAI,EAAO,CACjC,EAAK,IAAM,EAAI,WAEN,IAAoB,EAAQ,EAAK,EAAM,IAAW,CAC3D,IAAM,EAAM,EAAO,KAAK,IACxB,EAAQ,EAAI,UAAW,EAAK,EAAO,CACnC,IAAM,EAAO,EAAI,KAAK,IAAI,EAAO,CACjC,EAAK,IAAM,EAAI,UACf,EAAK,QAAU,KAAK,MAAM,KAAK,UAAU,EAAI,aAAa,CAAC,EAElD,IAAqB,EAAQ,EAAK,EAAM,IAAW,CAC5D,IAAM,EAAM,EAAO,KAAK,IACxB,EAAQ,EAAI,UAAW,EAAK,EAAO,CACnC,IAAM,EAAO,EAAI,KAAK,IAAI,EAAO,CACjC,EAAK,IAAM,EAAI,UACX,EAAI,KAAO,UACX,EAAK,UAAY,KAAK,MAAM,KAAK,UAAU,EAAI,aAAa,CAAC,GAExD,IAAkB,EAAQ,EAAK,EAAM,IAAW,CACzD,IAAM,EAAM,EAAO,KAAK,IACxB,EAAQ,EAAI,UAAW,EAAK,EAAO,CACnC,IAAM,EAAO,EAAI,KAAK,IAAI,EAAO,CACjC,EAAK,IAAM,EAAI,UACf,IAAI,EACJ,GAAI,CACA,EAAa,EAAI,WAAW,IAAA,GAAU,MAEpC,CACF,MAAU,MAAM,wDAAwD,CAE5E,EAAK,QAAU,GAEN,IAAiB,EAAQ,EAAK,EAAO,IAAW,CACzD,IAAM,EAAM,EAAO,KAAK,IAClB,EAAY,EAAI,KAAO,QAAW,EAAI,GAAG,KAAK,IAAI,OAAS,YAAc,EAAI,IAAM,EAAI,GAAM,EAAI,IACvG,EAAQ,EAAW,EAAK,EAAO,CAC/B,IAAM,EAAO,EAAI,KAAK,IAAI,EAAO,CACjC,EAAK,IAAM,GAEF,IAAqB,EAAQ,EAAK,EAAM,IAAW,CAC5D,IAAM,EAAM,EAAO,KAAK,IACxB,EAAQ,EAAI,UAAW,EAAK,EAAO,CACnC,IAAM,EAAO,EAAI,KAAK,IAAI,EAAO,CACjC,EAAK,IAAM,EAAI,UACf,EAAK,SAAW,IAQP,IAAqB,EAAQ,EAAK,EAAO,IAAW,CAC7D,IAAM,EAAM,EAAO,KAAK,IACxB,EAAQ,EAAI,UAAW,EAAK,EAAO,CACnC,IAAM,EAAO,EAAI,KAAK,IAAI,EAAO,CACjC,EAAK,IAAM,EAAI,WClgBN,GAA+B,EAAkB,kBAAmB,EAAM,IAAQ,CAC3F,GAAqB,KAAK,EAAM,EAAI,CACpC,EAAwB,KAAK,EAAM,EAAI,EACzC,CACF,SAAgB,GAAS,EAAQ,CAC7B,OAAOC,GAAkB,GAAgB,EAAO,CAEpD,IAAa,GAA2B,EAAkB,cAAe,EAAM,IAAQ,CACnF,GAAiB,KAAK,EAAM,EAAI,CAChC,EAAwB,KAAK,EAAM,EAAI,EACzC,CACF,SAAgB,GAAK,EAAQ,CACzB,OAAOC,GAAc,GAAY,EAAO,CAE5C,IAAa,GAA2B,EAAkB,cAAe,EAAM,IAAQ,CACnF,GAAiB,KAAK,EAAM,EAAI,CAChC,EAAwB,KAAK,EAAM,EAAI,EACzC,CACF,SAAgB,GAAK,EAAQ,CACzB,OAAOC,GAAc,GAAY,EAAO,CAE5C,IAAa,GAA+B,EAAkB,kBAAmB,EAAM,IAAQ,CAC3F,GAAqB,KAAK,EAAM,EAAI,CACpC,EAAwB,KAAK,EAAM,EAAI,EACzC,CACF,SAAgB,GAAS,EAAQ,CAC7B,OAAOC,GAAkB,GAAgB,EAAO,CCzBpD,IAAM,IAAe,EAAM,IAAW,CAClC,GAAU,KAAK,EAAM,EAAO,CAC5B,EAAK,KAAO,WACZ,OAAO,iBAAiB,EAAM,CAC1B,OAAQ,CACJ,MAAQ,GAAWC,GAAiB,EAAM,EAAO,CAEpD,CACD,QAAS,CACL,MAAQ,GAAWC,GAAkB,EAAM,EAAO,CAErD,CACD,SAAU,CACN,MAAQ,GAAU,CACd,EAAK,OAAO,KAAK,EAAM,CACvB,EAAK,QAAU,KAAK,UAAU,EAAK,OAAQC,GAA4B,EAAE,EAGhF,CACD,UAAW,CACP,MAAQ,GAAW,CACf,EAAK,OAAO,KAAK,GAAG,EAAO,CAC3B,EAAK,QAAU,KAAK,UAAU,EAAK,OAAQA,GAA4B,EAAE,EAGhF,CACD,QAAS,CACL,KAAM,CACF,OAAO,EAAK,OAAO,SAAW,GAGrC,CACJ,CAAC,EAOkBC,EAAkB,WAAY,GAAY,CAClE,IAAa,GAAeA,EAAkB,WAAY,GAAa,CACnE,OAAQ,MACX,CAAC,CC3CW,GAAwB,GAAY,GAAa,CACjD,GAA6B,GAAiB,GAAa,CAC3D,GAA4B,GAAgB,GAAa,CACzD,GAAiC,GAAqB,GAAa,CAEnE,GAAyB,GAAa,GAAa,CACnD,GAAyB,GAAa,GAAa,CACnD,GAA8B,GAAkB,GAAa,CAC7D,GAA8B,GAAkB,GAAa,CAC7D,GAA6B,GAAiB,GAAa,CAC3D,GAA6B,GAAiB,GAAa,CAC3D,GAAkC,GAAsB,GAAa,CACrE,GAAkC,GAAsB,GAAa,CCPrE,EAAwB,EAAkB,WAAY,EAAM,KACrE,EAAc,KAAK,EAAM,EAAI,CAC7B,OAAO,OAAO,EAAK,aAAc,CAC7B,WAAY,CACR,MAAO,GAA+B,EAAM,QAAQ,CACpD,OAAQ,GAA+B,EAAM,SAAS,CACzD,CACJ,CAAC,CACF,EAAK,aAAe,GAAyB,EAAM,EAAE,CAAC,CACtD,EAAK,IAAM,EACX,EAAK,KAAO,EAAI,KAChB,OAAO,eAAe,EAAM,OAAQ,CAAE,MAAO,EAAK,CAAC,CAEnD,EAAK,OAAS,GAAG,IACN,EAAK,MAAMC,GAAe,EAAK,CAClC,OAAQ,CACJ,GAAI,EAAI,QAAU,EAAE,CACpB,GAAG,EAAO,IAAK,GAAO,OAAO,GAAO,WAAa,CAAE,KAAM,CAAE,MAAO,EAAI,IAAK,CAAE,MAAO,SAAU,CAAE,SAAU,EAAE,CAAE,CAAE,CAAG,EAAG,CACzH,CACJ,CAAC,CAAE,CACA,OAAQ,GACX,CAAC,CAEN,EAAK,KAAO,EAAK,MACjB,EAAK,OAAS,EAAK,IAAWC,GAAW,EAAM,EAAK,EAAO,CAC3D,EAAK,UAAc,EACnB,EAAK,WAAa,EAAK,KACnB,EAAI,IAAI,EAAM,EAAK,CACZ,IAGX,EAAK,OAAS,EAAM,IAAWC,GAAY,EAAM,EAAM,EAAQ,CAAE,OAAQ,EAAK,MAAO,CAAC,CACtF,EAAK,WAAa,EAAM,IAAWC,GAAgB,EAAM,EAAM,EAAO,CACtE,EAAK,WAAa,MAAO,EAAM,IAAWC,GAAiB,EAAM,EAAM,EAAQ,CAAE,OAAQ,EAAK,WAAY,CAAC,CAC3G,EAAK,eAAiB,MAAO,EAAM,IAAWC,GAAqB,EAAM,EAAM,EAAO,CACtF,EAAK,IAAM,EAAK,eAEhB,EAAK,QAAU,EAAM,IAAWC,GAAa,EAAM,EAAM,EAAO,CAChE,EAAK,QAAU,EAAM,IAAWC,GAAa,EAAM,EAAM,EAAO,CAChE,EAAK,YAAc,MAAO,EAAM,IAAWC,GAAkB,EAAM,EAAM,EAAO,CAChF,EAAK,YAAc,MAAO,EAAM,IAAWC,GAAkB,EAAM,EAAM,EAAO,CAChF,EAAK,YAAc,EAAM,IAAWC,GAAiB,EAAM,EAAM,EAAO,CACxE,EAAK,YAAc,EAAM,IAAWC,GAAiB,EAAM,EAAM,EAAO,CACxE,EAAK,gBAAkB,MAAO,EAAM,IAAWC,GAAsB,EAAM,EAAM,EAAO,CACxF,EAAK,gBAAkB,MAAO,EAAM,IAAWC,GAAsB,EAAM,EAAM,EAAO,CAExF,EAAK,QAAU,EAAO,IAAW,EAAK,MAAM,GAAO,EAAO,EAAO,CAAC,CAClE,EAAK,YAAe,GAAe,EAAK,MAAM,GAAY,EAAW,CAAC,CACtE,EAAK,UAAa,GAAO,EAAK,MAAMC,GAAiB,EAAG,CAAC,CAEzD,EAAK,aAAiB,GAAS,EAAK,CACpC,EAAK,kBAAsB,GAAc,EAAK,CAC9C,EAAK,aAAiB,GAAS,EAAK,CACpC,EAAK,YAAgB,GAAS,GAAS,EAAK,CAAC,CAC7C,EAAK,YAAe,GAAW,GAAY,EAAM,EAAO,CACxD,EAAK,UAAc,EAAM,EAAK,CAC9B,EAAK,GAAM,GAAQ,GAAM,CAAC,EAAM,EAAI,CAAC,CACrC,EAAK,IAAO,GAAQ,GAAa,EAAM,EAAI,CAC3C,EAAK,UAAa,GAAO,GAAK,EAAM,GAAU,EAAG,CAAC,CAClD,EAAK,QAAW,GAAQ,GAAS,EAAM,EAAI,CAC3C,EAAK,SAAY,GAAQ,GAAS,EAAM,EAAI,CAE5C,EAAK,MAAS,GAAW,GAAO,EAAM,EAAO,CAC7C,EAAK,KAAQ,GAAW,GAAK,EAAM,EAAO,CAC1C,EAAK,aAAiB,GAAS,EAAK,CAEpC,EAAK,SAAY,GAAgB,CAC7B,IAAM,EAAK,EAAK,OAAO,CAEvB,OADA,GAAoB,IAAI,EAAI,CAAE,cAAa,CAAC,CACrC,GAEX,OAAO,eAAe,EAAM,cAAe,CACvC,KAAM,CACF,OAAA,GAA2B,IAAI,EAAK,EAAE,aAE1C,aAAc,GACjB,CAAC,CACF,EAAK,MAAQ,GAAG,IAAS,CACrB,GAAI,EAAK,SAAW,EAChB,OAAA,GAA2B,IAAI,EAAK,CAExC,IAAM,EAAK,EAAK,OAAO,CAEvB,OADA,GAAoB,IAAI,EAAI,EAAK,GAAG,CAC7B,GAGX,EAAK,eAAmB,EAAK,UAAU,IAAA,GAAU,CAAC,QAClD,EAAK,eAAmB,EAAK,UAAU,KAAK,CAAC,QAC7C,EAAK,MAAS,GAAO,EAAG,EAAK,CACtB,GACT,CAEW,GAA2B,EAAkB,cAAe,EAAM,IAAQ,CACnF,GAAgB,KAAK,EAAM,EAAI,CAC/B,EAAQ,KAAK,EAAM,EAAI,CACvB,EAAK,KAAK,mBAAqB,EAAK,EAAM,IAAWC,GAA2B,EAAM,EAAK,EAAM,EAAO,CACxG,IAAM,EAAM,EAAK,KAAK,IACtB,EAAK,OAAS,EAAI,QAAU,KAC5B,EAAK,UAAY,EAAI,SAAW,KAChC,EAAK,UAAY,EAAI,SAAW,KAEhC,EAAK,OAAS,GAAG,IAAS,EAAK,MAAMC,GAAa,GAAG,EAAK,CAAC,CAC3D,EAAK,UAAY,GAAG,IAAS,EAAK,MAAMC,GAAgB,GAAG,EAAK,CAAC,CACjE,EAAK,YAAc,GAAG,IAAS,EAAK,MAAMC,GAAkB,GAAG,EAAK,CAAC,CACrE,EAAK,UAAY,GAAG,IAAS,EAAK,MAAMC,GAAgB,GAAG,EAAK,CAAC,CACjE,EAAK,KAAO,GAAG,IAAS,EAAK,MAAMC,GAAiB,GAAG,EAAK,CAAC,CAC7D,EAAK,KAAO,GAAG,IAAS,EAAK,MAAMC,GAAiB,GAAG,EAAK,CAAC,CAC7D,EAAK,QAAU,GAAG,IAAS,EAAK,MAAMC,GAAc,GAAG,EAAK,CAAC,CAC7D,EAAK,UAAY,GAAG,IAAS,EAAK,MAAMF,GAAiB,EAAG,GAAG,EAAK,CAAC,CACrE,EAAK,UAAa,GAAW,EAAK,MAAMG,GAAiB,EAAO,CAAC,CACjE,EAAK,UAAa,GAAW,EAAK,MAAMC,GAAiB,EAAO,CAAC,CAEjE,EAAK,SAAa,EAAK,MAAMC,IAAa,CAAC,CAC3C,EAAK,WAAa,GAAG,IAAS,EAAK,MAAMC,GAAiB,GAAG,EAAK,CAAC,CACnE,EAAK,gBAAoB,EAAK,MAAMC,IAAoB,CAAC,CACzD,EAAK,gBAAoB,EAAK,MAAMC,IAAoB,CAAC,CACzD,EAAK,YAAgB,EAAK,MAAMC,IAAgB,CAAC,EACnD,CACW,GAA0B,EAAkB,aAAc,EAAM,IAAQ,CACjF,GAAgB,KAAK,EAAM,EAAI,CAC/B,GAAW,KAAK,EAAM,EAAI,CAC1B,EAAK,MAAS,GAAW,EAAK,MAAMC,GAAY,GAAU,EAAO,CAAC,CAClE,EAAK,IAAO,GAAW,EAAK,MAAMC,GAAU,GAAQ,EAAO,CAAC,CAC5D,EAAK,IAAO,GAAW,EAAK,MAAMC,GAAU,GAAQ,EAAO,CAAC,CAC5D,EAAK,MAAS,GAAW,EAAK,MAAMC,GAAY,GAAU,EAAO,CAAC,CAClE,EAAK,KAAQ,GAAW,EAAK,MAAMC,GAAW,GAAS,EAAO,CAAC,CAC/D,EAAK,KAAQ,GAAW,EAAK,MAAMC,GAAW,GAAS,EAAO,CAAC,CAC/D,EAAK,OAAU,GAAW,EAAK,MAAMC,GAAa,GAAS,EAAO,CAAC,CACnE,EAAK,OAAU,GAAW,EAAK,MAAMC,GAAa,GAAS,EAAO,CAAC,CACnE,EAAK,OAAU,GAAW,EAAK,MAAMC,GAAa,GAAS,EAAO,CAAC,CACnE,EAAK,OAAU,GAAW,EAAK,MAAMC,GAAa,GAAW,EAAO,CAAC,CACrE,EAAK,KAAQ,GAAW,EAAK,MAAML,GAAW,GAAS,EAAO,CAAC,CAC/D,EAAK,KAAQ,GAAW,EAAK,MAAMM,GAAW,GAAS,EAAO,CAAC,CAC/D,EAAK,MAAS,GAAW,EAAK,MAAMC,GAAY,GAAU,EAAO,CAAC,CAClE,EAAK,KAAQ,GAAW,EAAK,MAAMC,GAAW,GAAS,EAAO,CAAC,CAC/D,EAAK,OAAU,GAAW,EAAK,MAAMC,GAAa,GAAW,EAAO,CAAC,CACrE,EAAK,UAAa,GAAW,EAAK,MAAMC,GAAgB,GAAc,EAAO,CAAC,CAC9E,EAAK,IAAO,GAAW,EAAK,MAAMC,GAAU,GAAQ,EAAO,CAAC,CAC5D,EAAK,MAAS,GAAW,EAAK,MAAMC,GAAY,GAAU,EAAO,CAAC,CAClE,EAAK,KAAQ,GAAW,EAAK,MAAMC,GAAW,GAAS,EAAO,CAAC,CAC/D,EAAK,KAAQ,GAAW,EAAK,MAAMC,GAAW,GAAS,EAAO,CAAC,CAC/D,EAAK,OAAU,GAAW,EAAK,MAAMC,GAAa,GAAW,EAAO,CAAC,CACrE,EAAK,OAAU,GAAW,EAAK,MAAMC,GAAa,GAAW,EAAO,CAAC,CACrE,EAAK,KAAQ,GAAW,EAAK,MAAMC,GAAW,GAAS,EAAO,CAAC,CAE/D,EAAK,SAAY,GAAW,EAAK,MAAMC,GAAa,EAAO,CAAC,CAC5D,EAAK,KAAQ,GAAW,EAAK,MAAMC,GAAS,EAAO,CAAC,CACpD,EAAK,KAAQ,GAAW,EAAK,MAAMC,GAAS,EAAO,CAAC,CACpD,EAAK,SAAY,GAAW,EAAK,MAAMC,GAAa,EAAO,CAAC,EAC9D,CACF,SAAgB,EAAO,EAAQ,CAC3B,OAAOC,GAAa,GAAW,EAAO,CAE1C,IAAa,EAAgC,EAAkB,mBAAoB,EAAM,IAAQ,CAC7F,EAAsB,KAAK,EAAM,EAAI,CACrC,GAAW,KAAK,EAAM,EAAI,EAC5B,CACW,GAAyB,EAAkB,YAAa,EAAM,IAAQ,CAE/E,GAAe,KAAK,EAAM,EAAI,CAC9B,EAAgB,KAAK,EAAM,EAAI,EACjC,CAIW,GAAwB,EAAkB,WAAY,EAAM,IAAQ,CAE7E,GAAc,KAAK,EAAM,EAAI,CAC7B,EAAgB,KAAK,EAAM,EAAI,EACjC,CAIW,GAAwB,EAAkB,WAAY,EAAM,IAAQ,CAE7E,GAAc,KAAK,EAAM,EAAI,CAC7B,EAAgB,KAAK,EAAM,EAAI,EACjC,CAeW,GAAuB,EAAkB,UAAW,EAAM,IAAQ,CAE3E,GAAa,KAAK,EAAM,EAAI,CAC5B,EAAgB,KAAK,EAAM,EAAI,EACjC,CAWW,GAAyB,EAAkB,YAAa,EAAM,IAAQ,CAE/E,GAAe,KAAK,EAAM,EAAI,CAC9B,EAAgB,KAAK,EAAM,EAAI,EACjC,CAIW,GAA0B,EAAkB,aAAc,EAAM,IAAQ,CAEjF,GAAgB,KAAK,EAAM,EAAI,CAC/B,EAAgB,KAAK,EAAM,EAAI,EACjC,CAIW,GAAwB,EAAkB,WAAY,EAAM,IAAQ,CAE7E,GAAc,KAAK,EAAM,EAAI,CAC7B,EAAgB,KAAK,EAAM,EAAI,EACjC,CAIW,GAAyB,EAAkB,YAAa,EAAM,IAAQ,CAE/E,GAAe,KAAK,EAAM,EAAI,CAC9B,EAAgB,KAAK,EAAM,EAAI,EACjC,CAIW,GAAwB,EAAkB,WAAY,EAAM,IAAQ,CAE7E,GAAc,KAAK,EAAM,EAAI,CAC7B,EAAgB,KAAK,EAAM,EAAI,EACjC,CAIW,GAAuB,EAAkB,UAAW,EAAM,IAAQ,CAE3E,GAAa,KAAK,EAAM,EAAI,CAC5B,EAAgB,KAAK,EAAM,EAAI,EACjC,CAIW,GAAyB,EAAkB,YAAa,EAAM,IAAQ,CAE/E,GAAe,KAAK,EAAM,EAAI,CAC9B,EAAgB,KAAK,EAAM,EAAI,EACjC,CAIW,GAAwB,EAAkB,WAAY,EAAM,IAAQ,CAE7E,GAAc,KAAK,EAAM,EAAI,CAC7B,EAAgB,KAAK,EAAM,EAAI,EACjC,CAYW,GAAwB,EAAkB,WAAY,EAAM,IAAQ,CAE7E,GAAc,KAAK,EAAM,EAAI,CAC7B,EAAgB,KAAK,EAAM,EAAI,EACjC,CAIW,GAA0B,EAAkB,aAAc,EAAM,IAAQ,CACjF,GAAgB,KAAK,EAAM,EAAI,CAC/B,EAAgB,KAAK,EAAM,EAAI,EACjC,CAIW,GAA0B,EAAkB,aAAc,EAAM,IAAQ,CACjF,GAAgB,KAAK,EAAM,EAAI,CAC/B,EAAgB,KAAK,EAAM,EAAI,EACjC,CAIW,GAA0B,EAAkB,aAAc,EAAM,IAAQ,CAEjF,GAAgB,KAAK,EAAM,EAAI,CAC/B,EAAgB,KAAK,EAAM,EAAI,EACjC,CAIW,GAA6B,EAAkB,gBAAiB,EAAM,IAAQ,CAEvF,GAAmB,KAAK,EAAM,EAAI,CAClC,EAAgB,KAAK,EAAM,EAAI,EACjC,CAIW,GAAwB,EAAkB,WAAY,EAAM,IAAQ,CAE7E,GAAc,KAAK,EAAM,EAAI,CAC7B,EAAgB,KAAK,EAAM,EAAI,EACjC,CAIW,GAAuB,EAAkB,UAAW,EAAM,IAAQ,CAE3E,GAAa,KAAK,EAAM,EAAI,CAC5B,EAAgB,KAAK,EAAM,EAAI,EACjC,CA0BW,GAA0B,EAAkB,aAAc,EAAM,IAAQ,CACjF,GAAgB,KAAK,EAAM,EAAI,CAC/B,EAAQ,KAAK,EAAM,EAAI,CACvB,EAAK,KAAK,mBAAqB,EAAK,EAAM,IAAWC,GAA2B,EAAM,EAAK,EAAM,EAAO,CACxG,EAAK,IAAM,EAAO,IAAW,EAAK,MAAMC,GAAU,EAAO,EAAO,CAAC,CACjE,EAAK,KAAO,EAAO,IAAW,EAAK,MAAMC,GAAW,EAAO,EAAO,CAAC,CACnE,EAAK,KAAO,EAAO,IAAW,EAAK,MAAMA,GAAW,EAAO,EAAO,CAAC,CACnE,EAAK,IAAM,EAAO,IAAW,EAAK,MAAMC,GAAU,EAAO,EAAO,CAAC,CACjE,EAAK,KAAO,EAAO,IAAW,EAAK,MAAMC,GAAW,EAAO,EAAO,CAAC,CACnE,EAAK,KAAO,EAAO,IAAW,EAAK,MAAMA,GAAW,EAAO,EAAO,CAAC,CACnE,EAAK,IAAO,GAAW,EAAK,MAAM,GAAI,EAAO,CAAC,CAC9C,EAAK,KAAQ,GAAW,EAAK,MAAM,GAAI,EAAO,CAAC,CAC/C,EAAK,SAAY,GAAW,EAAK,MAAMH,GAAU,EAAG,EAAO,CAAC,CAC5D,EAAK,YAAe,GAAW,EAAK,MAAMC,GAAW,EAAG,EAAO,CAAC,CAChE,EAAK,SAAY,GAAW,EAAK,MAAMC,GAAU,EAAG,EAAO,CAAC,CAC5D,EAAK,YAAe,GAAW,EAAK,MAAMC,GAAW,EAAG,EAAO,CAAC,CAChE,EAAK,YAAc,EAAO,IAAW,EAAK,MAAMC,GAAkB,EAAO,EAAO,CAAC,CACjF,EAAK,MAAQ,EAAO,IAAW,EAAK,MAAMA,GAAkB,EAAO,EAAO,CAAC,CAE3E,EAAK,WAAe,EACpB,IAAM,EAAM,EAAK,KAAK,IACtB,EAAK,SACD,KAAK,IAAI,EAAI,SAAW,KAA0B,EAAI,kBAAoB,KAAyB,EAAI,KAC3G,EAAK,SACD,KAAK,IAAI,EAAI,SAAW,IAA0B,EAAI,kBAAoB,IAAyB,EAAI,KAC3G,EAAK,OAAS,EAAI,QAAU,IAAI,SAAS,MAAM,EAAI,OAAO,cAAc,EAAI,YAAc,GAAI,CAC9F,EAAK,SAAW,GAChB,EAAK,OAAS,EAAI,QAAU,MAC9B,CACF,SAAgB,GAAO,EAAQ,CAC3B,OAAOC,GAAa,GAAW,EAAO,CAE1C,IAAa,GAAgC,EAAkB,mBAAoB,EAAM,IAAQ,CAC7F,GAAsB,KAAK,EAAM,EAAI,CACrC,GAAU,KAAK,EAAM,EAAI,EAC3B,CACF,SAAgB,GAAI,EAAQ,CACxB,OAAOC,GAAU,GAAiB,EAAO,CAc7C,IAAa,GAA2B,EAAkB,cAAe,EAAM,IAAQ,CACnF,GAAiB,KAAK,EAAM,EAAI,CAChC,EAAQ,KAAK,EAAM,EAAI,CACvB,EAAK,KAAK,mBAAqB,EAAK,EAAM,IAAWC,GAA4B,EAAM,EAAK,EAAM,EAAO,EAC3G,CACF,SAAgB,GAAQ,EAAQ,CAC5B,OAAOC,GAAc,GAAY,EAAO,CAyE5C,IAAa,GAA2B,EAAkB,cAAe,EAAM,IAAQ,CACnF,GAAiB,KAAK,EAAM,EAAI,CAChC,EAAQ,KAAK,EAAM,EAAI,CACvB,EAAK,KAAK,mBAAqB,EAAK,EAAM,IAAWC,QACvD,CACF,SAAgB,GAAU,CACtB,OAAOC,GAAc,GAAW,CAEpC,IAAa,GAAyB,EAAkB,YAAa,EAAM,IAAQ,CAC/E,GAAe,KAAK,EAAM,EAAI,CAC9B,EAAQ,KAAK,EAAM,EAAI,CACvB,EAAK,KAAK,mBAAqB,EAAK,EAAM,IAAWC,GAA0B,EAAM,EAAK,EAAM,EAAO,EACzG,CACF,SAAgB,GAAM,EAAQ,CAC1B,OAAOC,GAAY,GAAU,EAAO,CAwBxC,IAAa,GAAyB,EAAkB,YAAa,EAAM,IAAQ,CAC/E,GAAe,KAAK,EAAM,EAAI,CAC9B,EAAQ,KAAK,EAAM,EAAI,CACvB,EAAK,KAAK,mBAAqB,EAAK,EAAM,IAAWC,GAA0B,EAAM,EAAK,EAAM,EAAO,CACvG,EAAK,QAAU,EAAI,QACnB,EAAK,KAAO,EAAW,IAAW,EAAK,MAAMnD,GAAiB,EAAW,EAAO,CAAC,CACjF,EAAK,SAAY,GAAW,EAAK,MAAMA,GAAiB,EAAG,EAAO,CAAC,CACnE,EAAK,KAAO,EAAW,IAAW,EAAK,MAAMC,GAAiB,EAAW,EAAO,CAAC,CACjF,EAAK,QAAU,EAAK,IAAW,EAAK,MAAMC,GAAc,EAAK,EAAO,CAAC,CACrE,EAAK,WAAe,EAAK,SAC3B,CACF,SAAgB,EAAM,EAAS,EAAQ,CACnC,OAAOkD,GAAY,GAAU,EAAS,EAAO,CAOjD,IAAa,GAA0B,EAAkB,aAAc,EAAM,IAAQ,CACjF,GAAmB,KAAK,EAAM,EAAI,CAClC,EAAQ,KAAK,EAAM,EAAI,CACvB,EAAK,KAAK,mBAAqB,EAAK,EAAM,IAAWC,GAA2B,EAAM,EAAK,EAAM,EAAO,CACxG,EAAgB,EAAM,YACX,EAAI,MACb,CACF,EAAK,UAAc,GAAM,OAAO,KAAK,EAAK,KAAK,IAAI,MAAM,CAAC,CAC1D,EAAK,SAAY,GAAa,EAAK,MAAM,CAAE,GAAG,EAAK,KAAK,IAAe,WAAU,CAAC,CAClF,EAAK,gBAAoB,EAAK,MAAM,CAAE,GAAG,EAAK,KAAK,IAAK,SAAU,GAAS,CAAE,CAAC,CAC9E,EAAK,UAAc,EAAK,MAAM,CAAE,GAAG,EAAK,KAAK,IAAK,SAAU,GAAS,CAAE,CAAC,CACxE,EAAK,WAAe,EAAK,MAAM,CAAE,GAAG,EAAK,KAAK,IAAK,SAAU,IAAO,CAAE,CAAC,CACvE,EAAK,UAAc,EAAK,MAAM,CAAE,GAAG,EAAK,KAAK,IAAK,SAAU,IAAA,GAAW,CAAC,CACxE,EAAK,OAAU,GACJC,GAAY,EAAM,EAAS,CAEtC,EAAK,WAAc,GACRC,GAAgB,EAAM,EAAS,CAE1C,EAAK,MAAS,GAAUC,GAAW,EAAM,EAAM,CAC/C,EAAK,KAAQ,GAASC,GAAU,EAAM,EAAK,CAC3C,EAAK,KAAQ,GAASC,GAAU,EAAM,EAAK,CAC3C,EAAK,SAAW,GAAG,IAASC,GAAa,GAAa,EAAM,EAAK,GAAG,CACpE,EAAK,UAAY,GAAG,IAASC,GAAc,GAAgB,EAAM,EAAK,GAAG,EAC3E,CACF,SAAgB,EAAO,EAAO,EAAQ,CAMlC,OAAO,IAAI,GALC,CACR,KAAM,SACN,MAAO,GAAS,EAAE,CAClB,GAAGC,EAAqB,EAAO,CAClC,CACwB,CAoB7B,IAAa,GAAyB,EAAkB,YAAa,EAAM,IAAQ,CAC/E,GAAe,KAAK,EAAM,EAAI,CAC9B,EAAQ,KAAK,EAAM,EAAI,CACvB,EAAK,KAAK,mBAAqB,EAAK,EAAM,IAAWC,GAA0B,EAAM,EAAK,EAAM,EAAO,CACvG,EAAK,QAAU,EAAI,SACrB,CACF,SAAgB,GAAM,EAAS,EAAQ,CACnC,OAAO,IAAI,GAAS,CAChB,KAAM,QACG,UACT,GAAGD,EAAqB,EAAO,CAClC,CAAC,CAgCN,IAAa,GAAgC,EAAkB,mBAAoB,EAAM,IAAQ,CAC7F,GAAsB,KAAK,EAAM,EAAI,CACrC,EAAQ,KAAK,EAAM,EAAI,CACvB,EAAK,KAAK,mBAAqB,EAAK,EAAM,IAAWE,GAAiC,EAAM,EAAK,EAAM,EAAO,EAChH,CACF,SAAgB,GAAa,EAAM,EAAO,CACtC,OAAO,IAAI,GAAgB,CACvB,KAAM,eACA,OACC,QACV,CAAC,CAsBN,IAAa,GAA0B,EAAkB,aAAc,EAAM,IAAQ,CACjF,GAAgB,KAAK,EAAM,EAAI,CAC/B,EAAQ,KAAK,EAAM,EAAI,CACvB,EAAK,KAAK,mBAAqB,EAAK,EAAM,IAAWC,GAA2B,EAAM,EAAK,EAAM,EAAO,CACxG,EAAK,QAAU,EAAI,QACnB,EAAK,UAAY,EAAI,WACvB,CACF,SAAgB,GAAO,EAAS,EAAW,EAAQ,CAC/C,OAAO,IAAI,GAAU,CACjB,KAAM,SACN,UACW,YACX,GAAGH,EAAqB,EAAO,CAClC,CAAC,CAyDN,IAAa,GAAwB,EAAkB,WAAY,EAAM,IAAQ,CAC7E,GAAc,KAAK,EAAM,EAAI,CAC7B,EAAQ,KAAK,EAAM,EAAI,CACvB,EAAK,KAAK,mBAAqB,EAAK,EAAM,IAAWI,GAAyB,EAAM,EAAK,EAAM,EAAO,CACtG,EAAK,KAAO,EAAI,QAChB,EAAK,QAAU,OAAO,OAAO,EAAI,QAAQ,CACzC,IAAM,EAAO,IAAI,IAAI,OAAO,KAAK,EAAI,QAAQ,CAAC,CAC9C,EAAK,SAAW,EAAQ,IAAW,CAC/B,IAAM,EAAa,EAAE,CACrB,IAAK,IAAM,KAAS,EAChB,GAAI,EAAK,IAAI,EAAM,CACf,EAAW,GAAS,EAAI,QAAQ,QAGhC,MAAU,MAAM,OAAO,EAAM,oBAAoB,CAEzD,OAAO,IAAI,GAAQ,CACf,GAAG,EACH,OAAQ,EAAE,CACV,GAAGJ,EAAqB,EAAO,CAC/B,QAAS,EACZ,CAAC,EAEN,EAAK,SAAW,EAAQ,IAAW,CAC/B,IAAM,EAAa,CAAE,GAAG,EAAI,QAAS,CACrC,IAAK,IAAM,KAAS,EAChB,GAAI,EAAK,IAAI,EAAM,CACf,OAAO,EAAW,QAGlB,MAAU,MAAM,OAAO,EAAM,oBAAoB,CAEzD,OAAO,IAAI,GAAQ,CACf,GAAG,EACH,OAAQ,EAAE,CACV,GAAGA,EAAqB,EAAO,CAC/B,QAAS,EACZ,CAAC,GAER,CACF,SAAS,GAAM,EAAQ,EAAQ,CAE3B,OAAO,IAAI,GAAQ,CACf,KAAM,OACN,QAHY,MAAM,QAAQ,EAAO,CAAG,OAAO,YAAY,EAAO,IAAK,GAAM,CAAC,EAAG,EAAE,CAAC,CAAC,CAAG,EAIpF,GAAGA,EAAqB,EAAO,CAClC,CAAC,CAiDN,IAAa,GAA6B,EAAkB,gBAAiB,EAAM,IAAQ,CACvF,GAAmB,KAAK,EAAM,EAAI,CAClC,EAAQ,KAAK,EAAM,EAAI,CACvB,EAAK,KAAK,mBAAqB,EAAK,EAAM,IAAWK,GAA8B,EAAM,EAAK,EAAM,EAAO,CAC3G,EAAK,KAAK,OAAS,EAAS,IAAS,CACjC,GAAI,EAAK,YAAc,WACnB,MAAM,IAAIC,GAAqB,EAAK,YAAY,KAAK,CAEzD,EAAQ,SAAY,GAAU,CAC1B,GAAI,OAAOC,GAAU,SACjB,EAAQ,OAAO,KAAKC,GAAWD,EAAO,EAAQ,MAAO,EAAI,CAAC,KAEzD,CAED,IAAM,EAASA,EACX,EAAO,QACP,EAAO,SAAW,IACtB,AAAgB,EAAO,OAAO,SAC9B,AAAiB,EAAO,QAAQ,EAAQ,MACxC,AAAgB,EAAO,OAAO,EAE9B,EAAQ,OAAO,KAAKC,GAAW,EAAO,CAAC,GAG/C,IAAM,EAAS,EAAI,UAAU,EAAQ,MAAO,EAAQ,CAQpD,OAPI,aAAkB,QACX,EAAO,KAAM,IAChB,EAAQ,MAAQ,EACT,GACT,EAEN,EAAQ,MAAQ,EACT,KAEb,CACF,SAAgB,GAAU,EAAI,CAC1B,OAAO,IAAI,GAAa,CACpB,KAAM,YACN,UAAW,EACd,CAAC,CAEN,IAAa,GAA4B,EAAkB,eAAgB,EAAM,IAAQ,CACrF,GAAkB,KAAK,EAAM,EAAI,CACjC,EAAQ,KAAK,EAAM,EAAI,CACvB,EAAK,KAAK,mBAAqB,EAAK,EAAM,IAAWC,GAA6B,EAAM,EAAK,EAAM,EAAO,CAC1G,EAAK,WAAe,EAAK,KAAK,IAAI,WACpC,CACF,SAAgB,GAAS,EAAW,CAChC,OAAO,IAAI,GAAY,CACnB,KAAM,WACK,YACd,CAAC,CAEN,IAAa,GAAiC,EAAkB,oBAAqB,EAAM,IAAQ,CAC/F,GAAuB,KAAK,EAAM,EAAI,CACtC,EAAQ,KAAK,EAAM,EAAI,CACvB,EAAK,KAAK,mBAAqB,EAAK,EAAM,IAAWA,GAA6B,EAAM,EAAK,EAAM,EAAO,CAC1G,EAAK,WAAe,EAAK,KAAK,IAAI,WACpC,CACF,SAAgB,GAAc,EAAW,CACrC,OAAO,IAAI,GAAiB,CACxB,KAAM,WACK,YACd,CAAC,CAEN,IAAa,GAA4B,EAAkB,eAAgB,EAAM,IAAQ,CACrF,GAAkB,KAAK,EAAM,EAAI,CACjC,EAAQ,KAAK,EAAM,EAAI,CACvB,EAAK,KAAK,mBAAqB,EAAK,EAAM,IAAWC,GAA6B,EAAM,EAAK,EAAM,EAAO,CAC1G,EAAK,WAAe,EAAK,KAAK,IAAI,WACpC,CACF,SAAgB,GAAS,EAAW,CAChC,OAAO,IAAI,GAAY,CACnB,KAAM,WACK,YACd,CAAC,CAMN,IAAa,GAA2B,EAAkB,cAAe,EAAM,IAAQ,CACnF,GAAiB,KAAK,EAAM,EAAI,CAChC,EAAQ,KAAK,EAAM,EAAI,CACvB,EAAK,KAAK,mBAAqB,EAAK,EAAM,IAAWC,GAA4B,EAAM,EAAK,EAAM,EAAO,CACzG,EAAK,WAAe,EAAK,KAAK,IAAI,UAClC,EAAK,cAAgB,EAAK,QAC5B,CACF,SAAgB,GAAS,EAAW,EAAc,CAC9C,OAAO,IAAI,GAAW,CAClB,KAAM,UACK,YACX,IAAI,cAAe,CACf,OAAO,OAAO,GAAiB,WAAa,GAAc,CAAGC,GAAkB,EAAa,EAEnG,CAAC,CAEN,IAAa,GAA4B,EAAkB,eAAgB,EAAM,IAAQ,CACrF,GAAkB,KAAK,EAAM,EAAI,CACjC,EAAQ,KAAK,EAAM,EAAI,CACvB,EAAK,KAAK,mBAAqB,EAAK,EAAM,IAAWC,GAA6B,EAAM,EAAK,EAAM,EAAO,CAC1G,EAAK,WAAe,EAAK,KAAK,IAAI,WACpC,CACF,SAAgB,GAAS,EAAW,EAAc,CAC9C,OAAO,IAAI,GAAY,CACnB,KAAM,WACK,YACX,IAAI,cAAe,CACf,OAAO,OAAO,GAAiB,WAAa,GAAc,CAAGD,GAAkB,EAAa,EAEnG,CAAC,CAEN,IAAa,GAA+B,EAAkB,kBAAmB,EAAM,IAAQ,CAC3F,GAAqB,KAAK,EAAM,EAAI,CACpC,EAAQ,KAAK,EAAM,EAAI,CACvB,EAAK,KAAK,mBAAqB,EAAK,EAAM,IAAWE,GAAgC,EAAM,EAAK,EAAM,EAAO,CAC7G,EAAK,WAAe,EAAK,KAAK,IAAI,WACpC,CACF,SAAgB,GAAY,EAAW,EAAQ,CAC3C,OAAO,IAAI,GAAe,CACtB,KAAM,cACK,YACX,GAAGd,EAAqB,EAAO,CAClC,CAAC,CAcN,IAAa,GAAyB,EAAkB,YAAa,EAAM,IAAQ,CAC/E,GAAe,KAAK,EAAM,EAAI,CAC9B,EAAQ,KAAK,EAAM,EAAI,CACvB,EAAK,KAAK,mBAAqB,EAAK,EAAM,IAAWe,GAA0B,EAAM,EAAK,EAAM,EAAO,CACvG,EAAK,WAAe,EAAK,KAAK,IAAI,UAClC,EAAK,YAAc,EAAK,QAC1B,CACF,SAAS,GAAO,EAAW,EAAY,CACnC,OAAO,IAAI,GAAS,CAChB,KAAM,QACK,YACX,WAAa,OAAO,GAAe,WAAa,MAAmB,EACtE,CAAC,CAWN,IAAa,GAAwB,EAAkB,WAAY,EAAM,IAAQ,CAC7E,GAAc,KAAK,EAAM,EAAI,CAC7B,EAAQ,KAAK,EAAM,EAAI,CACvB,EAAK,KAAK,mBAAqB,EAAK,EAAM,IAAWC,GAAyB,EAAM,EAAK,EAAM,EAAO,CACtG,EAAK,GAAK,EAAI,GACd,EAAK,IAAM,EAAI,KACjB,CACF,SAAgB,GAAK,EAAK,EAAK,CAC3B,OAAO,IAAI,GAAQ,CACf,KAAM,OACN,GAAI,EACC,MAER,CAAC,CAeN,IAAa,GAA4B,EAAkB,eAAgB,EAAM,IAAQ,CACrF,GAAkB,KAAK,EAAM,EAAI,CACjC,EAAQ,KAAK,EAAM,EAAI,CACvB,EAAK,KAAK,mBAAqB,EAAK,EAAM,IAAWC,GAA6B,EAAM,EAAK,EAAM,EAAO,CAC1G,EAAK,WAAe,EAAK,KAAK,IAAI,WACpC,CACF,SAAgB,GAAS,EAAW,CAChC,OAAO,IAAI,GAAY,CACnB,KAAM,WACK,YACd,CAAC,CAmDN,IAAa,GAA0B,EAAkB,aAAc,EAAM,IAAQ,CACjF,GAAgB,KAAK,EAAM,EAAI,CAC/B,EAAQ,KAAK,EAAM,EAAI,CACvB,EAAK,KAAK,mBAAqB,EAAK,EAAM,IAAWC,GAA2B,EAAM,EAAK,EAAM,EAAO,EAC1G,CAaF,SAAgB,GAAO,EAAI,EAAU,EAAE,CAAE,CACrC,OAAOC,GAAa,GAAW,EAAI,EAAQ,CAG/C,SAAgB,GAAY,EAAI,CAC5B,OAAOC,GAAkB,EAAG,CC7jChC,IAAa,GAAsB,EAAS,CAC1C,KAAM,GAAO,CAAC,OAAQ,YAAY,CAAC,CACnC,QAAS,GAAU,CACnB,UAAW,IAAU,CAAC,UAAU,CACjC,CAAC,CAEW,GAAoB,EAAS,CACxC,IAAK,GAAU,CACf,KAAM,GAAU,CAChB,SAAU,GAAU,CAAC,KAAK,CAAC,UAAU,CACrC,MAAO,GAAU,CAAC,UAAU,CAC5B,cAAe,GAAU,CAAC,UAAU,CACpC,IAAK,GAAU,CAAC,KAAK,CAErB,SAAU,GAAU,CAAC,UAAU,CAChC,CAAC,CAEW,GAAsB,EAAS,CAC1C,QAAS,EACP,EAAS,CACP,MAAO,GAAU,CAEjB,OAAQ,EAAS,CACf,MAAO,GAAU,CACjB,KAAM,GAAU,CAChB,QAAS,GAAW,CAAC,UAAU,CAChC,CAAC,CACH,CAAC,CACH,CACF,CAAC,CAEW,GAAwB,EAAS,EAAE,CAAC,CAEpC,GAAgB,EAAS,CACpC,MAAO,GAAU,CAAC,UAAU,CAC7B,CAAC,CAEI,GAA0B,EAAS,CACvC,IAAK,GAAU,CACf,KAAM,GAAU,CAChB,MAAO,GAAU,CACjB,SAAU,GAAU,CAAC,UAAU,CAC/B,OAAQ,IAAU,CAAC,UAAU,CAC7B,YAAa,IAAU,CAAC,UAAU,CACnC,CAAC,CAEW,GAAwB,EAAS,CAC5C,YAAa,GACb,SAAU,EAAQ,GAAwB,CAC1C,WAAY,EACV,EAAS,CACP,MAAO,GAAU,CACjB,OAAQ,EAAQ,GAAU,CAAC,CAC5B,CAAC,CACH,CACD,WAAY,EAAQ,GAAU,CAAC,CAC/B,aAAc,EAAQ,GAAU,CAAC,CAAC,UAAU,CAC5C,gBAAiB,GAAU,CAAC,UAAU,CACtC,WAAY,GAER,GAAU,CACV,EAAS,CACP,SAAU,EAAQ,GAAU,CAAC,CAAC,UAAU,CACxC,SAAU,EAAQ,GAAU,CAAC,CAAC,UAAU,CACzC,CAAC,CACH,CACA,UAAU,CACb,eAAgB,GAEZ,GAAU,CACV,EAAS,CACP,MAAO,GAAU,CACjB,KAAM,GAAU,CAChB,QAAS,GAAW,CAAC,UAAU,CAChC,CAAC,CACH,CACA,UAAU,CACd,CAAC,CAEW,GAAuB,EAAS,CAC3C,MAAO,GAAU,CACjB,UAAW,GAAO,CAAC,WAAY,WAAY,UAAU,CAAC,CAAC,UAAU,CAClE,CAAC,CAkBW,GAAmB,EAAS,CACvC,YAAa,EAjBoB,EAAS,CAC1C,QAAS,GAAS,GAAU,CAAE,GAAW,CAAC,CAC1C,KAAM,GAAU,CAAC,UAAU,CAC3B,OAAQ,GAAU,CAAC,UAAU,CAC7B,OAAQ,EAAQ,GAAqB,CAAC,UAAU,CAChD,mBAAoB,IAAU,CAAC,UAAU,CACzC,gBAAiB,GAAU,CAAC,UAAU,CACtC,OAAQ,EACE,CACN,MAAO,GAAU,CACjB,KAAM,GAAU,CAChB,QAAS,GAAW,CAAC,UAAU,CAChC,CAAC,CACD,UAAU,CACd,CAAC,CAGyC,CAC1C,CAAC,CAEW,GAA4B,EAAS,CAChD,MAAO,GAAU,CAAC,UAAU,CAC5B,KAAM,GAAU,CAAC,UAAU,CAC3B,YAAa,GAAU,CAAC,UAAU,CAClC,OAAQ,EAAS,CAAE,MAAO,GAAU,CAAE,KAAM,GAAU,CAAE,QAAS,GAAW,CAAC,UAAU,CAAE,CAAC,CAC3F,CAAC,CAEW,GAAwB,EAAS,CAC5C,QAAS,EACP,EAAS,CACP,KAAM,GAAU,CAChB,MAAO,GAAU,CAAC,UAAU,CAC5B,YAAa,GAAU,CAAC,UAAU,CAClC,OAAQ,EAAS,CAAE,MAAO,GAAU,CAAE,KAAM,GAAU,CAAE,QAAS,GAAW,CAAC,UAAU,CAAE,CAAC,CAC3F,CAAC,CACH,CACF,CAAC,CAEW,GAA+B,EAAS,CACnD,QAAS,EACP,EAAS,CACP,UAAW,GAAU,CACrB,gBAAiB,GAAU,CAAC,UAAU,CACtC,aAAc,GAAU,CAAC,UAAU,CACnC,MAAO,GAAU,CAAC,UAAU,CAC5B,OAAQ,EAAS,CAAE,MAAO,GAAU,CAAE,KAAM,GAAU,CAAE,QAAS,GAAW,CAAC,UAAU,CAAE,CAAC,CAC3F,CAAC,CACH,CACF,CAAC,CAEI,GAAsB,EAAS,CACnC,MAAO,GAAU,CACjB,KAAM,GAAU,CAChB,QAAS,GAAW,CAAC,UAAU,CAChC,CAAC,CAEI,GAAuB,EAAS,CACpC,KAAM,GAAU,CAAC,UAAU,CAC3B,MAAO,GAAU,CAAC,UAAU,CAC5B,aAAc,GAAU,CAAC,UAAU,CACnC,gBAAiB,GAAU,CAAC,UAAU,CACtC,KAAM,GAAU,CAAC,UAAU,CAC3B,UAAW,GAAU,CAAC,UAAU,CAChC,YAAa,GAAU,CAAC,UAAU,CAClC,eAAgB,GAAU,CAAC,UAAU,CACrC,aAAc,GAAU,CAAC,UAAU,CACnC,IAAK,GAAU,CAAC,UAAU,CAC1B,MAAO,GAAQ,CAAC,IAAU,CAAE,GAAU,CAAC,CAAC,CAAC,UAAU,CACnD,iBAAkB,GAAQ,CAAC,IAAU,CAAE,GAAU,CAAC,CAAC,CAAC,UAAU,CAC9D,MAAO,GAAU,CAAC,UAAU,CAC5B,SAAU,GAAU,CAAC,UAAU,CAC/B,UAAW,GAAU,CAAC,UAAU,CAChC,MAAO,GAAU,CAAC,UAAU,CAC5B,OAAQ,GAAU,CAAC,UAAU,CAC7B,UAAW,GAAU,CAAC,UAAU,CAChC,IAAK,GAAU,CAAC,UAAU,CAC1B,OAAQ,GAAU,CAAC,UAAU,CAC7B,YAAa,GAAU,CAAC,UAAU,CAClC,SAAU,IAAW,CAAC,UAAU,CAChC,QAAS,IAAW,CAAC,UAAU,CAChC,CAAC,CAEW,GAA4B,EAAS,CAChD,QAAS,EACC,CACN,IAAK,GAAU,CAAC,UAAU,CAC1B,KAAM,GAAU,CAAC,UAAU,CAC3B,OAAQ,EAAQ,GAAU,CAAC,CAAC,UAAU,CACtC,SAAU,GAAU,CAAC,UAAU,CAC/B,OAAQ,IAAU,CAAC,UAAU,CAC7B,YAAa,IAAU,CAAC,UAAU,CAClC,MAAO,GAAU,CAAC,UAAU,CAC5B,cAAe,GAAU,CAAC,UAAU,CACpC,eAAgB,GAAU,CAAC,UAAU,CACrC,gBAAiB,GAAU,CAAC,UAAU,CACtC,eAAgB,GAAU,CAAC,UAAU,CACrC,gBAAiB,GAAU,CAAC,UAAU,CACtC,mBAAoB,GAAO,CAAC,gBAAiB,SAAS,CAAC,CAAC,UAAU,CAClE,qBAAsB,GAAO,CAAC,gBAAiB,SAAS,CAAC,CAAC,UAAU,CACpE,oBAAqB,IAAU,CAAC,UAAU,CAC1C,YAAa,IAAW,CAAC,UAAU,CACnC,QAAS,IAAW,CAAC,UAAU,CAC/B,WAAY,EAAQ,GAAU,CAAC,CAAC,UAAU,CAC1C,SAAU,EAAQ,GAAqB,CAAC,UAAU,CAClD,eAAgB,EAAQ,GAAqB,CAAC,UAAU,CACxD,gBAAiB,EAAQ,GAAqB,CAAC,UAAU,CACzD,gBAAiB,EAAQ,GAAqB,CAAC,UAAU,CACzD,iBAAkB,EAAQ,GAAqB,CAAC,UAAU,CAC1D,IAAK,GAAU,CAAC,UAAU,CAC1B,SAAU,GAAU,CAAC,UAAU,CAC/B,YAAa,GAAU,CAAC,UAAU,CAClC,iBAAkB,GAAU,CAAC,UAAU,CACvC,gBAAiB,GAAU,CAAC,UAAU,CACtC,WAAY,GAAS,GAAU,CAAE,GAAW,CAAC,CAAC,UAAU,CACxD,UAAW,GAAS,GAAU,CAAE,GAAW,CAAC,CAAC,UAAU,CACvD,SAAU,EAEN,EAAS,CACP,KAAM,GAAU,CAAC,UAAU,CAC3B,IAAK,GAAU,CAAC,UAAU,CAC1B,MAAO,GAAU,CAAC,UAAU,CAC5B,MAAO,GAAQ,CAAC,GAAU,CAAE,IAAU,CAAE,IAAW,CAAC,CAAC,CAAC,UAAU,CACjE,CAAC,CACH,CACA,UAAU,CACb,eAAgB,GACP,CAAC,GAAS,GAAU,CAAE,GAAU,CAAC,CAAE,EAAQ,EAAS,CAAE,IAAK,GAAU,CAAE,MAAO,GAAU,CAAE,CAAC,CAAC,CAAC,CAAC,CACpG,UAAU,CACd,CAAC,CACD,UAAU,CACb,OAAQ,GAAoB,UAAU,CACvC,CAAC,CAEW,GAAoB,EAAS,CACxC,UAAW,IAAW,CAAC,UAAU,CAClC,CAAC,CASW,GAAyB,EAAS,CAC7C,QAAS,EARc,EAAS,CAChC,aAAc,GAAU,CAAC,UAAU,CACnC,YAAa,GAAU,CAAC,UAAU,CAClC,cAAe,GAAQ,CAAC,GAAU,CAAE,IAAU,CAAC,CAAC,CAAC,UAAU,CAC3D,WAAY,GAAU,CAAC,UAAU,CAClC,CAAC,CAGkC,CAAC,UAAU,CAC9C,CAAC,CAEW,GAAoB,EAAS,CACxC,YAAa,GAAU,CAAC,UAAU,CAClC,KAAM,EAAQ,GAAU,CAAC,CAAC,UAAU,CACpC,KAAM,EAAQ,GAAU,CAAC,CAAC,UAAU,CACrC,CAAC,CAEW,GAA4B,EAAS,CAChD,OAAQ,EAEJ,EAAS,CACP,UAAW,GAAU,CACrB,SAAU,EAAQ,GAAS,GAAU,CAAE,GAAW,CAAC,CAAC,CAAC,UAAU,CAChE,CAAC,CACH,CACA,UAAU,CACb,WAAY,EAER,EAAS,CACP,MAAO,GAAU,CACjB,OAAQ,GAAoB,UAAU,CACvC,CAAC,CACH,CACA,UAAU,CACd,CAAC,CAEW,GAAsB,EAAS,CAC1C,QAAS,GAAU,CAAC,UAAU,CAC9B,mBAAoB,EAAQ,GAAU,CAAC,CAAC,UAAU,CAClD,eAAgB,GAAU,CAAC,UAAU,CACtC,CAAC,CAUW,GAAc,CACzB,WAAY,CACV,cAAe,CACb,OAAQ,GACR,YAAa,4DACd,CACD,YAAa,CACX,OAAQ,GACR,YAAa,qDACd,CACD,cAAe,CACb,OAAQ,GACR,YAAa,kDACd,CACD,gBAAiB,CACf,OAAQ,GACR,YAAa,6DACd,CACD,QAAS,CACP,OAAQ,GACR,YAAa,4CACd,CACD,gBAAiB,CACf,OAAQ,GACR,YAAa,oFACd,CACD,WAAY,CACV,OAAQ,GACR,YAAa,oGACd,CACD,oBAAqB,CACnB,OAAQ,GACR,YAAa,kEACd,CACD,gBAAiB,CACf,OAAQ,GACR,YAAa,wEACd,CACD,uBAAwB,CACtB,OAAQ,GACR,YAAa,yEACd,CACD,oBAAqB,CACnB,OAAQ,GACR,YAAa,+EACd,CACD,YAAa,CACX,OAAQ,GACR,YAAa,6EACd,CACD,iBAAkB,CAChB,OAAQ,GACR,YAAa,qEACd,CACD,YAAa,CACX,OAAQ,GACR,YAAa,sCACd,CACD,oBAAqB,CACnB,OAAQ,GACR,YAAa,0DACd,CACD,cAAe,CACb,OAAQ,GACR,YAAa,sEACd,CACF,CACF,CCnQD,SAAS,GAAe,EAAwB,CAI9C,OAHI,EAAM,OAAS,IAAY,GAGxB,4BAA4B,KAAK,EAAM,CAIhD,SAAS,GAAa,EAAwC,CAC5D,OAAO,OAAO,GAAU,YAAY,GAAkB,OAAQ,EAAkC,MAAS,SAmB3G,IAAa,GAAb,MAAa,UAAoB,EAA6B,4CAEvB,kBACC,sBAEI,oBACG,kBACR,kBACS,yBACrB,2BAEkC,gCAK3B,sCACkB,GAAwB,CACnE,KAAK,wBACN,KAAK,wCAAwC,EAAE,EACnD,EAAE,gBAAgB,kCAE0B,GAAwB,CAC/D,KAAK,wBACN,KAAK,wCAAwC,EAAE,EACnD,EAAE,gBAAgB,kBAEe,EAAE,wBAGT,yBACA,IAAI,0BAEU,wBAEH,yBAEd,4BAEyD,uBACzC,CAAE,KAAM,UAAW,4BAE5B,gCACY,EAAE,kCACO,kCACJ,6BAEpB,IAAI,2BACa,EAAE,wBACA,qBACV,8BACV,2BACA,qBACL,4BACO,4BAEA,4BAE4C,EAAE,oCACrB,gBAC7B,6BACiC,4BAEN,2BAEN,iCAEC,4BAEqB,EAAE,wBAE5B,wBAEnB,wBAKnB,EAAE,qBAEkD,wBAElB,+BACK,IAAI,gBAEV,6BAOjC,EAAE,0BAG2C,mBAEJ,wBAEb,IAAI,wBAEX,IAAI,yBAKnB,iCAEsB,yBAEL,yCA7GwB,iCA4FP,GAmB7C,MAAgB,OAAO,EAAyC,CAI9D,GAHA,KAAK,MAAQ,KAAK,aAAa,EAAO,CACtC,KAAK,eAAiB,IAAI,MAAM,CAAC,aAAa,CAE1C,EAAO,aAAc,CACvB,IAAM,EAAM,GAAiB,EAAO,aAAa,CACjD,EAAO,iBAAmB,EAAI,iBAC1B,EAAO,kBAAoB,IAAA,KAC7B,EAAO,gBAAkB,EAAO,aAAa,WAE/C,KAAK,uBAA2B,EAAI,MAAM,KAAK,SAAW,IAAA,GAAU,CAItE,KAAK,QAAU,KAAK,KAAK,aAAa,CAAE,KAAM,OAAQ,CAAC,CAGvD,IAAM,EAAQ,SAAS,cAAc,QAAQ,CAC7C,EAAM,YAAc,GACpB,KAAK,QAAQ,YAAY,EAAM,CAI/B,IAAM,EAAO,KAAK,KACZ,GAAY,EAAgB,IAAoC,CAChE,GAAS,GAAe,EAAM,EAAE,EAAK,MAAM,YAAY,EAAQ,EAAM,EAM3E,GAHA,EAAS,2BAA4B,EAAO,SAAS,CACrD,EAAS,mCAAoC,EAAO,iBAAiB,CAEjE,EAAO,UACJ,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,EAAO,MAAM,CACjD,EAAI,WAAW,KAAK,EAAI,OAAO,GAAU,UAC3C,EAAS,EAAK,EAAM,CAK1B,KAAK,8BAA8B,EAAO,gBAAgB,qBAAqB,CAG/E,IAAM,EAAS,SAAS,cAAc,MAAM,CAC5C,EAAO,UAAY,oBACnB,KAAK,QAAU,EACf,KAAK,QAAQ,YAAY,EAAO,CAEhC,IAAM,EAAU,EAAO,SAAW,WAMlC,GALI,IAAY,UACd,EAAO,UAAU,IAAI,uBAAuB,CAI1C,IAAY,SAAU,CACxB,IAAM,EAAa,SAAS,cAAc,MAAM,CAChD,EAAW,UAAY,wBACvB,EAAW,aAAa,cAAe,OAAO,CAC9C,EAAW,aAAa,OAAQ,SAAS,CACzC,EAAW,aAAa,WAAY,KAAK,CACzC,EAAW,aAAa,aAAc,KAAK,MAAM,eAAe,CAChE,EAAW,iBAAiB,QAAU,GAAM,CAC1C,EAAE,gBAAgB,CAClB,EAAE,iBAAiB,CACnB,KAAK,OAAO,EACZ,CACF,KAAK,YAAc,EACnB,EAAO,QAAQ,EAAW,CAI5B,GAAI,IAAY,WAAY,CAC1B,IAAM,EAAmE,CACvE,YAAe,KAAK,MAAM,CAC1B,UAAW,KAAK,MAAM,WACvB,CACG,EAAO,mBAAqB,IAAA,GACvB,EAAO,cAAgB,IAAA,KAAW,EAAa,UAAY,EAAO,aADhC,EAAa,SAAW,EAAO,iBAEtE,EAAO,qBAAuB,IAAA,KAAW,EAAa,WAAa,EAAO,oBAC1E,EAAO,mBAAqB,IAAA,KAAW,EAAa,iBAAmB,EAAO,kBAC9E,EAAO,kBAAoB,IAAA,KAAW,EAAa,QAAU,EAAO,iBACxE,KAAK,UAAY,GAAe,EAAa,CAC7C,EAAO,YAAY,KAAK,UAAU,UAAU,CAI1C,IAAY,WACd,EAAO,UAAU,IAAI,wBAAwB,CAI/C,IAAM,EAAkB,SAAS,cAAc,MAAM,CACrD,EAAO,YAAY,EAAgB,CAEnC,KAAK,QAAU,IAAI,GAAW,EAAiB,CAC7C,KAAM,KAAK,MACX,QAAS,EAAM,IAAe,KAAK,aAAa,EAAM,EAAW,CACjE,YAAe,KAAK,OAAO,CAC3B,aAAe,GAAS,KAAK,kBAAkB,EAAK,CACpD,kBAAqB,CACnB,KAAK,SAAS,kBAAkB,EAAO,UAAU,EAEnD,oBAAuB,CACrB,KAAK,wBAAwB,EAE/B,WAAa,GAAc,KAAK,gBAAgB,EAAU,CAC1D,gBAAmB,KAAK,oBAAoB,CAC5C,mBAAsB,KAAK,QAAQ,iBAAiB,CACpD,iBAAoB,CAElB,GAAI,KAAK,kBAAmB,CAC1B,KAAK,sBAAwB,GAC7B,KAAK,wBAA0B,EAAE,CACjC,OAEF,KAAK,mBAAqB,EAAE,CAC5B,KAAK,sBAAwB,GAC7B,KAAK,wBAA0B,EAAE,CACjC,KAAK,oBAAsB,MAE7B,YAAa,EAAO,YACpB,gBAAiB,EAAO,gBACxB,iBAAkB,EAAO,iBACzB,YAAa,EAAO,YACpB,cAAe,EAAO,cACtB,oBAAqB,OAAO,EAAO,kBAAqB,YAAc,EAAO,wBAA0B,GACvG,gBAAmB,CACb,EAAO,cACT,KAAK,uBAAuB,EAAO,cAAc,CAEjD,EAAO,eAAe,EAG1B,qBAAwB,CAEtB,GADA,IAAkB,CACd,OAAO,EAAO,kBAAqB,WAAY,CACjD,EAAO,kBAAkB,CACzB,OAEF,KAAK,qBAAqB,EAE5B,mBAAsB,KAAK,YAAc,OACzC,sBAAyB,KAAK,kBAC9B,aAAe,GAAU,CACnB,IAAU,QACZ,KAAK,OAAO,EAEZ,KAAK,WAAa,EAClB,KAAK,wBAAwB,GAGjC,iBAAmB,GAAa,KAAK,kBAAkB,EAAS,CAChE,YAAc,GAAQ,CACpB,KAAK,uBAAuB,EAAI,EAElC,aAAc,EAAO,aACrB,UAAW,EAAO,OACd,GAAG,EAAO,OAAO,MAAM,IAAI,CAAC,IAAM,KAAK,IAAI,EAAO,OAAO,MAAM,IAAI,CAAC,IAAM,EAAO,OAAO,MAAM,IAAI,CAAC,IAAM,MAAM,aAAa,GAC5H,IAAA,GACJ,aAAc,CACZ,uBAAyB,GAAW,CAClC,KAAK,cAAc,eAAiB,GAEtC,wBAA0B,GAAgB,CACxC,KAAK,cAAc,gBAAkB,GAEvC,yBAA4B,CACtB,KAAK,cAAc,iBAAmB,KAAK,uBAAuB,EACpE,KAAK,SAAS,+BAA+B,GAAK,EAGtD,8BAAiC,KAAK,cAAc,6BAA6B,CACjF,+BAAkC,KAAK,2BAA2B,CACnE,CACF,CAAC,CAGF,KAAK,qBAAuB,IAAI,GAAoB,CAClD,SAAW,GAAa,KAAK,QAAQ,gBAAgB,EAAS,CAC9D,uBAAwB,EAAO,eAAiB,MAAW,EAAO,wBAA0B,IAC7F,CAAC,CAGF,KAAK,OAAS,IAAI,GAAa,CAC7B,WAAc,KAAK,QACnB,WAAc,KAAK,QACnB,oBAAuB,KAAK,iBAC5B,WAAc,KAAK,QACnB,wBAA2B,KAAK,qBAChC,SAAY,KAAK,MACjB,iBAAmB,GAAQ,KAAK,kBAAkB,EAAI,CACvD,CAAC,CAGE,IAAY,UACd,KAAK,QAAQ,YAAY,CAAC,UAAU,IAAI,8BAA8B,CAIzC,KAAK,QAAQ,kBAAkB,EAAO,UAAU,CAA/E,IAGM,EAAY,EAAO,WAAa,OAClC,IAAc,YAChB,KAAK,QAAQ,kBAAkB,GAAK,CAC3B,IAAc,YACvB,KAAK,QAAQ,kBAAkB,CAOjC,IAAM,EAAmB,eAAe,QAAQ,6BAA6B,CACvE,EAAa,eAAe,QAAQ,sBAAsB,CAC1D,EAAa,CAAC,EAAE,GAAoB,GACtC,IACF,eAAe,WAAW,6BAA6B,CACvD,eAAe,WAAW,sBAAsB,EAIlD,GAAI,CACF,IAAM,EAAM,IAAI,GAChB,MAAM,EAAI,MAAM,CAChB,KAAK,SAAW,IAAI,GAAmB,EAAI,CAC3C,MAAM,KAAK,sBAAsB,EAAW,MACtC,CAEN,KAAK,SAAW,IAAI,GAAmB,KAAK,CAI9C,KAAK,oBAAoB,CAGrB,EAAO,qBAAuB,IAAA,KAChC,KAAK,WAAa,EAAO,oBAE3B,KAAK,kBAAoB,EAAO,kBAAoB,IAEpD,KAAK,oBAAoB,CACzB,IAAM,MAAiB,KAAK,oBAAoB,CAKhD,GAJA,OAAO,iBAAiB,SAAU,EAAU,CAAE,QAAS,GAAM,CAAC,CAC9D,KAAK,eAAiB,OAAO,oBAAoB,SAAU,EAAS,CAAC,CAGjE,OAAO,eAAgB,CACzB,IAAM,MAAyB,CAC7B,GAAI,CAAC,KAAK,gBAAkB,CAAC,KAAK,kBAAmB,OACrD,IAAM,EAAK,KAAK,SAAS,YAAY,CACrC,GAAI,CAAC,EAAI,OACT,IAAM,EAAS,OAAO,aAAe,OAAO,gBAAgB,QAAU,OAAO,aAC7E,EAAG,MAAM,YAAY,4BAA6B,GAAG,KAAK,IAAI,EAAG,EAAO,CAAC,IAAI,EAE/E,OAAO,eAAe,iBAAiB,SAAU,EAAiB,CAClE,KAAK,eAAiB,OAAO,gBAAgB,oBAAoB,SAAU,EAAiB,CAAC,CAI3F,IAAY,WACd,KAAK,eAAiB,GACtB,KAAK,UAAY,GACjB,KAAK,wBAAwB,CAC7B,KAAK,cAAc,SAAS,GAAK,CACjC,eAAiB,KAAK,iCAAiC,CAAE,GAAG,EAI9D,IAAM,EAAyC,CAC7C,UAAW,OACX,UAAY,GAAQ,KAAK,qBAAqB,EAAI,CACnD,CACG,EAAO,iBAAmB,IAAA,KAAW,EAAW,eAAiB,EAAO,gBAC5E,KAAK,QAAU,IAAI,GAAoB,EAAW,CAGlD,KAAK,SAAW,KAAK,OAAO,aAAa,IAGzC,KAAK,cAAgB,GACrB,IAAK,IAAM,KAAW,KAAK,gBACzB,KAAK,YAAY,EAAQ,OAAQ,EAAQ,QAAQ,CAEnD,KAAK,gBAAkB,EAAE,CAErB,KAAK,oBAAsB,IAAY,aACzC,MAAM,KAAK,oBAAoB,CAC/B,KAAK,mBAAqB,MAG5B,EAAS,qBAAsB,EAAE,CAAC,CAClC,GAAa,OAAO,CACpB,EAAO,WAAW,CAGpB,SAAmB,EAAqC,CAClD,EAAQ,MAAQ,IAAA,IAAa,EAAQ,MAAQ,KAAK,WACpD,KAAK,SAAW,EAAQ,IACxB,KAAK,kBAAkB,EAI3B,QAAyB,CACvB,KAAK,aAAa,CAClB,KAAK,KAAK,OAAO,CACjB,EAAS,oBAAqB,CAAE,MAAO,KAAK,WAAY,CAAC,CACzD,GAAa,OAAO,CACpB,KAAK,OAAO,UAAU,CAGtB,KAAK,sBAAsB,CAI3B,KAAK,gCAAgC,CAGjC,CAAC,KAAK,cAAgB,KAAK,OAAO,aAAa,MACjD,KAAK,aAAe,GACpB,KAAK,oBAAsB,GAC3B,KAAK,YACH,CACE,MAAO,GACP,KAAM,sBACN,QAAS,CACP,IAAK,KAAK,OAAO,YAAY,IAC7B,GAAI,KAAK,iCAAiC,UAAU,CAChD,CAAE,gBAAiB,KAAK,iCAAiC,UAAU,CAAE,CACrE,EAAE,CACN,GAAI,KAAK,kCAAkC,UAAU,CACjD,CAAE,iBAAkB,KAAK,kCAAkC,UAAU,CAAE,CACvE,EAAE,CACP,CACF,CACD,CAAE,OAAQ,GAAM,WAAY,GAAM,cAAe,GAAM,CACxD,EAIL,QAAyB,EAGlB,KAAK,OAAO,SAAW,cAAgB,aAC1C,KAAK,KAAK,MAAM,QAAU,IAE5B,KAAK,aAAa,CAClB,KAAK,KAAK,QAAQ,CAClB,EAAS,qBAAsB,EAAE,CAAC,CAClC,KAAK,OAAO,WAAW,CAGzB,WAA4B,CAC1B,KAAK,gCAAgC,CACrC,KAAK,yBAAyB,CAC9B,KAAK,mBAAmB,QAAQ,CAChC,KAAK,kBAAoB,KACzB,KAAK,kBAAkB,MAAM,CAC7B,KAAK,iBAAmB,KACpB,KAAK,0BAA4B,OACnC,qBAAqB,KAAK,wBAAwB,CAClD,KAAK,wBAA0B,MAEjC,KAAK,SAAS,SAAS,CACvB,IAA2B,CAC3B,KAAK,QAAU,KACf,KAAK,SAAS,SAAS,CACvB,KAAK,QAAU,KACf,KAAK,qBAAuB,KAC5B,KAAK,QAAQ,SAAS,CACtB,KAAK,OAAS,KACd,KAAK,UAAU,OAAO,CACtB,KAAK,SAAW,KAChB,KAAK,mBAAqB,EAAE,CAC5B,KAAK,oBAAsB,KACvB,OAAO,SACT,OAAO,OAAO,QAAQ,KAExB,AAEE,KAAK,WADL,KAAK,QAAQ,UAAY,GACV,MAEjB,KAAK,QAAU,KAOjB,KAAK,EAAsE,CACrE,GAAS,QAAU,IAAA,KACrB,KAAK,WAAa,EAAQ,MACtB,KAAK,gBACP,KAAK,wBAAwB,EAGjC,KAAK,MAAM,CACP,GAAS,iBAAmB,IAAA,IAC9B,KAAK,aAAa,EAAQ,eAAe,CAI7C,eAAe,EAAuB,EAA2D,CAC3F,GAAS,MAAQ,IAAA,IACnB,KAAK,OAAO,CAAE,IAAK,EAAQ,IAAK,CAAC,CAGnC,KAAK,aAAe,GAEhB,GAAS,QAAU,IAAA,GAEZ,KAAK,oBACd,KAAK,WAAa,QAFlB,KAAK,WAAa,EAAQ,MAI5B,KAAK,MAAM,CACP,KAAK,gBACP,KAAK,wBAAwB,CAE/B,KAAK,YAAY,EAAO,CAI1B,YAAY,EAAoB,CAC9B,KAAK,aAAa,EAAK,CAIzB,WAAW,EAAuB,EAAsC,CACtE,KAAK,YAAY,EAAQ,EAAQ,CAGnC,OAAc,CACZ,KAAK,MAAM,CAGb,YAAY,EAAmB,EAAmB,CAChD,eAAe,QAAQ,6BAA8B,EAAU,CAC/D,eAAe,QAAQ,sBAAuB,EAAI,CAGpD,IAAI,QAAkB,CACpB,OAAO,KAAK,eAUd,YACE,EACA,EACY,CACZ,IAAI,EAAM,KAAK,gBAAgB,IAAI,EAAU,CAM7C,OALK,IACH,EAAM,IAAI,IACV,KAAK,gBAAgB,IAAI,EAAW,EAAI,EAE1C,EAAI,IAAI,EAAS,KACJ,CACX,EAAK,OAAO,EAAS,CACjB,EAAK,OAAS,GAAG,KAAK,gBAAgB,OAAO,EAAU,EAQ/D,8BAAsC,EAAmE,CACvG,IAAM,EAAO,KAAK,KACd,IAAS,SACX,EAAK,MAAM,YAAY,mCAAoC,wBAAwB,CAEnF,EAAK,MAAM,eAAe,mCAAmC,CAIjE,yBAAwC,CACtC,IAAK,IAAM,KAAc,KAAK,kBAC5B,EAAW,OAAO,CAEpB,KAAK,kBAAkB,OAAO,CAShC,2CAA0D,CACxD,IAAM,EAAsB,EAAE,CAC1B,EAAoB,GACxB,IAAK,IAAM,KAAK,KAAK,UAAW,CAQ9B,GAHE,EAAE,OAAS,cACV,EAAE,SAAW,aAAe,EAAE,SAAW,UACzC,EAAE,SAAW,MAAQ,EAAE,QAAQ,SAAW,GACnC,CACR,GAAI,EAAE,SAAU,CACd,KAAK,qBAAqB,OAAO,EAAE,SAAS,CAC5C,KAAK,cAAc,uBAAuB,EAAE,SAAS,CAGrD,KAAK,SACD,iBAAiB,oBAAoB,IAAI,OAAO,EAAE,SAAS,CAAC,2BAA2B,CACxF,QAAS,GAAO,EAAG,QAAQ,CAAC,CAC3B,KAAK,SACP,KAAK,OAAO,QAAU,KAAK,OAAO,QAAQ,OAAQ,GAAM,IAAM,EAAE,SAAS,EAE3E,IAAM,EAAS,KAAK,kBAAkB,OACtC,KAAK,kBAAoB,KAAK,kBAAkB,OAAQ,GAAM,EAAE,WAAa,EAAE,SAAS,CACpF,KAAK,kBAAkB,SAAW,IAAQ,EAAoB,IAEpE,KAAK,SAAS,oBAAoB,EAAE,GAAG,CACvC,KAAK,QAAQ,UAAU,OAAO,EAAE,GAAG,CACnC,KAAK,QAAQ,cAAc,OAAO,EAAE,GAAG,CACvC,SAEF,EAAK,KAAK,EAAE,CAEd,KAAK,UAAU,OAAS,EACxB,KAAK,UAAU,KAAK,GAAG,EAAK,CACxB,GAAmB,KAAK,SAAS,cAAc,KAAK,kBAAkB,CAI5E,kBAAiC,CAG/B,KAAK,uBAAyB,KAE9B,KAAK,yBAAyB,CAE9B,KAAK,mBAAmB,QAAQ,CAChC,KAAK,kBAAoB,KACzB,KAAK,kBAAkB,MAAM,CAC7B,KAAK,iBAAmB,KAExB,KAAK,UAAU,OAAS,EACxB,KAAK,SAAS,eAAe,CAE7B,KAAK,SAAS,YAAY,CAC1B,KAAK,oBAAsB,KAC3B,KAAK,OAAQ,UAAU,OAAO,CAC9B,KAAK,OAAQ,QAAU,EAAE,CAEzB,KAAK,kBAAoB,EAAE,CAC3B,KAAK,SAAS,cAAc,EAAE,CAAC,CAE/B,KAAK,sBAAwB,GAC7B,KAAK,wBAA0B,EAAE,CACjC,KAAK,mBAAmB,OAAO,CAE/B,KAAK,iBAAmB,KACxB,KAAK,cAAgB,KACrB,KAAK,oBAAsB,KAC3B,KAAK,eAAiB,IAAI,MAAM,CAAC,aAAa,CAE9C,KAAK,aAAe,GACpB,KAAK,oBAAsB,GAC3B,KAAK,oBAAsB,GAC3B,KAAK,oBAAsB,EAAE,CAC7B,KAAK,8BAAgC,KACrC,KAAK,cAAc,OAAO,CAC1B,KAAK,SAAS,qBAAqB,KAAK,CACxC,KAAK,SAAS,+BAA+B,GAAM,CAOrD,0CAAyD,CACvD,KAAK,mBAAmB,QAAQ,CAChC,KAAK,kBAAoB,KACzB,KAAK,SAAS,YAAY,CAC1B,KAAK,oBAAsB,KAC3B,KAAK,kBAAoB,EAAE,CAC3B,KAAK,SAAS,cAAc,EAAE,CAAC,CAC/B,KAAK,sBAAwB,GAC7B,KAAK,wBAA0B,EAAE,CACjC,KAAK,4BAA8B,KAC/B,KAAK,SACP,KAAK,OAAO,YAAc,KAC1B,KAAK,OAAO,mBAAmB,GAAG,CAClC,KAAK,OAAO,aAAa,GAAG,EAIhC,0BAAyC,CACvC,IAAM,EAAM,KAAK,cAAc,cAC/B,GAAI,CAAC,GAAO,CAAC,KAAK,QAAS,OAC3B,IAAI,EAAU,GACV,EAAI,OAAS,UAAY,EAAI,UAC/B,EAAU,KAAK,QAAQ,qBAAqB,EAAI,SAAU,EAAI,SAAS,CACvE,AAEE,KADA,KAAK,QAAQ,2BAA2B,EAAI,SAAS,CAC3C,KAEH,EAAI,OAAS,WACtB,KAAK,QAAQ,2BAA2B,EAAI,SAAS,CACrD,EAAU,IAER,GACF,KAAK,cAAc,qBAAqB,EAAI,GAAG,CAInD,2BAA0C,CACxC,KAAK,cAAc,sBAAsB,CACzC,KAAK,SAAS,qBAAqB,KAAK,CACxC,KAAK,SAAS,+BAA+B,GAAM,CAGrD,uBAAyC,CACvC,IAAM,EAAM,IAAI,IAChB,IAAK,IAAM,KAAK,KAAK,UACf,EAAE,UAAU,EAAI,IAAI,EAAE,SAAS,CAErC,OAAO,EAAI,KAAO,EAGpB,mBAAsC,CACpC,IAAM,EAAoB,EAAE,CACtB,EAAO,IAAI,IACjB,IAAK,IAAM,KAAK,KAAK,UACf,EAAE,UAAY,CAAC,EAAK,IAAI,EAAE,SAAS,GACrC,EAAK,IAAI,EAAE,SAAS,CACpB,EAAQ,KAAK,EAAE,SAAS,EAG5B,OAAO,EAGT,iCAAgD,CAC9C,GAAI,CAAC,KAAK,SAAW,CAAC,KAAK,cAAc,MAAO,OAChD,IAAM,EAAY,KAAK,mBAAmB,CAC1C,GAAI,EAAU,SAAW,EAAG,OAC5B,IAAM,EAAe,GAAiC,EAAW,KAAK,cAAc,CACpF,GAAI,CAAC,EAAc,OACnB,IAAM,EAAM,GAAG,EAAa,YACxB,KAAK,cAAc,0BAA4B,IAC/C,KAAK,cAAc,iBAAmB,CAAC,KAAK,cAAc,gBAC1D,KAAK,QAAQ,qBAAqB,EAAc,SAAS,EAC3D,KAAK,cAAc,sBAAsB,EAAI,EAIjD,qBAA6B,EAA0B,CACrD,OAAQ,EAAI,KAAZ,CACE,IAAK,WACH,KAAK,MAAM,CACX,MACF,IAAK,YACH,KAAK,OAAO,CACZ,MACF,IAAK,iCAAkC,CAIrC,IAAM,EAAU,EAAI,QACd,EAAS,GAAS,OAElB,EAAS,GAAa,EAAO,CAAG,EAAS,GAAa,EAAQ,CAAG,EAAU,KAC7E,GACF,KAAK,YAAY,EAAQ,CAAE,OAAQ,GAAM,CAAC,CAE5C,KAAK,MAAM,CACX,MAEF,IAAK,gCAAiC,CAGpC,IAAM,EAAM,EAAI,QAChB,GAAI,GAAO,OAAO,GAAQ,SAAU,CAClC,KAAK,eAAiB,EAEtB,IAAM,EAAM,EAAI,IAKhB,GAJI,GACF,KAAK,OAAO,CAAE,MAAK,CAAC,CAGlB,GAAa,EAAI,OAAO,CAAE,CAC5B,KAAK,aAAe,GACpB,KAAK,MAAM,CACX,KAAK,YAAY,EAAI,OAAO,CAC5B,OAGJ,KAAK,MAAM,CACX,MAEF,IAAK,iBAAkB,CAGrB,IAAM,EADU,EAAI,SACI,OACpB,GAAU,OAAO,GAAW,UAAY,SAAW,GACrD,KAAK,YAAY,EAAwB,CAE3C,MAEF,IAAK,iBACH,KAAK,cAAc,sBAAsB,SAAS,CAClD,eAAiB,KAAK,0BAA0B,CAAE,GAAG,CACrD,MACF,IAAK,mBAEH,KAAK,SAAS,KAAK,kBAAmB,EAAI,QAAQ,CAClD,MAEF,IAAK,sBAAuB,CAE1B,IAAM,EAAU,EAAI,QAChB,GAAW,aAAc,GAAW,OAAO,EAAQ,UAAa,WAClE,KAAK,cAAgB,EAAQ,UAE/B,MAEF,IAAK,wBAAyB,CAE5B,IAAM,EADU,EAAI,SACG,MACnB,OAAO,GAAU,UAAY,GAAS,GACxC,KAAK,SAAS,qBAAqB,EAAM,CAE3C,MAEF,IAAK,0BACH,KAAK,sBAAsB,gBAAgB,GAAK,CAChD,MACF,IAAK,gBAAiB,CAEpB,IAAM,EADU,EAAI,SACG,MACnB,OAAO,GAAU,UAAY,GAAe,EAAM,EAAI,KAAK,SAC5D,KAAK,QAAQ,KAAqB,MAAM,YAAY,oBAAqB,EAAM,CAElF,MAEF,QACE,OAIN,oBAAmC,CAC5B,OAAO,UAAS,OAAO,QAAU,EAAE,EACxC,OAAO,QAAQ,KAAO,CACpB,KAAO,GAAS,KAAK,KAAK,EAAK,CAC/B,gBAAiB,EAAQ,IAAS,KAAK,eAAe,EAAQ,EAAK,CACnE,YAAc,GAAS,KAAK,YAAY,EAAK,CAC7C,YAAa,EAAQ,IAAS,KAAK,WAAW,EAAQ,EAAK,CAC3D,UAAa,KAAK,OAAO,CACzB,aAAc,EAAK,IAAQ,KAAK,YAAY,EAAK,EAAI,CACrD,IAAI,QAAS,CACX,MAAO,IAET,IAAK,EAAO,IAAY,KAAK,GAAG,EAAO,EAAQ,CAC/C,eAAgB,EAAM,IAAS,KAAK,cAAc,EAAM,EAAK,CAC7D,qBAAuB,GAAS,KAAK,qBAAqB,EAAK,CAC/D,aAAc,EAAW,IAAa,KAAK,YAAY,EAAW,EAAS,CAC5E,CAED,OAAO,eAAe,OAAO,QAAQ,KAAM,SAAU,CACnD,QAAW,KAAK,eACjB,CAAC,CAGJ,aAA4B,CAC1B,GAAI,KAAK,eAAgB,OACzB,KAAK,eAAiB,GACtB,IAAM,EAAK,KAAK,SAAS,YAAY,CACjC,GACF,EAAG,UAAU,OAAO,8BAA8B,CAEpD,KAAK,wBAAwB,CAC7B,KAAK,SAAS,WAAW,CACnB,KAAK,mBAAqB,KAAK,aAAe,QAClD,KAAK,SAAS,YAAY,CAE5B,KAAK,sBAAsB,aAAa,GAAK,CAC7C,KAAK,cAAc,SAAS,GAAK,CACjC,eAAiB,KAAK,iCAAiC,CAAE,GAAG,CAI9D,sBAAqC,CACnC,GAAI,KAAK,UAAU,SAAW,EAAG,OAEjC,IAAM,EAAa,KAAK,2BAA2B,CAC7C,EAAiB,KAAK,iCAAiC,EAAW,CAcxE,GAbI,EAAe,OAAS,GAC1B,KAAK,SAAS,SACZ,EAAe,IAAK,IAAY,CAC9B,MAAO,EAAO,MACd,aAAgB,KAAK,YAAY,KAAK,gCAAgC,EAAQ,EAAW,CAAC,CAC3F,EAAE,CACJ,CAGC,IAAe,WAAa,KAAK,OAAO,aAAa,KAIrD,KAAK,8BAA8B,EAAW,CAChD,OAGF,IAAM,EAAiB,KAAK,iCAAiC,EAAW,CACxE,GAAI,CAAC,EAAgB,OAErB,IAAM,EAA0B,CAC9B,GAAI,IAAQ,CACZ,KAAM,YACN,QAAS,EACT,UAAW,KAAK,KAAK,CACrB,OAAQ,OACT,CACD,KAAK,UAAU,KAAK,EAAW,CAC/B,KAAK,SAAS,WAAW,EAAW,CAGtC,2BAAuD,CACrD,OAAQ,KAAK,OAAO,aAAa,SAAjC,CACE,IAAK,OACH,MAAO,OACT,IAAK,SACL,IAAK,MACH,MAAO,UACT,IAAK,MACH,MAAO,UACT,QACE,MAAO,WAIb,iCAAyC,EAAa,KAAK,2BAA2B,CAAsB,CAC1G,OACE,KAAK,OAAO,2BAA2B,KACtC,IAAe,UAA4D,IAAA,GAAhD,KAAK,OAAO,0BAA0B,UAClE,KAAK,OAAO,eAIhB,kCAA0C,EAAa,KAAK,2BAA2B,CAAsB,CAC3G,OACE,KAAK,OAAO,2BAA2B,KACtC,IAAe,UAA4D,IAAA,GAAhD,KAAK,OAAO,0BAA0B,SAItE,iCAAyC,EAAa,KAAK,2BAA2B,CAAoB,CACxG,IAAM,EACJ,KAAK,OAAO,0BAA0B,KACrC,IAAe,UAA2D,IAAA,GAA/C,KAAK,OAAO,yBAAyB,SAMnE,OAJI,GAAmB,OACd,EAAkB,OAAQ,GAAW,OAAO,GAAQ,OAAU,UAAY,EAAO,MAAM,MAAM,CAAC,OAAS,EAAE,EAG1G,KAAK,OAAO,gBAAkB,EAAE,EACrC,OAAQ,GAAU,OAAO,GAAU,UAAY,EAAM,MAAM,CAAC,OAAS,EAAE,CACvE,IAAK,IAAW,CAAE,QAAO,EAAE,CAGhC,gCACE,EACA,EAAa,KAAK,2BAA2B,CAC9B,CACf,IAAM,EAAQ,EAAO,MAAM,MAAM,CAC3B,EAAa,KAAK,OAAO,aAAa,KAAO,KAAK,wBAAwB,MAAM,CAChF,EAAc,KAAK,4BAA4B,eAAe,EAAI,EAAE,CA0B1E,OAxBI,EAAO,OAAS,UAAY,EACvB,CACL,QACA,KAAM,gBACN,QAAS,CAAE,IAAK,EAAY,CAC7B,CAGC,EAAO,OAAS,WAAa,EACxB,CACL,QACA,KAAM,cACN,QAAS,CAAE,IAAK,EAAY,CAC7B,CAGC,EAAO,OAAS,WAAa,IAAe,WAAa,EAAY,QAAU,EAC1E,CACL,QACA,KAAM,qBACN,QAAS,CAAE,SAAU,EAAY,MAAM,EAAG,EAAE,CAAE,CAC/C,CAGI,CACL,QACA,KAAM,eACN,QAAS,EACV,CAGH,wBAAgC,EAAiC,CAC/D,IAAM,EAAc,KAAK,OAAO,YAC1B,EACJ,GAAa,OAAS,OAAO,EAAY,OAAU,UAAY,CAAC,MAAM,QAAQ,EAAY,MAAM,CAC3F,EAAY,MACb,IAAA,GACA,EAAW,EAAI,QAAQ,aAAc,EAAQ,IAAmB,EAAO,aAAa,CAAC,CACrF,EAAY,IAAc,IAAQ,IAAQ,IAAQ,IAAc,IAAa,IAAQ,GAC3F,OAAO,OAAO,GAAc,UAAY,EAAU,MAAM,CAAC,OAAS,EAAI,EAAU,MAAM,CAAG,IAAA,GAG3F,4BAAoC,EAAmC,CACrE,IAAM,EAAc,KAAK,OAAO,YAC1B,EACJ,GAAa,OAAS,OAAO,EAAY,OAAU,UAAY,CAAC,MAAM,QAAQ,EAAY,MAAM,CAC3F,EAAY,MACb,IAAA,GACA,EAAW,EAAI,QAAQ,aAAc,EAAQ,IAAmB,EAAO,aAAa,CAAC,CACrF,EAAY,IAAc,IAAQ,IAAQ,IAAQ,IAAc,IAAa,IAAQ,GAC3F,GAAI,CAAC,MAAM,QAAQ,EAAU,CAAE,OAC/B,IAAM,EAAS,EAAU,OAAQ,GAAyB,OAAO,GAAS,UAAY,EAAK,MAAM,CAAC,OAAS,EAAE,CAC7G,OAAO,EAAO,OAAS,EAAI,EAAS,IAAA,GAGtC,+BAA6E,CAC3E,IAAM,EAAuC,EAAE,CAE/C,EAAY,IAAM,KAAK,OAAO,aAAa,KAAO,OAAO,SAAS,KAElE,IAAM,EAAY,KAAK,wBAAwB,aAAa,CACxD,IAAW,EAAY,WAAa,GAExC,IAAM,EAAkB,KAAK,wBAAwB,mBAAmB,CACpE,IAAiB,EAAY,iBAAmB,GAEpD,IAAM,EAAc,KAAK,wBAAwB,eAAe,CAC5D,IAAa,EAAY,aAAe,GAE5C,IAAM,EAAc,KAAK,4BAA4B,eAAe,CAChE,GAAa,SAAQ,EAAY,aAAe,GAEpD,IAAM,EAAkB,KAAK,4BAA4B,mBAAmB,CACxE,GAAiB,SAAQ,EAAY,iBAAmB,GAE5D,IAAM,EAAe,KAAK,OAAO,aAAa,cAAgB,KAAK,4BAA4B,gBAAgB,CAG/G,OAFI,GAAc,SAAQ,EAAY,cAAgB,GAE/C,OAAO,KAAK,EAAY,CAAC,OAAS,EAAI,EAAc,IAAA,GAG7D,8BAAsC,EAAa,KAAK,2BAA2B,CAAW,CAG5F,OAFI,KAAK,UAAU,SAAW,GAAK,KAAK,qBAAuB,KAAK,qBAChE,IAAe,UAAkB,GAC9B,GACL,KAAK,OAAO,0BACZ,KAAK,OAAO,0BACZ,KAAK,OAAO,yBAIhB,gCAA+C,CAC7C,IAAM,EAAa,KAAK,2BAA2B,CACnD,GAAI,CAAC,KAAK,8BAA8B,EAAW,CAAE,OAErD,KAAK,oBAAsB,GAE3B,IAAM,EAAmC,CACvC,KAAM,GACN,yBAA0B,EAC1B,oBAAqB,EACtB,CAEK,EAAc,KAAK,+BAA+B,CACpD,IAAa,EAAQ,aAAe,GAExC,IAAM,EAAiB,KAAK,iCAAiC,EAAW,CACpE,IAAgB,EAAQ,gBAAkB,GAE9C,IAAM,EAAkB,KAAK,kCAAkC,EAAW,CACtE,IAAiB,EAAQ,iBAAmB,GAEhD,KAAK,YACH,CACE,MAAO,GACP,KAAM,eACN,UACD,CACD,CAAE,OAAQ,GAAM,cAAe,GAAM,CACtC,CAGH,aAA4B,CAC1B,GAAI,CAAC,KAAK,eAAgB,OAC1B,KAAK,SAAS,cAAc,CAC5B,KAAK,mBAAmB,QAAQ,CAChC,KAAK,kBAAoB,KACzB,KAAK,kBAAkB,MAAM,CAC7B,KAAK,iBAAmB,KACxB,KAAK,eAAiB,GAEtB,KAAK,WAAa,OAIlB,IAAM,EAAK,KAAK,SAAS,YAAY,CACjC,GACF,EAAG,UAAU,IAAI,8BAA8B,CAEjD,KAAK,wBAAwB,CAC7B,KAAK,sBAAsB,aAAa,GAAM,CAC9C,KAAK,cAAc,SAAS,GAAM,CAClC,KAAK,SAAS,qBAAqB,KAAK,CACxC,KAAK,SAAS,+BAA+B,GAAM,CAGrD,oBAAmC,CAC5B,QAAK,QAIV,IAHA,KAAK,kBAAoB,OAAO,YAAc,KAAK,kBACnD,KAAK,QAAQ,UAAU,OAAO,4BAA6B,KAAK,kBAAkB,CAE9E,KAAK,UAAW,CAClB,IAAM,EAAe,KAAK,mBAAqB,KAAK,OAAO,qBAAuB,GAClF,KAAK,UAAU,UAAU,UAAU,OAAO,uCAAwC,EAAa,CAGjG,KAAK,wBAAwB,EAQ/B,2BAA6C,CAC3C,GAAI,CAAC,KAAK,eAAgB,MAAO,GACjC,IAAM,EAAU,KAAK,OAAO,SAAW,WAGvC,OAFI,IAAY,SAAiB,GAC7B,IAAY,UAAkB,GAC3B,KAAK,SAAS,gBAAgB,EAAI,GAG3C,wBAAuC,CACrC,GAAI,CAAC,KAAK,QAAS,OACnB,IAAM,EAAa,KAAK,gBAAkB,KAAK,mBAAqB,KAAK,aAAe,OAClF,EAAa,KAAK,gBAAkB,KAAK,mBAAqB,KAAK,aAAe,OAClF,EAAgB,KAAK,2BAA2B,CAKtD,GAJA,KAAK,QAAQ,UAAU,OAAO,0BAA2B,KAAK,eAAe,CAC7E,KAAK,QAAQ,UAAU,OAAO,iCAAkC,EAAW,CAC3E,KAAK,QAAQ,UAAU,OAAO,iCAAkC,EAAW,CAC3E,KAAK,QAAQ,UAAU,OAAO,2CAA4C,EAAc,CACpF,KAAK,YAAa,CACpB,IAAM,EAAkB,IAAkB,KAAK,OAAO,SAAW,cAAgB,SACjF,KAAK,YAAY,aAAa,cAAe,EAAkB,QAAU,OAAO,CAElF,KAAK,6BAA6B,CAGpC,6BAA4C,CACtC,YAAO,SAAa,KAExB,KADgB,KAAK,OAAO,SAAW,cACvB,SAAU,CACxB,KAAK,gCAAgC,CACrC,OAEE,KAAK,2BAA2B,CAClC,KAAK,8BAA8B,CAEnC,KAAK,gCAAgC,EASzC,wCAAgD,EAAoB,CAClE,GAAI,CACF,IAAM,EAAO,EAAG,cAAc,CAC9B,GAAI,CAAC,EAAK,SAAS,KAAK,KAAK,CAAE,MAAO,GAEtC,IAAK,IAAM,KAAK,EAAM,CACpB,GAAI,IAAM,KAAK,KAAM,MACrB,GAAI,EAAE,aAAa,aAAc,SAEjC,GAAI,KAAK,cAAgB,IAAM,KAAK,aAAe,KAAK,YAAY,SAAS,EAAE,EAC7E,MAAO,GAGT,IAAM,EAAK,OAAO,iBAAiB,EAAE,CAC/B,GAAQ,EAAG,YAAc,QAAU,EAAG,YAAc,WAAa,EAAE,aAAe,EAAE,aAAe,EACnG,GAAQ,EAAG,YAAc,QAAU,EAAG,YAAc,WAAa,EAAE,YAAc,EAAE,YAAc,EACvG,GAAI,GAAQ,EAAM,MAAO,GAE3B,MAAO,QACD,CACN,MAAO,IAIX,8BAA6C,CACvC,OAAO,SAAa,KAAe,KAAK,wBAC5C,SAAS,iBAAiB,YAAa,KAAK,8BAA+B,CAAE,QAAS,GAAM,QAAS,GAAO,CAAC,CAC7G,SAAS,iBAAiB,QAAS,KAAK,0BAA2B,CAAE,QAAS,GAAM,QAAS,GAAO,CAAC,CACrG,KAAK,sBAAwB,IAG/B,gCAA+C,CACzC,OAAO,SAAa,KAAe,CAAC,KAAK,wBAC7C,SAAS,oBAAoB,YAAa,KAAK,8BAA+B,CAAE,QAAS,GAAM,CAAC,CAChG,SAAS,oBAAoB,QAAS,KAAK,0BAA2B,CAAE,QAAS,GAAM,CAAC,CACxF,KAAK,sBAAwB,IAG/B,kBAA0B,EAAkB,CAC1C,IAAM,EAAS,GAAkB,EAAK,CACtC,GAAI,CAAC,EAAO,GAAI,CAEd,EAAS,uBAAwB,CAC/B,QAFc,EAAO,SAAW,eAAiB,KAAK,MAAM,gBAAkB,KAAK,MAAM,aAGzF,OAAQ,OACT,CAAC,CACF,OAEF,KAAK,SAAS,gBAAgB,EAAK,CAGrC,aAAqB,EAAc,EAAyB,CAC1D,GAAI,KAAK,oBAAqB,CAC5B,KAAK,oBAAoB,KAAK,IAAe,IAAA,GAAmC,CAAE,OAAM,CAA/B,CAAE,OAAM,aAAY,CAAY,CACzF,QAKyB,CAAC,KAAK,UAAU,KAAM,GAAM,EAAE,OAAS,OAAO,EAC/C,KAAK,SAAS,qBAAqB,IAC3D,GAAc,KAAK,OAAO,UAAU,CACpC,KAAK,SAAS,gBAAgB,EAGhC,IAAqB,CAEG,KAAK,UAAU,KAAM,GAAM,EAAE,OAAS,OAAO,EAEnE,IAA2B,CAG7B,IAAM,EACJ,IAAe,IAAA,GAEX,CAAE,MAAO,EAAM,KAAM,eAAgB,QAAS,EAAM,CADpD,CAAE,MAAO,EAAM,KAAM,cAAe,QAAS,EAAO,CAAE,OAAM,CAAG,EAAE,CAAE,CAErE,IAAe,IAAA,GAGjB,KAAK,YAAY,EAAO,CAFxB,KAAK,YAAY,EAAQ,CAAE,aAAY,CAAC,CAM5C,0BAAyC,CACvC,GAAI,KAAK,qBAAuB,KAAK,oBAAoB,SAAW,EAAG,OACvE,IAAM,EAAS,CAAC,GAAG,KAAK,oBAAoB,CAC5C,KAAK,oBAAsB,EAAE,CAC7B,IAAK,IAAM,KAAQ,EACjB,KAAK,aAAa,EAAK,KAAM,EAAK,WAAW,CAIjD,YAAoB,EAAuB,EAAmC,CAW5E,GATA,KAAK,mBAAmB,QAAQ,CAChC,KAAK,kBAAoB,KACzB,KAAK,kBAAkB,MAAM,CAC7B,KAAK,iBAAmB,KAGxB,KAAK,gBAAkB,CAAE,SAAQ,UAAS,CAGtC,CAAC,KAAK,cAAe,CACnB,KAAK,gBAAgB,OAAS,IAChC,KAAK,gBAAgB,KAAK,CAAE,SAAQ,UAAS,CAAC,CAEhD,OA0BF,GAtBA,KAAK,mBAAmB,QAAQ,CAChC,KAAK,kBAAoB,KAEpB,GAAS,eACZ,IAAiC,CAO/B,CAAC,GAAS,eAAiB,KAAK,uBAAyB,EAAO,OAAS,uBAC3E,KAAK,sBAAwB,GAC7B,KAAK,wBAA0B,EAAE,EAI9B,GAAS,gBACZ,KAAK,mBAAqB,EAAE,EAI1B,KAAK,kBAAoB,KAAK,eAAiB,KAAK,cAAgB,KAAK,iBAAkB,CAC7F,IAAM,EAAS,KAAK,iBAEd,EAAU,KAAK,UAAU,OAAQ,GAAM,EAAE,WAAa,IAAA,IAAa,EAAE,SAAW,EAAO,CAC7F,KAAK,UAAY,KAAK,UAAU,OAAQ,GAAM,CAAC,EAAE,UAAY,EAAE,UAAY,EAAO,CAElF,IAAK,IAAM,KAAO,EAChB,KAAK,SAAS,cAAc,qBAAqB,IAAI,OAAO,EAAI,GAAG,CAAC,IAAI,EAAE,QAAQ,CAClF,KAAK,OAAQ,UAAU,OAAO,EAAI,GAAG,CACrC,KAAK,OAAQ,cAAc,OAAO,EAAI,GAAG,EAGnB,KAAK,SAAS,iBAAiB,mBAAmB,GACzD,QAAS,GAAO,CAC3B,aAAc,aAAe,EAAG,QAAQ,UAAe,EAAG,QAAQ,SAAc,GAClF,EAAG,QAAQ,EAEb,CAIC,GAAS,eACZ,KAAK,SAAS,SAAS,EAAE,CAAC,CAE5B,KAAK,SAAS,qBAAqB,CAGnC,KAAK,SAAS,KAAK,eAAgB,GAAK,CAGxC,IAAM,EAAW,IAAQ,CACzB,KAAK,iBAAmB,EACxB,KAAK,cAAgB,EAGjB,KAAK,QAAU,EAAO,OAAS,wBACjC,KAAK,OAAO,eAAiB,EAAO,MAItC,IAAM,EAAkB,GAAS,gBAAkB,GAC7C,EACJ,EAAO,OAAS,uBAAyB,GAAS,SAAW,IAAQ,GAAS,aAAe,GAc/F,GAbK,IACH,KAAK,uBAAyB,GAO5B,CAAC,GAAS,QAAU,CAAC,GACvB,KAAK,SAAS,qBAAqB,EAAS,CAI1C,CAAC,GAAS,OAAQ,CACpB,IAAM,EACJ,OAAO,EAAO,SAAY,SACtB,EAAO,QACP,OAAQ,EAAO,SAAsC,MAAY,SAC7D,EAAO,QAAoC,KAC7C,EAAO,MAET,EAAU,KAAK,UAAU,OAAS,EAAI,KAAK,UAAU,KAAK,UAAU,OAAS,GAAK,IAAA,GAExF,GAAI,EADgB,IAAY,IAAA,IAAa,EAAQ,OAAS,QAAU,EAAQ,UAAY,GAC1E,CAChB,IAAM,EAAU,KAAK,eAAe,OAAQ,EAAS,CACrD,EAAQ,SAAW,EACf,GAAS,aAAe,IAAA,KAC1B,EAAQ,WAAa,EAAQ,YAE/B,KAAK,SAAS,WAAW,EAAQ,CACjC,KAAK,UAAU,KAAK,EAAQ,EAQhC,GAHE,CAAC,GAAS,QACV,KAAK,+BAA+B,GACnC,EAAO,OAAS,gBAAkB,EAAO,OAAS,aACX,CACxC,IAAM,EAAW,KAAK,MAAM,uBACtB,EAAS,KAAK,eAAe,YAAa,EAAS,CACzD,EAAO,SAAW,EAClB,EAAO,OAAS,OAChB,KAAK,UAAU,KAAK,EAAO,CAC3B,KAAK,gCAAgC,EAAO,CAC5C,KAAK,SAAS,iBAAiB,EAAO,GAAI,EAAS,CACnD,KAAK,SAAS,qBAAqB,EAAS,CAC5C,KAAK,SAAS,KAAK,eAAgB,GAAM,CACzC,KAAK,KAAK,UAAW,EAAO,CAC5B,KAAK,qBAAqB,CAAC,UAAY,GAErC,CACF,OASF,IAAI,EAAiB,KAAK,oBACtB,EAAyB,GACvB,MAAyC,CACzC,GAA0B,GAAS,gBACvC,EAAiB,KAAK,oBACtB,EAAyB,KAErB,MAAkC,CACjC,QAAK,SAAS,gBAAgB,CACnC,IAAI,EAAgB,CAClB,IAAM,EAAM,KAAK,qBAAqB,CAChC,EAAK,KAAK,uBAAuB,EAAgB,EAAI,CAC3D,KAAK,QAAQ,gBAAgB,EAAG,CAChC,KAAK,QAAQ,yBAAyB,KAAK,kCAAkC,EAAe,CAAC,CAC7F,KAAK,oBAAsB,OAE3B,KAAK,QAAQ,YAAY,CACzB,KAAK,oBAAsB,KAE7B,EAAiB,OAGf,EAAO,OAAS,uBAClB,KAAK,SAAS,iBAAiB,kBAAkB,CACjD,KAAK,QAAQ,uBAAuB,kBAAkB,EAGpD,CAAC,GAAS,QAAU,CAAC,GACvB,KAAK,2CAA2C,CAIlD,KAAK,SAAS,qBAAqB,CAGnC,IAAI,EAAe,GAGb,EAAS,KAAK,eAAe,YAAa,GAAG,CACnD,EAAO,SAAW,EAClB,EAAO,OAAS,YACZ,GAAS,SAAQ,EAAO,OAAS,IAErC,KAAK,UAAU,KAAK,EAAO,CAE3B,KAAK,cAAc,0BAA0B,EAAS,CACtD,KAAK,cAAc,mBAAmB,EAAU,SAAS,EAErD,GAAS,QAAU,IACrB,KAAK,SAAS,qBAAqB,EAAS,CAE9C,KAAK,SAAS,+BAA+B,GAAM,CACnD,eAAiB,KAAK,0BAA0B,CAAE,GAAG,CAGhD,GAAS,eACZ,KAAK,yBAAyB,CAGhC,IAAM,EAAiC,CACrC,cAAe,KAAK,OAAO,cAC3B,GAAI,KAAK,OAAO,UAAY,CAAE,UAAW,KAAK,OAAO,UAAW,CAAG,EAAE,CACtE,CACG,GAAS,aAAe,IAAA,KAC1B,EAAU,WAAa,EAAQ,YAIjC,IAAM,GADkB,KAAK,qBAAqB,CAK/C,OAAQ,GAAM,IAAM,IAAW,EAAE,SAAW,EAAE,OAAS,aAAa,CACpE,MAAM,IAAI,CACV,IAAK,IAAO,CACX,KAAM,EAAE,OAAS,OAAS,OAAS,QACnC,QAAS,EAAE,SAAW,GACvB,EAAE,CAGC,EAA2B,CAC/B,eAAgB,GAAuB,KAAK,OAAO,OAAO,CAC1D,UAAW,OAAO,SAAS,KAC3B,YAAa,OAAO,OAAO,WAAW,CACtC,aAAc,OAAO,OAAO,YAAY,CACxC,QAAS,GACT,GAAI,KAAK,OAAO,SAAS,WAAa,GACtC,OAAQ,KAAK,OAAO,SAAS,QAAU,GACvC,MAAO,KAAK,OAAO,UACnB,QAAS,EAAE,CACX,UAAW,KAAK,eAChB,aAAc,GAAY,KAAK,OAAO,UAAU,CAChD,aAAc,KAAK,OAAO,cAAgB,GAC1C,WACA,eAAgB,KAAK,OAAO,SAAS,gBAAkB,UACvD,SAAU,KAAK,kBAChB,CACG,KAAK,OAAO,SAAS,SAAW,IAAA,KAClC,EAAK,OAAS,KAAK,OAAO,QAAQ,QAIpC,IAAM,EAAiB,GAAoB,EAAQ,CACjD,YAAa,KAAK,OAAO,YACzB,eAAgB,KAAK,oBACrB,SAAU,KAAK,kBAChB,CAAC,CAEI,EAAmD,CACvD,WAAY,KAAK,OAAO,UACxB,WAAY,KAAK,OAAO,SAAS,WAAa,GAC9C,eAAgB,KAAK,OAAO,SAAS,WAAa,GAClD,KAAM,EAAe,KACrB,OAAQ,KAAK,OAAO,QAAU,KAC9B,OACA,QAAS,CAIP,GAAI,KAAK,qBAAuB,EAAE,CAClC,SAAU,GAEV,WAAY,KAAK,OAAO,SAAS,WAAa,GAC/C,CACF,CAGG,KAAK,OAAO,SAAS,SAAW,IAAA,KAClC,EAAQ,QAAU,KAAK,OAAO,QAAQ,QAEpC,KAAK,OAAO,SAAS,SAAW,IAAA,KAClC,EAAQ,QAAU,KAAK,OAAO,QAAQ,QAEpC,EAAe,UAAY,IAAA,KAC7B,EAAQ,QAAU,EAAe,SAE/B,KAAK,OAAO,aAAa,MAAQ,IAAA,KACnC,EAAQ,IAAM,KAAK,OAAO,YAAY,KAEpC,KAAK,OAAO,aAAa,WAAa,IAAA,KACxC,EAAQ,UAAY,KAAK,OAAO,YAAY,UAI9C,IAAM,EAAY,OAAO,YAAY,CAC/B,EAAc,KAAK,KAAK,CAC1B,EAAa,EACb,EAAmB,GACnB,EAAuB,GAEvB,GAA6B,GAC7B,GAA+B,GAC/B,EAAa,GAEb,EAAoC,KAElC,MAAsC,CACrC,QAAK,QACV,IAAI,KAAK,mBAAqB,CAAC,GAA4B,CACzD,KAAK,QAAQ,oBAAoB,SAAS,CAC1C,OAEE,KACC,EAGH,KAAK,QAAQ,oBAAoB,SAAS,CAF1C,KAAK,QAAQ,oBAAoB,YAAa,CAAE,eAAgB,KAAK,MAAM,yBAA0B,CAAC,IAM1G,KAAK,MACH,EAAiB,KAAK,kBAAkB,CAAE,CACxC,SAAU,iBACV,WAAY,EACZ,OAAQ,OACT,CAAC,CACH,CAED,IAAI,GAA2C,KAq2B/C,GAp2BA,GAAmB,GACjB,EACA,CACE,aAAc,EAAS,EAAS,IAAU,CAqBxC,GApBI,CAAC,GAAmB,IAAa,KAAK,yBAC1C,GAAgB,EAChB,KAAK,SAAS,uBAAuB,CAGjC,GAAO,mBACT,KAAK,kBAAoB,CAAE,GAAG,KAAK,kBAAmB,GAAG,EAAM,iBAAkB,EAE/E,GAAO,mBACT,KAAK,kBAAoB,EAAM,kBAGjC,KAAK,MACH,EAAiB,KAAK,kBAAkB,CAAE,CACxC,WAAY,EACZ,YAAa,IACb,OAAQ,OACT,CAAC,CACH,CAEG,CAAC,KAAK,SAAS,OAInB,IAAI,EAAc,EAClB,GAAI,GAAW,GAAa,EAAY,CAAE,CACxC,IAAM,EAAS,KAAK,OAAO,UAC3B,GAAI,CAAC,GAAY,EAAO,CAAE,CACxB,IAAM,EAAW,GAAiB,EAAY,CAC1C,EACF,KAAK,SAAS,eAAe,MAAgB,CAC3C,KAAK,SAAS,gBAAgB,CAC9B,GAAc,EAAO,EACrB,CAGF,GAAc,EAAO,CAGzB,EAAc,GAAe,EAAY,CAI3C,IAAM,EAAiB,KAAK,SAAS,cACnC,qBAAqB,EAAO,GAAG,8BAChC,CAYD,GAXI,EACF,EAAe,UAAY,EAAa,EAAY,EAEpD,EAAO,QAAU,EACb,EAAO,OAAS,aAAe,EAAO,UAAY,CAAC,KAAK,qBAAqB,IAAI,EAAO,SAAS,GACnG,KAAK,qBAAqB,IAAI,EAAO,SAAS,CAC9C,KAAK,QAAQ,oBAAoB,EAAO,GAAG,EAE7C,KAAK,QAAQ,WAAW,EAAO,EAG7B,EAAS,CACX,EAAO,QAAU,EACjB,EAAO,OAAS,OAChB,IAAyB,CAGzB,IAAM,EAAe,KAAK,SAAS,cACjC,qBAAqB,EAAO,GAAG,8BAChC,CACD,GAAI,EAAc,CAChB,KAAK,mBAAmB,QAAQ,CAChC,IAAM,EAAW,GAAO,gBACxB,KAAK,kBAAoB,GAAc,CACrC,UAAW,EACX,KAAM,EAAa,EAAY,CAC/B,WAAc,KAAK,SAAS,wBAAwB,CACpD,eAAkB,CAChB,KAAK,kBAAoB,KAErB,GAAY,EAAS,OAAS,GAAK,GACrC,GAAoB,CAClB,UAAW,EACX,WACA,eAAiB,GAAQ,CACvB,KAAK,YAAY,CACf,MAAO,EAAS,KAAM,GAAM,EAAE,MAAQ,EAAI,EAAE,YAAc,EAC1D,KAAM,sBACN,QAAS,CAAE,MAAK,CACjB,CAAC,EAEL,CAAC,EAGP,CAAC,IAIR,UAAW,EAAM,EAAQ,EAAW,IAAe,CAEjD,GADI,CAAC,GAAmB,IAAa,KAAK,wBACtC,IAAW,OAAQ,OAKnB,IACF,KAAK,0CAA0C,CAC/C,EAAmB,IAGrB,IAAM,EAAc,EAAK,SAAS,EAAK,MACjC,EAAgB,GAAa,MAAQ,UACrC,EAAqB,IAAkB,eAAiB,GAAa,OAAQ,iBAAsB,GAEnG,EACJ,KAAK,OAAO,yBAA2B,KACtC,IAAkB,uBAAyB,GAC1C,GAA0B,CAAC,IAC7B,KAAK,0CAA0C,CAC/C,EAAmB,IAErB,IAAM,EACJ,IAAkB,uBAAyB,IAAc,QAAW,QAAoB,EAC1F,KAAK,MACH,EAAkB,KAAK,kBAAkB,CAAE,CACzC,WAAY,EACZ,YAAa,EACb,eAAgB,EAChB,OAAQ,OACT,CAAC,CACH,CAED,IAAM,EAAgB,KAAK,qBAAqB,CAIhD,GAHA,EAAc,YAAc,GAGxB,IAAkB,kBAAmB,CACvC,IAAM,EAAW,GAAa,OAAQ,SACtC,GAAwB,MAAM,QAAQ,EAAS,CAAG,EAAS,OAAS,EAAE,CAEpE,IAAkB,eAEpB,GAAe,IAAA,GADI,GAAa,UAAU,QAAU,EACf,CAGvC,IAAM,EAAY,IAAuB,SAAW,KAAK,OAAS,KAAK,OAAO,YAAY,EAAK,CAAG,EAElG,GAAI,IAAuB,SAAW,KAAK,QAAU,CAAC,EAAwB,CAC5E,IAAM,EAA8B,CAAC,EACrC,EAAuB,GAEvB,IAAM,EAAc,GAA2B,CAC7C,gBACA,eAAgB,GAAa,OAAQ,iBAAsB,GAC3D,iBAAkB,KAAK,OAAO,YAC9B,gBAAiB,KAAK,SAAS,iBAAiB,EAAI,GACpD,eAAgB,KAAK,SAAS,gBAAgB,EAAI,GAClD,8BACD,CAAC,CAEF,EAAc,wBAA0B,IAAA,GACpC,IAAkB,gBAChB,IAAgB,iBAClB,EAAc,wBAA0B,KAAK,MAAM,sBAAwB,mBAE3E,KAAK,gCAAgC,EAAe,CAAE,KAAM,OAAQ,KAAM,EAAW,CAAC,EAItF,IAAgB,iBAClB,KAAK,uBAAuB,EAAW,EAAc,CAC5C,IAAgB,UACzB,KAAK,SAAS,mBAAmB,KAAK,cAAc,EAAW,EAAc,CAAC,CAC1E,KAAK,uBACP,KAAK,sBAAsB,GAI7B,KAAK,sBAAwB,GAC7B,KAAK,wBAA0B,EAAE,CACjC,KAAK,4BAA8B,KACnC,KAAK,SAAS,yBAAyB,KAAK,CAC5C,KAAK,SAAS,gBAAgB,KAAK,cAAc,EAAW,EAAc,CAAC,CAC3E,KAAK,oBAAsB,CAAE,KAAM,OAAQ,KAAM,EAAW,CAC5D,KAAK,OAAO,YAAc,GAE5B,KAAK,SAAS,0BAA0B,KAAK,OAAO,aAAe,KAAmB,cAAc,CAEhG,IAAkB,uBAAyB,EAAO,OAAS,uBAC7D,KAAK,iCAAiC,CAIpC,EAAO,UAAY,CAAC,KAAK,OAAO,QAAQ,SAAS,EAAO,SAAS,EACnE,KAAK,OAAO,QAAQ,KAAK,EAAO,SAAS,CAI3C,IAAM,EAAY,KAAK,OAAO,aAAe,EACvC,EAAe,GAAa,OAAQ,WAK1C,GAJA,KAAK,OAAO,aAAa,EAAW,EAAa,CACjD,KAAK,OAAO,mBAAmB,EAAc,CAGzC,IAAkB,eAAiB,IAAkB,sBAGvD,IAFA,GAA6B,CAAC,KAAK,kBAE/B,EAAoB,CACtB,IAAM,EAAW,KAAK,qBAAqB,CAC3C,EAAS,YAAc,GACvB,IAAM,EAAO,KAAK,cAAc,EAAoB,EAAS,CAC7D,GAA+B,GAC/B,KAAK,SAAS,oBAAoB,UAAW,CAAE,SAAU,EAAM,CAAC,CAChE,EAAqB,WAEd,IAAgB,kBAAoB,IAAgB,WAC7D,GAA6B,GAC7B,EAAqB,KACrB,KAAK,SAAS,oBAAoB,SAAS,EAU/C,GACE,IAAkB,uBAClB,IAAuB,UACtB,CAAC,EAAO,QAAU,GACnB,CACA,IAAM,EAAU,GAAa,OAAQ,QACrC,GAAI,EAAS,CACX,IAAM,EAAqB,CACzB,KAAM,OACN,SAAU,CACR,KAAM,CACJ,KAAM,qBACN,MAAO,CAAE,UAAS,CACnB,CACF,CACF,CACK,EAAoB,KAAK,SAAS,cAAc,yBAAyB,CAC/E,GAAI,EAAmB,CACrB,IAAM,EAAS,KAAK,cAAc,EAAY,EAAc,CACxD,EAAO,WACT,EAAO,QAAQ,SAAc,EAAO,UAEtC,IAAM,EAAS,KAAK,SAAS,cAAc,qBAAqB,EAAO,GAAG,IAAI,CAC1E,GAAU,EAAO,aAAe,EAClC,EAAO,MAAM,EAAO,CAEpB,EAAkB,YAAY,EAAO,CAEvC,EAAO,eAAe,CAAE,SAAU,OAAQ,MAAO,MAAO,CAAC,CACzD,KAAK,SAAS,8BAA8B,CAC5C,EAAuB,KAK7B,IAAM,EAAwB,IAAkB,cAAgB,IAAkB,kBAC9E,EAAyB,GACzB,EAAwB,GAI5B,GAHI,GAA0B,IAC5B,EAAc,wBAA0B,KAAK,MAAM,sBAAwB,oBAEzE,GAAyB,CAAC,KAAK,mBAAqB,CAAC,EAAO,OAC9D,GAAI,GAA4B,CAC9B,IAAM,EAAO,KAAK,cAAc,EAAM,EAAc,CACpD,GAA+B,GAC/B,KAAK,SAAS,oBAAoB,UAAW,CAAE,SAAU,EAAM,CAAC,CAChE,EAAyB,GACzB,EAAqB,UAErB,EAAqB,EACrB,EAAwB,GAI5B,IAAM,GAA0B,GAAmB,IAAkB,sBAUrE,IARG,CAAC,EAAO,QAAU,MAClB,IAAuB,SACtB,IAAkB,eACjB,GAA0B,IAAkB,gBAC/C,IAAkB,iBAClB,CAAC,GACD,EAAE,GAAyB,GAEL,CACtB,IAAM,EAAoB,KAAK,SAAS,cAAc,yBAAyB,CAC/E,GAAI,EAAmB,CACrB,IAAM,EAAS,KAAK,cAAc,EAAM,EAAc,CAClD,EAAO,WACT,EAAO,QAAQ,SAAc,EAAO,UAEtC,EAAkB,YAAY,EAAO,CACrC,EAAO,eAAe,CAAE,SAAU,OAAQ,MAAO,MAAO,CAAC,CACzD,KAAK,SAAS,8BAA8B,CACxC,GAA0B,IAAkB,gBAC9C,EAAuB,KAM7B,IAAK,IAAkB,eAAiB,IAAkB,gBAAkB,EAAO,SAAU,CAC3F,IAAM,EAAW,GAAa,UAAY,EAAE,CACtC,EACJ,IAAkB,cACb,EACE,IAAK,GAAO,EAAK,SAAS,IAAK,OAAQ,QAAkD,CACzF,OAAO,QAAQ,CACjB,CAAC,GAAa,OAAQ,QAAkD,CAAC,OAAO,QAAQ,CAK/F,IAAK,IAAM,KAAW,EAAU,CAC9B,IAAM,EAAM,EAAQ,IACd,EAAW,EAAQ,SACrB,GAAO,GACT,KAAK,kBAAkB,KAAK,CAAE,MAAK,WAAU,SAAU,EAAO,SAAU,CAAC,CAEvE,GACF,KAAK,mBAAmB,IAAI,EAAI,CAGpC,KAAK,SAAS,cAAc,KAAK,kBAAkB,CAIrD,GAAI,IAAkB,eAAiB,IAAkB,sBAAuB,CAS9E,IAAM,GAPJ,IAAkB,eACZ,GAAa,UAAY,EAAE,EAC1B,IAAK,GAAO,EAAK,SAAS,IAAK,OAAQ,QAAkD,CACzF,OAAO,QAAQ,CACjB,CACE,GAAa,OAAQ,SAAc,GAAa,MAClD,CAAC,OAAO,QAAQ,EAEpB,IAAK,GAAM,EAAE,SAAkC,CAC/C,OAAQ,GAAuB,OAAO,GAAQ,SAAS,CACvD,MAAM,EAAG,EAAE,CACV,EAAiB,OAAS,GAC5B,KAAK,SAAS,KAAK,gBAAiB,CAAE,OAAQ,EAAkB,CAAC,CAKrE,IAAM,EAAwB,GAAa,UAAU,QAAU,EAC/D,GACE,IAAkB,eAClB,IAAuB,SACvB,CAAC,GACD,EAAwB,GACxB,CAAC,KAAK,uBACN,CAAC,GAA0B,KAAK,kBAAoB,GAAG,CACvD,CACA,KAAK,mBAAmB,QAAQ,CAChC,KAAK,SAAS,iBAAiB,gCAAgC,CAAC,QAAS,GAAO,EAAG,QAAQ,CAAC,CAC5F,KAAK,kBAAoB,GAAqB,CAC5C,QAAS,KAAK,MAAM,sBACpB,WAAY,KAAK,MAAM,yBACvB,SAAU,KAAK,MAAM,kBACrB,SAAU,KAAK,kBAAoB,GACnC,iBAAkB,KAAK,MAAM,iBAC7B,eAAkB,CAChB,KAAK,sBAAwB,GAC7B,KAAK,kBAAoB,KACzB,KAAK,sBAAsB,EAE7B,cAAiB,CACf,KAAK,kBAAoB,MAE5B,CAAC,CAGF,IAAM,EAAU,KAAK,SAAS,cAAc,4BAA4B,CACxE,GAAI,EAIF,IAHA,EAAQ,YAAY,KAAK,kBAAkB,CAGvC,KAAK,mBAAqB,OAAO,eAAgB,CACnD,IAAM,EAAc,KAAK,kBACnB,MAAgC,CAChB,OAAO,eAAgB,OAAS,OAAO,YACzC,MAChB,EAAY,QAAQ,CAChB,KAAK,oBAAsB,IAC7B,KAAK,kBAAoB,MAE3B,OAAO,eAAgB,oBAAoB,SAAU,EAAkB,GAG3E,OAAO,eAAe,iBAAiB,SAAU,EAAkB,OAGrE,KAAK,kBAAoB,KAK7B,GAAI,IAAkB,gBAAiB,CACrC,IAAM,EAAU,GAAa,OAAQ,QASrC,GAAI,GAAW,EAAQ,OAAS,EAAG,CACjC,IAAM,EAAyF,EAAE,CAC3F,EAA8B,EAAE,CAEtC,IAAK,IAAM,KAAO,EAChB,GAAI,GAAkB,EAAI,CAAE,CAC1B,IAAM,EAA4E,CAChF,MAAO,EAAI,MACX,OAAQ,EAAI,OACb,CACG,EAAI,OAAM,EAAK,KAAO,EAAI,MAC9B,EAAW,KAAK,EAAK,MAErB,EAAY,KAAK,EAAI,CAIrB,EAAW,OAAS,GACtB,KAAK,SAAS,kBACZ,EAAW,IAAK,IAAU,CACxB,MAAO,EAAK,MACZ,aAAgB,KAAK,YAAY,EAAK,OAAO,CAC7C,GAAI,EAAK,KAAO,CAAE,KAAM,EAAK,KAAM,CAAG,EAAE,CACzC,EAAE,CACJ,CAGC,EAAY,OAAS,GACvB,KAAK,SAAS,SACZ,EAAY,IAAK,GAAQ,CACvB,IAAM,EAMF,CACF,MAAO,EAAI,MACX,aAAgB,KAAK,YAAY,EAAI,OAAO,CAC7C,CAID,OAHI,EAAI,OAAM,EAAK,KAAO,EAAI,MAC1B,EAAI,QAAO,EAAK,MAAQ,EAAI,OAC5B,EAAI,cAAa,EAAK,YAAc,EAAI,aACrC,GACP,CACH,EAKP,GAAyB,CACzB,EAAO,OAAS,GAElB,SAAW,GAAuB,CAC5B,MAAC,GAAmB,IAAa,KAAK,yBACtC,EAAM,OAAS,SAAU,CAC3B,IAAM,EAAkC,EAAE,CACtC,KAAK,OAAO,gBAAgB,sBAAwB,IAAA,KACtD,EAAW,oBAAsB,KAAK,OAAO,eAAe,qBAE1D,KAAK,OAAO,gBAAgB,kBAAoB,IAAA,KAClD,EAAW,gBAAkB,KAAK,OAAO,eAAe,iBAE1D,GACE,EACA,CACE,aAAgB,KAAK,MAAM,CAC3B,SAAW,GAAW,CACf,EAAU,EAAO,IAAI,GAC1B,KAAK,SAAS,KAAK,WAAY,EAAO,CAClC,EAAO,OACT,OAAO,KAAK,EAAO,IAAK,SAAU,sBAAsB,CAExD,OAAO,SAAS,KAAO,EAAO,MAGlC,YAAc,GAAW,KAAK,YAAY,EAAO,UAAW,EAAO,IAAI,CACvE,UAAY,GAAW,CACrB,EAAS,8BAA+B,EAAO,EAEjD,WAAa,GAAW,CACtB,EAAS,2BAA4B,EAAO,CAC5C,KAAK,OAAO,eAAe,EAAO,EAErC,CACD,EACD,GAGL,WAAa,GAAuB,CAC9B,MAAC,GAAmB,IAAa,KAAK,yBACtC,EAAM,OAAS,YAAc,EAAM,KAAM,CAW3C,IARE,EAAM,KAAK,QAAU,IAAA,IACrB,EAAM,KAAK,WAAa,IAAA,IACxB,EAAM,KAAK,aAAe,IAAA,MAE1B,KAAK,oBAAsB,EAAM,MAI/B,EAAM,KAAK,aAAc,CAC3B,IAAM,EACJ,OAAO,EAAM,KAAK,kBAAqB,SAAW,EAAM,KAAK,iBAAmB,IAAA,GAEhF,KAAK,OAAO,yBAA2B,KACtC,IAAgB,kBAAoB,IAAgB,4BAErD,EAAmB,GACnB,EAAuB,GAEvB,GAA4B,CACxB,KAAK,SAAQ,KAAK,OAAO,YAAc,MAC3C,KAAK,SAAS,iBAAiB,EAAY,CAEvC,GACF,KAAK,QAAQ,uBAAuB,EAAY,EAMtD,GAAI,EAAM,KAAK,MAAO,CAEpB,IAAM,EAAa,IAAI,YAAY,qBAAsB,CACvD,OAAQ,CAAE,QAAS,EAAM,KAAK,MAAO,CACrC,QAAS,GACT,WAAY,GACb,CAAC,CACI,EAAU,OAAO,cAAc,EAAW,CAIhD,GAHA,IAAoB,CAGhB,EAAS,CACX,IAAM,EAAe,EAAM,KAAK,MAC5B,EAAa,eACf,KAAK,kBAAkB,MAAM,CAC7B,KAAK,iBAAmB,GACtB,EAAa,aACb,EAAa,cAAgB,YAC9B,GAwBP,IAnBI,EAAM,KAAK,gBAAkB,EAAM,KAAK,WAC1C,EAAS,wBAAyB,CAChC,OAAQ,EAAM,KAAK,gBAAkB,KACrC,QAAS,EAAM,KAAK,UAAY,KACjC,CAAC,CAIA,EAAM,KAAK,kBAAoB,KAAK,OAAO,yBAA2B,KACxE,EAAmB,GACnB,EAAuB,GACvB,GAA4B,CACxB,KAAK,SAAQ,KAAK,OAAO,YAAc,MAC3C,KAAK,SAAS,kBAAkB,CAEhC,KAAK,QAAQ,uBAAuB,iBAAiB,EAInD,EAAM,KAAK,QAAS,CACtB,IAAM,EAAmB,MAAM,QAAQ,EAAM,KAAK,iBAAiB,CAC/D,EAAM,KAAK,iBAAiB,OAAQ,GAAyB,OAAO,GAAS,SAAS,CACtF,EAAE,CACF,EAAiB,OAAS,GAC5B,KAAK,SAAS,iBAAiB,EAAiB,CAE9C,OAAO,EAAM,KAAK,aAAgB,WACpC,KAAK,SAAS,gBAAgB,EAAM,KAAK,YAAY,CACrD,KAAK,SAAS,KAAK,iBAAkB,CAAE,KAAM,EAAM,KAAK,YAAa,CAAC,EAKtE,EAAM,KAAK,qBACb,KAAK,SAAS,KAAK,kBAAmB,EAAM,KAAK,oBAAoB,CAInE,EAAM,KAAK,UACb,KAAK,SAAS,KAAK,aAAc,CAC/B,KAAM,EAAM,KAAK,SACjB,KAAM,EAAM,KAAK,YAClB,CAAC,CAIA,EAAM,KAAK,iBACb,KAAK,SAAS,KAAK,kBAAmB,EAAM,KAAK,gBAAgB,CAGnE,EAAS,wBAAyB,CAAE,QAAS,EAAM,KAAM,CAAC,CAG1D,IAAM,EAAO,EAAM,KACf,OAAO,EAAK,eAAkB,UAAY,OAAO,EAAK,mBAAsB,UAC9E,KAAK,MACH,EAAc,KAAK,kBAAkB,CAAE,CACrC,MAAO,EAAM,OAAS,UACtB,cAAe,EAAK,cACpB,kBAAmB,EAAK,kBACxB,aACG,EAAK,cACL,EAAK,cAA4B,EAAK,kBAC1C,CAAC,CACH,GAIP,QAAU,GAAQ,CAGhB,GAFI,IAAkB,KAAK,kBAAkB,OAAO,GAAiB,CAEjE,CAAC,GAAmB,IAAa,KAAK,uBAAwB,OAClE,EAAa,GACb,KAAK,mBAAmB,QAAQ,CAChC,KAAK,kBAAoB,KACzB,GAAyB,CACzB,EAAqB,KACrB,KAAK,SAAS,KAAK,eAAgB,GAAM,CACzC,KAAK,SAAS,KAAK,iBAAkB,CAAE,KAAM,KAAM,CAAC,CACpD,KAAK,SAAS,uBAAuB,CACrC,KAAK,SAAS,qBAAqB,CAEnC,IAAM,EAAkB,EACpB,GAAoB,CAAC,GAAsB,GAAqB,CACpE,EAAmB,GACnB,EAAuB,GAGvB,IAAM,EACJ,EAAO,QACN,EAAO,SAAW,MAAQ,EAAO,QAAQ,OAAS,GACnD,EAAa,OAAS,GACtB,EACI,EACJ,OAAO,UAAc,KAAe,UAAU,SAAW,IAAS,GAA0B,EAAI,CAE5F,MAA+C,CACnD,KAAK,SAAS,cAAc,qBAAqB,IAAI,OAAO,EAAO,GAAG,CAAC,IAAI,EAAE,QAAQ,CACrF,IAAM,EAAM,KAAK,UAAU,QAAQ,EAAO,CACtC,GAAO,GAAG,KAAK,UAAU,OAAO,EAAK,EAAE,EAGzC,EAA2B,GAEzB,MAAuC,CAC3C,GAAI,EAA4B,OAChC,KAAK,KAAK,QAAS,EAAI,CACvB,IAAM,EAAS,EAAI,QACf,IAAW,KAAK,kBAClB,KAAK,0BAEL,KAAK,uBAAyB,EAC9B,KAAK,kBAAoB,GAE3B,IAAM,EAAgB,EAAI,QAAQ,MAAM,CAClC,EAAc,EAAc,OAAS,EAAI,EAAgB,KAAK,MAAM,aACpE,EAAkB,CACtB,YAAe,CACT,KAAK,iBACP,KAAK,YAAY,KAAK,gBAAgB,OAAQ,KAAK,gBAAgB,QAAQ,EAG/E,kBAAqB,CACnB,KAAK,SAAS,YAAY,EAE7B,CAED,GAAI,KAAK,wBAA0B,EAAG,CACpC,GAAkC,CAClC,EAA2B,GAC3B,KAAK,SAAS,sBAAsB,KAAK,MAAM,uBAAwB,EAAgB,CACvF,OAGF,GAAI,GAAgC,EAAK,EAAY,CAAE,CACrD,GAAkC,CAClC,EAA2B,GAC3B,KAAK,SAAS,sBAAsB,EAAa,EAAgB,CACjE,OAGF,EAAO,QAAU,EACjB,EAAO,OAAS,OAChB,IAAM,EAAa,EAAa,EAAY,QAAQ,QAAS;EAAK,CAAC,MAAM;EAAK,CAAC,KAAK,SAAS,CAAC,CAC9F,KAAK,gCAAgC,EAAO,CAC5C,KAAK,SAAS,iBAAiB,EAAO,GAAI,EAAW,CACrD,KAAK,SAAS,sBAAsB,EAAgB,EAGtD,GAAK,EAiBH,KAAK,SAAS,SAAS,EAAE,CAAC,CACrB,EAAO,QACV,GAA0B,SAlBxB,GAAmB,KAAK,+BAA+B,CAAE,CAC3D,KAAK,SAAS,SAAS,EAAE,CAAC,CAE1B,IAAM,EAAW,KAAK,MAAM,uBAC5B,EAAO,QAAU,EACjB,EAAO,OAAS,OAChB,KAAK,gCAAgC,EAAO,CAC5C,KAAK,SAAS,iBAAiB,EAAO,GAAI,EAAS,CACnD,KAAK,gCAAgC,SAErC,GAA0B,CACtB,EACF,OASF,IACF,KAAK,oBAAsB,GAC3B,KAAK,0BAA0B,EAE7B,CAAC,GAA4B,EAAO,SAAW,cACjD,EAAO,OAAS,SAKb,GACH,KAAK,MACH,EAAiB,KAAK,kBAAkB,CAAE,CACxC,WAAY,EACZ,WAAY,eACZ,cAAe,EAAI,QACnB,OAAQ,OACT,CAAC,CACH,EAGL,WAAc,CAGZ,GAFI,IAAkB,KAAK,kBAAkB,OAAO,GAAiB,CAEjE,CAAC,GAAmB,IAAa,KAAK,uBAAwB,OAIlE,GAHA,EAAa,GACb,GAAyB,CAErB,EAAoB,CACtB,IAAM,EAAW,KAAK,qBAAqB,CAC3C,EAAS,YAAc,GACvB,IAAM,EAAoB,KAAK,SAAS,cAAc,yBAAyB,CAC/E,GAAI,EAAmB,CACrB,IAAM,EAAS,KAAK,cAAc,EAAoB,EAAS,CAC3D,EAAO,WAAU,EAAO,QAAQ,SAAc,EAAO,UACzD,EAAkB,YAAY,EAAO,CACrC,EAAO,eAAe,CAAE,SAAU,OAAQ,MAAO,MAAO,CAAC,CACzD,KAAK,SAAS,8BAA8B,CAE9C,EAAqB,KAevB,GAbA,KAAK,uBAAyB,KAE9B,KAAK,uBAAyB,EAC9B,KAAK,kBAAoB,GACzB,KAAK,SAAS,KAAK,eAAgB,GAAM,CACzC,KAAK,SAAS,KAAK,iBAAkB,CAAE,KAAM,KAAM,CAAC,CACpD,KAAK,SAAS,uBAAuB,CACjC,GAAoB,CAAC,GAAsB,GAAqB,CACpE,EAAmB,GACnB,EAAuB,GAInB,GAAmB,CAAC,GAAgB,CAAC,EAAsB,CAC7D,IAAM,EAAW,KAAK,MAAM,uBAC5B,EAAO,QAAU,EACjB,KAAK,gCAAgC,EAAO,CAC5C,KAAK,SAAS,iBAAiB,EAAO,GAAI,EAAS,CACnD,KAAK,gCAAgC,CAEvC,GAAI,EAAiB,CACnB,KAAK,oBAAsB,GAC3B,IAAM,EAAoB,KAAK,oBAAoB,OAAS,EAC5D,KAAK,0BAA0B,CAC1B,GACH,KAAK,oCAAoC,CAIzC,EAAO,SAAW,cACpB,EAAO,OAAS,OAChB,IAAyB,EAE3B,KAAK,cAAc,uBAAuB,EAAS,CAGnD,IAAM,EAAmB,KAAK,SAAS,cAAc,8CAA8C,CAC/F,IACF,EAAiB,UAAU,OAAO,6CAA6C,CAC/E,EAAiB,UAAU,IAAI,6CAA6C,EAG9E,KAAK,KAAK,UAAW,EAAO,CAI5B,IAAM,EAAc,KAAK,oBACzB,KAAK,QAAQ,mBACX,EAAO,GACP,MACU,CACJ,IAAM,EAAM,KAAK,qBAAqB,CACtC,OAAO,KAAK,uBAAuB,EAAa,EAAI,EAEtD,IAAA,GACL,CAED,KAAK,MACH,EAAgB,KAAK,kBAAkB,CAAE,CACvC,WAAY,EACZ,WAAY,KAAK,KAAK,CAAG,EACzB,YAAa,EACb,OAAQ,OACT,CAAC,CACH,CAED,KAAK,MACH,EAAuB,KAAK,kBAAkB,CAAE,CAC9C,UAAW,eACX,SAAU,EACV,KAAM,UACP,CAAC,CACH,CAED,KAAK,MACH,EAAyB,KAAK,kBAAkB,CAAE,CAChD,cAAe,KAAK,UAAU,OAC9B,YAAa,KAAK,OAAO,SAAS,WAAa,GAC/C,gBAAiB,OAClB,CAAC,CACH,CAGD,KAAK,qBAAqB,CAAC,UAAY,GAErC,EAEL,CACD,EACD,CACD,KAAK,kBAAkB,IAAI,GAAiB,CAGxC,CAAC,GAAS,QAAU,CAAC,EAAiB,CACxC,IAAM,EAAO,GACb,KAAK,SAAS,mBAAqB,CACjC,EAAK,OAAO,CACZ,KAAK,kBAAkB,OAAO,EAAK,CACnC,KAAK,SAAS,uBAAuB,CACrC,KAAK,SAAS,KAAK,eAAgB,GAAM,CACzC,KAAK,SAAS,KAAK,iBAAkB,CAAE,KAAM,KAAM,CAAC,CAChD,EAAO,SAAW,cACpB,EAAO,OAAS,SAElB,EAKN,qBAA6C,CAC3C,IAAM,EAAO,KAAK,UAAU,OAAQ,GAAM,CAAC,EAAE,OAAO,CACpD,GAAI,CAAC,KAAK,iBAAkB,OAAO,EACnC,IAAM,EAAS,KAAK,iBACpB,OAAO,EAAK,OAAQ,GAAM,CAAC,EAAE,UAAY,EAAE,UAAY,EAAO,CAIhE,uBAA+B,EAAc,EAA+D,CAC1G,GAAI,CAAC,KAAK,QAAS,OACnB,IAAM,EAAU,KAAK,QAAQ,wBAAwB,CACrD,GAAI,CAAC,EAAS,OACd,EAAI,wBAA0B,KAAK,MAAM,sBAAwB,mBACjE,IAAM,EAAO,KAAK,cAAc,EAAM,EAAI,CAC1C,EAAK,UAAU,IAAI,wCAAwC,CAC3D,EAAQ,YAAY,EAAK,CACzB,KAAK,8BAA8B,EAAK,CAI1C,8BAAsC,EAA4B,CAChE,IAAM,EAAO,KAAK,oBACd,GAAM,OAAS,QAAU,KAAK,QAAQ,cAAgB,wBACxD,KAAK,oBAAsB,CACzB,KAAM,6BACN,QAAS,EAAK,KACd,eACD,EAKL,kCACE,EACA,EACA,EACa,CACb,KAAK,gCAAgC,EAAK,CACxC,KAAM,6BACN,UACA,eACD,CAAC,CACF,IAAM,EAAU,KAAK,cAAc,EAAS,EAAI,CAC1C,EAAO,KAAK,cAAc,EAAc,EAAI,CAGlD,OAFA,EAAK,UAAU,IAAI,wCAAwC,CAC3D,EAAQ,YAAY,EAAK,CAClB,EAIT,gCAAwC,EAA8B,EAA2B,CAC/F,KAAI,wBAA0B,IAAA,GACzB,KAAK,OACV,IAAI,EAAO,OAAS,OAAQ,CAC1B,IAAM,EAAO,EAAO,KAAK,SAAS,EAAO,KAAK,MAC1C,GAAM,OAAS,gBACP,EAAK,UAAU,QAAU,GAC3B,IACN,EAAI,wBAA0B,KAAK,OAAO,kBACxC,cACC,EAAK,OAAQ,YAAwC,IAAA,GACvD,UAGI,EAAO,OAAS,6BAA8B,CACvD,IAAM,EAAU,EAAO,aAAa,SAAS,EAAO,aAAa,MAC7D,GAAS,OAAS,gBAAkB,EAAQ,UAAU,QAAU,GAAK,IACvE,EAAI,wBAA0B,KAAK,MAAM,sBAAwB,sBAKvE,uBAA+B,EAAqB,EAA2C,CAQ7F,OAPA,KAAK,gCAAgC,EAAK,EAAO,CAC7C,EAAO,OAAS,YACX,KAAK,uBAAuB,CAEjC,EAAO,OAAS,6BACX,KAAK,kCAAkC,EAAO,QAAS,EAAO,aAAc,EAAI,CAElF,KAAK,cAAc,EAAO,KAAM,EAAI,CAG7C,gBAAwB,EAAyB,CAC/C,IAAM,EAAM,KAAK,UAAU,KAAM,GAAM,EAAE,KAAO,EAAU,CACrD,GAAK,UACV,KAAK,kBAAkB,EAAI,SAAS,CAGtC,oCAAmD,CACjD,IAAM,EAAM,KAAK,OAAO,aAAa,IAErC,GADI,CAAC,GAAO,CAAC,KAAK,SACd,KAAK,+BAA+B,CAAE,OAE1C,IAAM,EAAgC,UAChC,EAAa,KAAK,iCAAiC,EAAW,CACpE,GAAI,EAAW,OAAS,EAAG,CACzB,KAAK,QAAQ,kBACX,EAAW,IAAK,IAAU,CACxB,MAAO,EAAK,MACZ,aAAgB,KAAK,YAAY,KAAK,gCAAgC,EAAM,EAAW,CAAC,CACxF,GAAI,EAAK,KAAO,CAAE,KAAM,EAAK,KAAM,CAAG,EAAE,CACzC,EAAE,CACJ,CACD,OAGF,KAAK,QAAQ,kBAAkB,CAC7B,CACE,MAAO,KAAK,MAAM,mBAClB,KAAM,SACN,aACE,KAAK,YAAY,CACf,MAAO,KAAK,MAAM,qBAClB,KAAM,gBACN,QAAS,CAAE,MAAK,CACjB,CAAC,CACL,CACD,CACE,MAAO,KAAK,MAAM,iBAClB,KAAM,UACN,aACE,KAAK,YAAY,CACf,MAAO,KAAK,MAAM,iBAClB,KAAM,cACN,QAAS,CAAE,MAAK,CACjB,CAAC,CACL,CACF,CAAC,CAIJ,kBAA0B,EAAwB,CAEhD,GAAI,KAAK,QAAU,KAAK,OAAO,QAAQ,OAAS,GAAK,CAAC,KAAK,OAAO,QAAQ,SAAS,EAAS,EAEtF,CAAC,KAAK,UAAU,KAAM,GAAM,EAAE,WAAa,EAAS,CACtD,OAGJ,KAAK,iBAAmB,EACxB,KAAK,sBAAsB,gBAAgB,GAAM,CAMvB,KAAK,eAAiB,MAAQ,IAAa,KAAK,eAExE,KAAK,cAAc,mBAAmB,EAAS,CAC/C,KAAK,SAAS,qBAAqB,EAAS,GAE5C,KAAK,cAAc,sBAAsB,CACzC,KAAK,SAAS,qBAAqB,KAAK,EAE1C,KAAK,SAAS,+BAA+B,GAAM,CAGnD,IAAK,IAAM,KAAO,KAAK,UAAW,CAChC,IAAM,EAAS,KAAK,SAAS,cAAc,qBAAqB,IAAI,OAAO,EAAI,GAAG,CAAC,IAAI,CAClF,IACD,EAAI,UAAY,EAAI,SAAW,EACjC,EAAO,UAAU,IAAI,8BAA8B,CAEnD,EAAO,UAAU,OAAO,8BAA8B,EAK1D,KAAK,SAAS,iBAAiB,mBAAmB,CAAC,QAAS,GAAO,CAC7D,aAAc,aAAe,EAAG,QAAQ,UAAe,EAAG,QAAQ,SAAc,EAClF,EAAG,UAAU,IAAI,8BAA8B,CACtC,aAAc,aACvB,EAAG,UAAU,OAAO,8BAA8B,EAEpD,CAGF,IAAM,EAAY,KAAK,UAAU,KAAM,GAAM,EAAE,OAAS,aAAe,EAAE,WAAa,EAAS,CACzF,EAAW,EAAY,KAAK,QAAQ,kBAAkB,EAAU,GAAG,CAAG,GACvE,IACH,KAAK,SAAS,YAAY,CAC1B,KAAK,oBAAsB,MAEzB,GAAY,IAGd,KAAK,oBAAsB,MAG7B,IAAM,EAAY,KAAK,OAAQ,aAAe,GAY9C,GAXA,KAAK,QAAQ,aAAa,EAAU,CAGpC,KAAK,SAAS,SAAS,EAAE,CAAC,CAE1B,0BAA4B,CAC1B,KAAK,SAAS,qBAAqB,EAAU,OAAO,EACpD,CAIE,KAAK,UAAU,IAAM,KAAK,OAAO,SAAS,UAAW,CACvD,IAAM,EAAM,KAAK,OAAO,QAAQ,WAC1B,SAAY,CAChB,GAAI,CACF,IAAM,EAAM,MAAM,KAAK,UAAU,IAAI,YAAY,EAAK,EAAS,CAC3D,IAAK,KAAK,oBAAsB,EAAI,SACxC,MAAM,KAAK,UAAU,IAAI,0BAA0B,EAAK,EAAS,MAC3D,MAGN,EAQR,MAAc,qBAAqC,CAC7C,CAAC,KAAK,UAAY,CAAC,KAAK,OAAO,SAAS,WAC5C,MAAM,KAAK,SAAS,QAAQ,CAC1B,OAAQ,KAAK,OAAO,QAAQ,QAAU,GACtC,MAAO,KAAK,OAAO,UACnB,UAAW,KAAK,OAAO,QAAQ,UAC/B,SAAU,KAAK,UACf,gBAAiB,KAAK,iBACtB,aAAc,KAAK,cACnB,cAAe,KAAK,eACpB,eAAgB,KAAK,QAAQ,WAAa,IAAI,IAC9C,aAAc,KAAK,QAAQ,SAAW,EAAE,CACxC,iBAAkB,KAAK,kBACvB,mBAAoB,KAAK,oBACzB,IAAK,KAAK,OAAO,aAAa,IAC/B,CAAC,CAGJ,iBAAyB,EAAsB,CAC7C,GAAI,CAGF,OAFK,EAAI,MAAM,CACA,IAAI,IAAI,EAAK,OAAO,SAAS,KAAK,CACnC,SAAW,OAAO,SAAS,OAFjB,QAGlB,CACN,MAAO,IAIX,gCAA+C,CAC7C,KAAK,8BAAgC,KAAK,OAAO,aAAa,KAAO,KAGvE,iCAAgD,CAC9C,KAAK,8BAAgC,KAGvC,+BAAiD,CAC/C,IAAM,EAAa,KAAK,OAAO,aAAa,IAC5C,OAAO,IAAe,IAAA,IAAa,EAAW,OAAS,GAAK,KAAK,gCAAkC,EAGrG,gCAAwC,EAAwB,CAC/C,UAAK,SAAS,cAAc,qBAAqB,IAAI,OAAO,EAAI,GAAG,CAAC,IAAI,EACzE,CAAC,KAAK,SACpB,IAAI,EAAI,OAAS,aAAe,EAAI,UAAY,CAAC,KAAK,qBAAqB,IAAI,EAAI,SAAS,CAAE,CAC5F,KAAK,qBAAqB,IAAI,EAAI,SAAS,CAC3C,KAAK,QAAQ,WAAW,EAAI,CAC5B,KAAK,QAAQ,oBAAoB,EAAI,GAAG,CACxC,OAEF,KAAK,QAAQ,WAAW,EAAI,EAG9B,MAAc,uBAAuB,EAA4B,CAC1D,KAAK,UACV,MAAM,KAAK,SAAS,eAAe,MAAW,KAAK,qBAAqB,CAAE,KAAK,QAAQ,CAGzF,MAAc,aAAa,EAAkB,EAAwE,CAEnH,OADK,KAAK,SACH,KAAK,SAAS,YAAY,EAAU,EAAU,CAD1B,KAS7B,MAAc,sBAAsB,EAAuC,CACzE,GAAI,CAAC,KAAK,UAAU,GAAI,OACxB,IAAM,EAAY,KAAK,OAAO,SAAS,UACvC,GAAI,CAAC,EAAW,OAEhB,IAAM,EAAS,KAAK,OAAO,SAAS,QAAU,GACxC,EAAQ,KAAK,OAAO,UAO1B,GAJA,MAAM,KAAK,SAAS,cAAc,EAAQ,EAAM,CAChD,KAAK,SAAS,qBAAqB,KAAK,SAAS,cAAc,KAAK,CAGhE,CAAC,EAAe,OAEpB,IAAM,EAAU,MAAM,KAAK,SAAS,IAAI,YAAY,EAAQ,EAAO,EAAU,CAC7E,GAAI,CAAC,GAAW,EAAQ,SAAS,SAAW,EAAG,OAG/C,IAAM,EAAa,KAAK,OAAO,aAAa,IAC5C,GAAI,GAAc,EAAQ,KAAO,EAAQ,MAAQ,EAAY,OA2B7D,GAxBA,KAAK,aAAe,GAGpB,KAAK,SAAS,sBAAsB,CAGpC,KAAK,iBAAmB,EAAQ,gBAChC,KAAK,cAAgB,EAAQ,aAEzB,KAAK,kBAAoB,KAAK,eAAiB,KAAK,iBAAmB,KAAK,gBAC9E,KAAK,iBAAmB,KAAK,eAE/B,KAAK,eAAiB,EAAQ,UAG1B,EAAQ,eACV,KAAK,OAAQ,QAAU,EAAQ,cAE7B,EAAQ,mBACV,KAAK,kBAAoB,EAAQ,iBACjC,KAAK,SAAS,cAAc,KAAK,kBAAkB,EAIjD,EAAQ,kBACV,IAAK,GAAM,CAAC,EAAO,KAAS,OAAO,QAAQ,EAAQ,kBAAkB,CAAE,CACrE,IAAM,EAAY,SAAS,cAAc,MAAM,CAC/C,EAAU,UAAY,EAAa,EAAK,CACxC,KAAK,OAAQ,UAAU,IAAI,EAAO,EAAU,CAKhD,IAAI,EAAY,EAGhB,IAAK,IAAM,KAAO,EAAQ,SAAU,CAClC,IAAM,EAAuB,CAC3B,GAAI,EAAI,GACR,KAAM,EAAI,KACV,UAAW,EAAI,UACf,OAAQ,EAAI,OACb,CAQD,GAPI,EAAI,WAAa,IAAA,KAAW,EAAQ,SAAW,EAAI,UACnD,EAAI,UAAY,IAAA,KAAW,EAAQ,QAAU,EAAI,SACjD,EAAI,SAAQ,EAAQ,OAAS,IAEjC,KAAK,UAAU,KAAK,EAAQ,CAGxB,EAAQ,OAAQ,SAEhB,EAAQ,OAAS,aAAe,EAAQ,UAAY,CAAC,KAAK,qBAAqB,IAAI,EAAQ,SAAS,GACtG,KAAK,qBAAqB,IAAI,EAAQ,SAAS,CAC/C,KAAK,SAAS,oBAAoB,EAAQ,GAAG,EAE/C,KAAK,SAAS,WAAW,EAAQ,CAGjC,IAAM,EAAQ,SAAS,EAAI,GAAG,QAAQ,OAAQ,GAAG,CAAE,GAAG,CAItD,GAHI,CAAC,MAAM,EAAM,EAAI,EAAQ,IAAW,EAAY,GAGhD,EAAQ,OAAS,aAAe,EAAQ,SAAU,CACpD,IAAM,EAAS,MAAM,KAAK,aAAa,EAAQ,SAAU,EAAQ,GAAG,CAChE,IACF,EAAQ,OAAS,EACjB,KAAK,qBAAqB,EAAQ,CAElC,OAAO,EAAQ,SAWrB,GALI,EAAY,KAAK,oBACnB,KAAK,kBAAoB,GAIvB,KAAK,iBAAkB,CACzB,IAAI,EAAM,MAAM,KAAK,SAAS,IAAI,YAAY,EAAW,KAAK,iBAAiB,CAC/E,AACE,IAAM,MAAM,KAAK,SAAS,IAAI,kBAAkB,EAAU,CAExD,IAAK,KAAK,oBAAsB,EAAI,SAI1C,GAAI,KAAK,iBAAkB,CACzB,IAAM,EAAW,CAAC,GAAG,KAAK,UAAU,CACjC,SAAS,CACT,KAAM,GAAM,EAAE,OAAS,aAAe,EAAE,WAAa,KAAK,kBAAoB,CAAC,EAAE,OAAO,CACvF,GAAY,KAAK,OAAQ,UAAU,IAAI,EAAS,GAAG,EACrD,KAAK,QAAQ,kBAAkB,EAAS,GAAG,CAK/C,GAAI,KAAK,iBAAkB,CACzB,IAAM,EAAS,KAAK,iBACpB,IAAK,IAAM,KAAO,KAAK,UACjB,EAAI,UAAY,EAAI,SAAW,IAClB,KAAK,SAAS,cAAc,qBAAqB,IAAI,OAAO,EAAI,GAAG,CAAC,IAAI,GAC/E,UAAU,IAAI,8BAA8B,CAGxD,KAAK,SAAS,iBAAiB,mBAAmB,CAAC,QAAS,GAAO,CAC7D,aAAc,aAAe,EAAG,QAAQ,UAAe,EAAG,QAAQ,SAAc,GAClF,EAAG,UAAU,IAAI,8BAA8B,EAEjD,CAIJ,GAAI,KAAK,OAAQ,QAAQ,OAAS,GAAK,KAAK,iBAAkB,CAC5D,IAAM,EAAkB,KAAK,OAAQ,QAAQ,KAAK,OAAQ,QAAQ,OAAS,GAC3E,GAAI,EAAiB,CACnB,IAAM,EAAe,CAAC,GAAG,KAAK,UAAU,CACrC,SAAS,CACT,KAAM,GAAM,EAAE,OAAS,aAAe,EAAE,WAAa,EAAgB,CACxE,GAAI,GAAc,SAAU,CAC1B,IAAM,EAAS,MAAM,KAAK,aAAa,EAAa,SAAU,EAAa,GAAG,CAC9E,GAAI,EAAQ,CACV,IAAM,EAAS,EAAO,SAAS,EAAO,MAClC,GACF,KAAK,QAAQ,aAAa,EAAO,KAAK,IAOhD,KAAK,cAAc,sBAAsB,CACzC,KAAK,SAAS,qBAAqB,KAAK,CAGxC,eAAiB,CACf,KAAK,SAAS,oBAAoB,EACjC,IAAI,CAYT,oBAAmC,CACjC,IAAM,EAAO,KAAK,mBAAmB,KAAK,CAC1C,GAAI,EAAM,CACR,IAAM,EAAM,KAAK,qBAAqB,CAChC,EAAK,KAAK,uBAAuB,EAAK,OAAQ,EAAI,CACxD,KAAK,SAAS,gBAAgB,EAAG,CACjC,KAAK,SAAS,yBAAyB,KAAK,kCAAkC,EAAK,OAAO,CAAC,CAC3F,KAAK,oBAAsB,EAAK,OAChC,IAAM,EAAU,KAAK,mBAAmB,OAAS,IAAM,KAAK,QAAQ,QAAQ,QAAU,GAAK,EAC3F,KAAK,SAAS,kBAAkB,EAAS,GAAO,EAAK,MAAM,CAC3D,OAIF,GAAI,KAAK,kBAAmB,CAC1B,KAAK,SAAS,iBAAiB,CAC/B,OAEF,KAAK,QAAQ,cAAc,CAG7B,gCAAwC,EAAuB,CAC7D,OAAO,EAAK,SAAS,EAAK,OAAO,OAAS,cAG5C,kCAA0C,EAAqC,CAC7E,OAAO,GAAQ,OAAS,OAAS,KAAK,gCAAgC,EAAO,KAAK,CAAG,GAGvF,qBAA6B,EAAmB,CAC9C,GAAI,IAAQ,GACV,KAAK,sBAAwB,CAAC,KAAK,sBACnC,KAAK,4BAA8B,KAC/B,KAAK,wBACP,GAAuC,KAAK,kBAAoB,GAAG,CACnE,KAAK,mBAAmB,QAAQ,CAChC,KAAK,kBAAoB,KACzB,KAAK,SAAS,iBAAiB,gCAAgC,CAAC,QAAS,GAAO,EAAG,QAAQ,CAAC,EAEzF,KAAK,wBACR,KAAK,wBAA0B,EAAE,CACjC,KAAK,4BAA8B,KACnC,IAAsB,UAGZ,KAAK,wBAAwB,QAAQ,EAAI,EAC1C,EACT,KAAK,wBAA0B,KAAK,wBAAwB,OAAQ,GAAM,IAAM,EAAI,CACpF,KAAK,4BAA8B,SAC9B,CACL,GAAI,KAAK,wBAAwB,QAAU,EAAY,0BAA2B,CAChF,KAAK,4BACH,KAAK,MAAM,gBAAkB,wBAAwB,EAAY,0BAA0B,WACzF,KAAK,0BAA4B,MACnC,qBAAqB,KAAK,wBAAwB,CAEpD,KAAK,wBAA0B,0BAA4B,CACzD,KAAK,wBAA0B,KAC/B,KAAK,sBAAsB,EAC3B,CACF,OAEF,KAAK,wBAA0B,CAAC,GAAG,KAAK,wBAAyB,EAAI,CACrE,KAAK,4BAA8B,KACnC,GAA4B,EAAI,CAIhC,KAAK,0BAA4B,MACnC,qBAAqB,KAAK,wBAAwB,CAEpD,KAAK,wBAA0B,0BAA4B,CACzD,KAAK,wBAA0B,KAC/B,KAAK,sBAAsB,EAC3B,CAQJ,sBAAqC,CACnC,IAAM,EAAU,KAAK,SAAS,cAAc,sBAAsB,CAClE,GAAI,CAAC,EAAS,CACZ,KAAK,SAAS,yBAAyB,KAAK,CAC5C,OAGF,IAAM,EAAc,EAAQ,cAAc,qCAAqC,CACzE,EAAO,GAAa,cAAc,6BAA6B,CACrE,GAAI,CAAC,GAAe,CAAC,EAAM,CACzB,KAAK,SAAS,yBAAyB,KAAK,CAC5C,OAIF,IAAM,EAAY,EAAY,cAAc,sCAAsC,CAMlF,GALI,GACF,EAAU,UAAU,OAAO,6CAA8C,KAAK,sBAAsB,CAIlG,KAAK,sBAAuB,CAC9B,IAAM,EAAQ,EAAK,iBAA8B,uCAAuC,CACxF,IAAK,IAAM,KAAQ,EAAO,CACxB,GAAI,EAAK,eAAe,UAAU,SAAS,yCAAyC,CAAE,CAEpF,IAAM,EAAU,EAAK,cACf,EAAW,KAAK,wBAAwB,SAAS,EAAK,QAAQ,IAAQ,CAC5E,EAAQ,UAAU,OAAO,mDAAoD,EAAS,CACtF,IAAM,EAAS,EAAQ,cAAiC,oCAAoC,CAC5F,GAAI,EAAQ,CACV,EAAO,QAAQ,SAAc,EAAW,OAAS,QACjD,EAAO,aAAa,eAAgB,EAAW,OAAS,QAAQ,CAChE,IAAM,EAAO,EAAO,cAAc,yCAAyC,CACrE,EAAQ,EAAO,cAAc,0CAA0C,CACzE,IACF,EAAK,UAAY,EACb,wLACA,8DAEF,IACF,EAAM,YAAc,EACf,KAAK,MAAM,yBAA2B,WACtC,KAAK,MAAM,uBAAyB,qBAG7C,SAEF,IAAM,EAAM,EAAK,QAAQ,IACnB,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,yCACpB,IAAM,EAAW,KAAK,wBAAwB,SAAS,EAAI,CACvD,GAAU,EAAQ,UAAU,IAAI,mDAAmD,CACvF,IAAM,EAAS,SAAS,cAAc,SAAS,CAC/C,EAAO,KAAO,SACd,EAAO,UAAY,mCACnB,EAAO,QAAQ,SAAc,EAAW,OAAS,QACjD,EAAO,aAAa,eAAgB,EAAW,OAAS,QAAQ,CAChE,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,wCACjB,EAAK,UAAY,EACb,wLACA,6DACJ,IAAM,EAAQ,SAAS,cAAc,OAAO,CAC5C,EAAM,UAAY,yCAClB,EAAM,YAAc,EACf,KAAK,MAAM,yBAA2B,WACtC,KAAK,MAAM,uBAAyB,oBACzC,EAAO,YAAY,EAAK,CACxB,EAAO,YAAY,EAAM,CACzB,EAAO,iBAAiB,QAAU,GAAM,CACtC,EAAE,iBAAiB,CACnB,KAAK,qBAAqB,EAAI,EAC9B,CACF,EAAK,WAAY,aAAa,EAAS,EAAK,CAC5C,EAAQ,YAAY,EAAO,CAC3B,EAAQ,YAAY,EAAK,CAEzB,EAAQ,UAAU,IAAI,gBAAgB,CACtC,EAAQ,iBAAiB,QAAU,GAAM,CAClC,EAAE,OAAuB,QAAQ,oCAAoC,GAC1E,EAAE,iBAAiB,CACnB,KAAK,qBAAqB,EAAI,GAC9B,MAEC,CAEL,IAAM,EAAW,EAAK,iBAAiB,0CAA0C,CACjF,IAAK,IAAM,KAAW,EAAU,CAC9B,IAAM,EAAO,EAAQ,cAAc,6BAA6B,CAC5D,GAAQ,EAAQ,aAClB,EAAQ,WAAW,aAAa,EAAM,EAAQ,CAC9C,EAAQ,QAAQ,GAQtB,GAFyB,EAAY,cAAc,wCAAwC,EACzE,QAAQ,CACtB,KAAK,sBAAuB,CAC9B,IAAM,EAAO,GAA+B,KAAK,wBAAyB,KAAK,qBAAqB,CAAC,CACjG,KAAK,kBACP,KAAK,SAAS,yBAAyB,EAAK,EAE5C,KAAK,SAAS,yBAAyB,KAAK,CAC5C,EAAY,YAAY,EAAK,OAG/B,KAAK,SAAS,yBAAyB,KAAK,CAIhD,6BAAqC,EAA8E,CACjH,GAAI,OAAO,GAAY,WAAY,EAAkB,OAAO,KAC5D,IAAM,EAAM,EACN,EAAM,KAAK,uBAAuB,EAAI,IAAO,CAC7C,EAAW,KAAK,uBAAuB,EAAI,UAAe,EAAI,UAAa,CAC7E,EAAW,EAKf,OAJI,OAAO,EAAI,UAAgB,UAAY,OAAO,SAAS,EAAI,SAAY,EAAI,EAAI,SAAc,IAC/F,EAAW,KAAK,IAAI,EAAG,KAAK,MAAM,EAAI,SAAY,CAAC,EAEjD,CAAC,GAAO,CAAC,EAAiB,KACvB,CAAE,MAAK,WAAU,WAAU,CAGpC,uBAA+B,EAAwB,CAGrD,OAFI,OAAO,GAAU,UAAY,EAAM,OAAS,EAAU,EACtD,OAAO,GAAU,UAAY,OAAO,SAAS,EAAM,CAAS,OAAO,EAAM,CACtE,GAGT,sBAA8B,EAAmE,CAC/F,GAAI,KAAK,OAAO,cAAgB,IAAA,GAC9B,GAAI,CACF,IAAM,EAAkB,KAAK,OAAO,YAAY,EAAO,CACnD,aAAkB,SAAS,EAAO,MAAO,GAAiB,QAAQ,MAAM,wBAAyB,EAAI,CAAC,OACnG,EAAK,CACZ,QAAQ,MAAM,wBAAyB,EAAI,CAG/C,GAAgB,EAAO,IAAK,EAAO,SAAS,CAC5C,IAAM,EAAS,CACb,GAAG,EACH,UAAW,KAAK,OAAO,SAAS,WAAa,KAC9C,CACD,EAAS,2BAA4B,EAAO,CAC5C,KAAK,SAAS,KAAK,YAAa,EAAO,CAClC,KAAK,mBAAmB,mBAAoB,EAA6C,CAC9F,KAAK,MACH,EAAe,KAAK,kBAAkB,CAAE,CACtC,mBAAoB,OACpB,sBAAuB,OAAO,YAAY,CAC1C,WAAY,EACZ,SAAU,KAAK,OAAO,SAAS,cAAgB,MAC/C,WAAY,EAAO,SACnB,IAAK,EAAO,IACb,CAAC,CACH,CAGD,KAAK,YACH,CACE,MAAO,KAAK,MAAM,iBAAmB,cACrC,KAAM,YACN,QAAS,CAAE,IAAK,EAAO,IAAK,UAAW,EAAO,SAAU,SAAU,EAAO,SAAU,CACpF,CACD,CAAE,cAAe,GAAM,CACxB,CACD,IAAM,EAAW,KAAK,MAAM,kBAAoB,gBAChD,KAAK,SAAS,cAAc,EAAS,CACrC,KAAK,SAAS,gBAAgB,CAOhC,qBAAuD,CACrD,IAAM,EAA+B,CACnC,SAAW,GAAW,CAEpB,GADA,GAA0B,EAAO,MAAO,EAAO,KAAK,CAChD,EAAO,OAAS,YAAa,CAC/B,IAAM,EAAY,KAAK,6BAA6B,EAAO,QAAQ,CACnE,GAAI,EAAW,CACb,KAAK,sBAAsB,EAAU,CACrC,QAIJ,GAAI,EAAO,OAAS,sBAAuB,CACzC,KAAK,SAAS,yBAAyB,GAAM,CAC7C,IAAM,EACJ,OAAO,EAAO,SAAY,UAAY,EAAO,UAAY,MAAQ,QAAS,EAAO,QAC7E,OAAQ,EAAO,QAAoC,IAAI,CACvD,GACF,GAAK,GAAsB,EAAK,EAAO,MAAM,CAE/C,EAAO,OAAS,eAKlB,GAHE,OAAO,EAAO,SAAY,UAAY,EAAO,UAAY,MAAQ,QAAS,EAAO,QAC7E,OAAQ,EAAO,QAAoC,IAAI,CACvD,GACmB,CAEvB,EAAO,OAAS,sBAClB,GAAwB,KAAK,wBAAwB,CAGvD,IAAM,EAAgB,EAAO,OAAS,aAAe,EAAO,OAAS,OACrE,KAAK,YAAY,EAAQ,EAAgB,CAAE,cAAe,GAAM,CAAG,IAAA,GAAU,EAE/E,eAAiB,GAAW,CAK1B,GAJA,GAAsB,EAAO,IAAI,CAGV,KAAK,OAAO,gBAAkB,IAAQ,KAAK,iBAAiB,EAAO,IAAI,CAS5F,EAAS,gCAAiC,CACxC,IAAK,EAAO,IACZ,IAAK,EAAO,IACZ,UAAW,KAAK,OAAO,SAAS,WAAa,KAC9C,CAAC,CACF,KAAK,uBAAuB,EAAO,IAAI,KAbpB,CACnB,IAAM,EAAe,EAAO,MAAM,MAAM,CAAG,EAAO,KAAK,MAAM,CAAG,EAAO,IACvE,KAAK,YAAY,CACf,MAAO,EACP,KAAM,sBACN,QAAS,CAAE,IAAK,EAAO,IAAK,CAC7B,CAAC,GAUN,YAAc,GAAW,CACvB,KAAK,sBAAsB,EAAO,EAEpC,gBAAkB,GAAY,CAE5B,GAAI,KAAK,oBAAqB,CAC5B,IAAM,EAAe,KAAK,SAAS,qBAAqB,EAAI,GAC5D,KAAK,mBAAmB,KAAK,CAAE,OAAQ,KAAK,oBAAqB,MAAO,EAAc,CAAC,CACnF,KAAK,mBAAmB,OAAS,EAAY,oBAAoB,KAAK,mBAAmB,OAAO,CAEtG,IAAM,EAAa,KAAK,qBAAqB,CACvC,EAAmD,CACvD,KAAM,OACN,SAAU,CACR,KAAM,CAAE,KAAM,qBAAsB,MAAO,CAAE,UAAS,CAAE,CACzD,CACF,CACK,EAAoB,KAAK,SAAS,cAAc,yBAAyB,CAC/E,GAAI,EAAmB,CACrB,IAAM,EAAY,KAAK,cAAc,EAAa,EAAW,CACzD,KAAK,mBACP,EAAU,QAAQ,SAAc,KAAK,kBAEvC,EAAkB,YAAY,EAAU,CACxC,EAAU,eAAe,CAAE,SAAU,OAAQ,MAAO,MAAO,CAAC,CAC5D,KAAK,SAAS,8BAA8B,CAE9C,GAAI,KAAK,OAAO,yBAA2B,GAAM,CAC/C,KAAK,0CAA0C,CAC/C,OAEF,IAAM,EAAkD,CACtD,KAAM,OACN,SAAU,CACR,KAAM,CACJ,KAAM,sBACN,MAAO,CAAE,UAAS,CACnB,CACF,CACF,CACD,KAAK,SAAS,gBAAgB,KAAK,cAAc,EAAY,EAAI,CAAC,CAClE,KAAK,SAAS,yBAAyB,GAAM,CAC7C,KAAK,oBAAsB,CAAE,KAAM,OAAQ,KAAM,EAAY,CAC7D,KAAK,SAAS,kBAAkB,GAAM,GAAO,KAAK,MAAM,yBAAyB,EAEnF,KAAM,KAAK,MACX,QAAS,KAAK,OAAO,QACrB,eAAgB,KAAK,OAAO,eAC5B,YAAa,KAAK,aAClB,aAAe,GAAS,CACtB,KAAK,aAAe,GAEtB,qBAAsB,KAAK,sBAC3B,uBAAwB,KAAK,wBAC7B,uBAAwB,EAAY,0BACpC,2BAA4B,KAAK,4BACjC,sBAAwB,GAAQ,CAC9B,KAAK,qBAAqB,EAAI,EAEhC,cAAe,KAAK,UAAU,eAAiB,IAAI,IACnD,kBAAmB,EAAK,IAAY,CAC7B,KAAK,uBAAuB,EAAK,EAAQ,EAEhD,SAAU,KAAK,kBAChB,CACD,OAAO,EAGT,MAAc,gBAAgB,EAAa,EAAiD,CAC1F,GAAI,CAAC,KAAK,SAAU,OACpB,IAAM,EAAS,KAAK,OAAO,SAAS,QAAU,GACxC,EAAQ,KAAK,OAAO,UAC1B,MAAM,KAAK,SAAS,eAAe,EAAQ,EAAO,EAAK,EAAQ,CAC/D,KAAK,SAAS,qBAAqB,KAAK,SAAS,cAAc,KAAK,CAItE,uBAA+B,EAAmB,CAChD,IAAM,EAAO,KAAK,SAAS,iBAAiB,+BAA+B,IAAI,OAAO,EAAI,CAAC,IAAI,CAC1F,MAAM,OACX,IAAK,IAAM,KAAO,EAAM,CACtB,GAAI,EAAE,aAAe,mBAAoB,SACzC,EAAI,UAAU,OAAO,oCAAoC,CACzD,IAAM,EAAM,EAAI,cAAc,MAAM,CAChC,GACF,EAAI,aAAa,OAAQ,EAAI,UAAU,SAAS,oCAAoC,CAAG,eAAiB,OAAO,EASrH,MAAc,uBAAuB,EAAa,EAAiD,CAEjG,IAAM,EAAY,EADD,KAAK,UAAU,cAAc,IAAI,EAAI,EAAI,IAEpD,EAAS,CACb,MACA,UACA,YACA,UAAW,KAAK,OAAO,SAAS,WAAa,KAC9C,CAED,EAAS,gCAAiC,EAAO,CACjD,KAAK,SAAS,KAAK,kBAAmB,EAAO,CAE7C,IAAM,EAAY,KAAK,gBAAgB,IAAI,2BAA2B,CACtE,GAAI,GAAa,EAAU,KAAO,EAAG,CACnC,IAAK,IAAM,KAAM,EACf,GAAI,CACF,IAAM,EAAS,EAAG,EAAO,CAEzB,IADgB,aAAkB,QAAU,MAAM,EAAS,KAC3C,GAAO,CACrB,KAAK,uBAAuB,EAAI,CAChC,KAAK,uBAAuB,2BAA4B,EAAO,CAC/D,aAEI,CACN,KAAK,uBAAuB,EAAI,CAChC,KAAK,uBAAuB,2BAA4B,EAAO,CAC/D,OAKA,KAAK,WACH,EACF,KAAK,SAAS,cAAc,IAAI,EAAI,CAEpC,KAAK,SAAS,cAAc,OAAO,EAAI,CAEzC,KAAK,SAAS,qBAAqB,KAAK,SAAS,cAAc,KAAK,EAEtE,OAIF,GADA,MAAM,KAAK,gBAAgB,EAAK,EAAQ,CACpC,EAAW,CACb,GAAoB,EAAI,CACxB,IAAM,EAAe,EAAQ,MAAkC,EAC/D,KAAK,YACH,CACE,MAAO,EACP,KAAM,OACN,QAAS,CAAE,MAAK,CACjB,CACD,CAAE,cAAe,GAAM,CACxB,EAIL,qBAAoC,CAC7B,QAAK,QAGV,IAAI,KAAK,oBAAqB,CAC5B,IAAM,EAAe,KAAK,QAAQ,qBAAqB,EAAI,GAC3D,KAAK,mBAAmB,KAAK,CAAE,OAAQ,KAAK,oBAAqB,MAAO,EAAc,CAAC,CACnF,KAAK,mBAAmB,OAAS,EAAY,oBAAoB,KAAK,mBAAmB,OAAO,CAGtG,KAAK,QAAQ,gBAAgB,KAAK,uBAAuB,CAAC,CAC1D,KAAK,QAAQ,yBAAyB,GAAM,CAC5C,KAAK,oBAAsB,CAAE,KAAM,YAAa,CAChD,KAAK,QAAQ,kBAAkB,GAAM,GAAO,KAAK,MAAM,mBAAmB,EAG5E,uBAA6C,CAC3C,IAAM,EAAY,KAAK,UAAU,qBAAqB,EAAI,EAAE,CAE5D,GAAI,EAAU,SAAW,EAAG,CAC1B,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,+BAElB,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,oCACjB,EAAK,UAAY,iTACjB,EAAM,YAAY,EAAK,CAEvB,IAAM,EAAO,SAAS,cAAc,IAAI,CAIxC,MAHA,GAAK,YAAc,KAAK,MAAM,sBAC9B,EAAM,YAAY,EAAK,CAEhB,EAIT,IAAM,EAA4D,EAAE,CAC9D,EAAsB,EAAE,CAE9B,IAAK,GAAM,CAAC,EAAG,KAAQ,EAAU,SAAS,CAAE,CAC1C,IAAM,EAAM,QAAQ,IACpB,EAAU,KAAK,EAAI,CACnB,EAAS,GAAO,CACd,KAAM,cACN,MAAO,CACL,QAAS,CACP,IAAK,EAAI,IACT,KAAM,EAAI,KACV,SAAU,EAAI,SACd,MAAO,EAAI,MACZ,CACF,CACF,CAGH,EAAS,KAAU,CAAE,KAAM,cAAe,SAAU,EAAW,CAE/D,IAAM,EAA4C,CAAE,KAAM,OAAQ,WAAU,CAC5E,OAAO,KAAK,cAAc,EAAM,KAAK,qBAAqB,CAAC,CAO7D,MAAc,mBAAmB,EAAmB,EAAgD,CAClG,IAAM,EAAY,KAAK,gBAAgB,IAAI,EAAU,CACjD,MAAC,GAAa,EAAU,OAAS,GAErC,IAAK,IAAM,KAAM,EACf,GAAI,CACF,IAAM,EAAS,EAAG,EAAO,CAEzB,IADgB,aAAkB,QAAU,MAAM,EAAS,KAC3C,GAAO,CACrB,KAAK,uBAAuB,EAAW,EAAO,CAC9C,aAEI,CACN,KAAK,uBAAuB,EAAW,EAAO,CAC9C,QAQN,uBAA+B,EAAmB,EAAwC,CACxF,GAAI,IAAc,mBAAoB,CACpC,IAAM,EAAY,KAAK,MAAM,oBACvB,EAAS,KAAK,eAAe,YAAa,EAAU,CACtD,KAAK,mBAAkB,EAAO,SAAW,KAAK,kBAClD,KAAK,UAAU,KAAK,EAAO,CAC3B,KAAK,SAAS,WAAW,EAAO,CAIlC,GAAI,IAAc,2BAA4B,CAC5C,IAAM,EAAY,KAAK,MAAM,2BACvB,EAAS,KAAK,eAAe,YAAa,EAAU,CACtD,KAAK,mBAAkB,EAAO,SAAW,KAAK,kBAClD,KAAK,UAAU,KAAK,EAAO,CAC3B,KAAK,SAAS,WAAW,EAAO,EAQpC,qBAA6B,EAA4B,CACvD,GAAI,CAAC,EAAQ,QAAU,CAAC,KAAK,QAAS,OACtC,IAAM,EAAO,EAAQ,OACf,EAAc,EAAK,SAAS,EAAK,MACvC,GAAI,CAAC,EAAa,OAElB,IAAM,EAAgB,EAAY,KAYlC,GATI,IAAkB,iBAQlB,IAAkB,mBAEpB,IAAkB,eAClB,EAAY,OAAQ,iBAAsB,IAC1C,KAAK,OAAO,yBAA2B,GAEvC,OAEF,IAAM,EAAgB,KAAK,qBAAqB,CAC1C,EAAoB,KAAK,SAAS,cAAc,yBAAyB,CAC/E,GAAI,CAAC,EAAmB,OAGxB,GAAI,IAAkB,sBAAuB,CAC3C,IAAM,EAAU,EAAY,OAAQ,QACpC,GAAI,CAAC,EAAS,OACd,IAAM,EAAqB,CACzB,KAAM,OACN,SAAU,CAAE,KAAM,CAAE,KAAM,qBAAsB,MAAO,CAAE,UAAS,CAAE,CAAE,CACvE,CACK,EAAS,KAAK,cAAc,EAAY,EAAc,CACxD,EAAQ,WAAU,EAAO,QAAQ,SAAc,EAAQ,UAC3D,EAAkB,YAAY,EAAO,CACrC,KAAK,SAAS,8BAA8B,CAC5C,OAGF,IAAM,EAAS,KAAK,cAAc,EAAM,EAAc,CAClD,EAAQ,WACV,EAAO,QAAQ,SAAc,EAAQ,UAEvC,EAAkB,YAAY,EAAO,CACrC,KAAK,SAAS,8BAA8B,CAG9C,eAAuB,EAA4B,EAA8B,CAE/E,MADA,MAAK,oBACE,CACL,GAAI,OAAO,KAAK,oBAChB,OACA,UACA,UAAW,KAAK,KAAK,CACrB,OAAQ,OACT,CAGH,aAAqB,EAAoC,CAEvD,MAAO,CAAE,GADI,GAAkB,EAAO,OAAO,CAC3B,GAAG,EAAO,KAAM,CAGpC,wBAAqD,CAEnD,OAAO,EADc,IAAiC,CACb,KAAK,OAAO,UAAU,SAAS,CAG1E,cAAsB,EAAc,EAA+C,CACjF,IAAM,EAAW,KAAK,wBAAwB,CACxC,EAAkB,KAAK,OAAO,UAAU,iBAAmB,GAC3D,GAAiB,EAAmB,IACxC,GAAa,EAAW,EAAc,EAAU,EAAgB,CAE5D,EAAW,KAAK,OAAO,UAAU,aAQvC,OAPK,EAOE,EAAS,EAAM,EALwC,CAC5D,WACA,kBACA,gBACD,CACsC,CAPjB,EAAc,EAAM,EAAQ,GAetD,SAAgB,IAAgC,CAC9C,OAAO,IAAI"}