issue-sticker 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,35 +15,92 @@ var __privateWrapper = (obj, member, setter, getter) => ({
15
15
  }
16
16
  });
17
17
  var _focused, _cleanup, _setup, _b, _provider, _providerCalled, _c, _online, _cleanup2, _setup2, _d, _gcTimeout, _e, _queryType, _initialState, _revertState, _cache, _client, _retryer, _defaultOptions, _abortSignalConsumed, _Query_instances, isInitialPausedFetch_fn, dispatch_fn, _f, _client2, _currentQuery, _currentQueryInitialState, _currentResult, _currentResultState, _currentResultOptions, _currentThenable, _selectError, _selectFn, _selectResult, _lastQueryWithDefinedData, _staleTimeoutId, _refetchIntervalId, _currentRefetchInterval, _trackedProps, _QueryObserver_instances, executeFetch_fn, updateStaleTimeout_fn, computeRefetchInterval_fn, updateRefetchInterval_fn, updateTimers_fn, clearStaleTimeout_fn, clearRefetchInterval_fn, updateQuery_fn, notify_fn, _g, _client3, _observers, _mutationCache, _retryer2, _Mutation_instances, dispatch_fn2, _h, _mutations, _scopes, _mutationId, _i, _client4, _currentResult2, _currentMutation, _mutateOptions, _MutationObserver_instances, updateResult_fn, notify_fn2, _j, _queries, _k, _queryCache, _mutationCache2, _defaultOptions2, _queryDefaults, _mutationDefaults, _mountCount, _unsubscribeFocus, _unsubscribeOnline, _l, _m;
18
- import { O as OVERLAY_LAYER_INDEX, c as createLucideIcon$1, d as usePortalContainer, a as cn$1, e as OVERLAY_LAYER, s as stopEscapePropagation, P as Presence, f as Primitive, g as composeEventHandlers, h as useDismissableLayerSurface, i as useComposedRefs$1, j as Portal$1, k as createContextScope, l as createSlot, D as DismissableLayer, m as useControllableState, n as useId$1, o as useLayoutEffect2, p as useCallbackRef, u as ungzip_1, q as PortalContainerProvider, r as registerThree } from "./three-_bdgDAmI.js";
19
- import { A as trimLogBackupPayload, B as createSafeLogBackup, D as getLogBackupSessionId, N as NetworkCollector, C as ConsoleCollector, W as WebSocketCollector, E as createIndexedDbLogBackup, b as buildJiraCreateFields, P as Popover, F as PopoverTrigger, G as Input, H as PopoverContent, L as Label, J as Select, K as SelectTrigger, M as SelectValue, O as SelectContent, Q as SelectItem, R as JIRA_ISSUE_TYPE_VALUE_KEY, T as getJiraMetadata, U as buildJiraIssueFields, V as getJiraIssueTypeLabel, X as Button, Y as Root2$2, Z as Trigger$2, _ as ChevronDown, $ as Portal, a0 as Content2$1, a1 as Check, a2 as X$1, a3 as DatePicker, e as buildNotionCreateFields, a4 as getNotionPageMetadata, a5 as buildNotionFieldViews, c as captureWithModernScreenshot, p as pristineFetch, d as directUploadToSignedUrl, a6 as DirectUploadError, a7 as toast, a8 as getSnapshot, a9 as subscribe$1, a as uploadSignedMediaWithApi, t as sanitizeString, aa as getRegisteredUploadId, f as buildGuestOwnerQuery, j as devLogger, x as getCurrentPageUrl, ab as ChevronRight, ac as ReactRemoveScroll, ad as hideOthers, ae as useFocusGuards, af as FocusScope, ag as getExposedJiraFields, ah as isJiraFieldExposed, ai as normalizeExposedJiraFields, z as preloadModernScreenshot, u as uploadScreenshotDataUrls, k as filterExcludeServiceRelatedLogs, l as filterExcludeServiceRelatedConsoleLogs, s as selectNetworkLogsForSubmit, m as compactNetworkLogsForTransport, n as CONSOLE_LOG_SUBMIT_LIMIT, g as getElementPosition, o as buildExternalUserPayload, v as serializeSocketTranscript, w as uploadSocketTranscript, q as shrinkNetworkLogsToFit, r as measureJsonBytes, i as buildIssueIntegrationsPayload, y as SessionReplayCaptureController, aj as resolveJiraIssueTypeId, ak as pickJiraIssueTypeFields, al as switchJiraIssueType, am as getPrimaryJiraMapping, an as createCollection, ao as useDirection, ap as getPrimaryNotionMapping, aq as buttonVariants, ar as findElementBySelector, as as findElementByXPath, S as ScreenRecordingController, h as composeIssueFilterGroups, at as OnboardingProvider, au as useOnboardingTarget, av as useOnboardingFlow, aw as PopoverAnchor } from "./externalUserPayload-DNGAp9bP.js";
18
+ import { O as OVERLAY_LAYER_INDEX, c as createLucideIcon$1, d as usePortalContainer, a as cn$1, e as OVERLAY_LAYER, s as stopEscapePropagation, f as useControllableState, g as useId$1, P as Presence, h as Primitive, i as composeEventHandlers, j as useDismissableLayerSurface, k as useComposedRefs$1, l as Portal$1, m as createContextScope, n as createSlot, D as DismissableLayer, o as useLayoutEffect2, p as useCallbackRef, u as ungzip_1, q as PortalContainerProvider, r as registerThree } from "./three-B228j4M6.js";
19
+ import { L as toast, M as trimLogBackupPayload, O as createSafeLogBackup, P as getLogBackupSessionId, N as NetworkCollector, C as ConsoleCollector, W as WebSocketCollector, Q as createIndexedDbLogBackup, e as buildJiraCreateFields, R as Popover, T as PopoverTrigger, U as Input, X as PopoverContent, Y as Label, Z as Select, _ as SelectTrigger, $ as SelectValue, a0 as SelectContent, a1 as SelectItem, a2 as JIRA_ISSUE_TYPE_VALUE_KEY, a3 as getJiraMetadata, a4 as buildJiraIssueFields, a5 as getJiraIssueTypeLabel, a6 as Button, a7 as Root2$2, a8 as Trigger$2, a9 as ChevronDown, aa as Portal, ab as Content2$1, ac as Check, ad as X$1, ae as DatePicker, f as buildNotionCreateFields, af as getNotionPageMetadata, ag as buildNotionFieldViews, b as captureWithModernScreenshot, p as pristineFetch, d as directUploadToSignedUrl, ah as DirectUploadError, a as uploadSignedMediaWithApi, v as sanitizeString, ai as getRegisteredUploadId, h as buildGuestOwnerQuery, z as createIssueUploadQueues, G as loadTusClient, H as createIndexedDbPendingUploadStore, K as createMediaUploadApi, J as ATTACHMENT_UPLOAD_DB_NAME, V as VIDEO_UPLOAD_DB_NAME, aj as getSnapshot, ak as subscribe$1, k as devLogger, y as getCurrentPageUrl, al as ChevronRight, am as ReactRemoveScroll, an as hideOthers, ao as useFocusGuards, ap as FocusScope, aq as createShadowDismissRoot, ar as getExposedJiraFields, as as isJiraFieldExposed, at as normalizeExposedJiraFields, c as captureViewportWithElementCrop, B as preloadModernScreenshot, g as getElementPosition, u as uploadScreenshotDataUrls, l as filterExcludeServiceRelatedLogs, m as filterExcludeServiceRelatedConsoleLogs, s as selectNetworkLogsForSubmit, n as compactNetworkLogsForTransport, o as CONSOLE_LOG_SUBMIT_LIMIT, q as buildExternalUserPayload, w as collectAttachmentUploads, x as hasReplayUpload, r as shrinkNetworkLogsToFit, t as measureJsonBytes, j as buildIssueIntegrationsPayload, D as enqueueIssueUploads, E as normalizeUploadMimeType, A as SessionReplayCaptureController, au as resolveJiraIssueTypeId, av as pickJiraIssueTypeFields, aw as switchJiraIssueType, ax as getPrimaryJiraMapping, ay as createCollection, az as useDirection, aA as useShadowOutsideDismiss, aB as getPrimaryNotionMapping, aC as buttonVariants, aD as TEST_ATTR_KEYS, aE as jaccard, aF as textSimilarity, aG as collectHeadings, aH as buildElementLocator, aI as normalizeForCompare, aJ as CONTAINER_SELECTOR, aK as safeRole, aL as safeAccessibleName, aM as sameDigitGroups, aN as labelSimilarityKeepingDigits, aO as landmarkRole, aP as findElementBySelector, aQ as findElementByXPath, S as ScreenRecordingController, i as composeIssueFilterGroups, aR as OnboardingProvider, aS as useOnboardingTarget, aT as useOnboardingFlow, aU as PopoverAnchor } from "./externalUserPayload-DWcXyz2U.js";
20
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
20
21
  import * as React from "react";
21
22
  import { useRef, useState, useEffect, useId, useLayoutEffect, useCallback, forwardRef, createElement, createContext, useContext, Component, Suspense, useInsertionEffect, useMemo, Children, isValidElement, Fragment as Fragment$1, useSyncExternalStore, useDeferredValue } from "react";
22
- import { jsxs, jsx, Fragment } from "react/jsx-runtime";
23
23
  import { createRoot } from "react-dom/client";
24
24
  import { createPortal } from "react-dom";
25
- import { T as TooltipProvider, a as Tooltip, b as TooltipTrigger, c as TooltipContent } from "./tooltip-UijEbdDX.js";
26
- function readReleaseMarker() {
27
- try {
28
- const g = globalThis.__ISSUE_STICKER_RELEASE__;
29
- if (g && typeof g.id === "string" && g.id.length > 0) {
30
- return {
31
- release: g.id,
32
- release_source: typeof g.source === "string" && g.source.length > 0 ? g.source : "global"
33
- };
34
- }
35
- if (typeof document !== "undefined") {
36
- const meta = document.querySelector('meta[name="issue-sticker:release"]');
37
- const content2 = meta == null ? void 0 : meta.getAttribute("content");
38
- if (content2 && content2.length > 0) {
39
- return { release: content2, release_source: "meta" };
25
+ import { T as TooltipProvider, a as Tooltip, b as TooltipTrigger, c as TooltipContent } from "./tooltip-CT7e9xbU.js";
26
+ import { g as getDefaultExportFromCjs } from "./_commonjsHelpers-DWwsNxpa.js";
27
+ const ISSUE_STICKER_REPORT_FORM_CLASS = "issue-sticker-report-form";
28
+ function summarizeVideoUploads(states) {
29
+ if (states.length === 0) return void 0;
30
+ const remaining = states.filter((state) => state.status !== "failed");
31
+ const current = states.find((state) => state.status === "uploading");
32
+ const percent2 = current && current.bytesTotal > 0 ? Math.floor(current.bytesSent / current.bytesTotal * 100) : 0;
33
+ return { percent: percent2, count: remaining.length, failed: remaining.length === 0 };
34
+ }
35
+ const VIDEO_UPLOAD_COPY = {
36
+ created: "이슈가 생성되었습니다. 영상 업로드가 끝나면 이슈에 첨부됩니다.",
37
+ resumed: (count) => `업로드하지 못한 영상 ${count}개를 이어서 업로드합니다.`
38
+ };
39
+ function visibleKinds(kinds) {
40
+ const video = kinds.includes("video");
41
+ const replay = kinds.includes("replay");
42
+ if (video && replay) return "both";
43
+ if (video) return "video";
44
+ return replay ? "replay" : null;
45
+ }
46
+ const ISSUE_UPLOAD_COPY = {
47
+ failedMessage: "네트워크 연결을 확인한 뒤 다시 시도해주세요.",
48
+ retry: "다시 시도",
49
+ failedTitle: {
50
+ video: "영상 업로드 실패",
51
+ replay: "세션 리플레이 업로드 실패",
52
+ both: "영상·세션 리플레이 업로드 실패"
53
+ },
54
+ aborted: {
55
+ video: "영상을 업로드하지 못했습니다.",
56
+ replay: "세션 리플레이를 업로드하지 못했습니다.",
57
+ both: "영상과 세션 리플레이를 업로드하지 못했습니다."
58
+ },
59
+ /** 완료는 영상이 있을 때만 알린다. 리플레이만 붙은 경우의 문구는 없다. */
60
+ attached: {
61
+ video: "영상이 이슈에 첨부되었습니다.",
62
+ both: "영상과 세션 리플레이가 이슈에 첨부되었습니다."
63
+ }
64
+ };
65
+ function createIssueUploadToaster() {
66
+ const failureToastIds = /* @__PURE__ */ new Map();
67
+ return {
68
+ show(notice, retry) {
69
+ const previous2 = failureToastIds.get(notice.issueId);
70
+ if (previous2 !== void 0) {
71
+ toast.dismiss(previous2);
72
+ failureToastIds.delete(notice.issueId);
73
+ }
74
+ const failed = visibleKinds(notice.failed);
75
+ if (failed) {
76
+ const id2 = toast.error(ISSUE_UPLOAD_COPY.failedMessage, {
77
+ title: ISSUE_UPLOAD_COPY.failedTitle[failed],
78
+ duration: 0,
79
+ action: {
80
+ label: ISSUE_UPLOAD_COPY.retry,
81
+ onClick: () => {
82
+ failureToastIds.delete(notice.issueId);
83
+ retry(notice.issueId);
84
+ }
85
+ }
86
+ });
87
+ failureToastIds.set(notice.issueId, id2);
88
+ return;
89
+ }
90
+ const aborted = visibleKinds(notice.aborted);
91
+ if (aborted) {
92
+ toast.error(ISSUE_UPLOAD_COPY.aborted[aborted], {
93
+ title: ISSUE_UPLOAD_COPY.failedTitle[aborted]
94
+ });
95
+ return;
96
+ }
97
+ const completed = visibleKinds(notice.completed);
98
+ if (completed === "video" || completed === "both") {
99
+ toast.success(ISSUE_UPLOAD_COPY.attached[completed]);
40
100
  }
41
101
  }
42
- } catch {
43
- }
44
- return null;
102
+ };
45
103
  }
46
- const ISSUE_STICKER_REPORT_FORM_CLASS = "issue-sticker-report-form";
47
104
  const DEFAULT_DEBOUNCE_MS = 1e3;
48
105
  function bindLogBackup(options) {
49
106
  const { store, sessionId, snapshot, restoreInto } = options;
@@ -235,6 +292,82 @@ function getIntegrationFailureMessage(service) {
235
292
  const action = FAILED_ACTION_TEXT[service] ?? "연동 작업을 마치지 못했습니다.";
236
293
  return `이슈는 저장했지만 ${action}`;
237
294
  }
295
+ function debounce(func, debounceMs, { signal, edges } = {}) {
296
+ let pendingThis = void 0;
297
+ let pendingArgs = null;
298
+ const leading = edges != null && edges.includes("leading");
299
+ const trailing = edges == null || edges.includes("trailing");
300
+ const invoke = () => {
301
+ if (pendingArgs !== null) {
302
+ func.apply(pendingThis, pendingArgs);
303
+ pendingThis = void 0;
304
+ pendingArgs = null;
305
+ }
306
+ };
307
+ const onTimerEnd = () => {
308
+ if (trailing) invoke();
309
+ cancel();
310
+ };
311
+ let timeoutId = null;
312
+ const schedule = () => {
313
+ if (timeoutId != null) clearTimeout(timeoutId);
314
+ timeoutId = setTimeout(() => {
315
+ timeoutId = null;
316
+ onTimerEnd();
317
+ }, debounceMs);
318
+ };
319
+ const cancelTimer = () => {
320
+ if (timeoutId !== null) {
321
+ clearTimeout(timeoutId);
322
+ timeoutId = null;
323
+ }
324
+ };
325
+ const cancel = () => {
326
+ cancelTimer();
327
+ pendingThis = void 0;
328
+ pendingArgs = null;
329
+ };
330
+ const flush = () => {
331
+ invoke();
332
+ };
333
+ const debounced = function(...args) {
334
+ if (signal == null ? void 0 : signal.aborted) return;
335
+ pendingThis = this;
336
+ pendingArgs = args;
337
+ const isFirstCall = timeoutId == null;
338
+ schedule();
339
+ if (leading && isFirstCall) invoke();
340
+ };
341
+ debounced.schedule = schedule;
342
+ debounced.cancel = cancel;
343
+ debounced.flush = flush;
344
+ signal == null ? void 0 : signal.addEventListener("abort", cancel, { once: true });
345
+ return debounced;
346
+ }
347
+ function throttle(func, throttleMs, { signal, edges = ["leading", "trailing"] } = {}) {
348
+ let pendingAt = null;
349
+ const debounced = debounce(function(...args) {
350
+ pendingAt = Date.now();
351
+ func.apply(this, args);
352
+ }, throttleMs, {
353
+ signal,
354
+ edges
355
+ });
356
+ const throttled = function(...args) {
357
+ if (pendingAt == null) pendingAt = Date.now();
358
+ if (Date.now() - pendingAt >= throttleMs) {
359
+ pendingAt = Date.now();
360
+ func.apply(this, args);
361
+ debounced.cancel();
362
+ debounced.schedule();
363
+ return;
364
+ }
365
+ debounced.apply(this, args);
366
+ };
367
+ throttled.cancel = debounced.cancel;
368
+ throttled.flush = debounced.flush;
369
+ return throttled;
370
+ }
238
371
  function getJiraFieldDefaultValues(exposedFields, fieldDefinitions = []) {
239
372
  const values = {};
240
373
  for (const field of buildJiraCreateFields(exposedFields, fieldDefinitions)) {
@@ -447,17 +580,148 @@ const HOST_POINTER_GUARD_EVENTS = [
447
580
  "touchstart",
448
581
  "touchend"
449
582
  ];
583
+ const HOST_CAPTURE_GUARD_EVENTS = [
584
+ "pointerdown",
585
+ "mousedown",
586
+ "click",
587
+ "dblclick",
588
+ "auxclick",
589
+ "contextmenu"
590
+ ];
591
+ const CLICK_EVENTS = /* @__PURE__ */ new Set(["click", "dblclick", "auxclick", "contextmenu"]);
592
+ const ACTIVATION_INPUT_TYPES = /* @__PURE__ */ new Set([
593
+ "checkbox",
594
+ "radio",
595
+ "submit",
596
+ "reset",
597
+ "image",
598
+ "file",
599
+ "color",
600
+ "date",
601
+ "datetime-local",
602
+ "month",
603
+ "time",
604
+ "week"
605
+ ]);
606
+ function hasActivationBehavior(node2) {
607
+ if (!(node2 instanceof Element)) return false;
608
+ if (node2.matches("a[href], area[href], label, summary, [popovertarget], [commandfor]")) {
609
+ return true;
610
+ }
611
+ if (node2 instanceof HTMLButtonElement) return node2.type !== "button" && node2.form !== null;
612
+ if (node2 instanceof HTMLInputElement) return ACTIVATION_INPUT_TYPES.has(node2.type);
613
+ return false;
614
+ }
615
+ function cloneInsideShadow(event) {
616
+ const init = {
617
+ bubbles: event.bubbles,
618
+ cancelable: event.cancelable,
619
+ composed: false,
620
+ view: event.view,
621
+ detail: event.detail,
622
+ screenX: event.screenX,
623
+ screenY: event.screenY,
624
+ clientX: event.clientX,
625
+ clientY: event.clientY,
626
+ movementX: event.movementX,
627
+ movementY: event.movementY,
628
+ button: event.button,
629
+ buttons: event.buttons,
630
+ relatedTarget: event.relatedTarget,
631
+ ctrlKey: event.ctrlKey,
632
+ shiftKey: event.shiftKey,
633
+ altKey: event.altKey,
634
+ metaKey: event.metaKey
635
+ };
636
+ if (typeof PointerEvent !== "undefined" && event instanceof PointerEvent) {
637
+ return new PointerEvent(event.type, {
638
+ ...init,
639
+ pointerId: event.pointerId,
640
+ pointerType: event.pointerType,
641
+ isPrimary: event.isPrimary,
642
+ width: event.width,
643
+ height: event.height,
644
+ pressure: event.pressure,
645
+ tangentialPressure: event.tangentialPressure,
646
+ tiltX: event.tiltX,
647
+ tiltY: event.tiltY,
648
+ twist: event.twist
649
+ });
650
+ }
651
+ return new MouseEvent(event.type, init);
652
+ }
450
653
  function attachHostPointerGuard(shadowRoot) {
654
+ const view = shadowRoot.ownerDocument.defaultView;
451
655
  const stopLeakingToPage = (event) => {
452
656
  event.stopPropagation();
453
657
  };
658
+ const forwardInsideWidget = (event) => {
659
+ const path2 = event.composedPath();
660
+ const origin = path2[0];
661
+ if (!(origin instanceof Node) || origin.getRootNode() !== shadowRoot) return;
662
+ if (CLICK_EVENTS.has(event.type)) {
663
+ const insideWidget = path2.slice(0, path2.indexOf(shadowRoot));
664
+ if (insideWidget.some(hasActivationBehavior)) return;
665
+ }
666
+ event.stopImmediatePropagation();
667
+ if (!origin.dispatchEvent(cloneInsideShadow(event))) event.preventDefault();
668
+ };
454
669
  HOST_POINTER_GUARD_EVENTS.forEach((type) => {
455
670
  shadowRoot.addEventListener(type, stopLeakingToPage);
456
671
  });
672
+ HOST_CAPTURE_GUARD_EVENTS.forEach((type) => {
673
+ view == null ? void 0 : view.addEventListener(type, forwardInsideWidget, true);
674
+ });
457
675
  return () => {
458
676
  HOST_POINTER_GUARD_EVENTS.forEach((type) => {
459
677
  shadowRoot.removeEventListener(type, stopLeakingToPage);
460
678
  });
679
+ HOST_CAPTURE_GUARD_EVENTS.forEach((type) => {
680
+ view == null ? void 0 : view.removeEventListener(type, forwardInsideWidget, true);
681
+ });
682
+ };
683
+ }
684
+ const HOST_INTERACTION_GUARD_EVENTS = [
685
+ "focusin",
686
+ "focusout",
687
+ "input",
688
+ "beforeinput",
689
+ "wheel",
690
+ "touchmove",
691
+ "dragstart",
692
+ "dragenter",
693
+ "dragover",
694
+ "dragleave",
695
+ "drop",
696
+ "dragend"
697
+ ];
698
+ function attachHostInteractionGuard(shadowRoot) {
699
+ const host = shadowRoot.host;
700
+ const view = shadowRoot.ownerDocument.defaultView;
701
+ const stopLeakingToPage = (event) => {
702
+ event.stopPropagation();
703
+ };
704
+ const hideFocusLeavingForWidget = (event) => {
705
+ if (event.relatedTarget !== host) return;
706
+ if (event.composedPath().includes(host)) return;
707
+ event.stopPropagation();
708
+ };
709
+ const hideWidgetFocus = (event) => {
710
+ if (event.composedPath().includes(host)) event.stopImmediatePropagation();
711
+ };
712
+ HOST_INTERACTION_GUARD_EVENTS.forEach((type) => {
713
+ shadowRoot.addEventListener(type, stopLeakingToPage, { passive: true });
714
+ });
715
+ view == null ? void 0 : view.addEventListener("focusout", hideFocusLeavingForWidget, true);
716
+ view == null ? void 0 : view.addEventListener("blur", hideFocusLeavingForWidget, true);
717
+ view == null ? void 0 : view.addEventListener("focus", hideWidgetFocus, true);
718
+ return () => {
719
+ HOST_INTERACTION_GUARD_EVENTS.forEach((type) => {
720
+ shadowRoot.removeEventListener(type, stopLeakingToPage);
721
+ });
722
+ view == null ? void 0 : view.removeEventListener("focusout", hideFocusLeavingForWidget, true);
723
+ view == null ? void 0 : view.removeEventListener("blur", hideFocusLeavingForWidget, true);
724
+ view == null ? void 0 : view.removeEventListener("focus", hideWidgetFocus, true);
461
725
  };
462
726
  }
463
727
  const RUNTIME_OWNER_MARKER_KEY = "__issueStickerRuntimeOwner";
@@ -1379,7 +1643,7 @@ function MultiSelect({
1379
1643
  const contentRef = React.useRef(null);
1380
1644
  const portalContainer = usePortalContainer();
1381
1645
  React.useEffect(() => {
1382
- var _a, _b2;
1646
+ var _a, _b2, _c2, _d2;
1383
1647
  if (!open) {
1384
1648
  return;
1385
1649
  }
@@ -1390,8 +1654,12 @@ function MultiSelect({
1390
1654
  }
1391
1655
  setOpen(false);
1392
1656
  };
1393
- ownerDocument.addEventListener("click", closeOnOutsideClick, true);
1394
- return () => ownerDocument.removeEventListener("click", closeOnOutsideClick, true);
1657
+ const scopes = /* @__PURE__ */ new Set([
1658
+ ownerDocument,
1659
+ ((_c2 = contentRef.current) == null ? void 0 : _c2.getRootNode()) ?? ((_d2 = triggerRef.current) == null ? void 0 : _d2.getRootNode()) ?? ownerDocument
1660
+ ]);
1661
+ scopes.forEach((scope) => scope.addEventListener("click", closeOnOutsideClick, true));
1662
+ return () => scopes.forEach((scope) => scope.removeEventListener("click", closeOnOutsideClick, true));
1395
1663
  }, [open]);
1396
1664
  const emitChange = (next) => {
1397
1665
  onValueChange == null ? void 0 : onValueChange(next);
@@ -5024,149 +5292,44 @@ class WebPlatform {
5024
5292
  this.eventBus = eventBus;
5025
5293
  }
5026
5294
  }
5027
- class EventBus {
5028
- constructor() {
5029
- this.listeners = /* @__PURE__ */ new Map();
5030
- }
5031
- on(event, callback) {
5032
- if (!this.listeners.has(event)) {
5033
- this.listeners.set(event, /* @__PURE__ */ new Set());
5034
- }
5035
- this.listeners.get(event).add(callback);
5036
- return () => this.off(event, callback);
5037
- }
5038
- off(event, callback) {
5039
- var _a;
5040
- (_a = this.listeners.get(event)) == null ? void 0 : _a.delete(callback);
5041
- }
5042
- emit(event, ...args) {
5043
- var _a, _b2;
5044
- (_a = this.listeners.get(event)) == null ? void 0 : _a.forEach((cb) => cb(...args));
5045
- (_b2 = this.listeners.get("*")) == null ? void 0 : _b2.forEach((cb) => cb(event, ...args));
5046
- }
5047
- once(event, callback) {
5048
- const wrapper = (...args) => {
5049
- this.off(event, wrapper);
5050
- callback(...args);
5051
- };
5052
- return this.on(event, wrapper);
5053
- }
5054
- removeAllListeners() {
5055
- this.listeners.clear();
5056
- }
5295
+ function isHttpStatusError(error) {
5296
+ return typeof error === "object" && error !== null && typeof error.status === "number";
5057
5297
  }
5058
- const DEFAULT_FLAGS = {
5059
- networkLogs: true,
5060
- consoleLogs: true,
5061
- screenshot: true,
5062
- screenRecording: true,
5063
- stickers: true,
5064
- keyboardShortcuts: true,
5065
- jiraIntegration: false,
5066
- fab: true
5067
- };
5068
- class FeatureFlags {
5069
- constructor(overrides) {
5070
- this.flags = { ...DEFAULT_FLAGS, ...overrides };
5071
- }
5072
- get(name2) {
5073
- return this.flags[name2];
5074
- }
5075
- set(name2, enabled) {
5076
- this.flags[name2] = enabled;
5077
- }
5078
- getAll() {
5079
- return { ...this.flags };
5080
- }
5298
+ const MAX_RETRIES = 3;
5299
+ const MAX_BACKOFF_MS = 3e4;
5300
+ const MAX_RETRY_AFTER_MS = 6e4;
5301
+ function parseRetryAfterMs(value, now2 = Date.now()) {
5302
+ const trimmed = value == null ? void 0 : value.trim();
5303
+ if (!trimmed) return void 0;
5304
+ if (/^\d+$/.test(trimmed)) return Number(trimmed) * 1e3;
5305
+ const date = Date.parse(trimmed);
5306
+ return Number.isNaN(date) ? void 0 : Math.max(0, date - now2);
5081
5307
  }
5082
- const _SDKManager = class _SDKManager {
5083
- constructor() {
5084
- this._config = null;
5085
- this._platform = null;
5086
- this._eventBus = null;
5087
- this._featureFlags = null;
5088
- this._initialized = false;
5089
- }
5090
- static getInstance() {
5091
- if (!_SDKManager.instance) {
5092
- _SDKManager.instance = new _SDKManager();
5093
- }
5094
- return _SDKManager.instance;
5095
- }
5096
- init(config) {
5097
- if (this._initialized) {
5098
- this.updateConfig(config);
5099
- return;
5100
- }
5101
- this._config = config;
5102
- this._eventBus = new EventBus();
5103
- this._platform = new WebPlatform(this._eventBus);
5104
- this._featureFlags = new FeatureFlags({});
5105
- this._initialized = true;
5106
- }
5107
- updateConfig(config) {
5108
- if (!this._initialized) {
5109
- this.init(config);
5110
- return;
5111
- }
5112
- this._config = config;
5113
- }
5114
- destroy() {
5115
- var _a;
5116
- (_a = this._eventBus) == null ? void 0 : _a.removeAllListeners();
5117
- this._config = null;
5118
- this._platform = null;
5119
- this._eventBus = null;
5120
- this._featureFlags = null;
5121
- this._initialized = false;
5122
- }
5123
- isInitialized() {
5124
- return this._initialized;
5125
- }
5126
- get config() {
5127
- if (!this._config) throw new Error("SDK not initialized");
5128
- return this._config;
5129
- }
5130
- get platform() {
5131
- if (!this._platform) throw new Error("SDK not initialized");
5132
- return this._platform;
5133
- }
5134
- get eventBus() {
5135
- if (!this._eventBus) throw new Error("SDK not initialized");
5136
- return this._eventBus;
5137
- }
5138
- get featureFlags() {
5139
- if (!this._featureFlags) throw new Error("SDK not initialized");
5140
- return this._featureFlags;
5141
- }
5142
- static resetInstance() {
5143
- if (_SDKManager.instance) {
5144
- _SDKManager.instance.destroy();
5145
- _SDKManager.instance = null;
5146
- }
5147
- }
5148
- };
5149
- _SDKManager.instance = null;
5150
- let SDKManager = _SDKManager;
5151
- const pendingListeners = /* @__PURE__ */ new Map();
5152
- function addPendingSdkEventListener(event, callback) {
5153
- if (!pendingListeners.has(event)) {
5154
- pendingListeners.set(event, /* @__PURE__ */ new Set());
5155
- }
5156
- pendingListeners.get(event).add(callback);
5308
+ function retryAfterMsOf(response) {
5309
+ var _a;
5310
+ return parseRetryAfterMs((_a = response.headers) == null ? void 0 : _a.get("Retry-After"));
5157
5311
  }
5158
- function removePendingSdkEventListener(event, callback) {
5159
- var _a, _b2;
5160
- (_a = pendingListeners.get(event)) == null ? void 0 : _a.delete(callback);
5161
- if (((_b2 = pendingListeners.get(event)) == null ? void 0 : _b2.size) === 0) {
5162
- pendingListeners.delete(event);
5312
+ function jitteredBackoffMs(failureCount, capMs = MAX_BACKOFF_MS) {
5313
+ return Math.random() * Math.min(capMs, 1e3 * 2 ** failureCount);
5314
+ }
5315
+ function statusOf(error) {
5316
+ return isHttpStatusError(error) ? error.status : 0;
5317
+ }
5318
+ function retryAfterOf(error) {
5319
+ return isHttpStatusError(error) ? error.retryAfterMs : void 0;
5320
+ }
5321
+ function queryRetry(failureCount, error) {
5322
+ const status = statusOf(error);
5323
+ if (status === 429) {
5324
+ const retryAfterMs = retryAfterOf(error);
5325
+ return failureCount < 1 && retryAfterMs !== void 0 && retryAfterMs <= MAX_RETRY_AFTER_MS;
5163
5326
  }
5327
+ if (status >= 400 && status < 500 && status !== 408) return false;
5328
+ return failureCount < MAX_RETRIES;
5164
5329
  }
5165
- function flushPendingSdkEventListeners(eventBus) {
5166
- pendingListeners.forEach((listeners2, event) => {
5167
- listeners2.forEach((callback) => eventBus.on(event, callback));
5168
- });
5169
- pendingListeners.clear();
5330
+ function queryRetryDelay(failureCount, error) {
5331
+ const retryAfterMs = retryAfterOf(error);
5332
+ return retryAfterMs !== void 0 ? Math.min(retryAfterMs, MAX_RETRY_AFTER_MS) : jitteredBackoffMs(failureCount);
5170
5333
  }
5171
5334
  function readApiBaseUrlFromEnv() {
5172
5335
  if (typeof process !== "undefined" && process.env && Object.prototype.hasOwnProperty.call(process.env, "VITE_API_BASE_URL")) {
@@ -5268,10 +5431,11 @@ async function clearToken() {
5268
5431
  tokenMemoryCache = null;
5269
5432
  }
5270
5433
  class ApiError extends Error {
5271
- constructor(message, status, data) {
5434
+ constructor(message, status, data, retryAfterMs) {
5272
5435
  super(message);
5273
5436
  this.status = status;
5274
5437
  this.data = data;
5438
+ this.retryAfterMs = retryAfterMs;
5275
5439
  this.name = "ApiError";
5276
5440
  }
5277
5441
  }
@@ -5340,7 +5504,8 @@ async function requestResponse(endpoint, options = {}) {
5340
5504
  throw new ApiError(
5341
5505
  extractErrorMessage(json.error) || json.message || `요청 실패: ${response.status}`,
5342
5506
  response.status,
5343
- json
5507
+ json,
5508
+ retryAfterMsOf(response)
5344
5509
  );
5345
5510
  }
5346
5511
  return json;
@@ -5403,7 +5568,8 @@ async function uploadRequest(endpoint, formData, skipAuth = false) {
5403
5568
  throw new ApiError(
5404
5569
  extractErrorMessage(json.error) || json.message || `업로드 실패: ${response.status}`,
5405
5570
  response.status,
5406
- json
5571
+ json,
5572
+ retryAfterMsOf(response)
5407
5573
  );
5408
5574
  }
5409
5575
  return json.data;
@@ -5441,6 +5607,442 @@ const apiClient = {
5441
5607
  return uploadRequest(endpoint, formData, skipAuth);
5442
5608
  }
5443
5609
  };
5610
+ const queryClient = new QueryClient({
5611
+ defaultOptions: {
5612
+ queries: {
5613
+ staleTime: 5 * 60 * 1e3,
5614
+ // 5분
5615
+ gcTime: 10 * 60 * 1e3,
5616
+ // 10분 (v5에서는 cacheTime 대신 gcTime 사용)
5617
+ refetchOnWindowFocus: true,
5618
+ refetchOnReconnect: true,
5619
+ retry: queryRetry,
5620
+ retryDelay: queryRetryDelay
5621
+ },
5622
+ mutations: {
5623
+ retry: 0
5624
+ }
5625
+ }
5626
+ });
5627
+ async function uploadSignedMedia(file, mediaType, issueId) {
5628
+ return uploadSignedMediaWithApi(
5629
+ {
5630
+ post: (endpoint, body) => apiClient.post(endpoint, body),
5631
+ directUpload
5632
+ },
5633
+ file,
5634
+ mediaType,
5635
+ { issueId }
5636
+ );
5637
+ }
5638
+ function issuesEndpoint() {
5639
+ return "/api/sdk/issues";
5640
+ }
5641
+ function guestOwnerQuery() {
5642
+ var _a;
5643
+ const mode = getSdkMode();
5644
+ if (mode !== "public") return "";
5645
+ return buildGuestOwnerQuery(mode, (_a = SDKManager.getInstance().config.user) == null ? void 0 : _a.id);
5646
+ }
5647
+ let issuesCache = null;
5648
+ const CACHE_DURATION_MS = 30 * 1e3;
5649
+ const STICKER_PAGE_SIZE = 500;
5650
+ const STICKER_FETCH_LIMIT = 2e3;
5651
+ function getFilterKey(filters) {
5652
+ var _a;
5653
+ const normalizeArray = (value) => {
5654
+ if (value === "all" || !value) return "all";
5655
+ if (Array.isArray(value)) {
5656
+ return value.length === 0 ? "all" : [...value].sort().join(",");
5657
+ }
5658
+ return String(value);
5659
+ };
5660
+ const normalizeRecord = (record) => {
5661
+ if (!record) return "";
5662
+ const entries = Object.entries(record).map(([k, v]) => [k, [...v].sort()]).sort(([a], [b]) => a.localeCompare(b));
5663
+ return JSON.stringify(entries);
5664
+ };
5665
+ return JSON.stringify({
5666
+ sdkMode: getSdkMode() || "public",
5667
+ url: normalizeArray(filters.url),
5668
+ projectId: filters.projectId || "all",
5669
+ externalUserId: filters.externalUserId || "",
5670
+ search: ((_a = filters.search) == null ? void 0 : _a.trim()) || "",
5671
+ dateFrom: filters.dateFrom || "",
5672
+ dateTo: filters.dateTo || "",
5673
+ page: filters.page ?? 0,
5674
+ pageSize: filters.pageSize ?? 0,
5675
+ custom: normalizeRecord(filters.customFieldValues),
5676
+ jira: normalizeRecord(filters.jiraFieldValues),
5677
+ notion: normalizeRecord(filters.notionFieldValues),
5678
+ sortOrder: filters.sortOrder || ""
5679
+ });
5680
+ }
5681
+ function invalidateIssuesCache() {
5682
+ issuesCache = null;
5683
+ }
5684
+ function recordToFilterArray(record, keyFieldName) {
5685
+ if (!record) return null;
5686
+ const entries = Object.entries(record).filter(([, values]) => values.length > 0);
5687
+ if (entries.length === 0) return null;
5688
+ return entries.map(
5689
+ ([key, values]) => ({ [keyFieldName]: key, values })
5690
+ );
5691
+ }
5692
+ function buildFilterQueryParams(filters) {
5693
+ var _a;
5694
+ const params = new URLSearchParams();
5695
+ if (filters.projectId) {
5696
+ params.set("project_id", filters.projectId);
5697
+ }
5698
+ if (filters.url && filters.url !== "all") {
5699
+ if (Array.isArray(filters.url) && filters.url.length > 0) {
5700
+ params.set("url", filters.url.join(","));
5701
+ } else if (typeof filters.url === "string") {
5702
+ params.set("url", filters.url);
5703
+ }
5704
+ }
5705
+ const search2 = (_a = filters.search) == null ? void 0 : _a.trim();
5706
+ if (search2) {
5707
+ params.set("search", search2);
5708
+ }
5709
+ if (filters.dateFrom) {
5710
+ params.set("date_from", filters.dateFrom);
5711
+ }
5712
+ if (filters.dateTo) {
5713
+ params.set("date_to", filters.dateTo);
5714
+ }
5715
+ const customArr = recordToFilterArray(filters.customFieldValues, "fieldDefinitionId");
5716
+ if (customArr) {
5717
+ params.set("custom_field_filters", JSON.stringify(customArr));
5718
+ }
5719
+ const jiraArr = recordToFilterArray(filters.jiraFieldValues, "fieldName");
5720
+ if (jiraArr) {
5721
+ params.set("jira_field_filters", JSON.stringify(jiraArr));
5722
+ }
5723
+ const notionArr = recordToFilterArray(filters.notionFieldValues, "fieldName");
5724
+ if (notionArr) {
5725
+ params.set("notion_field_filters", JSON.stringify(notionArr));
5726
+ }
5727
+ if (filters.externalUserId) {
5728
+ params.set("external_user_id", filters.externalUserId);
5729
+ }
5730
+ if (typeof filters.page === "number" && filters.page > 0) {
5731
+ params.set("page", String(filters.page));
5732
+ }
5733
+ if (typeof filters.pageSize === "number" && filters.pageSize > 0) {
5734
+ params.set("page_size", String(filters.pageSize));
5735
+ }
5736
+ if (filters.sortOrder) {
5737
+ params.set("sort_order", filters.sortOrder);
5738
+ }
5739
+ const queryString = params.toString();
5740
+ return queryString ? `?${queryString}` : "";
5741
+ }
5742
+ const issueService = {
5743
+ async createIssue(issueData) {
5744
+ const sanitizedData = sanitizeString(issueData);
5745
+ const screenshotUploadIds = (sanitizedData.screenshot || []).map((url) => getRegisteredUploadId(url, "screenshot")).filter((id2) => typeof id2 === "string" && id2.length > 0);
5746
+ const videoUploadId = sanitizedData.video ? getRegisteredUploadId(sanitizedData.video, "video") : void 0;
5747
+ if (screenshotUploadIds.length > 0) {
5748
+ sanitizedData.screenshot_upload_ids = screenshotUploadIds;
5749
+ }
5750
+ if (videoUploadId) {
5751
+ sanitizedData.video_upload_id = videoUploadId;
5752
+ }
5753
+ const issue = await apiClient.post(issuesEndpoint(), sanitizedData);
5754
+ invalidateIssuesCache();
5755
+ return issue;
5756
+ },
5757
+ async getIssues(filters = {}, options = {}) {
5758
+ if (!filters.projectId) {
5759
+ throw new Error("프로젝트 ID가 필요합니다. 로그인 상태를 확인해주세요.");
5760
+ }
5761
+ const filterKey = getFilterKey(filters);
5762
+ const now2 = Date.now();
5763
+ if (!options.forceRefresh && issuesCache && issuesCache.filterKey === filterKey && now2 - issuesCache.timestamp < CACHE_DURATION_MS) {
5764
+ return issuesCache.data;
5765
+ }
5766
+ const queryParams = buildFilterQueryParams(filters);
5767
+ const { items: issues = [] } = await apiClient.get(
5768
+ `${issuesEndpoint()}${queryParams}`
5769
+ );
5770
+ issuesCache = {
5771
+ data: issues,
5772
+ timestamp: now2,
5773
+ filterKey
5774
+ };
5775
+ return issues;
5776
+ },
5777
+ async getIssueStickers(filters = {}, options = {}) {
5778
+ var _a, _b2;
5779
+ if (!filters.projectId) {
5780
+ throw new Error("프로젝트 ID가 필요합니다. 로그인 상태를 확인해주세요.");
5781
+ }
5782
+ const filterKey = `${getFilterKey(filters)}:stickers`;
5783
+ const now2 = Date.now();
5784
+ if (!options.forceRefresh && issuesCache && issuesCache.filterKey === filterKey && now2 - issuesCache.timestamp < CACHE_DURATION_MS) {
5785
+ return issuesCache.data;
5786
+ }
5787
+ const issues = [];
5788
+ let page = 1;
5789
+ let hasMore = true;
5790
+ while (hasMore && issues.length <= STICKER_FETCH_LIMIT) {
5791
+ const response = await apiClient.getResponse(
5792
+ `${issuesEndpoint()}/stickers${buildFilterQueryParams({
5793
+ ...filters,
5794
+ page,
5795
+ pageSize: STICKER_PAGE_SIZE
5796
+ })}`
5797
+ );
5798
+ const pageItems = ((_a = response.data) == null ? void 0 : _a.items) ?? [];
5799
+ issues.push(...pageItems);
5800
+ hasMore = Boolean((_b2 = response.data) == null ? void 0 : _b2.hasMore) && pageItems.length > 0;
5801
+ page += 1;
5802
+ }
5803
+ issuesCache = {
5804
+ data: issues,
5805
+ timestamp: now2,
5806
+ filterKey
5807
+ };
5808
+ return issues;
5809
+ },
5810
+ invalidateCache() {
5811
+ invalidateIssuesCache();
5812
+ },
5813
+ // 계약 기준: 백엔드 GET /api/sdk/issues/:id 는 back#101부터 읽기-시점-스크럽된
5814
+ // llm_prompt 를 상세 응답에 포함한다(목록 응답에는 없음) — 스코프를 이 반환 타입에만 한정한다.
5815
+ async getIssue(id2) {
5816
+ return apiClient.get(
5817
+ `${issuesEndpoint()}/${id2}${guestOwnerQuery()}`
5818
+ );
5819
+ },
5820
+ async updateIssue(id2, updates) {
5821
+ const sanitizedUpdates = sanitizeString(updates);
5822
+ const issue = await apiClient.put(
5823
+ `${issuesEndpoint()}/${id2}${guestOwnerQuery()}`,
5824
+ sanitizedUpdates
5825
+ );
5826
+ invalidateIssuesCache();
5827
+ return issue;
5828
+ },
5829
+ /**
5830
+ * 재생성. 스펙상 세 채널 모두에서 되고 역할과 무관하다 — 검사는 역할이 아니라
5831
+ * 그 이슈에 접근할 수 있는지다(백엔드가 판정한다).
5832
+ *
5833
+ * 응답으로 갱신된 이슈가 오지만 상태는 다시 pending 이다. 호출부는 응답을
5834
+ * 그대로 쓰지 말고 폴링으로 이어받아야 한다.
5835
+ */
5836
+ async regenerateIssue(id2) {
5837
+ const issue = await apiClient.post(
5838
+ `${issuesEndpoint()}/${encodeURIComponent(id2)}/regenerate${guestOwnerQuery()}`,
5839
+ {}
5840
+ );
5841
+ invalidateIssuesCache();
5842
+ return issue;
5843
+ },
5844
+ async deleteIssue(id2, options) {
5845
+ const endpoint = `${issuesEndpoint()}/${id2}${guestOwnerQuery()}`;
5846
+ if (options == null ? void 0 : options.deleteExternal) {
5847
+ await apiClient.delete(endpoint, {
5848
+ body: { deleteExternal: true }
5849
+ });
5850
+ } else {
5851
+ await apiClient.delete(endpoint);
5852
+ }
5853
+ invalidateIssuesCache();
5854
+ }
5855
+ };
5856
+ function invalidateIssueQueries(issueId) {
5857
+ issueService.invalidateCache();
5858
+ queryClient.invalidateQueries({ queryKey: ["contentIssues"] });
5859
+ queryClient.invalidateQueries({ queryKey: ["issue", issueId] });
5860
+ }
5861
+ function notifyIssueUpdated(issueId) {
5862
+ SDKManager.getInstance().eventBus.emit("issue:updated", issueId);
5863
+ }
5864
+ function applyIssueMutationEffects(issueId) {
5865
+ invalidateIssueQueries(issueId);
5866
+ notifyIssueUpdated(issueId);
5867
+ }
5868
+ let queues = null;
5869
+ const toaster = createIssueUploadToaster();
5870
+ function getQueues() {
5871
+ if (queues) return queues;
5872
+ queues = createIssueUploadQueues({
5873
+ api: createMediaUploadApi({ post: (endpoint, body) => apiClient.post(endpoint, body) }),
5874
+ clientInfo: "issue-sticker-sdk",
5875
+ videoStore: createIndexedDbPendingUploadStore(VIDEO_UPLOAD_DB_NAME),
5876
+ attachmentStore: createIndexedDbPendingUploadStore(ATTACHMENT_UPLOAD_DB_NAME),
5877
+ loadTus: loadTusClient,
5878
+ onEvent: (event) => {
5879
+ if (event.type !== "failed") invalidateIssueQueries(event.issueId);
5880
+ },
5881
+ onNotice: (notice) => toaster.show(notice, retryWidgetIssueUploads)
5882
+ });
5883
+ return queues;
5884
+ }
5885
+ function getWidgetVideoUploadQueue() {
5886
+ return getQueues().video;
5887
+ }
5888
+ function getWidgetAttachmentUploadQueue() {
5889
+ return getQueues().attachments;
5890
+ }
5891
+ function resumeWidgetIssueUploads(projectKey) {
5892
+ return getQueues().resume([projectKey]);
5893
+ }
5894
+ function retryWidgetIssueUploads(issueId) {
5895
+ queues == null ? void 0 : queues.retry(issueId);
5896
+ }
5897
+ function stopWidgetUploadQueues() {
5898
+ queues == null ? void 0 : queues.stop();
5899
+ queues = null;
5900
+ }
5901
+ class EventBus {
5902
+ constructor() {
5903
+ this.listeners = /* @__PURE__ */ new Map();
5904
+ }
5905
+ on(event, callback) {
5906
+ if (!this.listeners.has(event)) {
5907
+ this.listeners.set(event, /* @__PURE__ */ new Set());
5908
+ }
5909
+ this.listeners.get(event).add(callback);
5910
+ return () => this.off(event, callback);
5911
+ }
5912
+ off(event, callback) {
5913
+ var _a;
5914
+ (_a = this.listeners.get(event)) == null ? void 0 : _a.delete(callback);
5915
+ }
5916
+ emit(event, ...args) {
5917
+ var _a, _b2;
5918
+ (_a = this.listeners.get(event)) == null ? void 0 : _a.forEach((cb) => cb(...args));
5919
+ (_b2 = this.listeners.get("*")) == null ? void 0 : _b2.forEach((cb) => cb(event, ...args));
5920
+ }
5921
+ once(event, callback) {
5922
+ const wrapper = (...args) => {
5923
+ this.off(event, wrapper);
5924
+ callback(...args);
5925
+ };
5926
+ return this.on(event, wrapper);
5927
+ }
5928
+ removeAllListeners() {
5929
+ this.listeners.clear();
5930
+ }
5931
+ }
5932
+ const DEFAULT_FLAGS = {
5933
+ networkLogs: true,
5934
+ consoleLogs: true,
5935
+ screenshot: true,
5936
+ screenRecording: true,
5937
+ stickers: true,
5938
+ keyboardShortcuts: true,
5939
+ jiraIntegration: false,
5940
+ fab: true
5941
+ };
5942
+ class FeatureFlags {
5943
+ constructor(overrides) {
5944
+ this.flags = { ...DEFAULT_FLAGS, ...overrides };
5945
+ }
5946
+ get(name2) {
5947
+ return this.flags[name2];
5948
+ }
5949
+ set(name2, enabled) {
5950
+ this.flags[name2] = enabled;
5951
+ }
5952
+ getAll() {
5953
+ return { ...this.flags };
5954
+ }
5955
+ }
5956
+ const _SDKManager = class _SDKManager {
5957
+ constructor() {
5958
+ this._config = null;
5959
+ this._platform = null;
5960
+ this._eventBus = null;
5961
+ this._featureFlags = null;
5962
+ this._initialized = false;
5963
+ }
5964
+ static getInstance() {
5965
+ if (!_SDKManager.instance) {
5966
+ _SDKManager.instance = new _SDKManager();
5967
+ }
5968
+ return _SDKManager.instance;
5969
+ }
5970
+ init(config) {
5971
+ if (this._initialized) {
5972
+ this.updateConfig(config);
5973
+ return;
5974
+ }
5975
+ this._config = config;
5976
+ this._eventBus = new EventBus();
5977
+ this._platform = new WebPlatform(this._eventBus);
5978
+ this._featureFlags = new FeatureFlags({});
5979
+ this._initialized = true;
5980
+ }
5981
+ updateConfig(config) {
5982
+ if (!this._initialized) {
5983
+ this.init(config);
5984
+ return;
5985
+ }
5986
+ this._config = config;
5987
+ }
5988
+ destroy() {
5989
+ var _a;
5990
+ stopWidgetUploadQueues();
5991
+ (_a = this._eventBus) == null ? void 0 : _a.removeAllListeners();
5992
+ this._config = null;
5993
+ this._platform = null;
5994
+ this._eventBus = null;
5995
+ this._featureFlags = null;
5996
+ this._initialized = false;
5997
+ }
5998
+ isInitialized() {
5999
+ return this._initialized;
6000
+ }
6001
+ get config() {
6002
+ if (!this._config) throw new Error("SDK not initialized");
6003
+ return this._config;
6004
+ }
6005
+ get platform() {
6006
+ if (!this._platform) throw new Error("SDK not initialized");
6007
+ return this._platform;
6008
+ }
6009
+ get eventBus() {
6010
+ if (!this._eventBus) throw new Error("SDK not initialized");
6011
+ return this._eventBus;
6012
+ }
6013
+ get featureFlags() {
6014
+ if (!this._featureFlags) throw new Error("SDK not initialized");
6015
+ return this._featureFlags;
6016
+ }
6017
+ static resetInstance() {
6018
+ if (_SDKManager.instance) {
6019
+ _SDKManager.instance.destroy();
6020
+ _SDKManager.instance = null;
6021
+ }
6022
+ }
6023
+ };
6024
+ _SDKManager.instance = null;
6025
+ let SDKManager = _SDKManager;
6026
+ const pendingListeners = /* @__PURE__ */ new Map();
6027
+ function addPendingSdkEventListener(event, callback) {
6028
+ if (!pendingListeners.has(event)) {
6029
+ pendingListeners.set(event, /* @__PURE__ */ new Set());
6030
+ }
6031
+ pendingListeners.get(event).add(callback);
6032
+ }
6033
+ function removePendingSdkEventListener(event, callback) {
6034
+ var _a, _b2;
6035
+ (_a = pendingListeners.get(event)) == null ? void 0 : _a.delete(callback);
6036
+ if (((_b2 = pendingListeners.get(event)) == null ? void 0 : _b2.size) === 0) {
6037
+ pendingListeners.delete(event);
6038
+ }
6039
+ }
6040
+ function flushPendingSdkEventListeners(eventBus) {
6041
+ pendingListeners.forEach((listeners2, event) => {
6042
+ listeners2.forEach((callback) => eventBus.on(event, callback));
6043
+ });
6044
+ pendingListeners.clear();
6045
+ }
5444
6046
  let session = null;
5445
6047
  function resolveApiBaseUrl() {
5446
6048
  try {
@@ -5607,25 +6209,6 @@ const createShadowRoot = (hostElement, cssTexts = []) => {
5607
6209
  const getPortalContainer = () => {
5608
6210
  return _portalTarget || document.body;
5609
6211
  };
5610
- const queryClient = new QueryClient({
5611
- defaultOptions: {
5612
- queries: {
5613
- staleTime: 5 * 60 * 1e3,
5614
- // 5분
5615
- gcTime: 10 * 60 * 1e3,
5616
- // 10분 (v5에서는 cacheTime 대신 gcTime 사용)
5617
- refetchOnWindowFocus: true,
5618
- refetchOnReconnect: true,
5619
- retry: 3,
5620
- retryDelay: (attemptIndex) => Math.min(1e3 * 2 ** attemptIndex, 3e4)
5621
- },
5622
- mutations: {
5623
- retry: 1,
5624
- // Mutation은 1번만 재시도
5625
- retryDelay: 1e3
5626
- }
5627
- }
5628
- });
5629
6212
  /**
5630
6213
  * @license lucide-react v0.562.0 - ISC
5631
6214
  *
@@ -14554,7 +15137,10 @@ const ToastItem = forwardRef(function ToastItem2({ t, behindCount, onDismiss },
14554
15137
  ),
14555
15138
  children: /* @__PURE__ */ jsxs("div", { className: cn$1("flex items-center gap-2.5 px-3 py-2.5", style.tint), children: [
14556
15139
  t.type !== "default" ? /* @__PURE__ */ jsx(Icon2, { className: cn$1("h-4 w-4 shrink-0", style.iconColor) }) : null,
14557
- /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 text-sm font-medium text-text-primary", children: t.message }),
15140
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1 text-sm text-text-primary", children: [
15141
+ t.title ? /* @__PURE__ */ jsx("span", { className: "block font-semibold", children: t.title }) : null,
15142
+ /* @__PURE__ */ jsx("span", { className: t.title ? "block" : "font-medium", children: t.message })
15143
+ ] }),
14558
15144
  t.action ? /* @__PURE__ */ jsx(
14559
15145
  "button",
14560
15146
  {
@@ -14662,235 +15248,6 @@ function toIssueFilterParams(filters) {
14662
15248
  ...countRecord(filters.notionFieldValues) ? { notionFieldValues: filters.notionFieldValues } : {}
14663
15249
  };
14664
15250
  }
14665
- async function uploadSignedMedia(file, mediaType, issueId) {
14666
- return uploadSignedMediaWithApi(
14667
- {
14668
- post: (endpoint, body) => apiClient.post(endpoint, body),
14669
- directUpload
14670
- },
14671
- file,
14672
- mediaType,
14673
- { issueId }
14674
- );
14675
- }
14676
- function issuesEndpoint() {
14677
- return "/api/sdk/issues";
14678
- }
14679
- function guestOwnerQuery() {
14680
- var _a;
14681
- const mode = getSdkMode();
14682
- if (mode !== "public") return "";
14683
- return buildGuestOwnerQuery(mode, (_a = SDKManager.getInstance().config.user) == null ? void 0 : _a.id);
14684
- }
14685
- let issuesCache = null;
14686
- const CACHE_DURATION_MS = 30 * 1e3;
14687
- const STICKER_PAGE_SIZE = 500;
14688
- const STICKER_FETCH_LIMIT = 2e3;
14689
- function getFilterKey(filters) {
14690
- var _a;
14691
- const normalizeArray = (value) => {
14692
- if (value === "all" || !value) return "all";
14693
- if (Array.isArray(value)) {
14694
- return value.length === 0 ? "all" : [...value].sort().join(",");
14695
- }
14696
- return String(value);
14697
- };
14698
- const normalizeRecord = (record) => {
14699
- if (!record) return "";
14700
- const entries = Object.entries(record).map(([k, v]) => [k, [...v].sort()]).sort(([a], [b]) => a.localeCompare(b));
14701
- return JSON.stringify(entries);
14702
- };
14703
- return JSON.stringify({
14704
- sdkMode: getSdkMode() || "public",
14705
- url: normalizeArray(filters.url),
14706
- projectId: filters.projectId || "all",
14707
- externalUserId: filters.externalUserId || "",
14708
- search: ((_a = filters.search) == null ? void 0 : _a.trim()) || "",
14709
- dateFrom: filters.dateFrom || "",
14710
- dateTo: filters.dateTo || "",
14711
- page: filters.page ?? 0,
14712
- pageSize: filters.pageSize ?? 0,
14713
- custom: normalizeRecord(filters.customFieldValues),
14714
- jira: normalizeRecord(filters.jiraFieldValues),
14715
- notion: normalizeRecord(filters.notionFieldValues),
14716
- sortOrder: filters.sortOrder || ""
14717
- });
14718
- }
14719
- function invalidateIssuesCache() {
14720
- issuesCache = null;
14721
- }
14722
- function recordToFilterArray(record, keyFieldName) {
14723
- if (!record) return null;
14724
- const entries = Object.entries(record).filter(([, values]) => values.length > 0);
14725
- if (entries.length === 0) return null;
14726
- return entries.map(
14727
- ([key, values]) => ({ [keyFieldName]: key, values })
14728
- );
14729
- }
14730
- function buildFilterQueryParams(filters) {
14731
- var _a;
14732
- const params = new URLSearchParams();
14733
- if (filters.projectId) {
14734
- params.set("project_id", filters.projectId);
14735
- }
14736
- if (filters.url && filters.url !== "all") {
14737
- if (Array.isArray(filters.url) && filters.url.length > 0) {
14738
- params.set("url", filters.url.join(","));
14739
- } else if (typeof filters.url === "string") {
14740
- params.set("url", filters.url);
14741
- }
14742
- }
14743
- const search2 = (_a = filters.search) == null ? void 0 : _a.trim();
14744
- if (search2) {
14745
- params.set("search", search2);
14746
- }
14747
- if (filters.dateFrom) {
14748
- params.set("date_from", filters.dateFrom);
14749
- }
14750
- if (filters.dateTo) {
14751
- params.set("date_to", filters.dateTo);
14752
- }
14753
- const customArr = recordToFilterArray(filters.customFieldValues, "fieldDefinitionId");
14754
- if (customArr) {
14755
- params.set("custom_field_filters", JSON.stringify(customArr));
14756
- }
14757
- const jiraArr = recordToFilterArray(filters.jiraFieldValues, "fieldName");
14758
- if (jiraArr) {
14759
- params.set("jira_field_filters", JSON.stringify(jiraArr));
14760
- }
14761
- const notionArr = recordToFilterArray(filters.notionFieldValues, "fieldName");
14762
- if (notionArr) {
14763
- params.set("notion_field_filters", JSON.stringify(notionArr));
14764
- }
14765
- if (filters.externalUserId) {
14766
- params.set("external_user_id", filters.externalUserId);
14767
- }
14768
- if (typeof filters.page === "number" && filters.page > 0) {
14769
- params.set("page", String(filters.page));
14770
- }
14771
- if (typeof filters.pageSize === "number" && filters.pageSize > 0) {
14772
- params.set("page_size", String(filters.pageSize));
14773
- }
14774
- if (filters.sortOrder) {
14775
- params.set("sort_order", filters.sortOrder);
14776
- }
14777
- const queryString = params.toString();
14778
- return queryString ? `?${queryString}` : "";
14779
- }
14780
- const issueService = {
14781
- async createIssue(issueData) {
14782
- const sanitizedData = sanitizeString(issueData);
14783
- const screenshotUploadIds = (sanitizedData.screenshot || []).map((url) => getRegisteredUploadId(url, "screenshot")).filter((id2) => typeof id2 === "string" && id2.length > 0);
14784
- const videoUploadId = sanitizedData.video ? getRegisteredUploadId(sanitizedData.video, "video") : void 0;
14785
- if (screenshotUploadIds.length > 0) {
14786
- sanitizedData.screenshot_upload_ids = screenshotUploadIds;
14787
- }
14788
- if (videoUploadId) {
14789
- sanitizedData.video_upload_id = videoUploadId;
14790
- }
14791
- const issue = await apiClient.post(issuesEndpoint(), sanitizedData);
14792
- invalidateIssuesCache();
14793
- return issue;
14794
- },
14795
- async getIssues(filters = {}, options = {}) {
14796
- if (!filters.projectId) {
14797
- throw new Error("프로젝트 ID가 필요합니다. 로그인 상태를 확인해주세요.");
14798
- }
14799
- const filterKey = getFilterKey(filters);
14800
- const now2 = Date.now();
14801
- if (!options.forceRefresh && issuesCache && issuesCache.filterKey === filterKey && now2 - issuesCache.timestamp < CACHE_DURATION_MS) {
14802
- return issuesCache.data;
14803
- }
14804
- const queryParams = buildFilterQueryParams(filters);
14805
- const { items: issues = [] } = await apiClient.get(
14806
- `${issuesEndpoint()}${queryParams}`
14807
- );
14808
- issuesCache = {
14809
- data: issues,
14810
- timestamp: now2,
14811
- filterKey
14812
- };
14813
- return issues;
14814
- },
14815
- async getIssueStickers(filters = {}, options = {}) {
14816
- var _a, _b2;
14817
- if (!filters.projectId) {
14818
- throw new Error("프로젝트 ID가 필요합니다. 로그인 상태를 확인해주세요.");
14819
- }
14820
- const filterKey = `${getFilterKey(filters)}:stickers`;
14821
- const now2 = Date.now();
14822
- if (!options.forceRefresh && issuesCache && issuesCache.filterKey === filterKey && now2 - issuesCache.timestamp < CACHE_DURATION_MS) {
14823
- return issuesCache.data;
14824
- }
14825
- const issues = [];
14826
- let page = 1;
14827
- let hasMore = true;
14828
- while (hasMore && issues.length <= STICKER_FETCH_LIMIT) {
14829
- const response = await apiClient.getResponse(
14830
- `${issuesEndpoint()}/stickers${buildFilterQueryParams({
14831
- ...filters,
14832
- page,
14833
- pageSize: STICKER_PAGE_SIZE
14834
- })}`
14835
- );
14836
- const pageItems = ((_a = response.data) == null ? void 0 : _a.items) ?? [];
14837
- issues.push(...pageItems);
14838
- hasMore = Boolean((_b2 = response.data) == null ? void 0 : _b2.hasMore) && pageItems.length > 0;
14839
- page += 1;
14840
- }
14841
- issuesCache = {
14842
- data: issues,
14843
- timestamp: now2,
14844
- filterKey
14845
- };
14846
- return issues;
14847
- },
14848
- invalidateCache() {
14849
- invalidateIssuesCache();
14850
- },
14851
- // 계약 기준: 백엔드 GET /api/sdk/issues/:id 는 back#101부터 읽기-시점-스크럽된
14852
- // llm_prompt 를 상세 응답에 포함한다(목록 응답에는 없음) — 스코프를 이 반환 타입에만 한정한다.
14853
- async getIssue(id2) {
14854
- return apiClient.get(
14855
- `${issuesEndpoint()}/${id2}${guestOwnerQuery()}`
14856
- );
14857
- },
14858
- async updateIssue(id2, updates) {
14859
- const sanitizedUpdates = sanitizeString(updates);
14860
- const issue = await apiClient.put(
14861
- `${issuesEndpoint()}/${id2}${guestOwnerQuery()}`,
14862
- sanitizedUpdates
14863
- );
14864
- invalidateIssuesCache();
14865
- return issue;
14866
- },
14867
- /**
14868
- * 재생성. 스펙상 세 채널 모두에서 되고 역할과 무관하다 — 검사는 역할이 아니라
14869
- * 그 이슈에 접근할 수 있는지다(백엔드가 판정한다).
14870
- *
14871
- * 응답으로 갱신된 이슈가 오지만 상태는 다시 pending 이다. 호출부는 응답을
14872
- * 그대로 쓰지 말고 폴링으로 이어받아야 한다.
14873
- */
14874
- async regenerateIssue(id2) {
14875
- const issue = await apiClient.post(
14876
- `${issuesEndpoint()}/${encodeURIComponent(id2)}/regenerate${guestOwnerQuery()}`,
14877
- {}
14878
- );
14879
- invalidateIssuesCache();
14880
- return issue;
14881
- },
14882
- async deleteIssue(id2, options) {
14883
- const endpoint = `${issuesEndpoint()}/${id2}${guestOwnerQuery()}`;
14884
- if (options == null ? void 0 : options.deleteExternal) {
14885
- await apiClient.delete(endpoint, {
14886
- body: { deleteExternal: true }
14887
- });
14888
- } else {
14889
- await apiClient.delete(endpoint);
14890
- }
14891
- invalidateIssuesCache();
14892
- }
14893
- };
14894
15251
  async function loadIssuesFromServer(url, projectId, externalUserId, filters, options = {}) {
14895
15252
  if (!projectId) return [];
14896
15253
  try {
@@ -15068,18 +15425,18 @@ function UnrenderedStickerDialog({
15068
15425
  {
15069
15426
  role: "dialog",
15070
15427
  "aria-modal": "false",
15071
- "aria-label": "미표시 스티커 목록",
15428
+ "aria-label": "표시하지 못한 스티커 목록",
15072
15429
  className: "pointer-events-auto flex h-[min(480px,calc(100vh-64px))] w-[min(560px,calc(100vw-32px))] flex-col overflow-hidden rounded-xl border border-slate-200 bg-white text-slate-900 shadow-2xl",
15073
15430
  children: [
15074
15431
  /* @__PURE__ */ jsxs("header", { className: "flex items-center justify-between border-b border-slate-100 px-5 py-3.5", children: [
15075
- /* @__PURE__ */ jsx("h2", { className: "min-w-0 truncate text-sm font-semibold", children: "미표시 스티커" }),
15432
+ /* @__PURE__ */ jsx("h2", { className: "min-w-0 truncate text-sm font-semibold", children: "표시하지 못한 스티커" }),
15076
15433
  /* @__PURE__ */ jsx(
15077
15434
  "button",
15078
15435
  {
15079
15436
  type: "button",
15080
15437
  className: "inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-slate-500 hover:bg-slate-100 hover:text-slate-900",
15081
15438
  onClick: () => onOpenChange(false),
15082
- "aria-label": "미표시 스티커 목록 닫기",
15439
+ "aria-label": "표시하지 못한 스티커 목록 닫기",
15083
15440
  children: /* @__PURE__ */ jsx(X$1, { className: "h-4 w-4" })
15084
15441
  }
15085
15442
  )
@@ -15141,7 +15498,7 @@ const FOCUSABLE_SELECTOR = [
15141
15498
  "textarea:not([disabled])",
15142
15499
  '[tabindex]:not([tabindex="-1"])'
15143
15500
  ].join(",");
15144
- function isVisible(element2, container) {
15501
+ function isVisible$1(element2, container) {
15145
15502
  if (getComputedStyle(element2).visibility === "hidden") return false;
15146
15503
  let node2 = element2;
15147
15504
  while (node2) {
@@ -15153,7 +15510,7 @@ function isVisible(element2, container) {
15153
15510
  }
15154
15511
  function tabbables(container) {
15155
15512
  return Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR)).filter(
15156
- (element2) => element2.tabIndex >= 0 && isVisible(element2, container)
15513
+ (element2) => element2.tabIndex >= 0 && isVisible$1(element2, container)
15157
15514
  );
15158
15515
  }
15159
15516
  function useOverlayFocus({
@@ -15257,7 +15614,8 @@ function OverlayShell({
15257
15614
  }
15258
15615
  const ISSUE_DRAFT_DISABLED_MESSAGE = "지금은 AI 작성을 사용할 수 없습니다. 제목과 설명을 직접 작성해주세요.";
15259
15616
  const LABEL = "기대한 동작";
15260
- const PLACEHOLDER = "예: 할인 쿠폰을 적용하면 결제 금액에서 즉시 차감되어야 합니다";
15617
+ const ISSUE_INTENT_PLACEHOLDER = "기대한 동작을 적으면 AI가 제목과 설명을 작성합니다.\n예: 쿠폰을 적용하면 결제 금액이 바로 줄어야 합니다";
15618
+ const PLACEHOLDER = ISSUE_INTENT_PLACEHOLDER;
15261
15619
  const SWITCH_TO_MANUAL = "직접 작성";
15262
15620
  const SWITCH_TO_MANUAL_TOOLTIP = "제목, 설명 등 이슈 필드를 직접 작성합니다";
15263
15621
  const SUBMIT = "보내기";
@@ -15294,20 +15652,28 @@ function IssueIntentBubble({
15294
15652
  if (isFocused(node2) || Date.now() >= deadline) return;
15295
15653
  frame2 = requestAnimationFrame(attempt);
15296
15654
  };
15297
- window.addEventListener("pointerdown", abandon, { capture: true });
15655
+ const pointerScopes = /* @__PURE__ */ new Set([window, node2.getRootNode()]);
15656
+ pointerScopes.forEach(
15657
+ (scope) => scope.addEventListener("pointerdown", abandon, { capture: true })
15658
+ );
15298
15659
  window.addEventListener("keydown", abandon, { capture: true });
15299
15660
  attempt();
15300
15661
  return () => {
15301
- window.removeEventListener("pointerdown", abandon, { capture: true });
15662
+ pointerScopes.forEach(
15663
+ (scope) => scope.removeEventListener("pointerdown", abandon, { capture: true })
15664
+ );
15302
15665
  window.removeEventListener("keydown", abandon, { capture: true });
15303
15666
  if (frame2) cancelAnimationFrame(frame2);
15304
15667
  };
15305
15668
  }, []);
15669
+ const minHeightRef = useRef(null);
15306
15670
  useLayoutEffect(() => {
15307
15671
  const node2 = inputRef.current;
15308
15672
  if (!node2) return;
15309
15673
  node2.style.height = "auto";
15310
- node2.style.height = `${Math.min(node2.scrollHeight, MAX_INPUT_HEIGHT)}px`;
15674
+ minHeightRef.current ?? (minHeightRef.current = node2.scrollHeight);
15675
+ const next = Math.max(node2.scrollHeight, minHeightRef.current);
15676
+ node2.style.height = `${Math.min(next, MAX_INPUT_HEIGHT)}px`;
15311
15677
  }, [value]);
15312
15678
  return (
15313
15679
  /*
@@ -15323,7 +15689,7 @@ function IssueIntentBubble({
15323
15689
  /* @__PURE__ */ jsxs(
15324
15690
  "div",
15325
15691
  {
15326
- className: `${ISSUE_STICKER_REPORT_FORM_CLASS} w-[min(20rem,calc(100vw_-_1.5rem))] space-y-1.5 rounded-md border border-line-subtle bg-surface-1 p-1.5 transition-colors duration-fast ease-out focus-within:border-line-focus`,
15692
+ className: `${ISSUE_STICKER_REPORT_FORM_CLASS} w-[min(24rem,calc(100vw_-_1.5rem))] space-y-1.5 rounded-md border border-line-subtle bg-surface-1 p-1.5 transition-colors duration-fast ease-out focus-within:border-line-focus`,
15327
15693
  children: [
15328
15694
  /* @__PURE__ */ jsxs("div", { className: "flex items-end gap-1.5", children: [
15329
15695
  /* @__PURE__ */ jsx(
@@ -15633,7 +15999,9 @@ DialogClose$1.displayName = CLOSE_NAME;
15633
15999
  function getState$2(open) {
15634
16000
  return open ? "open" : "closed";
15635
16001
  }
15636
- const Dialog = Dialog$1;
16002
+ const [Dialog, useDialogContentDismissRef] = createShadowDismissRoot(Dialog$1, {
16003
+ focusOutside: false
16004
+ });
15637
16005
  const DialogClose = DialogClose$1;
15638
16006
  const DialogPortal = ({ children }) => {
15639
16007
  const portalContainer = usePortalContainer();
@@ -15666,13 +16034,13 @@ const DialogContent = React.forwardRef(
15666
16034
  ...props
15667
16035
  }, ref) => {
15668
16036
  const [content2, setContent] = React.useState(null);
16037
+ const dismissRef = useDialogContentDismissRef(ref);
15669
16038
  const composedRef = React.useCallback(
15670
16039
  (node2) => {
15671
16040
  setContent(node2);
15672
- if (typeof ref === "function") ref(node2);
15673
- else if (ref) ref.current = node2;
16041
+ dismissRef(node2);
15674
16042
  },
15675
- [ref]
16043
+ [dismissRef]
15676
16044
  );
15677
16045
  useOverlayFocus({ enabled: true, container: content2, initialFocusRef });
15678
16046
  return /* @__PURE__ */ jsxs(DialogPortal, { children: [
@@ -15945,7 +16313,11 @@ function appendSnippet(base, body) {
15945
16313
  ${trimmedBody}`;
15946
16314
  }
15947
16315
  const TRIGGER_LABEL = "고정 문구 선택";
16316
+ const TRIGGER_HINT = "이슈 설명 끝에 추가할 고정 문구를 선택합니다";
15948
16317
  const CLEAR_OPTION_LABEL = "선택 안 함";
16318
+ const EMPTY_TITLE = "등록된 고정 문구가 없습니다";
16319
+ const EMPTY_DESCRIPTION = "테스트 계정처럼 이슈마다 반복해서 적는 내용을 등록해두면 이슈 설명 끝에 추가됩니다.";
16320
+ const DEFAULT_EMPTY_HINT = "어드민 프로젝트 상세의 고정 문구 탭에서 등록합니다.";
15949
16321
  const GROUP_LABEL = {
15950
16322
  project: "공용 문구",
15951
16323
  personal: "내 문구"
@@ -15954,7 +16326,8 @@ function SnippetPicker({
15954
16326
  snippets,
15955
16327
  selectedId,
15956
16328
  onSelect,
15957
- disabled = false
16329
+ disabled = false,
16330
+ emptyHint = DEFAULT_EMPTY_HINT
15958
16331
  }) {
15959
16332
  const [open, setOpen] = useState(false);
15960
16333
  const listRef = useRef(null);
@@ -16013,7 +16386,8 @@ function SnippetPicker({
16013
16386
  "button",
16014
16387
  {
16015
16388
  type: "button",
16016
- disabled: disabled || isEmpty,
16389
+ title: TRIGGER_HINT,
16390
+ disabled,
16017
16391
  className: cn$1(
16018
16392
  "inline-flex max-w-[10rem] items-center gap-1 rounded-sm border border-transparent px-2 py-1 text-xs text-text-secondary outline-none",
16019
16393
  "hover:bg-surface-2 focus-visible:border-line-focus disabled:cursor-not-allowed disabled:opacity-40"
@@ -16031,8 +16405,18 @@ function SnippetPicker({
16031
16405
  align: "start",
16032
16406
  sideOffset: 4,
16033
16407
  onEscapeKeyDown: stopEscapePropagation(),
16034
- className: cn$1(`panel-shell-subtle w-72 p-1.5 shadow-lg ${OVERLAY_LAYER.floating}`),
16035
- children: /* @__PURE__ */ jsxs(
16408
+ className: cn$1(
16409
+ `panel-shell-subtle p-1.5 shadow-lg ${OVERLAY_LAYER.floating}`,
16410
+ isEmpty ? "w-[min(30rem,calc(100vw_-_1.5rem))]" : "w-72"
16411
+ ),
16412
+ children: isEmpty ? (
16413
+ // 비어 있다고 버튼을 막으면 무엇을 하는 버튼인지조차 알 수 없다. 열어서 이유를 알려준다.
16414
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1 break-keep px-2.5 py-2 [overflow-wrap:anywhere]", children: [
16415
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-text-primary", children: EMPTY_TITLE }),
16416
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-text-secondary", children: EMPTY_DESCRIPTION }),
16417
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-text-secondary", children: emptyHint })
16418
+ ] })
16419
+ ) : /* @__PURE__ */ jsxs(
16036
16420
  "div",
16037
16421
  {
16038
16422
  ref: listRef,
@@ -16428,6 +16812,16 @@ async function captureScreenshot(options) {
16428
16812
  }
16429
16813
  return void 0;
16430
16814
  }
16815
+ async function captureViewportAndElement(element2, excludeSelectors) {
16816
+ try {
16817
+ if (SDKManager.getInstance().isInitialized()) {
16818
+ return await captureViewportWithElementCrop({ element: element2, excludeSelectors });
16819
+ }
16820
+ } catch (err) {
16821
+ devLogger.error("screenshotCapture", "스크린샷 캡처 실패:", err);
16822
+ }
16823
+ return {};
16824
+ }
16431
16825
  function toScreenshotList(dataUrls, dataUrl) {
16432
16826
  if (dataUrls && dataUrls.length > 0) {
16433
16827
  return dataUrls;
@@ -16549,15 +16943,17 @@ function useClickPosition(isCreatingIssueRef, recordedVideoFile) {
16549
16943
  useEffect(() => {
16550
16944
  const captureScreenshotsInBackground = async (position2) => {
16551
16945
  await waitForFormOpen();
16552
- const [fullScreenshotDataUrl, elementScreenshotDataUrl] = await pauseNetworkLogCollectionWhile(
16553
- () => Promise.all([
16554
- captureScreenshot({ excludeSelectors: SCREENSHOT_EXCLUDE_SELECTORS }),
16555
- captureScreenshot({
16556
- element: position2.element,
16557
- excludeSelectors: SCREENSHOT_EXCLUDE_SELECTORS
16558
- })
16559
- ])
16560
- );
16946
+ const { fullScreenshotDataUrl, elementScreenshotDataUrl } = await pauseNetworkLogCollectionWhile(async () => {
16947
+ const { fullDataUrl, elementDataUrl } = await captureViewportAndElement(
16948
+ position2.element,
16949
+ SCREENSHOT_EXCLUDE_SELECTORS
16950
+ );
16951
+ const element2 = elementDataUrl ?? await captureScreenshot({
16952
+ element: position2.element,
16953
+ excludeSelectors: SCREENSHOT_EXCLUDE_SELECTORS
16954
+ });
16955
+ return { fullScreenshotDataUrl: fullDataUrl, elementScreenshotDataUrl: element2 };
16956
+ });
16561
16957
  const captured = {
16562
16958
  screenshotDataUrl: elementScreenshotDataUrl,
16563
16959
  screenshotDataUrls: [fullScreenshotDataUrl, elementScreenshotDataUrl].filter(
@@ -16597,6 +16993,7 @@ function useClickPosition(isCreatingIssueRef, recordedVideoFile) {
16597
16993
  x: e.clientX + window.scrollX,
16598
16994
  y: e.clientY + window.scrollY,
16599
16995
  element: selectedElement,
16996
+ tracked: getElementPosition(selectedElement, e),
16600
16997
  videoFile: recordedVideoFile ?? void 0
16601
16998
  };
16602
16999
  if (!newClickPosition.videoFile) {
@@ -16683,43 +17080,6 @@ function useScreenshotUpload() {
16683
17080
  uploadMany
16684
17081
  };
16685
17082
  }
16686
- async function uploadVideo(videoFile, issueId) {
16687
- devLogger.log("videoUpload", "영상 파일 업로드 시작", {
16688
- 크기: `${(videoFile.size / 1024 / 1024).toFixed(2)}MB`,
16689
- 타입: videoFile.type
16690
- });
16691
- const result = await uploadSignedMedia(videoFile, "video", issueId);
16692
- devLogger.log("videoUpload", "영상 파일 업로드 성공:", result.url);
16693
- return result.url;
16694
- }
16695
- function useVideoUpload() {
16696
- const upload = async (videoFile) => uploadVideo(videoFile);
16697
- return {
16698
- upload
16699
- };
16700
- }
16701
- async function uploadSessionReplay(snapshot) {
16702
- try {
16703
- const file = new File([snapshot.payload], "session-replay.gz", {
16704
- type: "application/gzip"
16705
- });
16706
- const { id: id2 } = await uploadSignedMedia(file, "replay");
16707
- const marker = readReleaseMarker();
16708
- return {
16709
- uploadId: id2,
16710
- meta: {
16711
- window_seconds: snapshot.windowSeconds,
16712
- event_count: snapshot.eventCount,
16713
- size_bytes: snapshot.sizeBytes,
16714
- rrweb_version: snapshot.rrwebVersion,
16715
- privacy_config_version: snapshot.privacyConfigVersion,
16716
- ...marker ? { release: marker.release, release_source: marker.release_source } : {}
16717
- }
16718
- };
16719
- } catch {
16720
- return null;
16721
- }
16722
- }
16723
17083
  function assertSubmittable(data) {
16724
17084
  var _a, _b2, _c2;
16725
17085
  const hasIntent = Boolean((_a = data.intent) == null ? void 0 : _a.trim());
@@ -16745,18 +17105,13 @@ function useIssueSubmission({
16745
17105
  const { fetchConsoleLogs } = useConsoleLogs();
16746
17106
  const { getAuthInfo } = useAuthInfo();
16747
17107
  const { uploadMany: uploadScreenshots2 } = useScreenshotUpload();
16748
- const { upload: uploadVideo2 } = useVideoUpload();
16749
17108
  const createIssueMutation = useDebouncedMutation({
16750
17109
  mutationFn: async (data) => {
16751
17110
  var _a, _b2, _c2, _d2, _e2, _f2;
16752
17111
  assertSubmittable(data);
16753
17112
  let screenshotUrls;
16754
- let videoUrl;
16755
- if (data.videoFile) {
16756
- videoUrl = await uploadVideo2(data.videoFile);
16757
- } else if (data.position.videoFile) {
16758
- videoUrl = await uploadVideo2(data.position.videoFile);
16759
- } else {
17113
+ const videoFile2 = data.videoFile ?? data.position.videoFile;
17114
+ if (!videoFile2) {
16760
17115
  const screenshotDataUrls = await resolveClickPositionScreenshots(data.position);
16761
17116
  if (screenshotDataUrls.length > 0) {
16762
17117
  const uploadedUrls = await uploadScreenshots2(screenshotDataUrls, {
@@ -16774,7 +17129,7 @@ function useIssueSubmission({
16774
17129
  const limitedNetworkLogs = selectNetworkLogsForSubmit(filteredNetworkLogs);
16775
17130
  const networkLogs = compactNetworkLogsForTransport(limitedNetworkLogs) ?? [];
16776
17131
  const consoleLogs = filteredConsoleLogs.slice(-CONSOLE_LOG_SUBMIT_LIMIT);
16777
- const positionData = getElementPosition(data.position.element, {
17132
+ const positionData = data.position.tracked ?? getElementPosition(data.position.element, {
16778
17133
  clientX: data.position.x - window.scrollX,
16779
17134
  clientY: data.position.y - window.scrollY
16780
17135
  });
@@ -16792,10 +17147,12 @@ function useIssueSubmission({
16792
17147
  selector: positionData.selector,
16793
17148
  xpath: positionData.xpath,
16794
17149
  viewport: positionData.viewport,
16795
- elementRect: positionData.elementRect
17150
+ elementRect: positionData.elementRect,
17151
+ locator: positionData.locator,
17152
+ component: positionData.component
16796
17153
  },
16797
17154
  screenshot: screenshotUrls && screenshotUrls.length > 0 ? screenshotUrls : void 0,
16798
- video: videoUrl,
17155
+ ...videoFile2 ? { video_pending: true } : {},
16799
17156
  network_logs: networkLogs,
16800
17157
  console_logs: consoleLogs.length > 0 ? consoleLogs : void 0,
16801
17158
  user_agent: userAgent,
@@ -16810,39 +17167,17 @@ function useIssueSubmission({
16810
17167
  if (((_e2 = issueData.custom_field_values) == null ? void 0 : _e2.length) === 0) {
16811
17168
  delete issueData.custom_field_values;
16812
17169
  }
16813
- if (sessionReplay == null ? void 0 : sessionReplay.canAttach) {
16814
- try {
16815
- const snapshot = await sessionReplay.takeSnapshot();
16816
- if (snapshot) {
16817
- const uploaded = await uploadSessionReplay(snapshot);
16818
- if (uploaded) {
16819
- issueData.session_replay_upload_id = uploaded.uploadId;
16820
- issueData.session_replay_meta = uploaded.meta;
16821
- }
16822
- }
16823
- } catch (replayError) {
16824
- devLogger.error(
16825
- "useIssueSubmission",
16826
- "세션 리플레이 첨부 실패 (이슈 생성 계속):",
16827
- replayError
16828
- );
16829
- }
16830
- }
16831
- try {
16832
- const socketSnapshot = await serializeSocketTranscript(
16833
- getCollectorSession().socket.getConnections()
16834
- );
16835
- if (socketSnapshot) {
16836
- const uploaded = await uploadSocketTranscript(
16837
- { post: (endpoint, body) => apiClient.post(endpoint, body), directUpload },
16838
- socketSnapshot
16839
- );
16840
- if (uploaded) {
16841
- issueData.socket_log_upload_id = uploaded.uploadId;
16842
- }
16843
- }
16844
- } catch (socketError) {
16845
- devLogger.error("useIssueSubmission", "소켓 전사 첨부 실패 (이슈 생성 계속):", socketError);
17170
+ const attachments = await collectAttachmentUploads({
17171
+ takeReplay: (sessionReplay == null ? void 0 : sessionReplay.canAttach) ? () => sessionReplay.takeSnapshot() : void 0,
17172
+ getConnections: () => getCollectorSession().socket.getConnections(),
17173
+ onError: (kind, error) => devLogger.error(
17174
+ "useIssueSubmission",
17175
+ kind === "replay" ? "세션 리플레이 스냅샷 실패 (이슈 생성 계속):" : "소켓 전사 직렬화 실패 (이슈 생성 계속):",
17176
+ error
17177
+ )
17178
+ });
17179
+ if (hasReplayUpload(attachments)) {
17180
+ issueData.session_replay_pending = true;
16846
17181
  }
16847
17182
  const { networkLogs: fittedNetworkLogs, stillOverBudget } = shrinkNetworkLogsToFit(
16848
17183
  issueData.network_logs ?? [],
@@ -16861,6 +17196,16 @@ function useIssueSubmission({
16861
17196
  notionProperties: data.notionFields
16862
17197
  });
16863
17198
  const createdIssue = await issueService.createIssue({ ...issueData, integrations });
17199
+ if (videoFile2 || attachments.length > 0) {
17200
+ await enqueueIssueUploads({
17201
+ videoQueue: getWidgetVideoUploadQueue(),
17202
+ attachmentQueue: getWidgetAttachmentUploadQueue(),
17203
+ issueId: createdIssue.id,
17204
+ projectKey: authInfo.projectId,
17205
+ video: videoFile2 ? { blob: videoFile2, mimeType: normalizeUploadMimeType(videoFile2.type, "video") } : null,
17206
+ attachments
17207
+ });
17208
+ }
16864
17209
  for (const service of createdIssue.failed_services ?? []) {
16865
17210
  toast.error(getIntegrationFailureMessage(service));
16866
17211
  }
@@ -17502,6 +17847,7 @@ function IssueFormError({ error }) {
17502
17847
  return /* @__PURE__ */ jsx("div", { className: "rounded-[18px] border border-danger/20 bg-danger-subtle px-4 py-3 text-sm text-danger", children: error });
17503
17848
  }
17504
17849
  const SWITCH_TO_AI = "AI 작성으로 돌아가기";
17850
+ const SWITCH_TO_AI_HINT = "기대한 동작만 적으면 AI가 제목과 설명을 작성합니다.";
17505
17851
  function IssueFormHeader({ title, onClose, onSwitchToAi }) {
17506
17852
  return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
17507
17853
  /* @__PURE__ */ jsx("h2", { className: "m-0 text-lg font-semibold text-text-primary", children: title }),
@@ -17518,7 +17864,10 @@ function IssueFormHeader({ title, onClose, onSwitchToAi }) {
17518
17864
  children: /* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4" })
17519
17865
  }
17520
17866
  ) }),
17521
- /* @__PURE__ */ jsx(TooltipContent, { side: "top", sideOffset: 10, children: SWITCH_TO_AI })
17867
+ /* @__PURE__ */ jsxs(TooltipContent, { side: "top", sideOffset: 10, className: "max-w-[16rem]", children: [
17868
+ /* @__PURE__ */ jsx("p", { className: "font-medium", children: SWITCH_TO_AI }),
17869
+ /* @__PURE__ */ jsx("p", { className: "mt-0.5 opacity-80", children: SWITCH_TO_AI_HINT })
17870
+ ] })
17522
17871
  ] }) }) : null,
17523
17872
  /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: onClose, "aria-label": "닫기", children: /* @__PURE__ */ jsx(X$1, { className: "h-4 w-4" }) })
17524
17873
  ] })
@@ -18069,18 +18418,6 @@ function IssueForm({
18069
18418
  getPortalContainer()
18070
18419
  );
18071
18420
  }
18072
- function invalidateIssueQueries(issueId) {
18073
- issueService.invalidateCache();
18074
- queryClient.invalidateQueries({ queryKey: ["contentIssues"] });
18075
- queryClient.invalidateQueries({ queryKey: ["issue", issueId] });
18076
- }
18077
- function notifyIssueUpdated(issueId) {
18078
- SDKManager.getInstance().eventBus.emit("issue:updated", issueId);
18079
- }
18080
- function applyIssueMutationEffects(issueId) {
18081
- invalidateIssueQueries(issueId);
18082
- notifyIssueUpdated(issueId);
18083
- }
18084
18421
  function useJiraIssueSync(issue) {
18085
18422
  const [isSyncingAll, setIsSyncingAll] = useState(false);
18086
18423
  const mapping = getPrimaryJiraMapping(issue);
@@ -18640,7 +18977,7 @@ function ResizableSplitPane({
18640
18977
  dividerClassName,
18641
18978
  separatorLabel = "패널 크기 조절"
18642
18979
  }) {
18643
- const [ratio, setRatio] = React.useState(() => clamp$1(defaultRatio, minRatio, maxRatio));
18980
+ const [ratio2, setRatio] = React.useState(() => clamp$1(defaultRatio, minRatio, maxRatio));
18644
18981
  const containerRef = React.useRef(null);
18645
18982
  const dragCleanupRef = React.useRef(null);
18646
18983
  const updateRatio = React.useCallback(
@@ -18664,12 +19001,13 @@ function ResizableSplitPane({
18664
19001
  var _a;
18665
19002
  event.preventDefault();
18666
19003
  (_a = dragCleanupRef.current) == null ? void 0 : _a.call(dragCleanupRef);
19004
+ const releaseScopes = /* @__PURE__ */ new Set([document, event.currentTarget.getRootNode()]);
18667
19005
  const handleDocumentMouseMove = (moveEvent) => {
18668
19006
  updateRatio(moveEvent.clientX);
18669
19007
  };
18670
19008
  const stopDragging = () => {
18671
19009
  document.removeEventListener("mousemove", handleDocumentMouseMove);
18672
- document.removeEventListener("mouseup", stopDragging);
19010
+ releaseScopes.forEach((scope) => scope.removeEventListener("mouseup", stopDragging));
18673
19011
  window.removeEventListener("blur", stopDragging);
18674
19012
  if (dragCleanupRef.current === stopDragging) {
18675
19013
  dragCleanupRef.current = null;
@@ -18677,7 +19015,7 @@ function ResizableSplitPane({
18677
19015
  };
18678
19016
  dragCleanupRef.current = stopDragging;
18679
19017
  document.addEventListener("mousemove", handleDocumentMouseMove);
18680
- document.addEventListener("mouseup", stopDragging);
19018
+ releaseScopes.forEach((scope) => scope.addEventListener("mouseup", stopDragging));
18681
19019
  window.addEventListener("blur", stopDragging);
18682
19020
  },
18683
19021
  [updateRatio]
@@ -18697,14 +19035,14 @@ function ResizableSplitPane({
18697
19035
  );
18698
19036
  const minPercent = toPercent(minRatio);
18699
19037
  const maxPercent = toPercent(maxRatio);
18700
- const currentPercent = toPercent(ratio);
19038
+ const currentPercent = toPercent(ratio2);
18701
19039
  const hasEnd = end !== null && end !== void 0 && end !== false;
18702
19040
  return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: cn$1("flex overflow-hidden", className), children: [
18703
19041
  /* @__PURE__ */ jsx(
18704
19042
  "div",
18705
19043
  {
18706
19044
  className: cn$1("flex min-w-0 flex-col overflow-hidden", startClassName),
18707
- style: { width: hasEnd ? formatRatioWidth(ratio) : "100%" },
19045
+ style: { width: hasEnd ? formatRatioWidth(ratio2) : "100%" },
18708
19046
  children: start
18709
19047
  }
18710
19048
  ),
@@ -21126,9 +21464,6 @@ const svg = merge([aria, svg$1, xlink, xmlns, xml], "svg");
21126
21464
  function stringify(values) {
21127
21465
  return values.join(" ").trim();
21128
21466
  }
21129
- function getDefaultExportFromCjs(x) {
21130
- return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
21131
- }
21132
21467
  var cjs$2 = {};
21133
21468
  var cjs$1;
21134
21469
  var hasRequiredCjs$2;
@@ -34733,9 +35068,20 @@ const markdownComponents = {
34733
35068
  h2: ({ children }) => /* @__PURE__ */ jsx("h2", { className: "my-3 whitespace-pre-wrap text-lg font-semibold text-text-primary", children }),
34734
35069
  h3: ({ children }) => /* @__PURE__ */ jsx("h3", { className: "my-2.5 whitespace-pre-wrap text-base font-semibold text-text-primary", children }),
34735
35070
  p: ({ children }) => /* @__PURE__ */ jsx("p", { className: "my-2 whitespace-pre-wrap text-text-primary", children }),
34736
- ul: ({ children }) => /* @__PURE__ */ jsx("ul", { className: "my-2 list-disc pl-5 text-text-primary", children }),
35071
+ // 노션 할 일 블록은 GFM 작업 목록(`- [ ]`)으로 들어온다. 글머리표 대신 체크박스가 그 자리를 맡는다.
35072
+ ul: ({ children, className }) => (className == null ? void 0 : className.includes("contains-task-list")) ? /* @__PURE__ */ jsx("ul", { className: "my-2 list-none pl-0 text-text-primary [li_&]:pl-5", children }) : /* @__PURE__ */ jsx("ul", { className: "my-2 list-disc pl-5 text-text-primary", children }),
34737
35073
  ol: ({ children }) => /* @__PURE__ */ jsx("ol", { className: "my-2 list-decimal pl-5 text-text-primary", children }),
34738
35074
  li: ({ children }) => /* @__PURE__ */ jsx("li", { className: "my-1", children }),
35075
+ input: ({ type, checked }) => type === "checkbox" ? /* @__PURE__ */ jsx(
35076
+ "input",
35077
+ {
35078
+ type: "checkbox",
35079
+ checked,
35080
+ disabled: true,
35081
+ readOnly: true,
35082
+ className: "mr-2 align-middle accent-brand"
35083
+ }
35084
+ ) : null,
34739
35085
  code: ({ children, className }) => {
34740
35086
  const isInline = !className;
34741
35087
  if (isInline) {
@@ -34752,7 +35098,8 @@ const markdownComponents = {
34752
35098
  // 깊이만큼 밀린다.
34753
35099
  details: ({ children }) => /* @__PURE__ */ jsx("details", { className: "my-2 [&>*:not(summary)]:pl-4", children }),
34754
35100
  summary: ({ children }) => /* @__PURE__ */ jsx("summary", { className: "cursor-pointer text-text-primary marker:text-text-tertiary", children }),
34755
- a: ({ href, children }) => /* @__PURE__ */ jsx(
35101
+ // 노션의 빈 파일·PDF 블록은 `[pdf]()` 로 들어온다. 빈 href 는 현재 페이지로 이동하므로 글자로만 둔다.
35102
+ a: ({ href, children }) => href ? /* @__PURE__ */ jsx(
34756
35103
  "a",
34757
35104
  {
34758
35105
  href,
@@ -34761,7 +35108,7 @@ const markdownComponents = {
34761
35108
  className: "text-brand underline underline-offset-2 hover:text-brand-hover",
34762
35109
  children
34763
35110
  }
34764
- ),
35111
+ ) : /* @__PURE__ */ jsx("span", { className: "text-text-secondary", children }),
34765
35112
  blockquote: ({ children }) => /* @__PURE__ */ jsx("blockquote", { className: "my-2 border-l-4 border-line-strong pl-3 italic text-text-secondary", children }),
34766
35113
  hr: () => /* @__PURE__ */ jsx("hr", { className: "my-4 border-0 border-t border-line" }),
34767
35114
  strong: ({ children }) => /* @__PURE__ */ jsx("strong", { className: "font-semibold", children }),
@@ -35248,6 +35595,10 @@ function ImageViewer({
35248
35595
  title = "이미지 전체화면 보기"
35249
35596
  }) {
35250
35597
  const portalContainer = usePortalContainer();
35598
+ const dismissRef = useShadowOutsideDismiss(
35599
+ useCallback(() => onOpenChange(false), [onOpenChange]),
35600
+ { focusOutside: false }
35601
+ );
35251
35602
  return /* @__PURE__ */ jsx(Dialog$1, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogPortal$1, { container: portalContainer, children: [
35252
35603
  /* @__PURE__ */ jsx(
35253
35604
  DialogOverlay$1,
@@ -35262,6 +35613,7 @@ function ImageViewer({
35262
35613
  /* @__PURE__ */ jsxs(
35263
35614
  DialogContent$1,
35264
35615
  {
35616
+ ref: dismissRef,
35265
35617
  onEscapeKeyDown: stopEscapePropagation(),
35266
35618
  style: {
35267
35619
  left: `calc(50% - (var(${BODY_SCROLLBAR_WIDTH_CSS_VAR}, 0px) / 2))`
@@ -35299,6 +35651,11 @@ function ImageViewer({
35299
35651
  )
35300
35652
  ] }) });
35301
35653
  }
35654
+ function orderScreenshotsForDisplay(urls) {
35655
+ if (urls.length !== 2) return [...urls];
35656
+ const [full, element2] = urls;
35657
+ return [element2, full];
35658
+ }
35302
35659
  function parseScreenshotUrls(screenshot) {
35303
35660
  if (Array.isArray(screenshot)) {
35304
35661
  return screenshot.filter((url) => typeof url === "string" && url.length > 0);
@@ -35321,7 +35678,7 @@ function parseScreenshotUrls(screenshot) {
35321
35678
  return [screenshot];
35322
35679
  }
35323
35680
  function IssueScreenshot({ screenshot }) {
35324
- const screenshotUrls = parseScreenshotUrls(screenshot);
35681
+ const screenshotUrls = orderScreenshotsForDisplay(parseScreenshotUrls(screenshot));
35325
35682
  const [selectedImageUrl, setSelectedImageUrl] = useState(
35326
35683
  screenshotUrls[0] ?? null
35327
35684
  );
@@ -36584,81 +36941,372 @@ function IssuePopover({ issueId, onClose, onUpdate }) {
36584
36941
  getPortalContainer()
36585
36942
  );
36586
36943
  }
36587
- function debounce(func, debounceMs, { signal, edges } = {}) {
36588
- let pendingThis = void 0;
36589
- let pendingArgs = null;
36590
- const leading = edges != null && edges.includes("leading");
36591
- const trailing = edges == null || edges.includes("trailing");
36592
- const invoke = () => {
36593
- if (pendingArgs !== null) {
36594
- func.apply(pendingThis, pendingArgs);
36595
- pendingThis = void 0;
36596
- pendingArgs = null;
36597
- }
36598
- };
36599
- const onTimerEnd = () => {
36600
- if (trailing) invoke();
36601
- cancel();
36602
- };
36603
- let timeoutId = null;
36604
- const schedule = () => {
36605
- if (timeoutId != null) clearTimeout(timeoutId);
36606
- timeoutId = setTimeout(() => {
36607
- timeoutId = null;
36608
- onTimerEnd();
36609
- }, debounceMs);
36610
- };
36611
- const cancelTimer = () => {
36612
- if (timeoutId !== null) {
36613
- clearTimeout(timeoutId);
36614
- timeoutId = null;
36944
+ const LOCATOR_WEIGHTS = {
36945
+ testAttr: 3,
36946
+ role: 1.5,
36947
+ name: 1.5,
36948
+ text: 1.5,
36949
+ rowText: 1.5,
36950
+ tag: 1,
36951
+ heading: 1,
36952
+ landmark: 1,
36953
+ attrs: 1,
36954
+ roleIndex: 0.5,
36955
+ classes: 0.5,
36956
+ structural: 0.5,
36957
+ location: 0.5,
36958
+ size: 0.5
36959
+ };
36960
+ const LOCATION_RANGE_PX = 100;
36961
+ function textScore(stored, observed) {
36962
+ return observed ? textSimilarity(stored, observed) : 0;
36963
+ }
36964
+ function locationScore(stored, observed) {
36965
+ const dx = stored.left + stored.width / 2 - (observed.left + observed.width / 2);
36966
+ const dy = stored.top + stored.height / 2 - (observed.top + observed.height / 2);
36967
+ const distance2 = Math.hypot(dx, dy);
36968
+ return distance2 >= LOCATION_RANGE_PX ? 0 : 1 - distance2 / LOCATION_RANGE_PX;
36969
+ }
36970
+ function ratio(a, b) {
36971
+ if (a <= 0 || b <= 0) return a === b ? 1 : 0;
36972
+ return Math.min(a, b) / Math.max(a, b);
36973
+ }
36974
+ function sizeScore(stored, observed) {
36975
+ return ratio(stored.width, observed.width) * ratio(stored.height, observed.height);
36976
+ }
36977
+ function scoreLocator(stored, storedRect, observed) {
36978
+ var _a, _b2, _c2, _d2, _e2, _f2, _g2;
36979
+ const seen = observed.locator;
36980
+ let weighted = 0;
36981
+ let total = 0;
36982
+ const add = (weight, similarity) => {
36983
+ weighted += weight * similarity;
36984
+ total += weight;
36985
+ };
36986
+ const testKey = TEST_ATTR_KEYS.find((key) => {
36987
+ var _a2;
36988
+ return ((_a2 = stored.attrs) == null ? void 0 : _a2[key]) !== void 0;
36989
+ });
36990
+ if (testKey)
36991
+ add(LOCATOR_WEIGHTS.testAttr, ((_a = seen.attrs) == null ? void 0 : _a[testKey]) === ((_b2 = stored.attrs) == null ? void 0 : _b2[testKey]) ? 1 : 0);
36992
+ add(LOCATOR_WEIGHTS.tag, seen.tag === stored.tag ? 1 : 0);
36993
+ if (stored.role) add(LOCATOR_WEIGHTS.role, seen.role === stored.role ? 1 : 0);
36994
+ if (stored.name) add(LOCATOR_WEIGHTS.name, textScore(stored.name, seen.name));
36995
+ if (stored.text) add(LOCATOR_WEIGHTS.text, textScore(stored.text, seen.text));
36996
+ const context = stored.context;
36997
+ if (context == null ? void 0 : context.rowText) {
36998
+ add(
36999
+ LOCATOR_WEIGHTS.rowText,
37000
+ context.rowText.toLowerCase() === ((_d2 = (_c2 = seen.context) == null ? void 0 : _c2.rowText) == null ? void 0 : _d2.toLowerCase()) ? 1 : 0
37001
+ );
37002
+ }
37003
+ if (context == null ? void 0 : context.heading)
37004
+ add(LOCATOR_WEIGHTS.heading, context.heading === ((_e2 = seen.context) == null ? void 0 : _e2.heading) ? 1 : 0);
37005
+ if (context == null ? void 0 : context.landmark)
37006
+ add(LOCATOR_WEIGHTS.landmark, context.landmark === ((_f2 = seen.context) == null ? void 0 : _f2.landmark) ? 1 : 0);
37007
+ const otherKeys = Object.keys(stored.attrs ?? {}).filter(
37008
+ (key) => !TEST_ATTR_KEYS.includes(key)
37009
+ );
37010
+ if (otherKeys.length) {
37011
+ const matched = otherKeys.filter((key) => {
37012
+ var _a2, _b3;
37013
+ return ((_a2 = seen.attrs) == null ? void 0 : _a2[key]) === ((_b3 = stored.attrs) == null ? void 0 : _b3[key]);
37014
+ }).length;
37015
+ add(LOCATOR_WEIGHTS.attrs, matched / otherKeys.length);
37016
+ }
37017
+ if (stored.roleIndex !== void 0 && seen.roleIndex !== void 0) {
37018
+ const gap = Math.abs(stored.roleIndex - seen.roleIndex);
37019
+ add(LOCATOR_WEIGHTS.roleIndex, gap === 0 ? 1 : gap === 1 ? 0.5 : 0);
37020
+ }
37021
+ if ((_g2 = stored.classes) == null ? void 0 : _g2.length)
37022
+ add(LOCATOR_WEIGHTS.classes, jaccard(stored.classes, seen.classes ?? []));
37023
+ if (observed.structuralHit !== void 0)
37024
+ add(LOCATOR_WEIGHTS.structural, observed.structuralHit ? 1 : 0);
37025
+ if (storedRect) {
37026
+ add(LOCATOR_WEIGHTS.location, locationScore(storedRect, observed.rect));
37027
+ add(LOCATOR_WEIGHTS.size, sizeScore(storedRect, observed.rect));
37028
+ }
37029
+ return total === 0 ? 0 : weighted / total;
37030
+ }
37031
+ const LOCATOR_ACCEPT_SCORE = 0.65;
37032
+ const LOCATOR_MIN_MARGIN = 0.1;
37033
+ const CANDIDATE_LIMIT = 300;
37034
+ const NAME_EVAL_LIMIT = 20;
37035
+ const TEXT_PROBE_LENGTH = 640;
37036
+ const LABEL_MIN_SIMILARITY = 0.5;
37037
+ const LOCATOR_MISS_RETRY_MS = 1e3;
37038
+ function documentRect(element2) {
37039
+ const rect = element2.getBoundingClientRect();
37040
+ const view = element2.ownerDocument.defaultView;
37041
+ return {
37042
+ left: rect.left + ((view == null ? void 0 : view.scrollX) ?? 0),
37043
+ top: rect.top + ((view == null ? void 0 : view.scrollY) ?? 0),
37044
+ width: rect.width,
37045
+ height: rect.height
37046
+ };
37047
+ }
37048
+ const HIDDEN_ANCESTOR_LIMIT = 50;
37049
+ function isVisible(element2) {
37050
+ return typeof element2.checkVisibility !== "function" || element2.checkVisibility();
37051
+ }
37052
+ function isRendered(node2) {
37053
+ if (!isVisible(node2)) return false;
37054
+ const view = node2.ownerDocument.defaultView;
37055
+ let current = node2;
37056
+ for (let depth = 0; current && depth < HIDDEN_ANCESTOR_LIMIT; depth += 1) {
37057
+ if (current.hasAttribute("hidden") || current.getAttribute("aria-hidden") === "true") {
37058
+ return false;
36615
37059
  }
36616
- };
36617
- const cancel = () => {
36618
- cancelTimer();
36619
- pendingThis = void 0;
36620
- pendingArgs = null;
36621
- };
36622
- const flush = () => {
36623
- invoke();
36624
- };
36625
- const debounced = function(...args) {
36626
- if (signal == null ? void 0 : signal.aborted) return;
36627
- pendingThis = this;
36628
- pendingArgs = args;
36629
- const isFirstCall = timeoutId == null;
36630
- schedule();
36631
- if (leading && isFirstCall) invoke();
36632
- };
36633
- debounced.schedule = schedule;
36634
- debounced.cancel = cancel;
36635
- debounced.flush = flush;
36636
- signal == null ? void 0 : signal.addEventListener("abort", cancel, { once: true });
36637
- return debounced;
37060
+ if (current.localName === "dialog" && !current.hasAttribute("open")) return false;
37061
+ const style = view == null ? void 0 : view.getComputedStyle(current);
37062
+ if ((style == null ? void 0 : style.display) === "none") return false;
37063
+ if (current === node2 && (style == null ? void 0 : style.visibility) === "hidden") return false;
37064
+ current = current.parentElement;
37065
+ }
37066
+ return true;
36638
37067
  }
36639
- function throttle(func, throttleMs, { signal, edges = ["leading", "trailing"] } = {}) {
36640
- let pendingAt = null;
36641
- const debounced = debounce(function(...args) {
36642
- pendingAt = Date.now();
36643
- func.apply(this, args);
36644
- }, throttleMs, {
36645
- signal,
36646
- edges
37068
+ function findScopes(locator, doc) {
37069
+ var _a;
37070
+ const container = (_a = locator.context) == null ? void 0 : _a.container;
37071
+ if (!container) return [doc];
37072
+ const wanted = container.name ? normalizeForCompare(container.name) : null;
37073
+ const scopes = Array.from(doc.querySelectorAll(CONTAINER_SELECTOR)).filter((node2) => {
37074
+ if (safeRole(node2) !== container.role) return false;
37075
+ if (!isRendered(node2)) return false;
37076
+ if (!wanted) return true;
37077
+ const name2 = safeAccessibleName(node2);
37078
+ return name2 !== void 0 && normalizeForCompare(name2) === wanted;
36647
37079
  });
36648
- const throttled = function(...args) {
36649
- if (pendingAt == null) pendingAt = Date.now();
36650
- if (Date.now() - pendingAt >= throttleMs) {
36651
- pendingAt = Date.now();
36652
- func.apply(this, args);
36653
- debounced.cancel();
36654
- debounced.schedule();
36655
- return;
37080
+ return scopes.length ? scopes : null;
37081
+ }
37082
+ function collectCandidates(locator, scopes) {
37083
+ const found = /* @__PURE__ */ new Set();
37084
+ const role = locator.role && /^[a-z]+$/.test(locator.role) ? locator.role : void 0;
37085
+ for (const scope of scopes) {
37086
+ for (const el of Array.from(scope.getElementsByTagName(locator.tag))) {
37087
+ found.add(el);
37088
+ }
37089
+ if (role) {
37090
+ scope.querySelectorAll(`[role="${role}"]`).forEach((el) => found.add(el));
37091
+ }
37092
+ }
37093
+ const candidates = Array.from(found);
37094
+ if (candidates.length <= CANDIDATE_LIMIT) return candidates;
37095
+ const label = locator.text ?? locator.name;
37096
+ if (!label) return null;
37097
+ const firstWord = normalizeForCompare(label).split(" ")[0] ?? "";
37098
+ const narrowed = candidates.filter(
37099
+ (el) => normalizeForCompare((el.textContent ?? "").slice(0, TEXT_PROBE_LENGTH)).includes(firstWord)
37100
+ );
37101
+ return narrowed.length <= CANDIDATE_LIMIT ? narrowed : null;
37102
+ }
37103
+ function isInScopes(element2, scopes) {
37104
+ return scopes.some((scope) => scope === element2.ownerDocument || scope.contains(element2));
37105
+ }
37106
+ function testAttrKey(stored) {
37107
+ return TEST_ATTR_KEYS.find((key) => {
37108
+ var _a;
37109
+ return ((_a = stored.attrs) == null ? void 0 : _a[key]) !== void 0;
37110
+ });
37111
+ }
37112
+ const IDENTITY_ATTR_KEYS = ["href", "src", "alt", "placeholder", "name"];
37113
+ const URL_ATTR_KEYS = ["href", "src"];
37114
+ function sameAttrs(stored, seen, keys2) {
37115
+ return keys2.every((key) => {
37116
+ var _a, _b2;
37117
+ const wanted = (_a = stored.attrs) == null ? void 0 : _a[key];
37118
+ return wanted === void 0 || ((_b2 = seen.attrs) == null ? void 0 : _b2[key]) === wanted;
37119
+ });
37120
+ }
37121
+ function sameRowText(stored, seen) {
37122
+ var _a, _b2, _c2;
37123
+ const wanted = (_a = stored.context) == null ? void 0 : _a.rowText;
37124
+ return !wanted || wanted.toLowerCase() === ((_c2 = (_b2 = seen.context) == null ? void 0 : _b2.rowText) == null ? void 0 : _c2.toLowerCase());
37125
+ }
37126
+ function sameLandmark(stored, seen) {
37127
+ var _a, _b2;
37128
+ const wanted = (_a = stored.context) == null ? void 0 : _a.landmark;
37129
+ return !wanted || wanted === ((_b2 = seen.context) == null ? void 0 : _b2.landmark);
37130
+ }
37131
+ function sameHeading(stored, seen, exact) {
37132
+ var _a, _b2;
37133
+ const wanted = (_a = stored.context) == null ? void 0 : _a.heading;
37134
+ if (!wanted) return true;
37135
+ const got = (_b2 = seen.context) == null ? void 0 : _b2.heading;
37136
+ if (got === void 0) return false;
37137
+ return exact ? got.toLowerCase() === wanted.toLowerCase() : normalizeForCompare(got) === normalizeForCompare(wanted);
37138
+ }
37139
+ const WRAPPER_LANDMARKS = /* @__PURE__ */ new Set(["region", "form"]);
37140
+ function withinStoredLandmark(stored, element2) {
37141
+ var _a;
37142
+ const wanted = (_a = stored.context) == null ? void 0 : _a.landmark;
37143
+ if (!wanted) return true;
37144
+ for (let node2 = element2.parentElement; node2; node2 = node2.parentElement) {
37145
+ const role = landmarkRole(node2);
37146
+ if (role === wanted) return true;
37147
+ if (role !== void 0 && !WRAPPER_LANDMARKS.has(role)) return false;
37148
+ }
37149
+ return false;
37150
+ }
37151
+ function matchesContainerPresence(stored, element2) {
37152
+ var _a, _b2;
37153
+ return Boolean((_a = stored.context) == null ? void 0 : _a.container) || !((_b2 = element2.parentElement) == null ? void 0 : _b2.closest(CONTAINER_SELECTOR));
37154
+ }
37155
+ function sameFastIdentity(stored, seen) {
37156
+ var _a, _b2;
37157
+ const testKey = testAttrKey(stored);
37158
+ if (testKey && ((_a = seen.attrs) == null ? void 0 : _a[testKey]) !== ((_b2 = stored.attrs) == null ? void 0 : _b2[testKey])) return false;
37159
+ for (const field of ["name", "text"]) {
37160
+ const wanted = stored[field];
37161
+ if (wanted === void 0) continue;
37162
+ const got = seen[field];
37163
+ if (got === void 0 || normalizeForCompare(got) !== normalizeForCompare(wanted)) return false;
37164
+ }
37165
+ return sameAttrs(stored, seen, URL_ATTR_KEYS) && sameHeading(stored, seen, false) && sameRowText(stored, seen);
37166
+ }
37167
+ function acceptsKnownElement(target, element2, observed) {
37168
+ const { locator, elementRect } = target;
37169
+ return matchesContainerPresence(locator, element2) && withinStoredLandmark(locator, element2) && sameFastIdentity(locator, observed.locator) && scoreLocator(locator, elementRect, observed) >= LOCATOR_ACCEPT_SCORE;
37170
+ }
37171
+ function passesScoredIdentity(stored, seen, nameKnown) {
37172
+ var _a, _b2;
37173
+ const testKey = testAttrKey(stored);
37174
+ if (testKey && ((_a = seen.attrs) == null ? void 0 : _a[testKey]) !== ((_b2 = stored.attrs) == null ? void 0 : _b2[testKey])) return false;
37175
+ if (!sameRowText(stored, seen) || !sameLandmark(stored, seen)) return false;
37176
+ if (!sameHeading(stored, seen, true) || !sameAttrs(stored, seen, IDENTITY_ATTR_KEYS))
37177
+ return false;
37178
+ for (const field of ["name", "text"]) {
37179
+ const wanted = stored[field];
37180
+ const got = seen[field];
37181
+ if (wanted && got && !sameDigitGroups(wanted, got)) return false;
37182
+ }
37183
+ if (stored.name && !nameKnown) return true;
37184
+ const similarities = [];
37185
+ for (const field of ["name", "text"]) {
37186
+ const wanted = stored[field];
37187
+ if (!wanted) continue;
37188
+ const got = seen[field];
37189
+ similarities.push(got ? labelSimilarityKeepingDigits(wanted, got) : 0);
37190
+ }
37191
+ return similarities.length === 0 || Math.max(...similarities) >= LABEL_MIN_SIMILARITY;
37192
+ }
37193
+ function hasContextIdentity(stored) {
37194
+ const context = stored.context;
37195
+ return Boolean(
37196
+ testAttrKey(stored) || (context == null ? void 0 : context.rowText) || (context == null ? void 0 : context.heading) || (context == null ? void 0 : context.landmark) || (context == null ? void 0 : context.container)
37197
+ );
37198
+ }
37199
+ function hasElementIdentity(stored) {
37200
+ var _a;
37201
+ return Boolean(
37202
+ testAttrKey(stored) || stored.name || stored.text || ((_a = stored.context) == null ? void 0 : _a.rowText) || IDENTITY_ATTR_KEYS.some((key) => {
37203
+ var _a2;
37204
+ return ((_a2 = stored.attrs) == null ? void 0 : _a2[key]) !== void 0;
37205
+ })
37206
+ );
37207
+ }
37208
+ function resolveByLocator(target, structuralElement, doc = document, options = {}) {
37209
+ var _a, _b2;
37210
+ try {
37211
+ const { locator, elementRect } = target;
37212
+ const scopes = findScopes(locator, doc);
37213
+ if (!scopes) {
37214
+ return { kind: "container-closed", containerName: (_b2 = (_a = locator.context) == null ? void 0 : _a.container) == null ? void 0 : _b2.name };
37215
+ }
37216
+ const headings = options.headings ?? collectHeadings(doc);
37217
+ const observe = (element2, includeName) => ({
37218
+ locator: buildElementLocator(element2, { includeName, headings, uniqueUrlAttrs: false }),
37219
+ rect: documentRect(element2),
37220
+ structuralHit: structuralElement ? element2 === structuralElement : void 0
37221
+ });
37222
+ if (structuralElement && isInScopes(structuralElement, scopes)) {
37223
+ if (acceptsKnownElement(target, structuralElement, observe(structuralElement, true))) {
37224
+ return { kind: "found", element: structuralElement, via: "fast" };
37225
+ }
36656
37226
  }
36657
- debounced.apply(this, args);
37227
+ if (!hasElementIdentity(locator)) return { kind: "not-found" };
37228
+ const collected = collectCandidates(locator, scopes);
37229
+ if (!collected) return { kind: "not-found" };
37230
+ const candidates = collected.filter((el) => matchesContainerPresence(locator, el));
37231
+ const withoutName = { ...locator, name: void 0 };
37232
+ const shortlist = candidates.map((element2) => ({ element: element2, observed: observe(element2, false) })).filter(({ observed }) => passesScoredIdentity(locator, observed.locator, false)).map(({ element: element2, observed }) => ({
37233
+ element: element2,
37234
+ score: scoreLocator(withoutName, elementRect, observed)
37235
+ })).sort((a, b) => b.score - a.score).slice(0, NAME_EVAL_LIMIT);
37236
+ const identified = shortlist.map(({ element: element2 }) => ({ element: element2, observed: observe(element2, true) })).filter(({ observed }) => passesScoredIdentity(locator, observed.locator, true));
37237
+ if (identified.some(({ element: element2 }) => !isVisible(element2))) return { kind: "not-found" };
37238
+ const ranked = identified.map(({ element: element2, observed }) => ({
37239
+ element: element2,
37240
+ score: scoreLocator(locator, elementRect, observed)
37241
+ })).sort((a, b) => b.score - a.score);
37242
+ const [first, second] = ranked;
37243
+ if (!first || first.score < LOCATOR_ACCEPT_SCORE) return { kind: "not-found" };
37244
+ if (second && first.score - second.score < LOCATOR_MIN_MARGIN) return { kind: "ambiguous" };
37245
+ if (!hasContextIdentity(locator)) return { kind: "not-found" };
37246
+ return { kind: "found", element: first.element, via: "scored" };
37247
+ } catch {
37248
+ return { kind: "not-found" };
37249
+ }
37250
+ }
37251
+ function acceptsCachedElement(target, element2, doc, headings) {
37252
+ if (!element2.isConnected || !isVisible(element2)) return false;
37253
+ const scopes = findScopes(target.locator, doc);
37254
+ if (!scopes || !isInScopes(element2, scopes)) return false;
37255
+ const observed = {
37256
+ locator: buildElementLocator(element2, {
37257
+ includeName: Boolean(target.locator.name),
37258
+ includeRoleIndex: false,
37259
+ uniqueUrlAttrs: false,
37260
+ headings
37261
+ }),
37262
+ rect: documentRect(element2)
36658
37263
  };
36659
- throttled.cancel = debounced.cancel;
36660
- throttled.flush = debounced.flush;
36661
- return throttled;
37264
+ return acceptsKnownElement(target, element2, observed);
37265
+ }
37266
+ function createLocatorResolverCache() {
37267
+ return { found: /* @__PURE__ */ new Map(), missed: /* @__PURE__ */ new Map() };
37268
+ }
37269
+ function pruneLocatorCache(cache, liveKeys) {
37270
+ for (const [key, element2] of cache.found) {
37271
+ if (!liveKeys.has(key) || !element2.isConnected) cache.found.delete(key);
37272
+ }
37273
+ for (const key of cache.missed.keys()) {
37274
+ if (!liveKeys.has(key)) cache.missed.delete(key);
37275
+ }
37276
+ }
37277
+ function reusableMiss(cache, key, childListVersion, now2) {
37278
+ const missed = cache.missed.get(key);
37279
+ if (!missed) return void 0;
37280
+ if (missed.version === childListVersion) return missed.result;
37281
+ const due = missed.at + LOCATOR_MISS_RETRY_MS;
37282
+ if (now2 >= due) return void 0;
37283
+ cache.retryAt = Math.min(cache.retryAt ?? due, due);
37284
+ return missed.result;
37285
+ }
37286
+ function resolveByLocatorCached(key, target, structural, cache, childListVersion, doc = document, options = {}) {
37287
+ try {
37288
+ const cached2 = cache.found.get(key);
37289
+ if (cached2 && acceptsCachedElement(target, cached2, doc, options.headings)) {
37290
+ return { kind: "found", element: cached2, via: "fast" };
37291
+ }
37292
+ cache.found.delete(key);
37293
+ const now2 = Date.now();
37294
+ const reused = reusableMiss(cache, key, childListVersion, now2);
37295
+ if (reused) return reused;
37296
+ const structuralElement = typeof structural === "function" ? structural() : structural;
37297
+ const result = resolveByLocator(target, structuralElement, doc, options);
37298
+ if (result.kind === "found") {
37299
+ cache.found.set(key, result.element);
37300
+ cache.missed.delete(key);
37301
+ } else if (result.kind === "container-closed") {
37302
+ cache.missed.delete(key);
37303
+ } else {
37304
+ cache.missed.set(key, { version: childListVersion, at: now2, result });
37305
+ }
37306
+ return result;
37307
+ } catch {
37308
+ return { kind: "not-found" };
37309
+ }
36662
37310
  }
36663
37311
  const STICKER_RENDER_MODE_SETTING_KEY = "sticker_render_mode";
36664
37312
  const DEFAULT_STICKER_RENDER_MODE = "absolute";
@@ -36735,7 +37383,7 @@ function isUniqueSelectorMatch(selector, element2) {
36735
37383
  return false;
36736
37384
  }
36737
37385
  }
36738
- function resolveTargetElement(position2) {
37386
+ function resolveStructuralElement(position2) {
36739
37387
  const selectorElement = position2.selector ? findElementBySelector(position2.selector) : null;
36740
37388
  if (selectorElement && position2.selector && isUniqueSelectorMatch(position2.selector, selectorElement)) {
36741
37389
  return selectorElement;
@@ -36747,10 +37395,30 @@ function resolveTargetElement(position2) {
36747
37395
  if (selectorElement) return selectorElement;
36748
37396
  return null;
36749
37397
  }
36750
- function resolveTargetStickerPoint(position2, metrics) {
36751
- const element2 = resolveTargetElement(position2);
36752
- if (!element2) return null;
37398
+ function locatorOf(position2) {
37399
+ var _a;
37400
+ return ((_a = position2.locator) == null ? void 0 : _a.v) === 1 ? position2.locator : void 0;
37401
+ }
37402
+ function resolveTarget(issueId, position2, cache, childListVersion, options) {
37403
+ const locator = locatorOf(position2);
37404
+ if (!locator) {
37405
+ const structural = resolveStructuralElement(position2);
37406
+ return structural ? { kind: "found", element: structural } : { kind: "not-found" };
37407
+ }
37408
+ const target = { locator, elementRect: position2.elementRect };
37409
+ return cache ? resolveByLocatorCached(
37410
+ issueId,
37411
+ target,
37412
+ () => resolveStructuralElement(position2),
37413
+ cache,
37414
+ childListVersion,
37415
+ document,
37416
+ options
37417
+ ) : resolveByLocator(target, resolveStructuralElement(position2), document, options);
37418
+ }
37419
+ function pointOfElement(element2, metrics) {
36753
37420
  const rect = element2.getBoundingClientRect();
37421
+ if (rect.width === 0 && rect.height === 0) return null;
36754
37422
  return {
36755
37423
  x: metrics.scrollX + rect.right,
36756
37424
  y: metrics.scrollY + rect.top
@@ -36775,6 +37443,12 @@ function resolveStickerPlacements(issues, mode, options = {}) {
36775
37443
  const limit = options.limit ?? DEFAULT_STICKER_LIMIT;
36776
37444
  const placements = [];
36777
37445
  const unrendered = [];
37446
+ const cache = options.resolverCache;
37447
+ if (cache) {
37448
+ cache.retryAt = void 0;
37449
+ pruneLocatorCache(cache, new Set(issues.map((issue) => issue.id)));
37450
+ }
37451
+ const resolveOptions = mode === "target" && typeof document !== "undefined" && issues.some((issue) => issue.position && locatorOf(issue.position)) ? { headings: collectHeadings(document) } : {};
36778
37452
  for (const issue of issues) {
36779
37453
  if (placements.length >= limit) {
36780
37454
  unrendered.push({
@@ -36792,7 +37466,35 @@ function resolveStickerPlacements(issues, mode, options = {}) {
36792
37466
  });
36793
37467
  continue;
36794
37468
  }
36795
- const point2 = mode === "target" ? resolveTargetStickerPoint(issue.position, metrics) : resolveAbsoluteStickerPoint(issue.position, metrics.viewport);
37469
+ let point2;
37470
+ if (mode === "target") {
37471
+ const resolution = resolveTarget(
37472
+ issue.id,
37473
+ issue.position,
37474
+ cache,
37475
+ options.childListVersion ?? 0,
37476
+ resolveOptions
37477
+ );
37478
+ if (resolution.kind === "container-closed") {
37479
+ unrendered.push({
37480
+ issue,
37481
+ status: "container-closed",
37482
+ reason: resolution.containerName ? `'${resolution.containerName}' 팝업을 열면 표시됩니다.` : "팝업을 열면 표시됩니다."
37483
+ });
37484
+ continue;
37485
+ }
37486
+ if (resolution.kind === "ambiguous") {
37487
+ unrendered.push({
37488
+ issue,
37489
+ status: "ambiguous",
37490
+ reason: "비슷한 요소가 여러 개라 위치를 정할 수 없습니다."
37491
+ });
37492
+ continue;
37493
+ }
37494
+ point2 = resolution.kind === "found" ? pointOfElement(resolution.element, metrics) : null;
37495
+ } else {
37496
+ point2 = resolveAbsoluteStickerPoint(issue.position, metrics.viewport);
37497
+ }
36796
37498
  if (!point2 || !isFinitePoint(point2)) {
36797
37499
  unrendered.push({
36798
37500
  issue,
@@ -36819,7 +37521,7 @@ function resolveStickerPlacements(issues, mode, options = {}) {
36819
37521
  hash: buildPlacementHash(issue, point2, mode)
36820
37522
  });
36821
37523
  }
36822
- return { placements, unrendered };
37524
+ return { placements, unrendered, retryAt: cache == null ? void 0 : cache.retryAt };
36823
37525
  }
36824
37526
  function getPlacementSortKey(placement) {
36825
37527
  return `${placement.issue.created_at || ""}:${placement.issue.id}`;
@@ -37216,6 +37918,7 @@ function useDocumentLayerScrollVars(ref, enabled) {
37216
37918
  }
37217
37919
  function useDocumentLayoutVersion(enabled, mode) {
37218
37920
  const [version, setVersion] = useState(0);
37921
+ const contentVersionRef = useRef(0);
37219
37922
  useEffect(() => {
37220
37923
  if (!enabled || typeof window === "undefined" || typeof document === "undefined") {
37221
37924
  return void 0;
@@ -37232,8 +37935,18 @@ function useDocumentLayoutVersion(enabled, mode) {
37232
37935
  const scheduleThrottled = throttle(scheduleFrame, LAYOUT_RECALC_THROTTLE_MS);
37233
37936
  window.addEventListener("resize", scheduleThrottled);
37234
37937
  window.addEventListener("orientationchange", scheduleThrottled);
37235
- const observer2 = new MutationObserver(scheduleThrottled);
37236
- observer2.observe(document.body, { attributes: true, childList: true, subtree: true });
37938
+ const observer2 = new MutationObserver((records) => {
37939
+ if (records.some((record) => record.type === "childList" || record.type === "characterData")) {
37940
+ contentVersionRef.current += 1;
37941
+ }
37942
+ scheduleThrottled();
37943
+ });
37944
+ observer2.observe(document.body, {
37945
+ attributes: true,
37946
+ childList: true,
37947
+ characterData: true,
37948
+ subtree: true
37949
+ });
37237
37950
  const resizeObserver = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(scheduleThrottled);
37238
37951
  if (resizeObserver) {
37239
37952
  resizeObserver.observe(document.documentElement);
@@ -37253,7 +37966,7 @@ function useDocumentLayoutVersion(enabled, mode) {
37253
37966
  if (frame2) window.cancelAnimationFrame(frame2);
37254
37967
  };
37255
37968
  }, [enabled, mode]);
37256
- return version;
37969
+ return { version, contentVersion: contentVersionRef.current };
37257
37970
  }
37258
37971
  function getClusterDocumentPoint(cluster) {
37259
37972
  return {
@@ -37289,19 +38002,34 @@ function IssueStickersContainer$1({
37289
38002
  const closeTimerRef = useRef(null);
37290
38003
  const mode = normalizeStickerRenderMode(renderMode);
37291
38004
  const markerSize = useStickerSize();
37292
- const layoutVersion = useDocumentLayoutVersion(visible, mode);
38005
+ const { version: layoutVersion, contentVersion } = useDocumentLayoutVersion(visible, mode);
38006
+ const resolverCacheRef = useRef(createLocatorResolverCache());
38007
+ const [retryVersion, setRetryVersion] = useState(0);
37293
38008
  useDocumentLayerScrollVars(layerRef, visible);
37294
38009
  const metrics = useMemo(() => getCurrentStickerDocumentMetrics(), [layoutVersion, visible]);
37295
- const { clusters, unrendered } = useMemo(() => {
38010
+ const { clusters, unrendered, retryAt } = useMemo(() => {
37296
38011
  if (!visible) {
37297
- return { clusters: [], unrendered: [] };
38012
+ return { clusters: [], unrendered: [], retryAt: void 0 };
37298
38013
  }
37299
- const resolved = resolveStickerPlacements(issues, mode, { metrics });
38014
+ const resolved = resolveStickerPlacements(issues, mode, {
38015
+ metrics,
38016
+ resolverCache: resolverCacheRef.current,
38017
+ childListVersion: contentVersion
38018
+ });
37300
38019
  return {
37301
38020
  clusters: clusterStickerPlacements(resolved.placements, markerSize),
37302
- unrendered: resolved.unrendered
38021
+ unrendered: resolved.unrendered,
38022
+ retryAt: resolved.retryAt
37303
38023
  };
37304
- }, [issues, layoutVersion, markerSize, metrics, mode, visible]);
38024
+ }, [contentVersion, issues, layoutVersion, markerSize, metrics, mode, retryVersion, visible]);
38025
+ useEffect(() => {
38026
+ if (retryAt === void 0) return void 0;
38027
+ const timer = window.setTimeout(
38028
+ () => setRetryVersion((current) => current + 1),
38029
+ Math.max(0, retryAt - Date.now()) + 1
38030
+ );
38031
+ return () => window.clearTimeout(timer);
38032
+ }, [retryAt, retryVersion]);
37305
38033
  const unrenderedSignature = useMemo(
37306
38034
  () => unrendered.map((item) => `${item.issue.id}:${item.status}:${item.reason}`).sort().join("|"),
37307
38035
  [unrendered]
@@ -37362,6 +38090,7 @@ function IssueStickersContainer$1({
37362
38090
  [clearClosingCluster, collapseExpandedCluster, expandedClusterId]
37363
38091
  );
37364
38092
  useEffect(() => {
38093
+ var _a;
37365
38094
  if (!expandedClusterId) return void 0;
37366
38095
  const handlePointerDown = (event) => {
37367
38096
  const root2 = rootRef.current;
@@ -37370,8 +38099,9 @@ function IssueStickersContainer$1({
37370
38099
  collapseExpandedCluster();
37371
38100
  }
37372
38101
  };
37373
- document.addEventListener("pointerdown", handlePointerDown, true);
37374
- return () => document.removeEventListener("pointerdown", handlePointerDown, true);
38102
+ const scopes = /* @__PURE__ */ new Set([document, ((_a = rootRef.current) == null ? void 0 : _a.getRootNode()) ?? document]);
38103
+ scopes.forEach((scope) => scope.addEventListener("pointerdown", handlePointerDown, true));
38104
+ return () => scopes.forEach((scope) => scope.removeEventListener("pointerdown", handlePointerDown, true));
37375
38105
  }, [collapseExpandedCluster, expandedClusterId]);
37376
38106
  if (!visible) {
37377
38107
  return null;
@@ -37635,6 +38365,9 @@ function useKeyboardShortcuts(isCreatingIssueRef, onToggleIssueMode, onToggleRec
37635
38365
  return;
37636
38366
  }
37637
38367
  if (isRecordingShortcut(eventLike)) {
38368
+ if (isIssueFormOpen) {
38369
+ return;
38370
+ }
37638
38371
  e.preventDefault();
37639
38372
  e.stopPropagation();
37640
38373
  if (isProcessingRecordingRef.current) {
@@ -37962,15 +38695,16 @@ function useContentAppState() {
37962
38695
  }, [isCreatingIssue]);
37963
38696
  const handleIssueCreated = useCallback(
37964
38697
  async (hasIntegrationFailure = false) => {
38698
+ const hadVideo = Boolean(clickPosition == null ? void 0 : clickPosition.videoFile);
37965
38699
  clearClickPosition();
37966
38700
  screenRecording.clearRecordedFile();
37967
38701
  await loadIssues();
37968
38702
  await toggleIssueMode(false);
37969
38703
  if (!hasIntegrationFailure) {
37970
- showToast("이슈가 생성되었습니다.");
38704
+ showToast(hadVideo ? VIDEO_UPLOAD_COPY.created : "이슈가 생성되었습니다.");
37971
38705
  }
37972
38706
  },
37973
- [clearClickPosition, loadIssues, screenRecording, toggleIssueMode, showToast]
38707
+ [clickPosition, clearClickPosition, loadIssues, screenRecording, toggleIssueMode, showToast]
37974
38708
  );
37975
38709
  return {
37976
38710
  // 상태
@@ -37999,6 +38733,31 @@ function useContentAppState() {
37999
38733
  setFieldFilters
38000
38734
  };
38001
38735
  }
38736
+ function useWidgetVideoUploads(projectId) {
38737
+ const [states, setStates] = useState([]);
38738
+ useEffect(() => getWidgetVideoUploadQueue().subscribe(setStates), []);
38739
+ useEffect(() => {
38740
+ if (!projectId) return;
38741
+ void resumeWidgetIssueUploads(projectId).then((count) => {
38742
+ if (count > 0) toast.info(VIDEO_UPLOAD_COPY.resumed(count));
38743
+ void getWidgetVideoUploadQueue().pruneExpired();
38744
+ void getWidgetAttachmentUploadQueue().pruneExpired();
38745
+ });
38746
+ }, [projectId]);
38747
+ useEffect(() => {
38748
+ const handleBeforeUnload = (event) => {
38749
+ if (getWidgetVideoUploadQueue().hasActiveUploads() || getWidgetAttachmentUploadQueue().hasActiveUploads()) {
38750
+ event.preventDefault();
38751
+ }
38752
+ };
38753
+ window.addEventListener("beforeunload", handleBeforeUnload);
38754
+ return () => window.removeEventListener("beforeunload", handleBeforeUnload);
38755
+ }, []);
38756
+ const retryFailedUploads = () => {
38757
+ states.filter((state) => state.status === "failed").forEach((state) => retryWidgetIssueUploads(state.issueId));
38758
+ };
38759
+ return { uploadProgress: summarizeVideoUploads(states), retryFailedUploads };
38760
+ }
38002
38761
  const STORAGE_KEY = "issue-sticker-sticker-render-mode";
38003
38762
  const QUERY_ROOT = ["stickerRenderMode"];
38004
38763
  function readLocalRenderMode() {
@@ -38364,7 +39123,7 @@ function MoreMenu({
38364
39123
  },
38365
39124
  {
38366
39125
  id: "unrendered",
38367
- label: unrenderedStickerCount > 0 ? `미표시 스티커 ${unrenderedStickerCount}개` : "미표시 스티커 없음",
39126
+ label: unrenderedStickerCount > 0 ? `표시하지 못한 스티커 ${unrenderedStickerCount}개` : "표시하지 못한 스티커 없음",
38368
39127
  icon: /* @__PURE__ */ jsx(CircleAlert, { size: ICON_SIZE }),
38369
39128
  onClick: onOpenUnrenderedStickers,
38370
39129
  // 0개일 때도 노출한다. 숨기면 목록을 열어 확인할 진입점 자체가 없어진다.
@@ -38609,6 +39368,35 @@ const TOOLBAR_STYLES = `
38609
39368
  border: 2px solid hsl(var(--surface-1));
38610
39369
  pointer-events: none;
38611
39370
  }
39371
+ .toolbar-upload {
39372
+ width: 34px;
39373
+ height: 34px;
39374
+ display: flex;
39375
+ align-items: center;
39376
+ justify-content: center;
39377
+ flex: none;
39378
+ border-radius: 50%;
39379
+ border: none;
39380
+ background: transparent;
39381
+ padding: 0;
39382
+ outline: none;
39383
+ }
39384
+ button.toolbar-upload { cursor: pointer; }
39385
+ .toolbar-upload:focus-visible { box-shadow: var(--shadow-focus); }
39386
+ .toolbar-upload svg { transform: rotate(-90deg); }
39387
+ .toolbar-upload-track {
39388
+ fill: none;
39389
+ stroke: hsl(var(--border-subtle));
39390
+ stroke-width: 2.5;
39391
+ }
39392
+ .toolbar-upload-bar {
39393
+ fill: none;
39394
+ stroke: var(--brand);
39395
+ stroke-width: 2.5;
39396
+ stroke-linecap: round;
39397
+ transition: stroke-dashoffset 0.3s ease;
39398
+ }
39399
+ .toolbar-upload[data-failed="true"] .toolbar-upload-bar { stroke: hsl(var(--danger)); }
38612
39400
 
38613
39401
  .toolbar-menu {
38614
39402
  position: absolute;
@@ -38886,6 +39674,13 @@ function formatElapsed(startedAt, now2) {
38886
39674
  const ss = String(seconds % 60).padStart(2, "0");
38887
39675
  return `${mm}:${ss}`;
38888
39676
  }
39677
+ const UPLOAD_RING_RADIUS = 7;
39678
+ const UPLOAD_RING_LENGTH = 2 * Math.PI * UPLOAD_RING_RADIUS;
39679
+ function formatUploadLabel({ percent: percent2, count, failed }) {
39680
+ if (failed) return "영상 업로드 실패";
39681
+ const waiting = count > 1 ? ` · 대기 ${count - 1}개` : "";
39682
+ return `영상 업로드 중 ${percent2}%${waiting}`;
39683
+ }
38889
39684
  function Toolbar({
38890
39685
  onCreateIssue,
38891
39686
  onToggleStickers,
@@ -38907,7 +39702,9 @@ function Toolbar({
38907
39702
  targets,
38908
39703
  account,
38909
39704
  blocked,
38910
- filter: filter2
39705
+ filter: filter2,
39706
+ uploadProgress,
39707
+ onUploadRetry
38911
39708
  }) {
38912
39709
  const rootRef = useRef(null);
38913
39710
  const [internalOpen, setInternalOpen] = useState(false);
@@ -39011,6 +39808,20 @@ function Toolbar({
39011
39808
  }, []);
39012
39809
  const anchor = toCssAnchor(placement);
39013
39810
  const menuPlacement = getMenuPlacement(placement);
39811
+ const uploadRing = uploadProgress ? /* @__PURE__ */ jsxs("svg", { width: "18", height: "18", viewBox: "0 0 18 18", "aria-hidden": "true", children: [
39812
+ /* @__PURE__ */ jsx("circle", { className: "toolbar-upload-track", cx: "9", cy: "9", r: UPLOAD_RING_RADIUS }),
39813
+ /* @__PURE__ */ jsx(
39814
+ "circle",
39815
+ {
39816
+ className: "toolbar-upload-bar",
39817
+ cx: "9",
39818
+ cy: "9",
39819
+ r: UPLOAD_RING_RADIUS,
39820
+ strokeDasharray: UPLOAD_RING_LENGTH,
39821
+ strokeDashoffset: uploadProgress.failed ? 0 : UPLOAD_RING_LENGTH * (1 - uploadProgress.percent / 100)
39822
+ }
39823
+ )
39824
+ ] }) : null;
39014
39825
  const idleBody = /* @__PURE__ */ jsxs(TooltipProvider, { delayDuration: 240, skipDelayDuration: 120, disableHoverableContent: true, children: [
39015
39826
  /* @__PURE__ */ jsxs(Tooltip, { children: [
39016
39827
  /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
@@ -39049,6 +39860,30 @@ function Toolbar({
39049
39860
  /* @__PURE__ */ jsx("span", { className: "block font-normal opacity-70", children: "공유할 화면을 선택하는 창이 열립니다" })
39050
39861
  ] })
39051
39862
  ] }),
39863
+ uploadProgress ? /* @__PURE__ */ jsxs(Tooltip, { children: [
39864
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: uploadProgress.failed && onUploadRetry ? /* @__PURE__ */ jsx(
39865
+ "button",
39866
+ {
39867
+ type: "button",
39868
+ className: "toolbar-upload",
39869
+ "aria-label": ISSUE_UPLOAD_COPY.retry,
39870
+ "data-failed": true,
39871
+ onClick: onUploadRetry,
39872
+ children: uploadRing
39873
+ }
39874
+ ) : /* @__PURE__ */ jsx(
39875
+ "span",
39876
+ {
39877
+ className: "toolbar-upload",
39878
+ role: "status",
39879
+ "aria-label": formatUploadLabel(uploadProgress),
39880
+ "data-failed": uploadProgress.failed,
39881
+ tabIndex: 0,
39882
+ children: uploadRing
39883
+ }
39884
+ ) }),
39885
+ /* @__PURE__ */ jsx(TooltipContent, { side: "top", sideOffset: 10, children: formatUploadLabel(uploadProgress) })
39886
+ ] }) : null,
39052
39887
  /* @__PURE__ */ jsx("div", { className: "toolbar-divider" }),
39053
39888
  /* @__PURE__ */ jsxs(
39054
39889
  "button",
@@ -39100,7 +39935,7 @@ function Toolbar({
39100
39935
  children: /* @__PURE__ */ jsx(X$1, { size: 15 })
39101
39936
  }
39102
39937
  )
39103
- ] }) : state === "writing" ? /* @__PURE__ */ jsx("span", { className: "toolbar-hint", children: "이슈를 작성 중입니다" }) : state === "recording" ? /* @__PURE__ */ jsxs(Fragment, { children: [
39938
+ ] }) : state === "recording" ? /* @__PURE__ */ jsxs(Fragment, { children: [
39104
39939
  /* @__PURE__ */ jsx("span", { className: "toolbar-pulse", "aria-hidden": "true" }),
39105
39940
  /* @__PURE__ */ jsx("span", { className: "toolbar-elapsed", "aria-hidden": "true", children: formatElapsed(recordingStartedAt, now2) }),
39106
39941
  /* @__PURE__ */ jsx("div", { className: "toolbar-divider" }),
@@ -39115,6 +39950,7 @@ function Toolbar({
39115
39950
  }
39116
39951
  )
39117
39952
  ] }) : idleBody;
39953
+ if (state === "writing") return null;
39118
39954
  return /* @__PURE__ */ jsxs(Fragment, { children: [
39119
39955
  /* @__PURE__ */ jsx("style", { children: TOOLBAR_STYLES }),
39120
39956
  /* @__PURE__ */ jsxs(Popover, { open: filterOpen, onOpenChange: setFilterOpen, children: [
@@ -39270,6 +40106,7 @@ function ContentAppWrapper({
39270
40106
  const { renderMode, saveRenderMode } = useStickerRenderMode();
39271
40107
  const { isMember } = useAuthContext();
39272
40108
  const { selectedProjectId } = useProject();
40109
+ const { uploadProgress, retryFailedUploads } = useWidgetVideoUploads(selectedProjectId);
39273
40110
  const filterProjectId = isMember ? selectedProjectId : null;
39274
40111
  const { data: filterGroups = [] } = useQuery({
39275
40112
  queryKey: ["issueFilterGroups", filterProjectId],
@@ -39312,6 +40149,8 @@ function ContentAppWrapper({
39312
40149
  recordingStartedAt: screenRecording.startedAt,
39313
40150
  showStickers,
39314
40151
  toolbar,
40152
+ uploadProgress,
40153
+ onUploadRetry: retryFailedUploads,
39315
40154
  stickerRenderMode: renderMode,
39316
40155
  onToggleStickerRenderMode: () => void saveRenderMode(nextRenderMode),
39317
40156
  unrenderedStickerCount: unrenderedCount,
@@ -39407,12 +40246,13 @@ function AuthGate() {
39407
40246
  ) })
39408
40247
  ] });
39409
40248
  }
39410
- const globalsCSS = `:root,:host{color-scheme:light;--brand-h: 150;--brand-s: 100%;--brand-l: 34%;--brand-hsl: var(--brand-h) var(--brand-s) var(--brand-l);--brand: hsl(var(--brand-hsl));--brand-hover: hsl(var(--brand-h) calc(var(--brand-s) + 3%) calc(var(--brand-l) - 4%));--brand-active: hsl(var(--brand-h) calc(var(--brand-s) + 2%) calc(var(--brand-l) - 8%));--brand-subtle: hsl(var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 14%) / .14);--brand-border: hsl(var(--brand-h) calc(var(--brand-s) + 8%) calc(var(--brand-l) + 8%) / .34);--brand-fg: hsl(0 0% 100%);--brand-ring: hsl(var(--brand-h) calc(var(--brand-s) + 12%) calc(var(--brand-l) + 10%) / .22);--font-sans: "Pretendard", -apple-system, BlinkMacSystemFont, system-ui, Roboto, "Helvetica Neue", "Segoe UI", "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", sans-serif;--font-display: var(--font-sans);--font-mono: var(--font-sans);--surface-0: 0 0% 99%;--surface-1: 0 0% 100%;--surface-2: 144 8% 96%;--surface-3: 144 6% 93%;--surface-inset: 144 6% 95%;--overlay-backdrop: hsl(0 0% 0% / .24);--overlay-backdrop-strong: hsl(0 0% 0% / .48);--text-primary: 150 10% 12%;--text-secondary: 148 6% 36%;--text-tertiary: 145 5% 42%;--text-disabled: 145 4% 70%;--text-inverse: 0 0% 100%;--border-subtle: 144 8% 91%;--border-default: 144 6% 85%;--border-strong: 144 5% 72%;--border-focus: var(--brand-h) var(--brand-s) var(--brand-l);--success: var(--brand-h) var(--brand-s) var(--brand-l);--success-subtle: var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 14%) / .14;--success-surface: var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 14%) / .24;--warning: 34 82% 52%;--warning-subtle: 34 82% 52% / .14;--danger: 3 68% 56%;--danger-subtle: 3 68% 56% / .12;--danger-deep: 3 62% 40%;--info: 202 52% 44%;--info-subtle: 202 52% 44% / .12;--accent-terra: 13 52% 47%;--accent-terra-subtle: 13 52% 47% / .1;--accent-terra-deep: 13 48% 44%;--accent-blue: 213 86% 56%;--accent-blue-subtle: 213 92% 56% / .12;--accent-blue-deep: 216 78% 40%;--accent-blue-border: 213 86% 62% / .36;--accent-cream: 38 52% 88%;--accent-cream-warm: 40 56% 95%;--accent-cream-deep: 38 48% 82%;--accent-highlight: 55 96% 70%;--accent-ink: 32 13% 11%;--radius-xs: 4px;--radius-sm: 8px;--radius-md: 12px;--radius-lg: 16px;--radius-xl: 20px;--radius-2xl: 24px;--radius-pill: 9999px;--shadow-xs: 0 1px 2px 0 hsl(0 0% 0% / .04);--shadow-sm: 0 1px 3px 0 hsl(0 0% 0% / .06), 0 1px 2px -1px hsl(0 0% 0% / .06);--shadow-md: 0 4px 6px -1px hsl(0 0% 0% / .06), 0 2px 4px -2px hsl(0 0% 0% / .04);--shadow-lg: 0 10px 15px -3px hsl(0 0% 0% / .06), 0 4px 6px -4px hsl(0 0% 0% / .04);--shadow-xl: 0 20px 25px -5px hsl(0 0% 0% / .06), 0 8px 10px -6px hsl(0 0% 0% / .04);--shadow-focus: 0 0 0 3px var(--brand-ring);--shadow-inset: inset 0 1px 2px 0 hsl(0 0% 0% / .04);--shadow-brand: 0 2px 8px hsl(var(--brand-h) calc(var(--brand-s) - 60%) calc(var(--brand-l) - 14%) / .24), 0 6px 20px hsl(var(--brand-h) calc(var(--brand-s) - 70%) calc(var(--brand-l) - 19%) / .16);--duration-instant: 90ms;--duration-fast: .16s;--duration-base: .26s;--duration-out: .18s;--duration-slow: .36s;--ease-out: cubic-bezier(.23, 1, .32, 1);--ease-in-out: cubic-bezier(.45, 0, .15, 1);--ease-spring: cubic-bezier(.18, .94, .32, 1.08);--grid-line: 0 0% 92%}.dark,:host(.dark){color-scheme:dark;--brand-l: 34%;--brand-fg: hsl(0 0% 100%);--surface-0: 150 8% 7%;--surface-1: 150 6% 10%;--surface-2: 150 5% 14%;--surface-3: 150 4% 18%;--surface-inset: 150 6% 6%;--overlay-backdrop: hsl(0 0% 0% / .56);--overlay-backdrop-strong: hsl(0 0% 0% / .8);--text-primary: 40 16% 93%;--text-secondary: 36 8% 68%;--text-tertiary: 34 6% 50%;--text-disabled: 30 5% 34%;--text-inverse: 150 8% 10%;--border-subtle: 150 6% 16%;--border-default: 150 5% 22%;--border-strong: 150 4% 34%;--success: var(--brand-h) var(--brand-s) var(--brand-l);--success-subtle: var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 14%) / .14;--success-surface: var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 14%) / .24;--warning: 34 82% 62%;--danger: 2 70% 64%;--danger-deep: 2 80% 76%;--info: 202 54% 62%;--accent-terra: 13 56% 62%;--accent-terra-subtle: 13 56% 62% / .14;--accent-terra-deep: 13 52% 56%;--accent-blue: 210 92% 68%;--accent-blue-subtle: 210 92% 68% / .18;--accent-blue-deep: 209 100% 78%;--accent-blue-border: 210 92% 72% / .44;--accent-cream: 38 30% 26%;--accent-cream-warm: 38 22% 18%;--accent-cream-deep: 38 26% 32%;--accent-highlight: 55 96% 70%;--accent-ink: 32 13% 8%;--shadow-xs: 0 1px 2px 0 hsl(0 0% 0% / .2);--shadow-sm: 0 1px 3px 0 hsl(0 0% 0% / .3), 0 1px 2px -1px hsl(0 0% 0% / .24);--shadow-md: 0 4px 6px -1px hsl(0 0% 0% / .34), 0 2px 4px -2px hsl(0 0% 0% / .2);--shadow-lg: 0 10px 15px -3px hsl(0 0% 0% / .38), 0 4px 6px -4px hsl(0 0% 0% / .24);--shadow-xl: 0 20px 25px -5px hsl(0 0% 0% / .44), 0 8px 10px -6px hsl(0 0% 0% / .28);--shadow-inset: inset 0 1px 2px 0 hsl(0 0% 0% / .16);--shadow-brand: 0 2px 8px hsl(var(--brand-h) calc(var(--brand-s) - 60%) calc(var(--brand-l) - 24%) / .44), 0 6px 20px hsl(0 0% 0% / .32);--grid-line: 150 6% 14%}*{border-color:hsl(var(--border-default))}html{font-family:var(--font-sans),ui-sans-serif,system-ui,-apple-system,sans-serif;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;font-feature-settings:"cv11","ss01","ss03";background-color:hsl(var(--surface-0))}body{margin:0;min-height:100vh;color:hsl(var(--text-primary));background-color:hsl(var(--surface-0))}::-moz-selection{background-color:hsl(var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 16%) / .22);color:hsl(var(--text-primary))}::selection{background-color:hsl(var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 16%) / .22);color:hsl(var(--text-primary))}code,kbd,samp,pre{font-family:var(--font-mono),ui-monospace,SFMono-Regular,Menlo,monospace;font-feature-settings:"ss01","ss02","cv11"}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:hsl(var(--border-strong) / .52);border-radius:var(--radius-pill);border:2px solid transparent;background-clip:padding-box}::-webkit-scrollbar-thumb:hover{background:hsl(var(--border-strong) / .9);background-clip:padding-box;border:2px solid transparent}.app-canvas,.grid-canvas{background-color:hsl(var(--surface-0))}.onboarding-canvas{word-break:keep-all;overflow-wrap:break-word;--surface-0: var(--brand-h) var(--brand-s) var(--brand-l);--surface-1: 0 0% 100%;--surface-2: var(--brand-h) 62% 28%;--surface-3: var(--brand-h) 60% 25%;--surface-inset: var(--brand-h) 62% 28%;--text-primary: 0 0% 100%;--text-secondary: var(--brand-h) 46% 90%;--text-tertiary: var(--brand-h) 34% 84%;--text-disabled: var(--brand-h) 20% 72%;--border-subtle: var(--brand-h) 44% 52%;--border-default: var(--brand-h) 40% 58%;--border-strong: var(--brand-h) 36% 70%;--border-focus: 0 0% 100%;--brand-hsl: 0 0% 100%;--brand: hsl(var(--brand-hsl));--brand-hover: hsl(var(--brand-h) 24% 94%);--brand-active: hsl(var(--brand-h) 20% 88%);--brand-fg: hsl(var(--brand-h) var(--brand-s) 24%);--brand-ring: hsl(0 0% 100% / .4);--danger: 6 96% 86%;--danger-subtle: 6 96% 86% / .18;--danger-deep: 6 96% 92%;--warning: 42 96% 82%;--warning-subtle: 42 96% 82% / .18}.onboarding-canvas .bg-surface-1,.onboarding-canvas input,.onboarding-canvas textarea,.onboarding-canvas [role=combobox]{--text-primary: 150 10% 12%;--text-secondary: 148 6% 36%;--text-tertiary: 145 5% 42%;--surface-2: 144 8% 96%;--surface-inset: 144 6% 95%;--border-subtle: 144 8% 91%;--border-default: 144 6% 85%;--brand-hsl: var(--brand-h) var(--brand-s) var(--brand-l);--brand: hsl(var(--brand-hsl));--brand-hover: hsl(var(--brand-h) calc(var(--brand-s) + 3%) calc(var(--brand-l) - 4%));--brand-active: hsl(var(--brand-h) calc(var(--brand-s) + 2%) calc(var(--brand-l) - 8%));--brand-fg: hsl(0 0% 100%);--brand-ring: hsl(var(--brand-h) calc(var(--brand-s) + 12%) calc(var(--brand-l) + 10%) / .22);--warning: 34 82% 52%;--warning-subtle: 34 82% 52% / .14;--danger: 3 68% 56%;--danger-subtle: 3 68% 56% / .12;--danger-deep: 3 62% 40%}@keyframes onboarding-step-in{0%{opacity:0;transform:translate(96px)}to{opacity:1;transform:translate(0)}}.onboarding-step-in{animation:onboarding-step-in var(--duration-slow) var(--ease-out) forwards}@keyframes onboarding-enter-up{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}.onboarding-enter{position:fixed;top:0;right:0;bottom:0;left:0;overflow-x:hidden;overflow-y:auto;animation:onboarding-enter-up var(--duration-base) var(--ease-out) forwards}body:has(.onboarding-canvas){background-color:hsl(var(--brand-h) var(--brand-s) var(--brand-l))}.panel-shell,.surface-shell{border:1px solid hsl(var(--border-subtle));border-radius:var(--radius-xl);background:hsl(var(--surface-1));box-shadow:var(--shadow-sm)}.panel-shell-muted,.surface-shell-muted{background:hsl(var(--surface-2));box-shadow:var(--shadow-xs)}.panel-shell-subtle{border:1px solid hsl(var(--border-subtle));border-radius:var(--radius-lg);background:hsl(var(--surface-1));box-shadow:var(--shadow-xs)}.panel-shell-elevated{border:1px solid hsl(var(--border-default));border-radius:var(--radius-xl);background:hsl(var(--surface-1));box-shadow:var(--shadow-md)}.utility-chip,.floating-chip{display:inline-flex;align-items:center;gap:8px;border:1px solid hsl(var(--border-default));border-radius:var(--radius-pill);background:hsl(var(--surface-1));box-shadow:var(--shadow-xs)}.hairline{border-bottom:1px solid hsl(var(--border-subtle))}.mono-label{font-family:var(--font-mono),ui-monospace,monospace;font-size:11px;text-transform:uppercase;color:hsl(var(--text-tertiary))}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:var(--font-sans),ui-sans-serif,system-ui,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--font-mono),ui-monospace,SFMono-Regular,Menlo,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root,:host{--font-sans: "Pretendard", -apple-system, BlinkMacSystemFont, system-ui, Roboto, "Helvetica Neue", "Segoe UI", "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", sans-serif;--font-mono: var(--font-sans);--issue-marker-expand-duration: .52s;--surface-0: 0 0% 99%;--surface-1: 0 0% 100%;--surface-2: 144 8% 96%;--surface-3: 144 6% 93%;--surface-inset: 144 6% 95%;--overlay-backdrop: hsl(0 0% 0% / .24);--overlay-backdrop-strong: hsl(0 0% 0% / .48);--text-primary: 150 10% 12%;--text-secondary: 148 6% 36%;--text-tertiary: 145 5% 52%;--text-disabled: 145 4% 70%;--border-subtle: 144 8% 91%;--border-default: 144 6% 85%;--border-strong: 144 5% 72%;--grid-line: 0 0% 92%;--shadow-xs: 0 1px 2px 0 hsl(0 0% 0% / .04);--shadow-sm: 0 1px 3px 0 hsl(0 0% 0% / .06), 0 1px 2px -1px hsl(0 0% 0% / .06);--shadow-md: 0 4px 6px -1px hsl(0 0% 0% / .06), 0 2px 4px -2px hsl(0 0% 0% / .04);--shadow-lg: 0 10px 15px -3px hsl(0 0% 0% / .06), 0 4px 6px -4px hsl(0 0% 0% / .04);--shadow-xl: 0 20px 25px -5px hsl(0 0% 0% / .06), 0 8px 10px -6px hsl(0 0% 0% / .04);--shadow-focus: 0 0 0 4px var(--brand-ring);--shadow-inset: inset 0 1px 2px 0 hsl(0 0% 0% / .04)}*{--tw-border-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-border-opacity, 1))}#issue-sticker-shadow-root{all:initial;display:block;background-color:transparent;--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1));font-family:var(--font-sans);line-height:1.5}.\\!container{width:100%!important}.container{width:100%}@media(min-width:640px){.\\!container{max-width:640px!important}.container{max-width:640px}}@media(min-width:768px){.\\!container{max-width:768px!important}.container{max-width:768px}}@media(min-width:1024px){.\\!container{max-width:1024px!important}.container{max-width:1024px}}@media(min-width:1280px){.\\!container{max-width:1280px!important}.container{max-width:1280px}}@media(min-width:1536px){.\\!container{max-width:1536px!important}.container{max-width:1536px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.\\!visible{visibility:visible!important}.visible{visibility:visible}.invisible{visibility:hidden}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.inset-x-0{left:0;right:0}.inset-x-1{left:4px;right:4px}.inset-y-0{top:0;bottom:0}.-bottom-px{bottom:-1px}.bottom-0{bottom:0}.bottom-4{bottom:16px}.bottom-6{bottom:24px}.left-0{left:0}.left-1\\/2{left:50%}.left-2{left:8px}.left-4{left:16px}.left-\\[50\\%\\]{left:50%}.right-0{right:0}.right-2{right:8px}.right-3{right:12px}.right-4{right:16px}.right-5{right:20px}.right-6{right:24px}.top-0{top:0}.top-1{top:4px}.top-1\\/2{top:50%}.top-2{top:8px}.top-3{top:12px}.top-4{top:16px}.top-5{top:20px}.top-\\[50\\%\\]{top:50%}.top-\\[calc\\(\\(3\\.5rem-1\\.75rem\\)\\/2\\)\\]{top:14px}.top-full{top:100%}.isolate{isolation:isolate}.z-10{z-index:10}.z-20{z-index:20}.z-\\[1000000\\]{z-index:1000000}.z-\\[1000003\\]{z-index:1000003}.z-\\[1000004\\]{z-index:1000004}.z-\\[1000005\\]{z-index:1000005}.z-\\[1000006\\]{z-index:1000006}.z-\\[1000007\\]{z-index:1000007}.z-\\[1000008\\]{z-index:1000008}.z-\\[1000010\\]{z-index:1000010}.z-\\[1000011\\]{z-index:1000011}.z-\\[10001\\]{z-index:10001}.z-\\[999999\\]{z-index:999999}.z-\\[9999\\]{z-index:9999}.m-0{margin:0}.-mx-2{margin-left:-8px;margin-right:-8px}.mx-3{margin-left:12px;margin-right:12px}.my-1{margin-top:4px;margin-bottom:4px}.my-2{margin-top:8px;margin-bottom:8px}.my-2\\.5{margin-top:10px;margin-bottom:10px}.my-3{margin-top:12px;margin-bottom:12px}.my-4{margin-top:16px;margin-bottom:16px}.mb-0\\.5{margin-bottom:2px}.mb-1{margin-bottom:4px}.mb-2{margin-bottom:8px}.mb-3{margin-bottom:12px}.mb-5{margin-bottom:20px}.ml-0\\.5{margin-left:2px}.ml-4{margin-left:16px}.mr-1\\.5{margin-right:6px}.mt-0{margin-top:0}.mt-0\\.5{margin-top:2px}.mt-1{margin-top:4px}.mt-2{margin-top:8px}.mt-3{margin-top:12px}.mt-4{margin-top:16px}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.aspect-square{aspect-ratio:1 / 1}.\\!h-auto{height:auto!important}.h-1\\.5{height:6px}.h-10{height:40px}.h-11{height:44px}.h-2{height:8px}.h-24{height:96px}.h-3{height:12px}.h-3\\.5{height:14px}.h-4{height:16px}.h-5{height:20px}.h-6{height:24px}.h-7{height:28px}.h-8{height:32px}.h-9{height:36px}.h-\\[1\\.4rem\\]{height:22.4px}.h-\\[100vh\\]{height:100vh}.h-\\[18px\\]{height:18px}.h-\\[220px\\]{height:220px}.h-\\[22px\\]{height:22px}.h-\\[30px\\]{height:30px}.h-\\[min\\(36vh\\,320px\\)\\]{height:min(36vh,320px)}.h-\\[min\\(480px\\,calc\\(100vh-64px\\)\\)\\]{height:min(480px,calc(100vh - 64px))}.h-\\[min\\(54vh\\,520px\\)\\]{height:min(54vh,520px)}.h-\\[min\\(560px\\,calc\\(100vh-32px\\)\\)\\]{height:min(560px,calc(100vh - 32px))}.h-\\[var\\(--issue-sticker-size\\)\\]{height:var(--issue-sticker-size)}.h-\\[var\\(--radix-select-trigger-height\\)\\]{height:var(--radix-select-trigger-height)}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.max-h-24{max-height:96px}.max-h-40{max-height:160px}.max-h-56{max-height:224px}.max-h-80{max-height:320px}.max-h-96{max-height:384px}.max-h-\\[150px\\]{max-height:150px}.max-h-\\[220px\\]{max-height:220px}.max-h-\\[320px\\]{max-height:320px}.max-h-\\[420px\\]{max-height:420px}.max-h-\\[90vh\\]{max-height:90vh}.max-h-\\[calc\\(100vh-3rem\\)\\]{max-height:calc(100vh - 48px)}.max-h-\\[min\\(80vh\\,640px\\)\\]{max-height:min(80vh,640px)}.min-h-0{min-height:0px}.min-h-11{min-height:44px}.min-h-\\[108px\\]{min-height:108px}.min-h-\\[120px\\]{min-height:120px}.min-h-\\[140px\\]{min-height:140px}.min-h-\\[220px\\]{min-height:220px}.w-0\\.5{width:2px}.w-1{width:4px}.w-1\\.5{width:6px}.w-1\\/2{width:50%}.w-10{width:40px}.w-12{width:48px}.w-14{width:56px}.w-16{width:64px}.w-2{width:8px}.w-3{width:12px}.w-3\\.5{width:14px}.w-4{width:16px}.w-5{width:20px}.w-6{width:24px}.w-7{width:28px}.w-72{width:288px}.w-8{width:32px}.w-80{width:320px}.w-9{width:36px}.w-\\[1\\.4rem\\]{width:22.4px}.w-\\[18px\\]{width:18px}.w-\\[22\\.5rem\\]{width:360px}.w-\\[22rem\\]{width:352px}.w-\\[30px\\]{width:30px}.w-\\[38px\\]{width:38px}.w-\\[calc\\(100vw-32px\\)\\]{width:calc(100vw - 32px)}.w-\\[min\\(20rem\\,calc\\(100vw_-_1\\.5rem\\)\\)\\]{width:min(320px,calc(100vw - 24px))}.w-\\[min\\(560px\\,calc\\(100vw-32px\\)\\)\\]{width:min(560px,calc(100vw - 32px))}.w-\\[var\\(--radix-popover-trigger-width\\)\\]{width:var(--radix-popover-trigger-width)}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.min-w-0{min-width:0px}.min-w-9{min-width:36px}.min-w-\\[12rem\\]{min-width:192px}.min-w-\\[200px\\]{min-width:200px}.min-w-\\[var\\(--radix-popover-trigger-width\\)\\]{min-width:var(--radix-popover-trigger-width)}.min-w-\\[var\\(--radix-select-trigger-width\\)\\]{min-width:var(--radix-select-trigger-width)}.max-w-\\[10rem\\]{max-width:160px}.max-w-\\[120px\\]{max-width:120px}.max-w-\\[160px\\]{max-width:160px}.max-w-\\[280px\\]{max-width:280px}.max-w-\\[28rem\\]{max-width:448px}.max-w-\\[300px\\]{max-width:300px}.max-w-\\[360px\\]{max-width:360px}.max-w-\\[36rem\\]{max-width:576px}.max-w-\\[400px\\]{max-width:400px}.max-w-\\[420px\\]{max-width:420px}.max-w-\\[428px\\]{max-width:428px}.max-w-\\[480px\\]{max-width:480px}.max-w-\\[62ch\\]{max-width:62ch}.max-w-\\[640px\\]{max-width:640px}.max-w-\\[70ch\\]{max-width:70ch}.max-w-\\[90vw\\]{max-width:90vw}.max-w-\\[960px\\]{max-width:960px}.max-w-\\[calc\\(100vw-24px\\)\\]{max-width:calc(100vw - 24px)}.max-w-\\[var\\(--issue-sticker-size\\)\\]{max-width:var(--issue-sticker-size)}.max-w-full{max-width:100%}.max-w-md{max-width:448px}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.grow{flex-grow:1}.caption-bottom{caption-side:bottom}.border-collapse{border-collapse:collapse}.-translate-x-1{--tw-translate-x: -4px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-x-1\\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-1{--tw-translate-x: 4px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\\[-50\\%\\]{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\\[18px\\]{--tw-translate-x: 18px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\\[2px\\]{--tw-translate-x: 2px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-\\[-50\\%\\]{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes pulse-ring{0%,to{box-shadow:0 0 0 0 var(--brand-ring)}50%{box-shadow:0 0 0 8px transparent}}.animate-pulse-ring{animation:pulse-ring 1.8s var(--ease-in-out) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-col-resize{cursor:col-resize}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.resize-y{resize:vertical}.resize{resize:both}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-row-reverse{flex-direction:row-reverse}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.content-center{align-content:center}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0{gap:0px}.gap-0\\.5{gap:2px}.gap-1{gap:4px}.gap-1\\.5{gap:6px}.gap-2{gap:8px}.gap-2\\.5{gap:10px}.gap-3{gap:12px}.gap-3\\.5{gap:14px}.gap-4{gap:16px}.gap-5{gap:20px}.gap-6{gap:24px}.gap-x-5{-moz-column-gap:20px;column-gap:20px}.gap-y-2{row-gap:8px}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(4px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(4px * var(--tw-space-y-reverse))}.space-y-1\\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(6px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(6px * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(8px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(8px * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(12px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(12px * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(16px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(16px * var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(20px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(20px * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(24px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(24px * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-line-subtle>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-divide-opacity, 1))}.self-start{align-self:flex-start}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-line{white-space:pre-line}.whitespace-pre-wrap{white-space:pre-wrap}.break-all{word-break:break-all}.break-keep{word-break:keep-all}.\\!rounded-none{border-radius:0!important}.rounded{border-radius:4px}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-\\[18px\\]{border-radius:18px}.rounded-\\[20px\\]{border-radius:20px}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-none{border-radius:0}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-xs{border-radius:var(--radius-xs)}.rounded-b-md{border-bottom-right-radius:var(--radius-md);border-bottom-left-radius:var(--radius-md)}.rounded-t-md{border-top-left-radius:var(--radius-md);border-top-right-radius:var(--radius-md)}.border{border-width:1px}.border-0{border-width:0px}.border-2{border-width:2px}.border-y{border-top-width:1px;border-bottom-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-l-4{border-left-width:4px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-t-0{border-top-width:0px}.border-dashed{border-style:dashed}.border-\\[hsl\\(144_8\\%_91\\%\\)\\]{--tw-border-opacity: 1;border-color:hsl(144 8% 91% / var(--tw-border-opacity, 1))}.border-brand{--tw-border-opacity: 1;border-color:hsl(var(--brand-hsl) / var(--tw-border-opacity, 1))}.border-brand-border{border-color:var(--brand-border)}.border-brand\\/30{border-color:hsl(var(--brand-hsl) / .3)}.border-brand\\/40{border-color:hsl(var(--brand-hsl) / .4)}.border-danger\\/20{border-color:hsl(var(--danger) / .2)}.border-danger\\/30{border-color:hsl(var(--danger) / .3)}.border-info\\/20{border-color:hsl(var(--info) / .2)}.border-info\\/30{border-color:hsl(var(--info) / .3)}.border-line{--tw-border-opacity: 1;border-color:hsl(var(--border-default) / var(--tw-border-opacity, 1))}.border-line-focus{--tw-border-opacity: 1;border-color:hsl(var(--border-focus) / var(--tw-border-opacity, 1))}.border-line-strong{--tw-border-opacity: 1;border-color:hsl(var(--border-strong) / var(--tw-border-opacity, 1))}.border-line-subtle{--tw-border-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-border-opacity, 1))}.border-line-subtle\\/50{border-color:hsl(var(--border-subtle) / .5)}.border-line-subtle\\/90{border-color:hsl(var(--border-subtle) / .9)}.border-slate-100{--tw-border-opacity: 1;border-color:rgb(241 245 249 / var(--tw-border-opacity, 1))}.border-slate-200{--tw-border-opacity: 1;border-color:rgb(226 232 240 / var(--tw-border-opacity, 1))}.border-success{--tw-border-opacity: 1;border-color:hsl(var(--success) / var(--tw-border-opacity, 1))}.border-success\\/20{border-color:hsl(var(--success) / .2)}.border-success\\/30{border-color:hsl(var(--success) / .3)}.border-text-primary{--tw-border-opacity: 1;border-color:hsl(var(--text-primary) / var(--tw-border-opacity, 1))}.border-transparent{border-color:transparent}.border-warning\\/20{border-color:hsl(var(--warning) / .2)}.border-warning\\/30{border-color:hsl(var(--warning) / .3)}.border-white\\/70{border-color:#ffffffb3}.border-white\\/80{border-color:#fffc}.border-white\\/90{border-color:#ffffffe6}.border-l-brand{--tw-border-opacity: 1;border-left-color:hsl(var(--brand-hsl) / var(--tw-border-opacity, 1))}.\\!bg-success-surface{background-color:hsl(var(--success-surface))!important}.bg-\\[var\\(--overlay-backdrop-strong\\)\\]{background-color:var(--overlay-backdrop-strong)}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-black\\/10{background-color:#0000001a}.bg-black\\/40{background-color:#0006}.bg-black\\/80{background-color:#000c}.bg-brand{--tw-bg-opacity: 1;background-color:hsl(var(--brand-hsl) / var(--tw-bg-opacity, 1))}.bg-brand-subtle{background-color:var(--brand-subtle)}.bg-brand\\/10{background-color:hsl(var(--brand-hsl) / .1)}.bg-brand\\/5{background-color:hsl(var(--brand-hsl) / .05)}.bg-danger{--tw-bg-opacity: 1;background-color:hsl(var(--danger) / var(--tw-bg-opacity, 1))}.bg-danger-subtle{background-color:hsl(var(--danger-subtle))}.bg-danger\\/10{background-color:hsl(var(--danger) / .1)}.bg-info-subtle{background-color:hsl(var(--info-subtle))}.bg-line{--tw-bg-opacity: 1;background-color:hsl(var(--border-default) / var(--tw-bg-opacity, 1))}.bg-line-subtle{--tw-bg-opacity: 1;background-color:hsl(var(--border-subtle) / var(--tw-bg-opacity, 1))}.bg-success{--tw-bg-opacity: 1;background-color:hsl(var(--success) / var(--tw-bg-opacity, 1))}.bg-success-subtle{background-color:hsl(var(--success-subtle))}.bg-success-surface{background-color:hsl(var(--success-surface))}.bg-surface-0{--tw-bg-opacity: 1;background-color:hsl(var(--surface-0) / var(--tw-bg-opacity, 1))}.bg-surface-1{--tw-bg-opacity: 1;background-color:hsl(var(--surface-1) / var(--tw-bg-opacity, 1))}.bg-surface-2{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.bg-surface-3{--tw-bg-opacity: 1;background-color:hsl(var(--surface-3) / var(--tw-bg-opacity, 1))}.bg-surface-inset{--tw-bg-opacity: 1;background-color:hsl(var(--surface-inset) / var(--tw-bg-opacity, 1))}.bg-text-primary{--tw-bg-opacity: 1;background-color:hsl(var(--text-primary) / var(--tw-bg-opacity, 1))}.bg-text-tertiary{--tw-bg-opacity: 1;background-color:hsl(var(--text-tertiary) / var(--tw-bg-opacity, 1))}.bg-transparent{background-color:transparent}.bg-warning{--tw-bg-opacity: 1;background-color:hsl(var(--warning) / var(--tw-bg-opacity, 1))}.bg-warning-subtle{background-color:hsl(var(--warning-subtle))}.bg-warning\\/10{background-color:hsl(var(--warning) / .1)}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-white\\/10{background-color:#ffffff1a}.bg-white\\/25{background-color:#ffffff40}.fill-text-primary{fill:hsl(var(--text-primary) / 1)}.stroke-\\[3\\]{stroke-width:3}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.\\!p-0{padding:0!important}.p-0{padding:0}.p-0\\.5{padding:2px}.p-1{padding:4px}.p-1\\.5{padding:6px}.p-2{padding:8px}.p-3{padding:12px}.p-3\\.5{padding:14px}.p-4{padding:16px}.p-5{padding:20px}.p-6{padding:24px}.p-7{padding:28px}.p-8{padding:32px}.p-\\[18px\\]{padding:18px}.px-0{padding-left:0;padding-right:0}.px-1\\.5{padding-left:6px;padding-right:6px}.px-2{padding-left:8px;padding-right:8px}.px-2\\.5{padding-left:10px;padding-right:10px}.px-3{padding-left:12px;padding-right:12px}.px-4{padding-left:16px;padding-right:16px}.px-5{padding-left:20px;padding-right:20px}.px-6{padding-left:24px;padding-right:24px}.px-9{padding-left:36px;padding-right:36px}.py-0\\.5{padding-top:2px;padding-bottom:2px}.py-1{padding-top:4px;padding-bottom:4px}.py-1\\.5{padding-top:6px;padding-bottom:6px}.py-10{padding-top:40px;padding-bottom:40px}.py-16{padding-top:64px;padding-bottom:64px}.py-2{padding-top:8px;padding-bottom:8px}.py-2\\.5{padding-top:10px;padding-bottom:10px}.py-3{padding-top:12px;padding-bottom:12px}.py-3\\.5{padding-top:14px;padding-bottom:14px}.py-4{padding-top:16px;padding-bottom:16px}.py-5{padding-top:20px;padding-bottom:20px}.py-6{padding-top:24px;padding-bottom:24px}.py-8{padding-top:32px;padding-bottom:32px}.pb-1{padding-bottom:4px}.pb-3{padding-bottom:12px}.pb-4{padding-bottom:16px}.pl-3{padding-left:12px}.pl-4{padding-left:16px}.pl-5{padding-left:20px}.pl-8{padding-left:32px}.pr-10{padding-right:40px}.pr-3{padding-right:12px}.pr-4{padding-right:16px}.pt-0{padding-top:0}.pt-1{padding-top:4px}.pt-2\\.5{padding-top:10px}.pt-4{padding-top:16px}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-top{vertical-align:top}.align-middle{vertical-align:middle}.font-display{font-family:var(--font-display),ui-sans-serif,system-ui,sans-serif}.font-mono{font-family:var(--font-mono),ui-monospace,SFMono-Regular,Menlo,monospace}.font-sans{font-family:var(--font-sans),ui-sans-serif,system-ui,sans-serif}.text-2xs{font-size:clamp(10.5px,10.19px + .082vw,11.5px);line-height:1.333}.text-\\[0\\.72rem\\]{font-size:11.52px}.text-\\[1\\.45rem\\]{font-size:23.2px}.text-\\[1\\.75rem\\]{font-size:28px}.text-\\[10px\\]{font-size:10px}.text-\\[11px\\]{font-size:11px}.text-base{font-size:clamp(14px,13.39px + .163vw,16px);line-height:1.5}.text-lg{font-size:clamp(17px,16.39px + .163vw,19px);line-height:1.471}.text-sm{font-size:clamp(13px,12.54px + .122vw,14.5px);line-height:1.462}.text-xl{font-size:clamp(20px,19.08px + .245vw,23px);line-height:1.45}.text-xs{font-size:clamp(12px,11.69px + .082vw,13px);line-height:1.333}.font-black{font-weight:900}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-4{line-height:16px}.leading-5{line-height:20px}.leading-8{line-height:32px}.leading-\\[1\\.55\\]{line-height:1.55}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.leading-tight{line-height:1.25}.text-\\[hsl\\(145_5\\%_42\\%\\)\\]{--tw-text-opacity: 1;color:hsl(145 5% 42% / var(--tw-text-opacity, 1))}.text-brand{--tw-text-opacity: 1;color:hsl(var(--brand-hsl) / var(--tw-text-opacity, 1))}.text-brand-fg{color:var(--brand-fg)}.text-brand\\/50{color:hsl(var(--brand-hsl) / .5)}.text-danger{--tw-text-opacity: 1;color:hsl(var(--danger) / var(--tw-text-opacity, 1))}.text-danger\\/80{color:hsl(var(--danger) / .8)}.text-info{--tw-text-opacity: 1;color:hsl(var(--info) / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-slate-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.text-slate-500{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity, 1))}.text-slate-700{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity, 1))}.text-slate-900{--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity, 1))}.text-success{--tw-text-opacity: 1;color:hsl(var(--success) / var(--tw-text-opacity, 1))}.text-text-disabled{--tw-text-opacity: 1;color:hsl(var(--text-disabled) / var(--tw-text-opacity, 1))}.text-text-inverse{--tw-text-opacity: 1;color:hsl(var(--text-inverse) / var(--tw-text-opacity, 1))}.text-text-primary{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.text-text-secondary{--tw-text-opacity: 1;color:hsl(var(--text-secondary) / var(--tw-text-opacity, 1))}.text-text-tertiary{--tw-text-opacity: 1;color:hsl(var(--text-tertiary) / var(--tw-text-opacity, 1))}.text-transparent{color:transparent}.text-warning{--tw-text-opacity: 1;color:hsl(var(--warning) / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-white\\/90{color:#ffffffe6}.underline{text-decoration-line:underline}.line-through{text-decoration-line:line-through}.underline-offset-2{text-underline-offset:2px}.underline-offset-4{text-underline-offset:4px}.accent-brand{accent-color:hsl(var(--brand-hsl) / 1)}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.\\!shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1) !important;--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color) !important;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)!important}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\\[0_24px_70px_-28px_rgba\\(16\\,24\\,18\\,0\\.45\\)\\]{--tw-shadow: 0 24px 70px -28px rgba(16,24,18,.45);--tw-shadow-colored: 0 24px 70px -28px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: var(--shadow-lg);--tw-shadow-colored: var(--shadow-lg);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: var(--shadow-md);--tw-shadow-colored: var(--shadow-md);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: var(--shadow-sm);--tw-shadow-colored: var(--shadow-sm);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: var(--shadow-xl);--tw-shadow-colored: var(--shadow-xl);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xs{--tw-shadow: var(--shadow-xs);--tw-shadow-colored: var(--shadow-xs);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.outline-2{outline-width:2px}.outline-offset-0{outline-offset:0px}.outline-transparent{outline-color:transparent}.ring{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-2{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-4{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-brand{--tw-ring-opacity: 1;--tw-ring-color: hsl(var(--brand-hsl) / var(--tw-ring-opacity, 1))}.ring-brand-subtle{--tw-ring-color: var(--brand-subtle)}.blur{--tw-blur: blur(8px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-\\[2px\\]{--tw-backdrop-blur: blur(2px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[background-color\\,border-color\\,color\\,box-shadow\\]{transition-property:background-color,border-color,color,box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[background-color\\,color\\,border-color\\,box-shadow\\,opacity\\]{transition-property:background-color,color,border-color,box-shadow,opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[background-color\\,color\\,border-color\\,box-shadow\\]{transition-property:background-color,color,border-color,box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[background-color\\,color\\,box-shadow\\]{transition-property:background-color,color,box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[background-color\\,color\\]{transition-property:background-color,color;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[border-color\\,box-shadow\\,background-color\\]{transition-property:border-color,box-shadow,background-color;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[border-color\\,outline-color\\,box-shadow\\,background-color\\,transform\\]{transition-property:border-color,outline-color,box-shadow,background-color,transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[max-width\\,height\\,box-shadow\\,background-color\\,border-color\\,transform\\,opacity\\]{transition-property:max-width,height,box-shadow,background-color,border-color,transform,opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[max-width\\]{transition-property:max-width;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[opacity\\,transform\\]{transition-property:opacity,transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-300{transition-duration:.3s}.duration-\\[var\\(--issue-marker-expand-duration\\)\\]{transition-duration:var(--issue-marker-expand-duration)}.duration-fast{transition-duration:var(--duration-fast)}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-in-out{transition-timing-function:var(--ease-in-out)}.ease-out{transition-timing-function:var(--ease-out)}@keyframes float-gentle{0%,to{transform:translateY(0)}50%{transform:translateY(-2px)}}@keyframes toast-slide{0%{opacity:0;transform:translateY(-14px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes gradient-shift{0%,to{background-position:0% 50%}50%{background-position:100% 50%}}.issue-overlay-shell{border:1px solid hsl(var(--border-subtle));border-radius:var(--radius-lg);background:hsl(var(--surface-1));box-shadow:var(--shadow-md)}.issue-marker-breathe{animation:float-gentle 4.8s var(--ease-in-out) infinite}.issue-marker-breathe:hover,.issue-marker-breathe:focus-visible,.issue-marker-breathe:focus-within{animation-play-state:paused}.\\[overflow-wrap\\:anywhere\\]{overflow-wrap:anywhere}.marker\\:text-text-tertiary *::marker{color:hsl(var(--text-tertiary))}.marker\\:text-text-tertiary::marker{color:hsl(var(--text-tertiary))}.file\\:border-0::file-selector-button{border-width:0px}.file\\:bg-transparent::file-selector-button{background-color:transparent}.file\\:text-sm::file-selector-button{font-size:clamp(13px,12.54px + .122vw,14.5px);line-height:1.462}.file\\:font-medium::file-selector-button{font-weight:500}.file\\:text-text-primary::file-selector-button{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.placeholder\\:text-text-tertiary::-moz-placeholder{--tw-text-opacity: 1;color:hsl(var(--text-tertiary) / var(--tw-text-opacity, 1))}.placeholder\\:text-text-tertiary::placeholder{--tw-text-opacity: 1;color:hsl(var(--text-tertiary) / var(--tw-text-opacity, 1))}.first\\:mt-0:first-child{margin-top:0}.first\\:border-t-0:first-child{border-top-width:0px}.first\\:pt-0:first-child{padding-top:0}.focus-within\\:z-\\[10001\\]:focus-within{z-index:10001}.focus-within\\:border-line-focus:focus-within{--tw-border-opacity: 1;border-color:hsl(var(--border-focus) / var(--tw-border-opacity, 1))}.hover\\:z-\\[10001\\]:hover{z-index:10001}.hover\\:border-line:hover{--tw-border-opacity: 1;border-color:hsl(var(--border-default) / var(--tw-border-opacity, 1))}.hover\\:border-line-strong:hover{--tw-border-opacity: 1;border-color:hsl(var(--border-strong) / var(--tw-border-opacity, 1))}.hover\\:border-line-subtle:hover{--tw-border-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-border-opacity, 1))}.hover\\:bg-\\[hsl\\(144_8\\%_96\\%\\)\\]:hover{--tw-bg-opacity: 1;background-color:hsl(144 8% 96% / var(--tw-bg-opacity, 1))}.hover\\:bg-blue-500:hover{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.hover\\:bg-brand-hover:hover{background-color:var(--brand-hover)}.hover\\:bg-brand\\/40:hover{background-color:hsl(var(--brand-hsl) / .4)}.hover\\:bg-danger-subtle:hover{background-color:hsl(var(--danger-subtle))}.hover\\:bg-danger\\/10:hover{background-color:hsl(var(--danger) / .1)}.hover\\:bg-slate-100:hover{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity, 1))}.hover\\:bg-slate-50:hover{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1))}.hover\\:bg-surface-2:hover{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.hover\\:bg-surface-2\\/60:hover{background-color:hsl(var(--surface-2) / .6)}.hover\\:bg-surface-3:hover{--tw-bg-opacity: 1;background-color:hsl(var(--surface-3) / var(--tw-bg-opacity, 1))}.hover\\:bg-surface-inset:hover{--tw-bg-opacity: 1;background-color:hsl(var(--surface-inset) / var(--tw-bg-opacity, 1))}.hover\\:bg-transparent:hover{background-color:transparent}.hover\\:bg-white\\/20:hover{background-color:#fff3}.hover\\:text-\\[hsl\\(150_10\\%_12\\%\\)\\]:hover{--tw-text-opacity: 1;color:hsl(150 10% 12% / var(--tw-text-opacity, 1))}.hover\\:text-brand-hover:hover{color:var(--brand-hover)}.hover\\:text-danger:hover{--tw-text-opacity: 1;color:hsl(var(--danger) / var(--tw-text-opacity, 1))}.hover\\:text-slate-900:hover{--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity, 1))}.hover\\:text-text-primary:hover{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.hover\\:text-text-secondary:hover{--tw-text-opacity: 1;color:hsl(var(--text-secondary) / var(--tw-text-opacity, 1))}.hover\\:underline:hover{text-decoration-line:underline}.hover\\:opacity-90:hover{opacity:.9}.focus\\:border-brand:focus{--tw-border-opacity: 1;border-color:hsl(var(--brand-hsl) / var(--tw-border-opacity, 1))}.focus\\:bg-surface-2:focus{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.focus\\:text-text-primary:focus{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.focus\\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus-visible\\:h-\\[var\\(--issue-sticker-hover-size\\)\\]:focus-visible{height:var(--issue-sticker-hover-size)}.focus-visible\\:max-w-\\[160px\\]:focus-visible{max-width:160px}.focus-visible\\:max-w-\\[480px\\]:focus-visible{max-width:480px}.focus-visible\\:border-line-focus:focus-visible{--tw-border-opacity: 1;border-color:hsl(var(--border-focus) / var(--tw-border-opacity, 1))}.focus-visible\\:border-transparent:focus-visible{border-color:transparent}.focus-visible\\:bg-slate-50:focus-visible{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1))}.focus-visible\\:bg-surface-1:focus-visible{--tw-bg-opacity: 1;background-color:hsl(var(--surface-1) / var(--tw-bg-opacity, 1))}.focus-visible\\:bg-surface-2:focus-visible{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.focus-visible\\:text-text-primary:focus-visible{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.focus-visible\\:shadow-focus:focus-visible{--tw-shadow: var(--shadow-focus);--tw-shadow-colored: var(--shadow-focus);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.focus-visible\\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\\:outline-line-focus:focus-visible{outline-color:hsl(var(--border-focus) / 1)}.focus-visible\\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\\:ring-4:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\\:ring-brand-subtle:focus-visible{--tw-ring-color: var(--brand-subtle)}.focus-visible\\:ring-white\\/40:focus-visible{--tw-ring-color: rgb(255 255 255 / .4)}.active\\:bg-brand-active:active{background-color:var(--brand-active)}.active\\:bg-brand\\/60:active{background-color:hsl(var(--brand-hsl) / .6)}.disabled\\:pointer-events-none:disabled{pointer-events:none}.disabled\\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\\:opacity-40:disabled{opacity:.4}.disabled\\:opacity-50:disabled{opacity:.5}.group\\/marker:hover .group-hover\\/marker\\:h-\\[var\\(--issue-sticker-hover-size\\)\\]{height:var(--issue-sticker-hover-size)}.group\\/marker:hover .group-hover\\/marker\\:max-w-\\[160px\\]{max-width:160px}.group\\/marker:hover .group-hover\\/marker\\:max-w-\\[480px\\]{max-width:480px}.group\\/marker:hover .group-hover\\/marker\\:translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\\/marker:hover .group-hover\\/marker\\:bg-brand-hover{background-color:var(--brand-hover)}.group\\/marker:hover .group-hover\\/marker\\:opacity-100{opacity:1}.group\\/marker:hover .group-hover\\/marker\\:shadow-xl{--tw-shadow: var(--shadow-xl);--tw-shadow-colored: var(--shadow-xl);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.group:focus-visible .group-focus-visible\\:translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:focus-visible .group-focus-visible\\:opacity-100{opacity:1}.group.toaster .group-\\[\\.toaster\\]\\:isolate{isolation:isolate}.group.toast .group-\\[\\.toast\\]\\:rounded-md,.group.toaster .group-\\[\\.toaster\\]\\:rounded-md{border-radius:var(--radius-md)}.group.toaster .group-\\[\\.toaster\\]\\:border{border-width:1px}.group.toaster .group-\\[\\.toaster\\]\\:\\!border-danger\\/30{border-color:hsl(var(--danger) / .3)!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!border-info\\/30{border-color:hsl(var(--info) / .3)!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!border-success\\/30{border-color:hsl(var(--success) / .3)!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!border-warning\\/30{border-color:hsl(var(--warning) / .3)!important}.group.toaster .group-\\[\\.toaster\\]\\:border-line-subtle{--tw-border-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-border-opacity, 1))}.group.toast .group-\\[\\.toast\\]\\:bg-brand{--tw-bg-opacity: 1;background-color:hsl(var(--brand-hsl) / var(--tw-bg-opacity, 1))}.group.toast .group-\\[\\.toast\\]\\:bg-surface-2{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:\\!bg-danger-subtle{background-color:hsl(var(--danger-subtle))!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!bg-info-subtle{background-color:hsl(var(--info-subtle))!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!bg-success-surface{background-color:hsl(var(--success-surface))!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!bg-warning-subtle{background-color:hsl(var(--warning-subtle))!important}.group.toaster .group-\\[\\.toaster\\]\\:bg-surface-1{--tw-bg-opacity: 1;background-color:hsl(var(--surface-1) / var(--tw-bg-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:px-4{padding-left:16px;padding-right:16px}.group.toaster .group-\\[\\.toaster\\]\\:py-3{padding-top:12px;padding-bottom:12px}.group.toast .group-\\[\\.toast\\]\\:text-sm{font-size:clamp(13px,12.54px + .122vw,14.5px);line-height:1.462}.group.toast .group-\\[\\.toast\\]\\:text-xs{font-size:clamp(12px,11.69px + .082vw,13px);line-height:1.333}.group.toast .group-\\[\\.toast\\]\\:font-medium{font-weight:500}.group.toast .group-\\[\\.toast\\]\\:text-brand-fg{color:var(--brand-fg)}.group.toast .group-\\[\\.toast\\]\\:text-text-primary{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.group.toast .group-\\[\\.toast\\]\\:text-text-secondary{--tw-text-opacity: 1;color:hsl(var(--text-secondary) / var(--tw-text-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:text-text-primary{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:shadow-sm{--tw-shadow: var(--shadow-sm);--tw-shadow-colored: var(--shadow-sm);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.group.toaster .group-\\[\\.toaster\\]\\:before\\:absolute:before{content:var(--tw-content);position:absolute}.group.toaster .group-\\[\\.toaster\\]\\:before\\:inset-0:before{content:var(--tw-content);top:0;right:0;bottom:0;left:0}.group.toaster .group-\\[\\.toaster\\]\\:before\\:-z-10:before{content:var(--tw-content);z-index:-10}.group.toaster .group-\\[\\.toaster\\]\\:before\\:rounded-md:before{content:var(--tw-content);border-radius:var(--radius-md)}.group.toaster .group-\\[\\.toaster\\]\\:before\\:bg-surface-1:before{content:var(--tw-content);--tw-bg-opacity: 1;background-color:hsl(var(--surface-1) / var(--tw-bg-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:before\\:content-\\[\\'\\'\\]:before{--tw-content: "";content:var(--tw-content)}.peer:focus-visible~.peer-focus-visible\\:shadow-focus{--tw-shadow: var(--shadow-focus);--tw-shadow-colored: var(--shadow-focus);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.peer:disabled~.peer-disabled\\:cursor-not-allowed{cursor:not-allowed}.peer:disabled~.peer-disabled\\:opacity-60{opacity:.6}.aria-disabled\\:pointer-events-none[aria-disabled=true]{pointer-events:none}.aria-disabled\\:opacity-40[aria-disabled=true]{opacity:.4}.data-\\[disabled\\]\\:pointer-events-none[data-disabled]{pointer-events:none}@keyframes dialog-out{0%{opacity:1;transform:translate(-50%,-50%) scale(1)}to{opacity:0;transform:translate(-50%,calc(-50% + 8px)) scale(.985)}}.data-\\[state\\=closed\\]\\:animate-dialog-out[data-state=closed]{animation:dialog-out var(--duration-out) var(--ease-out) forwards}@keyframes fade-out{0%{opacity:1}to{opacity:0}}.data-\\[state\\=closed\\]\\:animate-fade-out[data-state=closed]{animation:fade-out var(--duration-out) var(--ease-out) forwards}@keyframes scale-out{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.97)}}.data-\\[state\\=closed\\]\\:animate-scale-out[data-state=closed]{animation:scale-out var(--duration-out) var(--ease-out) forwards}@keyframes sheet-out-left{0%{transform:translate(0)}to{transform:translate(-100%);opacity:.6}}.data-\\[state\\=closed\\]\\:animate-sheet-out-left[data-state=closed]{animation:sheet-out-left var(--duration-base) var(--ease-out) forwards}@keyframes sheet-out-right{0%{transform:translate(0);opacity:1}to{transform:translate(100%);opacity:.6}}.data-\\[state\\=closed\\]\\:animate-sheet-out-right[data-state=closed]{animation:sheet-out-right var(--duration-base) var(--ease-out) forwards}.data-\\[state\\=delayed-open\\]\\:animate-scale-in[data-state=delayed-open]{animation:scale-in var(--duration-base) var(--ease-out) forwards}@keyframes dialog-in{0%{opacity:0;transform:translate(-50%,calc(-50% + 12px)) scale(.985)}to{opacity:1;transform:translate(-50%,-50%) scale(1)}}.data-\\[state\\=open\\]\\:animate-dialog-in[data-state=open]{animation:dialog-in var(--duration-base) var(--ease-out) forwards}@keyframes fade-in{0%{opacity:0}to{opacity:1}}.data-\\[state\\=open\\]\\:animate-fade-in[data-state=open]{animation:fade-in var(--duration-base) var(--ease-out) forwards}@keyframes scale-in{0%{opacity:0;transform:scale(.98)}to{opacity:1;transform:scale(1)}}.data-\\[state\\=open\\]\\:animate-scale-in[data-state=open]{animation:scale-in var(--duration-base) var(--ease-out) forwards}@keyframes sheet-in-left{0%{transform:translate(-100%);opacity:.6}to{transform:translate(0);opacity:1}}.data-\\[state\\=open\\]\\:animate-sheet-in-left[data-state=open]{animation:sheet-in-left var(--duration-base) var(--ease-out) forwards}@keyframes sheet-in-right{0%{transform:translate(100%);opacity:.6}to{transform:translate(0)}}.data-\\[state\\=open\\]\\:animate-sheet-in-right[data-state=open]{animation:sheet-in-right var(--duration-base) var(--ease-out) forwards}.data-\\[range-end\\=true\\]\\:rounded-md[data-range-end=true]{border-radius:var(--radius-md)}.data-\\[range-middle\\=true\\]\\:rounded-none[data-range-middle=true]{border-radius:0}.data-\\[range-start\\=true\\]\\:rounded-md[data-range-start=true]{border-radius:var(--radius-md)}.data-\\[state\\=open\\]\\:border-line-focus[data-state=open]{--tw-border-opacity: 1;border-color:hsl(var(--border-focus) / var(--tw-border-opacity, 1))}.data-\\[highlighted\\]\\:bg-surface-2[data-highlighted]{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.data-\\[range-end\\=true\\]\\:bg-brand[data-range-end=true]{--tw-bg-opacity: 1;background-color:hsl(var(--brand-hsl) / var(--tw-bg-opacity, 1))}.data-\\[range-middle\\=true\\]\\:bg-transparent[data-range-middle=true]{background-color:transparent}.data-\\[range-start\\=true\\]\\:bg-brand[data-range-start=true],.data-\\[selected-single\\=true\\]\\:bg-brand[data-selected-single=true]{--tw-bg-opacity: 1;background-color:hsl(var(--brand-hsl) / var(--tw-bg-opacity, 1))}.data-\\[state\\=selected\\]\\:bg-brand-subtle[data-state=selected]{background-color:var(--brand-subtle)}.data-\\[today\\=true\\]\\:font-semibold[data-today=true]{font-weight:600}.data-\\[highlighted\\]\\:text-text-primary[data-highlighted]{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.data-\\[placeholder\\]\\:text-text-tertiary[data-placeholder]{--tw-text-opacity: 1;color:hsl(var(--text-tertiary) / var(--tw-text-opacity, 1))}.data-\\[range-end\\=true\\]\\:text-brand-fg[data-range-end=true]{color:var(--brand-fg)}.data-\\[range-middle\\=true\\]\\:text-text-primary[data-range-middle=true]{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.data-\\[range-start\\=true\\]\\:text-brand-fg[data-range-start=true],.data-\\[selected-single\\=true\\]\\:text-brand-fg[data-selected-single=true]{color:var(--brand-fg)}.data-\\[state\\=active\\]\\:text-text-primary[data-state=active],.data-\\[state\\=checked\\]\\:text-text-primary[data-state=checked]{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.data-\\[today\\=true\\]\\:text-brand[data-today=true]{--tw-text-opacity: 1;color:hsl(var(--brand-hsl) / var(--tw-text-opacity, 1))}.data-\\[disabled\\]\\:opacity-50[data-disabled]{opacity:.5}.data-\\[state\\=open\\]\\:shadow-focus[data-state=open]{--tw-shadow: var(--shadow-focus);--tw-shadow-colored: var(--shadow-focus);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.data-\\[range-end\\=true\\]\\:hover\\:bg-brand-hover:hover[data-range-end=true],.data-\\[range-start\\=true\\]\\:hover\\:bg-brand-hover:hover[data-range-start=true],.data-\\[selected-single\\=true\\]\\:hover\\:bg-brand-hover:hover[data-selected-single=true]{background-color:var(--brand-hover)}.data-\\[range-end\\=true\\]\\:hover\\:text-brand-fg:hover[data-range-end=true],.data-\\[range-start\\=true\\]\\:hover\\:text-brand-fg:hover[data-range-start=true],.data-\\[selected-single\\=true\\]\\:hover\\:text-brand-fg:hover[data-selected-single=true]{color:var(--brand-fg)}@media(prefers-reduced-motion:reduce){.motion-reduce\\:animate-none{animation:none}}@media(min-width:640px){.sm\\:flex-row{flex-direction:row}.sm\\:items-start{align-items:flex-start}.sm\\:items-center{align-items:center}.sm\\:justify-end{justify-content:flex-end}.sm\\:justify-between{justify-content:space-between}.sm\\:gap-2\\.5{gap:10px}.sm\\:p-5{padding:20px}.sm\\:p-6{padding:24px}.sm\\:text-2xl{font-size:clamp(26px,24.78px + .327vw,30px);line-height:1.308}.sm\\:text-\\[2\\.1rem\\]{font-size:33.6px}}@media(min-width:768px){.md\\:flex{display:flex}.md\\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\\:grid-cols-\\[minmax\\(0\\,1fr\\)_minmax\\(260px\\,340px\\)\\]{grid-template-columns:minmax(0,1fr) minmax(260px,340px)}}.\\[\\&\\>\\*\\:not\\(summary\\)\\]\\:pl-4>*:not(summary){padding-left:16px}.\\[\\&\\>span\\]\\:line-clamp-1>span{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1}.\\[\\&\\>tr\\]\\:last\\:border-b-0:last-child>tr{border-bottom-width:0px}.\\[\\&\\[data-state\\=open\\]\\>span\\[data-chevron\\]\\]\\:rotate-180[data-state=open]>span[data-chevron]{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group.toaster .group-\\[\\.toaster\\]\\:\\[\\&_\\[data-icon\\]\\]\\:text-danger [data-icon]{--tw-text-opacity: 1;color:hsl(var(--danger) / var(--tw-text-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:\\[\\&_\\[data-icon\\]\\]\\:text-info [data-icon]{--tw-text-opacity: 1;color:hsl(var(--info) / var(--tw-text-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:\\[\\&_\\[data-icon\\]\\]\\:text-success [data-icon]{--tw-text-opacity: 1;color:hsl(var(--success) / var(--tw-text-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:\\[\\&_\\[data-icon\\]\\]\\:text-warning [data-icon]{--tw-text-opacity: 1;color:hsl(var(--warning) / var(--tw-text-opacity, 1))}.\\[\\&_tr\\]\\:border-b tr{border-bottom-width:1px}.\\[\\&_tr\\]\\:border-line-subtle tr{--tw-border-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-border-opacity, 1))}`;
40249
+ const globalsCSS = `:root,:host{color-scheme:light;--brand-h: 150;--brand-s: 100%;--brand-l: 34%;--brand-hsl: var(--brand-h) var(--brand-s) var(--brand-l);--brand: hsl(var(--brand-hsl));--brand-hover: hsl(var(--brand-h) calc(var(--brand-s) + 3%) calc(var(--brand-l) - 4%));--brand-active: hsl(var(--brand-h) calc(var(--brand-s) + 2%) calc(var(--brand-l) - 8%));--brand-subtle: hsl(var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 14%) / .14);--brand-border: hsl(var(--brand-h) calc(var(--brand-s) + 8%) calc(var(--brand-l) + 8%) / .34);--brand-fg: hsl(0 0% 100%);--brand-ring: hsl(var(--brand-h) calc(var(--brand-s) + 12%) calc(var(--brand-l) + 10%) / .22);--font-sans: "Pretendard", -apple-system, BlinkMacSystemFont, system-ui, Roboto, "Helvetica Neue", "Segoe UI", "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", sans-serif;--font-display: var(--font-sans);--font-mono: var(--font-sans);--surface-0: 0 0% 99%;--surface-1: 0 0% 100%;--surface-2: 144 8% 96%;--surface-3: 144 6% 93%;--surface-inset: 144 6% 95%;--overlay-backdrop: hsl(0 0% 0% / .24);--overlay-backdrop-strong: hsl(0 0% 0% / .48);--text-primary: 150 10% 12%;--text-secondary: 148 6% 36%;--text-tertiary: 145 5% 42%;--text-disabled: 145 4% 70%;--text-inverse: 0 0% 100%;--border-subtle: 144 8% 91%;--border-default: 144 6% 85%;--border-strong: 144 5% 72%;--border-focus: var(--brand-h) var(--brand-s) var(--brand-l);--success: var(--brand-h) var(--brand-s) var(--brand-l);--success-subtle: var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 14%) / .14;--success-surface: var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 14%) / .24;--warning: 34 82% 52%;--warning-subtle: 34 82% 52% / .14;--danger: 3 68% 56%;--danger-subtle: 3 68% 56% / .12;--danger-deep: 3 62% 40%;--info: 202 52% 44%;--info-subtle: 202 52% 44% / .12;--accent-terra: 13 52% 47%;--accent-terra-subtle: 13 52% 47% / .1;--accent-terra-deep: 13 48% 44%;--accent-blue: 213 86% 56%;--accent-blue-subtle: 213 92% 56% / .12;--accent-blue-deep: 216 78% 40%;--accent-blue-border: 213 86% 62% / .36;--accent-cream: 38 52% 88%;--accent-cream-warm: 40 56% 95%;--accent-cream-deep: 38 48% 82%;--accent-highlight: 55 96% 70%;--accent-ink: 32 13% 11%;--radius-xs: 4px;--radius-sm: 8px;--radius-md: 12px;--radius-lg: 16px;--radius-xl: 20px;--radius-2xl: 24px;--radius-pill: 9999px;--shadow-xs: 0 1px 2px 0 hsl(0 0% 0% / .04);--shadow-sm: 0 1px 3px 0 hsl(0 0% 0% / .06), 0 1px 2px -1px hsl(0 0% 0% / .06);--shadow-md: 0 4px 6px -1px hsl(0 0% 0% / .06), 0 2px 4px -2px hsl(0 0% 0% / .04);--shadow-lg: 0 10px 15px -3px hsl(0 0% 0% / .06), 0 4px 6px -4px hsl(0 0% 0% / .04);--shadow-xl: 0 20px 25px -5px hsl(0 0% 0% / .06), 0 8px 10px -6px hsl(0 0% 0% / .04);--shadow-focus: 0 0 0 3px var(--brand-ring);--shadow-inset: inset 0 1px 2px 0 hsl(0 0% 0% / .04);--shadow-brand: 0 2px 8px hsl(var(--brand-h) calc(var(--brand-s) - 60%) calc(var(--brand-l) - 14%) / .24), 0 6px 20px hsl(var(--brand-h) calc(var(--brand-s) - 70%) calc(var(--brand-l) - 19%) / .16);--duration-instant: 90ms;--duration-fast: .16s;--duration-base: .26s;--duration-out: .18s;--duration-slow: .36s;--ease-out: cubic-bezier(.23, 1, .32, 1);--ease-in-out: cubic-bezier(.45, 0, .15, 1);--ease-spring: cubic-bezier(.18, .94, .32, 1.08);--grid-line: 0 0% 92%}.dark,:host(.dark){color-scheme:dark;--brand-l: 34%;--brand-fg: hsl(0 0% 100%);--surface-0: 150 8% 7%;--surface-1: 150 6% 10%;--surface-2: 150 5% 14%;--surface-3: 150 4% 18%;--surface-inset: 150 6% 6%;--overlay-backdrop: hsl(0 0% 0% / .56);--overlay-backdrop-strong: hsl(0 0% 0% / .8);--text-primary: 40 16% 93%;--text-secondary: 36 8% 68%;--text-tertiary: 34 6% 50%;--text-disabled: 30 5% 34%;--text-inverse: 150 8% 10%;--border-subtle: 150 6% 16%;--border-default: 150 5% 22%;--border-strong: 150 4% 34%;--success: var(--brand-h) var(--brand-s) var(--brand-l);--success-subtle: var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 14%) / .14;--success-surface: var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 14%) / .24;--warning: 34 82% 62%;--danger: 2 70% 64%;--danger-deep: 2 80% 76%;--info: 202 54% 62%;--accent-terra: 13 56% 62%;--accent-terra-subtle: 13 56% 62% / .14;--accent-terra-deep: 13 52% 56%;--accent-blue: 210 92% 68%;--accent-blue-subtle: 210 92% 68% / .18;--accent-blue-deep: 209 100% 78%;--accent-blue-border: 210 92% 72% / .44;--accent-cream: 38 30% 26%;--accent-cream-warm: 38 22% 18%;--accent-cream-deep: 38 26% 32%;--accent-highlight: 55 96% 70%;--accent-ink: 32 13% 8%;--shadow-xs: 0 1px 2px 0 hsl(0 0% 0% / .2);--shadow-sm: 0 1px 3px 0 hsl(0 0% 0% / .3), 0 1px 2px -1px hsl(0 0% 0% / .24);--shadow-md: 0 4px 6px -1px hsl(0 0% 0% / .34), 0 2px 4px -2px hsl(0 0% 0% / .2);--shadow-lg: 0 10px 15px -3px hsl(0 0% 0% / .38), 0 4px 6px -4px hsl(0 0% 0% / .24);--shadow-xl: 0 20px 25px -5px hsl(0 0% 0% / .44), 0 8px 10px -6px hsl(0 0% 0% / .28);--shadow-inset: inset 0 1px 2px 0 hsl(0 0% 0% / .16);--shadow-brand: 0 2px 8px hsl(var(--brand-h) calc(var(--brand-s) - 60%) calc(var(--brand-l) - 24%) / .44), 0 6px 20px hsl(0 0% 0% / .32);--grid-line: 150 6% 14%}*{border-color:hsl(var(--border-default))}html{font-family:var(--font-sans),ui-sans-serif,system-ui,-apple-system,sans-serif;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;font-feature-settings:"cv11","ss01","ss03";background-color:hsl(var(--surface-0))}body{margin:0;min-height:100vh;color:hsl(var(--text-primary));background-color:hsl(var(--surface-0))}::-moz-selection{background-color:hsl(var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 16%) / .22);color:hsl(var(--text-primary))}::selection{background-color:hsl(var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 16%) / .22);color:hsl(var(--text-primary))}code,kbd,samp,pre{font-family:var(--font-mono),ui-monospace,SFMono-Regular,Menlo,monospace;font-feature-settings:"ss01","ss02","cv11"}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:hsl(var(--border-strong) / .52);border-radius:var(--radius-pill);border:2px solid transparent;background-clip:padding-box}::-webkit-scrollbar-thumb:hover{background:hsl(var(--border-strong) / .9);background-clip:padding-box;border:2px solid transparent}.app-canvas,.grid-canvas{background-color:hsl(var(--surface-0))}.onboarding-canvas{word-break:keep-all;overflow-wrap:break-word;--surface-0: var(--brand-h) var(--brand-s) var(--brand-l);--surface-1: 0 0% 100%;--surface-2: var(--brand-h) 62% 28%;--surface-3: var(--brand-h) 60% 25%;--surface-inset: var(--brand-h) 62% 28%;--text-primary: 0 0% 100%;--text-secondary: var(--brand-h) 46% 90%;--text-tertiary: var(--brand-h) 34% 84%;--text-disabled: var(--brand-h) 20% 72%;--border-subtle: var(--brand-h) 44% 52%;--border-default: var(--brand-h) 40% 58%;--border-strong: var(--brand-h) 36% 70%;--border-focus: 0 0% 100%;--brand-hsl: 0 0% 100%;--brand: hsl(var(--brand-hsl));--brand-hover: hsl(var(--brand-h) 24% 94%);--brand-active: hsl(var(--brand-h) 20% 88%);--brand-fg: hsl(var(--brand-h) var(--brand-s) 24%);--brand-ring: hsl(0 0% 100% / .4);--danger: 6 96% 86%;--danger-subtle: 6 96% 86% / .18;--danger-deep: 6 96% 92%;--warning: 42 96% 82%;--warning-subtle: 42 96% 82% / .18}.onboarding-canvas .bg-surface-1,.onboarding-canvas input,.onboarding-canvas textarea,.onboarding-canvas [role=combobox]{--text-primary: 150 10% 12%;--text-secondary: 148 6% 36%;--text-tertiary: 145 5% 42%;--surface-2: 144 8% 96%;--surface-inset: 144 6% 95%;--border-subtle: 144 8% 91%;--border-default: 144 6% 85%;--brand-hsl: var(--brand-h) var(--brand-s) var(--brand-l);--brand: hsl(var(--brand-hsl));--brand-hover: hsl(var(--brand-h) calc(var(--brand-s) + 3%) calc(var(--brand-l) - 4%));--brand-active: hsl(var(--brand-h) calc(var(--brand-s) + 2%) calc(var(--brand-l) - 8%));--brand-fg: hsl(0 0% 100%);--brand-ring: hsl(var(--brand-h) calc(var(--brand-s) + 12%) calc(var(--brand-l) + 10%) / .22);--warning: 34 82% 52%;--warning-subtle: 34 82% 52% / .14;--danger: 3 68% 56%;--danger-subtle: 3 68% 56% / .12;--danger-deep: 3 62% 40%}@keyframes onboarding-step-in{0%{opacity:0;transform:translate(96px)}to{opacity:1;transform:translate(0)}}.onboarding-step-in{animation:onboarding-step-in var(--duration-slow) var(--ease-out) forwards}@keyframes onboarding-enter-up{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}.onboarding-enter{position:fixed;top:0;right:0;bottom:0;left:0;overflow-x:hidden;overflow-y:auto;animation:onboarding-enter-up var(--duration-base) var(--ease-out) forwards}body:has(.onboarding-canvas){background-color:hsl(var(--brand-h) var(--brand-s) var(--brand-l))}.panel-shell,.surface-shell{border:1px solid hsl(var(--border-subtle));border-radius:var(--radius-xl);background:hsl(var(--surface-1));box-shadow:var(--shadow-sm)}.panel-shell-muted,.surface-shell-muted{background:hsl(var(--surface-2));box-shadow:var(--shadow-xs)}.panel-shell-subtle{border:1px solid hsl(var(--border-subtle));border-radius:var(--radius-lg);background:hsl(var(--surface-1));box-shadow:var(--shadow-xs)}.panel-shell-elevated{border:1px solid hsl(var(--border-default));border-radius:var(--radius-xl);background:hsl(var(--surface-1));box-shadow:var(--shadow-md)}.utility-chip,.floating-chip{display:inline-flex;align-items:center;gap:8px;border:1px solid hsl(var(--border-default));border-radius:var(--radius-pill);background:hsl(var(--surface-1));box-shadow:var(--shadow-xs)}.hairline{border-bottom:1px solid hsl(var(--border-subtle))}.mono-label{font-family:var(--font-mono),ui-monospace,monospace;font-size:11px;text-transform:uppercase;color:hsl(var(--text-tertiary))}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:var(--font-sans),ui-sans-serif,system-ui,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--font-mono),ui-monospace,SFMono-Regular,Menlo,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root,:host{--font-sans: "Pretendard", -apple-system, BlinkMacSystemFont, system-ui, Roboto, "Helvetica Neue", "Segoe UI", "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", sans-serif;--font-mono: var(--font-sans);--issue-marker-expand-duration: .52s;--surface-0: 0 0% 99%;--surface-1: 0 0% 100%;--surface-2: 144 8% 96%;--surface-3: 144 6% 93%;--surface-inset: 144 6% 95%;--overlay-backdrop: hsl(0 0% 0% / .24);--overlay-backdrop-strong: hsl(0 0% 0% / .48);--text-primary: 150 10% 12%;--text-secondary: 148 6% 36%;--text-tertiary: 145 5% 52%;--text-disabled: 145 4% 70%;--border-subtle: 144 8% 91%;--border-default: 144 6% 85%;--border-strong: 144 5% 72%;--grid-line: 0 0% 92%;--shadow-xs: 0 1px 2px 0 hsl(0 0% 0% / .04);--shadow-sm: 0 1px 3px 0 hsl(0 0% 0% / .06), 0 1px 2px -1px hsl(0 0% 0% / .06);--shadow-md: 0 4px 6px -1px hsl(0 0% 0% / .06), 0 2px 4px -2px hsl(0 0% 0% / .04);--shadow-lg: 0 10px 15px -3px hsl(0 0% 0% / .06), 0 4px 6px -4px hsl(0 0% 0% / .04);--shadow-xl: 0 20px 25px -5px hsl(0 0% 0% / .06), 0 8px 10px -6px hsl(0 0% 0% / .04);--shadow-focus: 0 0 0 4px var(--brand-ring);--shadow-inset: inset 0 1px 2px 0 hsl(0 0% 0% / .04)}*{--tw-border-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-border-opacity, 1))}#issue-sticker-shadow-root{all:initial;display:block;background-color:transparent;--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1));font-family:var(--font-sans);line-height:1.5}.\\!container{width:100%!important}.container{width:100%}@media(min-width:640px){.\\!container{max-width:640px!important}.container{max-width:640px}}@media(min-width:768px){.\\!container{max-width:768px!important}.container{max-width:768px}}@media(min-width:1024px){.\\!container{max-width:1024px!important}.container{max-width:1024px}}@media(min-width:1280px){.\\!container{max-width:1280px!important}.container{max-width:1280px}}@media(min-width:1536px){.\\!container{max-width:1536px!important}.container{max-width:1536px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.\\!visible{visibility:visible!important}.visible{visibility:visible}.invisible{visibility:hidden}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.inset-x-0{left:0;right:0}.inset-x-1{left:4px;right:4px}.inset-y-0{top:0;bottom:0}.-bottom-px{bottom:-1px}.bottom-0{bottom:0}.bottom-4{bottom:16px}.bottom-6{bottom:24px}.left-0{left:0}.left-1\\/2{left:50%}.left-2{left:8px}.left-4{left:16px}.left-\\[50\\%\\]{left:50%}.right-0{right:0}.right-2{right:8px}.right-3{right:12px}.right-4{right:16px}.right-5{right:20px}.right-6{right:24px}.top-0{top:0}.top-1{top:4px}.top-1\\/2{top:50%}.top-2{top:8px}.top-3{top:12px}.top-4{top:16px}.top-5{top:20px}.top-\\[50\\%\\]{top:50%}.top-\\[calc\\(\\(3\\.5rem-1\\.75rem\\)\\/2\\)\\]{top:14px}.top-full{top:100%}.isolate{isolation:isolate}.z-10{z-index:10}.z-20{z-index:20}.z-\\[1000000\\]{z-index:1000000}.z-\\[1000003\\]{z-index:1000003}.z-\\[1000004\\]{z-index:1000004}.z-\\[1000005\\]{z-index:1000005}.z-\\[1000006\\]{z-index:1000006}.z-\\[1000007\\]{z-index:1000007}.z-\\[1000008\\]{z-index:1000008}.z-\\[1000010\\]{z-index:1000010}.z-\\[1000011\\]{z-index:1000011}.z-\\[10001\\]{z-index:10001}.z-\\[999999\\]{z-index:999999}.z-\\[9999\\]{z-index:9999}.m-0{margin:0}.-mx-2{margin-left:-8px;margin-right:-8px}.mx-3{margin-left:12px;margin-right:12px}.my-1{margin-top:4px;margin-bottom:4px}.my-2{margin-top:8px;margin-bottom:8px}.my-2\\.5{margin-top:10px;margin-bottom:10px}.my-3{margin-top:12px;margin-bottom:12px}.my-4{margin-top:16px;margin-bottom:16px}.mb-0\\.5{margin-bottom:2px}.mb-1{margin-bottom:4px}.mb-2{margin-bottom:8px}.mb-3{margin-bottom:12px}.mb-5{margin-bottom:20px}.ml-0\\.5{margin-left:2px}.ml-4{margin-left:16px}.mr-1\\.5{margin-right:6px}.mr-2{margin-right:8px}.mt-0{margin-top:0}.mt-0\\.5{margin-top:2px}.mt-1{margin-top:4px}.mt-2{margin-top:8px}.mt-3{margin-top:12px}.mt-4{margin-top:16px}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.aspect-square{aspect-ratio:1 / 1}.\\!h-auto{height:auto!important}.h-1\\.5{height:6px}.h-10{height:40px}.h-11{height:44px}.h-2{height:8px}.h-24{height:96px}.h-3{height:12px}.h-3\\.5{height:14px}.h-4{height:16px}.h-5{height:20px}.h-6{height:24px}.h-7{height:28px}.h-8{height:32px}.h-9{height:36px}.h-\\[1\\.4rem\\]{height:22.4px}.h-\\[100vh\\]{height:100vh}.h-\\[18px\\]{height:18px}.h-\\[220px\\]{height:220px}.h-\\[22px\\]{height:22px}.h-\\[30px\\]{height:30px}.h-\\[min\\(36vh\\,320px\\)\\]{height:min(36vh,320px)}.h-\\[min\\(480px\\,calc\\(100vh-64px\\)\\)\\]{height:min(480px,calc(100vh - 64px))}.h-\\[min\\(54vh\\,520px\\)\\]{height:min(54vh,520px)}.h-\\[min\\(560px\\,calc\\(100vh-32px\\)\\)\\]{height:min(560px,calc(100vh - 32px))}.h-\\[var\\(--issue-sticker-size\\)\\]{height:var(--issue-sticker-size)}.h-\\[var\\(--radix-select-trigger-height\\)\\]{height:var(--radix-select-trigger-height)}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.max-h-24{max-height:96px}.max-h-40{max-height:160px}.max-h-56{max-height:224px}.max-h-80{max-height:320px}.max-h-96{max-height:384px}.max-h-\\[150px\\]{max-height:150px}.max-h-\\[220px\\]{max-height:220px}.max-h-\\[320px\\]{max-height:320px}.max-h-\\[420px\\]{max-height:420px}.max-h-\\[90vh\\]{max-height:90vh}.max-h-\\[calc\\(100vh-3rem\\)\\]{max-height:calc(100vh - 48px)}.max-h-\\[min\\(80vh\\,640px\\)\\]{max-height:min(80vh,640px)}.min-h-0{min-height:0px}.min-h-11{min-height:44px}.min-h-\\[108px\\]{min-height:108px}.min-h-\\[120px\\]{min-height:120px}.min-h-\\[140px\\]{min-height:140px}.min-h-\\[220px\\]{min-height:220px}.w-0\\.5{width:2px}.w-1{width:4px}.w-1\\.5{width:6px}.w-1\\/2{width:50%}.w-10{width:40px}.w-12{width:48px}.w-14{width:56px}.w-16{width:64px}.w-2{width:8px}.w-3{width:12px}.w-3\\.5{width:14px}.w-4{width:16px}.w-5{width:20px}.w-6{width:24px}.w-7{width:28px}.w-72{width:288px}.w-8{width:32px}.w-80{width:320px}.w-9{width:36px}.w-\\[1\\.4rem\\]{width:22.4px}.w-\\[18px\\]{width:18px}.w-\\[22\\.5rem\\]{width:360px}.w-\\[22rem\\]{width:352px}.w-\\[30px\\]{width:30px}.w-\\[38px\\]{width:38px}.w-\\[calc\\(100vw-32px\\)\\]{width:calc(100vw - 32px)}.w-\\[min\\(24rem\\,calc\\(100vw_-_1\\.5rem\\)\\)\\]{width:min(384px,calc(100vw - 24px))}.w-\\[min\\(30rem\\,calc\\(100vw_-_1\\.5rem\\)\\)\\]{width:min(480px,calc(100vw - 24px))}.w-\\[min\\(560px\\,calc\\(100vw-32px\\)\\)\\]{width:min(560px,calc(100vw - 32px))}.w-\\[var\\(--radix-popover-trigger-width\\)\\]{width:var(--radix-popover-trigger-width)}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.min-w-0{min-width:0px}.min-w-9{min-width:36px}.min-w-\\[12rem\\]{min-width:192px}.min-w-\\[200px\\]{min-width:200px}.min-w-\\[var\\(--radix-popover-trigger-width\\)\\]{min-width:var(--radix-popover-trigger-width)}.min-w-\\[var\\(--radix-select-trigger-width\\)\\]{min-width:var(--radix-select-trigger-width)}.max-w-\\[10rem\\]{max-width:160px}.max-w-\\[120px\\]{max-width:120px}.max-w-\\[160px\\]{max-width:160px}.max-w-\\[16rem\\]{max-width:256px}.max-w-\\[260px\\]{max-width:260px}.max-w-\\[280px\\]{max-width:280px}.max-w-\\[28rem\\]{max-width:448px}.max-w-\\[300px\\]{max-width:300px}.max-w-\\[360px\\]{max-width:360px}.max-w-\\[36rem\\]{max-width:576px}.max-w-\\[400px\\]{max-width:400px}.max-w-\\[420px\\]{max-width:420px}.max-w-\\[428px\\]{max-width:428px}.max-w-\\[480px\\]{max-width:480px}.max-w-\\[62ch\\]{max-width:62ch}.max-w-\\[640px\\]{max-width:640px}.max-w-\\[70ch\\]{max-width:70ch}.max-w-\\[90vw\\]{max-width:90vw}.max-w-\\[960px\\]{max-width:960px}.max-w-\\[calc\\(100vw-24px\\)\\]{max-width:calc(100vw - 24px)}.max-w-\\[var\\(--issue-sticker-size\\)\\]{max-width:var(--issue-sticker-size)}.max-w-full{max-width:100%}.max-w-md{max-width:448px}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.grow{flex-grow:1}.caption-bottom{caption-side:bottom}.border-collapse{border-collapse:collapse}.-translate-x-1{--tw-translate-x: -4px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-x-1\\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-1{--tw-translate-x: 4px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\\[-50\\%\\]{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\\[18px\\]{--tw-translate-x: 18px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\\[2px\\]{--tw-translate-x: 2px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-\\[-50\\%\\]{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes pulse-ring{0%,to{box-shadow:0 0 0 0 var(--brand-ring)}50%{box-shadow:0 0 0 8px transparent}}.animate-pulse-ring{animation:pulse-ring 1.8s var(--ease-in-out) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-col-resize{cursor:col-resize}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.resize-y{resize:vertical}.resize{resize:both}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.list-none{list-style-type:none}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-row-reverse{flex-direction:row-reverse}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.content-center{align-content:center}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0{gap:0px}.gap-0\\.5{gap:2px}.gap-1{gap:4px}.gap-1\\.5{gap:6px}.gap-2{gap:8px}.gap-2\\.5{gap:10px}.gap-3{gap:12px}.gap-3\\.5{gap:14px}.gap-4{gap:16px}.gap-5{gap:20px}.gap-6{gap:24px}.gap-x-5{-moz-column-gap:20px;column-gap:20px}.gap-y-2{row-gap:8px}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(4px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(4px * var(--tw-space-y-reverse))}.space-y-1\\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(6px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(6px * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(8px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(8px * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(12px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(12px * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(16px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(16px * var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(20px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(20px * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(24px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(24px * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-line-subtle>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-divide-opacity, 1))}.self-start{align-self:flex-start}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-line{white-space:pre-line}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.break-keep{word-break:keep-all}.\\!rounded-none{border-radius:0!important}.rounded{border-radius:4px}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-\\[18px\\]{border-radius:18px}.rounded-\\[20px\\]{border-radius:20px}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-none{border-radius:0}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-xs{border-radius:var(--radius-xs)}.rounded-b-md{border-bottom-right-radius:var(--radius-md);border-bottom-left-radius:var(--radius-md)}.rounded-t-md{border-top-left-radius:var(--radius-md);border-top-right-radius:var(--radius-md)}.border{border-width:1px}.border-0{border-width:0px}.border-2{border-width:2px}.border-y{border-top-width:1px;border-bottom-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-l-4{border-left-width:4px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-t-0{border-top-width:0px}.border-dashed{border-style:dashed}.border-\\[hsl\\(144_8\\%_91\\%\\)\\]{--tw-border-opacity: 1;border-color:hsl(144 8% 91% / var(--tw-border-opacity, 1))}.border-brand{--tw-border-opacity: 1;border-color:hsl(var(--brand-hsl) / var(--tw-border-opacity, 1))}.border-brand-border{border-color:var(--brand-border)}.border-brand\\/30{border-color:hsl(var(--brand-hsl) / .3)}.border-brand\\/40{border-color:hsl(var(--brand-hsl) / .4)}.border-danger\\/20{border-color:hsl(var(--danger) / .2)}.border-danger\\/30{border-color:hsl(var(--danger) / .3)}.border-info\\/20{border-color:hsl(var(--info) / .2)}.border-info\\/30{border-color:hsl(var(--info) / .3)}.border-line{--tw-border-opacity: 1;border-color:hsl(var(--border-default) / var(--tw-border-opacity, 1))}.border-line-focus{--tw-border-opacity: 1;border-color:hsl(var(--border-focus) / var(--tw-border-opacity, 1))}.border-line-strong{--tw-border-opacity: 1;border-color:hsl(var(--border-strong) / var(--tw-border-opacity, 1))}.border-line-subtle{--tw-border-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-border-opacity, 1))}.border-line-subtle\\/50{border-color:hsl(var(--border-subtle) / .5)}.border-line-subtle\\/90{border-color:hsl(var(--border-subtle) / .9)}.border-slate-100{--tw-border-opacity: 1;border-color:rgb(241 245 249 / var(--tw-border-opacity, 1))}.border-slate-200{--tw-border-opacity: 1;border-color:rgb(226 232 240 / var(--tw-border-opacity, 1))}.border-success{--tw-border-opacity: 1;border-color:hsl(var(--success) / var(--tw-border-opacity, 1))}.border-success\\/20{border-color:hsl(var(--success) / .2)}.border-success\\/30{border-color:hsl(var(--success) / .3)}.border-text-primary{--tw-border-opacity: 1;border-color:hsl(var(--text-primary) / var(--tw-border-opacity, 1))}.border-transparent{border-color:transparent}.border-warning\\/20{border-color:hsl(var(--warning) / .2)}.border-warning\\/30{border-color:hsl(var(--warning) / .3)}.border-white\\/70{border-color:#ffffffb3}.border-white\\/80{border-color:#fffc}.border-white\\/90{border-color:#ffffffe6}.border-l-brand{--tw-border-opacity: 1;border-left-color:hsl(var(--brand-hsl) / var(--tw-border-opacity, 1))}.\\!bg-success-surface{background-color:hsl(var(--success-surface))!important}.bg-\\[var\\(--overlay-backdrop-strong\\)\\]{background-color:var(--overlay-backdrop-strong)}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-black\\/10{background-color:#0000001a}.bg-black\\/40{background-color:#0006}.bg-black\\/80{background-color:#000c}.bg-brand{--tw-bg-opacity: 1;background-color:hsl(var(--brand-hsl) / var(--tw-bg-opacity, 1))}.bg-brand-subtle{background-color:var(--brand-subtle)}.bg-brand\\/10{background-color:hsl(var(--brand-hsl) / .1)}.bg-brand\\/5{background-color:hsl(var(--brand-hsl) / .05)}.bg-danger{--tw-bg-opacity: 1;background-color:hsl(var(--danger) / var(--tw-bg-opacity, 1))}.bg-danger-subtle{background-color:hsl(var(--danger-subtle))}.bg-danger\\/10{background-color:hsl(var(--danger) / .1)}.bg-info-subtle{background-color:hsl(var(--info-subtle))}.bg-line{--tw-bg-opacity: 1;background-color:hsl(var(--border-default) / var(--tw-bg-opacity, 1))}.bg-line-subtle{--tw-bg-opacity: 1;background-color:hsl(var(--border-subtle) / var(--tw-bg-opacity, 1))}.bg-success{--tw-bg-opacity: 1;background-color:hsl(var(--success) / var(--tw-bg-opacity, 1))}.bg-success-subtle{background-color:hsl(var(--success-subtle))}.bg-success-surface{background-color:hsl(var(--success-surface))}.bg-surface-0{--tw-bg-opacity: 1;background-color:hsl(var(--surface-0) / var(--tw-bg-opacity, 1))}.bg-surface-1{--tw-bg-opacity: 1;background-color:hsl(var(--surface-1) / var(--tw-bg-opacity, 1))}.bg-surface-2{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.bg-surface-3{--tw-bg-opacity: 1;background-color:hsl(var(--surface-3) / var(--tw-bg-opacity, 1))}.bg-surface-inset{--tw-bg-opacity: 1;background-color:hsl(var(--surface-inset) / var(--tw-bg-opacity, 1))}.bg-text-primary{--tw-bg-opacity: 1;background-color:hsl(var(--text-primary) / var(--tw-bg-opacity, 1))}.bg-text-tertiary{--tw-bg-opacity: 1;background-color:hsl(var(--text-tertiary) / var(--tw-bg-opacity, 1))}.bg-transparent{background-color:transparent}.bg-warning{--tw-bg-opacity: 1;background-color:hsl(var(--warning) / var(--tw-bg-opacity, 1))}.bg-warning-subtle{background-color:hsl(var(--warning-subtle))}.bg-warning\\/10{background-color:hsl(var(--warning) / .1)}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-white\\/10{background-color:#ffffff1a}.bg-white\\/25{background-color:#ffffff40}.fill-text-primary{fill:hsl(var(--text-primary) / 1)}.stroke-\\[3\\]{stroke-width:3}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.\\!p-0{padding:0!important}.p-0{padding:0}.p-0\\.5{padding:2px}.p-1{padding:4px}.p-1\\.5{padding:6px}.p-2{padding:8px}.p-3{padding:12px}.p-3\\.5{padding:14px}.p-4{padding:16px}.p-5{padding:20px}.p-6{padding:24px}.p-7{padding:28px}.p-8{padding:32px}.p-\\[18px\\]{padding:18px}.px-0{padding-left:0;padding-right:0}.px-1\\.5{padding-left:6px;padding-right:6px}.px-2{padding-left:8px;padding-right:8px}.px-2\\.5{padding-left:10px;padding-right:10px}.px-3{padding-left:12px;padding-right:12px}.px-4{padding-left:16px;padding-right:16px}.px-5{padding-left:20px;padding-right:20px}.px-6{padding-left:24px;padding-right:24px}.px-9{padding-left:36px;padding-right:36px}.py-0\\.5{padding-top:2px;padding-bottom:2px}.py-1{padding-top:4px;padding-bottom:4px}.py-1\\.5{padding-top:6px;padding-bottom:6px}.py-10{padding-top:40px;padding-bottom:40px}.py-16{padding-top:64px;padding-bottom:64px}.py-2{padding-top:8px;padding-bottom:8px}.py-2\\.5{padding-top:10px;padding-bottom:10px}.py-3{padding-top:12px;padding-bottom:12px}.py-3\\.5{padding-top:14px;padding-bottom:14px}.py-4{padding-top:16px;padding-bottom:16px}.py-5{padding-top:20px;padding-bottom:20px}.py-6{padding-top:24px;padding-bottom:24px}.py-8{padding-top:32px;padding-bottom:32px}.pb-1{padding-bottom:4px}.pb-3{padding-bottom:12px}.pb-4{padding-bottom:16px}.pl-0{padding-left:0}.pl-3{padding-left:12px}.pl-4{padding-left:16px}.pl-5{padding-left:20px}.pl-8{padding-left:32px}.pr-10{padding-right:40px}.pr-3{padding-right:12px}.pr-4{padding-right:16px}.pt-0{padding-top:0}.pt-1{padding-top:4px}.pt-2\\.5{padding-top:10px}.pt-4{padding-top:16px}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-top{vertical-align:top}.align-middle{vertical-align:middle}.font-display{font-family:var(--font-display),ui-sans-serif,system-ui,sans-serif}.font-mono{font-family:var(--font-mono),ui-monospace,SFMono-Regular,Menlo,monospace}.font-sans{font-family:var(--font-sans),ui-sans-serif,system-ui,sans-serif}.text-2xs{font-size:clamp(10.5px,10.19px + .082vw,11.5px);line-height:1.333}.text-\\[0\\.72rem\\]{font-size:11.52px}.text-\\[1\\.45rem\\]{font-size:23.2px}.text-\\[1\\.75rem\\]{font-size:28px}.text-\\[10px\\]{font-size:10px}.text-\\[11px\\]{font-size:11px}.text-base{font-size:clamp(14px,13.39px + .163vw,16px);line-height:1.5}.text-lg{font-size:clamp(17px,16.39px + .163vw,19px);line-height:1.471}.text-sm{font-size:clamp(13px,12.54px + .122vw,14.5px);line-height:1.462}.text-xl{font-size:clamp(20px,19.08px + .245vw,23px);line-height:1.45}.text-xs{font-size:clamp(12px,11.69px + .082vw,13px);line-height:1.333}.font-black{font-weight:900}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-4{line-height:16px}.leading-5{line-height:20px}.leading-8{line-height:32px}.leading-\\[1\\.55\\]{line-height:1.55}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.leading-tight{line-height:1.25}.text-\\[hsl\\(145_5\\%_42\\%\\)\\]{--tw-text-opacity: 1;color:hsl(145 5% 42% / var(--tw-text-opacity, 1))}.text-brand{--tw-text-opacity: 1;color:hsl(var(--brand-hsl) / var(--tw-text-opacity, 1))}.text-brand-fg{color:var(--brand-fg)}.text-brand\\/50{color:hsl(var(--brand-hsl) / .5)}.text-danger{--tw-text-opacity: 1;color:hsl(var(--danger) / var(--tw-text-opacity, 1))}.text-danger\\/80{color:hsl(var(--danger) / .8)}.text-info{--tw-text-opacity: 1;color:hsl(var(--info) / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-slate-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.text-slate-500{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity, 1))}.text-slate-700{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity, 1))}.text-slate-900{--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity, 1))}.text-success{--tw-text-opacity: 1;color:hsl(var(--success) / var(--tw-text-opacity, 1))}.text-text-disabled{--tw-text-opacity: 1;color:hsl(var(--text-disabled) / var(--tw-text-opacity, 1))}.text-text-inverse{--tw-text-opacity: 1;color:hsl(var(--text-inverse) / var(--tw-text-opacity, 1))}.text-text-primary{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.text-text-secondary{--tw-text-opacity: 1;color:hsl(var(--text-secondary) / var(--tw-text-opacity, 1))}.text-text-tertiary{--tw-text-opacity: 1;color:hsl(var(--text-tertiary) / var(--tw-text-opacity, 1))}.text-transparent{color:transparent}.text-warning{--tw-text-opacity: 1;color:hsl(var(--warning) / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-white\\/90{color:#ffffffe6}.underline{text-decoration-line:underline}.line-through{text-decoration-line:line-through}.underline-offset-2{text-underline-offset:2px}.underline-offset-4{text-underline-offset:4px}.accent-brand{accent-color:hsl(var(--brand-hsl) / 1)}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.\\!shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1) !important;--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color) !important;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)!important}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\\[0_24px_70px_-28px_rgba\\(16\\,24\\,18\\,0\\.45\\)\\]{--tw-shadow: 0 24px 70px -28px rgba(16,24,18,.45);--tw-shadow-colored: 0 24px 70px -28px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: var(--shadow-lg);--tw-shadow-colored: var(--shadow-lg);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: var(--shadow-md);--tw-shadow-colored: var(--shadow-md);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: var(--shadow-sm);--tw-shadow-colored: var(--shadow-sm);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: var(--shadow-xl);--tw-shadow-colored: var(--shadow-xl);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xs{--tw-shadow: var(--shadow-xs);--tw-shadow-colored: var(--shadow-xs);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.outline-2{outline-width:2px}.outline-offset-0{outline-offset:0px}.outline-transparent{outline-color:transparent}.ring{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-2{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-4{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-brand{--tw-ring-opacity: 1;--tw-ring-color: hsl(var(--brand-hsl) / var(--tw-ring-opacity, 1))}.ring-brand-subtle{--tw-ring-color: var(--brand-subtle)}.blur{--tw-blur: blur(8px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-\\[2px\\]{--tw-backdrop-blur: blur(2px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[background-color\\,border-color\\,color\\,box-shadow\\]{transition-property:background-color,border-color,color,box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[background-color\\,color\\,border-color\\,box-shadow\\,opacity\\]{transition-property:background-color,color,border-color,box-shadow,opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[background-color\\,color\\,border-color\\,box-shadow\\]{transition-property:background-color,color,border-color,box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[background-color\\,color\\,box-shadow\\]{transition-property:background-color,color,box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[background-color\\,color\\]{transition-property:background-color,color;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[border-color\\,box-shadow\\,background-color\\]{transition-property:border-color,box-shadow,background-color;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[border-color\\,outline-color\\,box-shadow\\,background-color\\,transform\\]{transition-property:border-color,outline-color,box-shadow,background-color,transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[max-width\\,height\\,box-shadow\\,background-color\\,border-color\\,transform\\,opacity\\]{transition-property:max-width,height,box-shadow,background-color,border-color,transform,opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[max-width\\]{transition-property:max-width;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[opacity\\,transform\\]{transition-property:opacity,transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-300{transition-duration:.3s}.duration-\\[var\\(--issue-marker-expand-duration\\)\\]{transition-duration:var(--issue-marker-expand-duration)}.duration-fast{transition-duration:var(--duration-fast)}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-in-out{transition-timing-function:var(--ease-in-out)}.ease-out{transition-timing-function:var(--ease-out)}@keyframes float-gentle{0%,to{transform:translateY(0)}50%{transform:translateY(-2px)}}@keyframes toast-slide{0%{opacity:0;transform:translateY(-14px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes gradient-shift{0%,to{background-position:0% 50%}50%{background-position:100% 50%}}.issue-overlay-shell{border:1px solid hsl(var(--border-subtle));border-radius:var(--radius-lg);background:hsl(var(--surface-1));box-shadow:var(--shadow-md)}.issue-marker-breathe{animation:float-gentle 4.8s var(--ease-in-out) infinite}.issue-marker-breathe:hover,.issue-marker-breathe:focus-visible,.issue-marker-breathe:focus-within{animation-play-state:paused}.\\[overflow-wrap\\:anywhere\\]{overflow-wrap:anywhere}.marker\\:text-text-tertiary *::marker{color:hsl(var(--text-tertiary))}.marker\\:text-text-tertiary::marker{color:hsl(var(--text-tertiary))}.file\\:border-0::file-selector-button{border-width:0px}.file\\:bg-transparent::file-selector-button{background-color:transparent}.file\\:text-sm::file-selector-button{font-size:clamp(13px,12.54px + .122vw,14.5px);line-height:1.462}.file\\:font-medium::file-selector-button{font-weight:500}.file\\:text-text-primary::file-selector-button{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.placeholder\\:text-text-tertiary::-moz-placeholder{--tw-text-opacity: 1;color:hsl(var(--text-tertiary) / var(--tw-text-opacity, 1))}.placeholder\\:text-text-tertiary::placeholder{--tw-text-opacity: 1;color:hsl(var(--text-tertiary) / var(--tw-text-opacity, 1))}.first\\:mt-0:first-child{margin-top:0}.first\\:border-t-0:first-child{border-top-width:0px}.first\\:pt-0:first-child{padding-top:0}.focus-within\\:z-\\[10001\\]:focus-within{z-index:10001}.focus-within\\:border-line-focus:focus-within{--tw-border-opacity: 1;border-color:hsl(var(--border-focus) / var(--tw-border-opacity, 1))}.hover\\:z-\\[10001\\]:hover{z-index:10001}.hover\\:border-line:hover{--tw-border-opacity: 1;border-color:hsl(var(--border-default) / var(--tw-border-opacity, 1))}.hover\\:border-line-strong:hover{--tw-border-opacity: 1;border-color:hsl(var(--border-strong) / var(--tw-border-opacity, 1))}.hover\\:border-line-subtle:hover{--tw-border-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-border-opacity, 1))}.hover\\:bg-\\[hsl\\(144_8\\%_96\\%\\)\\]:hover{--tw-bg-opacity: 1;background-color:hsl(144 8% 96% / var(--tw-bg-opacity, 1))}.hover\\:bg-blue-500:hover{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.hover\\:bg-brand-hover:hover{background-color:var(--brand-hover)}.hover\\:bg-brand\\/40:hover{background-color:hsl(var(--brand-hsl) / .4)}.hover\\:bg-danger-subtle:hover{background-color:hsl(var(--danger-subtle))}.hover\\:bg-danger\\/10:hover{background-color:hsl(var(--danger) / .1)}.hover\\:bg-slate-100:hover{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity, 1))}.hover\\:bg-slate-50:hover{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1))}.hover\\:bg-surface-2:hover{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.hover\\:bg-surface-2\\/60:hover{background-color:hsl(var(--surface-2) / .6)}.hover\\:bg-surface-3:hover{--tw-bg-opacity: 1;background-color:hsl(var(--surface-3) / var(--tw-bg-opacity, 1))}.hover\\:bg-surface-inset:hover{--tw-bg-opacity: 1;background-color:hsl(var(--surface-inset) / var(--tw-bg-opacity, 1))}.hover\\:bg-transparent:hover{background-color:transparent}.hover\\:bg-white\\/20:hover{background-color:#fff3}.hover\\:text-\\[hsl\\(150_10\\%_12\\%\\)\\]:hover{--tw-text-opacity: 1;color:hsl(150 10% 12% / var(--tw-text-opacity, 1))}.hover\\:text-brand-hover:hover{color:var(--brand-hover)}.hover\\:text-danger:hover{--tw-text-opacity: 1;color:hsl(var(--danger) / var(--tw-text-opacity, 1))}.hover\\:text-slate-900:hover{--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity, 1))}.hover\\:text-text-primary:hover{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.hover\\:text-text-secondary:hover{--tw-text-opacity: 1;color:hsl(var(--text-secondary) / var(--tw-text-opacity, 1))}.hover\\:underline:hover{text-decoration-line:underline}.hover\\:opacity-90:hover{opacity:.9}.focus\\:border-brand:focus{--tw-border-opacity: 1;border-color:hsl(var(--brand-hsl) / var(--tw-border-opacity, 1))}.focus\\:bg-surface-2:focus{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.focus\\:text-text-primary:focus{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.focus\\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus-visible\\:h-\\[var\\(--issue-sticker-hover-size\\)\\]:focus-visible{height:var(--issue-sticker-hover-size)}.focus-visible\\:max-w-\\[160px\\]:focus-visible{max-width:160px}.focus-visible\\:max-w-\\[480px\\]:focus-visible{max-width:480px}.focus-visible\\:border-line-focus:focus-visible{--tw-border-opacity: 1;border-color:hsl(var(--border-focus) / var(--tw-border-opacity, 1))}.focus-visible\\:border-transparent:focus-visible{border-color:transparent}.focus-visible\\:bg-slate-50:focus-visible{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1))}.focus-visible\\:bg-surface-1:focus-visible{--tw-bg-opacity: 1;background-color:hsl(var(--surface-1) / var(--tw-bg-opacity, 1))}.focus-visible\\:bg-surface-2:focus-visible{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.focus-visible\\:text-text-primary:focus-visible{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.focus-visible\\:shadow-focus:focus-visible{--tw-shadow: var(--shadow-focus);--tw-shadow-colored: var(--shadow-focus);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.focus-visible\\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\\:outline-line-focus:focus-visible{outline-color:hsl(var(--border-focus) / 1)}.focus-visible\\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\\:ring-4:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\\:ring-brand-subtle:focus-visible{--tw-ring-color: var(--brand-subtle)}.focus-visible\\:ring-white\\/40:focus-visible{--tw-ring-color: rgb(255 255 255 / .4)}.active\\:bg-brand-active:active{background-color:var(--brand-active)}.active\\:bg-brand\\/60:active{background-color:hsl(var(--brand-hsl) / .6)}.disabled\\:pointer-events-none:disabled{pointer-events:none}.disabled\\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\\:opacity-40:disabled{opacity:.4}.disabled\\:opacity-50:disabled{opacity:.5}.group\\/marker:hover .group-hover\\/marker\\:h-\\[var\\(--issue-sticker-hover-size\\)\\]{height:var(--issue-sticker-hover-size)}.group\\/marker:hover .group-hover\\/marker\\:max-w-\\[160px\\]{max-width:160px}.group\\/marker:hover .group-hover\\/marker\\:max-w-\\[480px\\]{max-width:480px}.group\\/marker:hover .group-hover\\/marker\\:translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\\/marker:hover .group-hover\\/marker\\:bg-brand-hover{background-color:var(--brand-hover)}.group\\/marker:hover .group-hover\\/marker\\:opacity-100{opacity:1}.group\\/marker:hover .group-hover\\/marker\\:shadow-xl{--tw-shadow: var(--shadow-xl);--tw-shadow-colored: var(--shadow-xl);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.group:focus-visible .group-focus-visible\\:translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:focus-visible .group-focus-visible\\:opacity-100{opacity:1}.group.toaster .group-\\[\\.toaster\\]\\:isolate{isolation:isolate}.group.toast .group-\\[\\.toast\\]\\:rounded-md,.group.toaster .group-\\[\\.toaster\\]\\:rounded-md{border-radius:var(--radius-md)}.group.toaster .group-\\[\\.toaster\\]\\:border{border-width:1px}.group.toaster .group-\\[\\.toaster\\]\\:\\!border-danger\\/30{border-color:hsl(var(--danger) / .3)!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!border-info\\/30{border-color:hsl(var(--info) / .3)!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!border-success\\/30{border-color:hsl(var(--success) / .3)!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!border-warning\\/30{border-color:hsl(var(--warning) / .3)!important}.group.toaster .group-\\[\\.toaster\\]\\:border-line-subtle{--tw-border-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-border-opacity, 1))}.group.toast .group-\\[\\.toast\\]\\:bg-brand{--tw-bg-opacity: 1;background-color:hsl(var(--brand-hsl) / var(--tw-bg-opacity, 1))}.group.toast .group-\\[\\.toast\\]\\:bg-surface-2{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:\\!bg-danger-subtle{background-color:hsl(var(--danger-subtle))!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!bg-info-subtle{background-color:hsl(var(--info-subtle))!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!bg-success-surface{background-color:hsl(var(--success-surface))!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!bg-warning-subtle{background-color:hsl(var(--warning-subtle))!important}.group.toaster .group-\\[\\.toaster\\]\\:bg-surface-1{--tw-bg-opacity: 1;background-color:hsl(var(--surface-1) / var(--tw-bg-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:px-4{padding-left:16px;padding-right:16px}.group.toaster .group-\\[\\.toaster\\]\\:py-3{padding-top:12px;padding-bottom:12px}.group.toast .group-\\[\\.toast\\]\\:text-sm{font-size:clamp(13px,12.54px + .122vw,14.5px);line-height:1.462}.group.toast .group-\\[\\.toast\\]\\:text-xs{font-size:clamp(12px,11.69px + .082vw,13px);line-height:1.333}.group.toast .group-\\[\\.toast\\]\\:font-medium{font-weight:500}.group.toast .group-\\[\\.toast\\]\\:text-brand-fg{color:var(--brand-fg)}.group.toast .group-\\[\\.toast\\]\\:text-text-primary{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.group.toast .group-\\[\\.toast\\]\\:text-text-secondary{--tw-text-opacity: 1;color:hsl(var(--text-secondary) / var(--tw-text-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:text-text-primary{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:shadow-sm{--tw-shadow: var(--shadow-sm);--tw-shadow-colored: var(--shadow-sm);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.group.toaster .group-\\[\\.toaster\\]\\:before\\:absolute:before{content:var(--tw-content);position:absolute}.group.toaster .group-\\[\\.toaster\\]\\:before\\:inset-0:before{content:var(--tw-content);top:0;right:0;bottom:0;left:0}.group.toaster .group-\\[\\.toaster\\]\\:before\\:-z-10:before{content:var(--tw-content);z-index:-10}.group.toaster .group-\\[\\.toaster\\]\\:before\\:rounded-md:before{content:var(--tw-content);border-radius:var(--radius-md)}.group.toaster .group-\\[\\.toaster\\]\\:before\\:bg-surface-1:before{content:var(--tw-content);--tw-bg-opacity: 1;background-color:hsl(var(--surface-1) / var(--tw-bg-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:before\\:content-\\[\\'\\'\\]:before{--tw-content: "";content:var(--tw-content)}.peer:focus-visible~.peer-focus-visible\\:shadow-focus{--tw-shadow: var(--shadow-focus);--tw-shadow-colored: var(--shadow-focus);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.peer:disabled~.peer-disabled\\:cursor-not-allowed{cursor:not-allowed}.peer:disabled~.peer-disabled\\:opacity-60{opacity:.6}.aria-disabled\\:pointer-events-none[aria-disabled=true]{pointer-events:none}.aria-disabled\\:opacity-40[aria-disabled=true]{opacity:.4}.data-\\[disabled\\]\\:pointer-events-none[data-disabled]{pointer-events:none}@keyframes dialog-out{0%{opacity:1;transform:translate(-50%,-50%) scale(1)}to{opacity:0;transform:translate(-50%,calc(-50% + 8px)) scale(.985)}}.data-\\[state\\=closed\\]\\:animate-dialog-out[data-state=closed]{animation:dialog-out var(--duration-out) var(--ease-out) forwards}@keyframes fade-out{0%{opacity:1}to{opacity:0}}.data-\\[state\\=closed\\]\\:animate-fade-out[data-state=closed]{animation:fade-out var(--duration-out) var(--ease-out) forwards}@keyframes scale-out{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.97)}}.data-\\[state\\=closed\\]\\:animate-scale-out[data-state=closed]{animation:scale-out var(--duration-out) var(--ease-out) forwards}@keyframes sheet-out-left{0%{transform:translate(0)}to{transform:translate(-100%);opacity:.6}}.data-\\[state\\=closed\\]\\:animate-sheet-out-left[data-state=closed]{animation:sheet-out-left var(--duration-base) var(--ease-out) forwards}@keyframes sheet-out-right{0%{transform:translate(0);opacity:1}to{transform:translate(100%);opacity:.6}}.data-\\[state\\=closed\\]\\:animate-sheet-out-right[data-state=closed]{animation:sheet-out-right var(--duration-base) var(--ease-out) forwards}.data-\\[state\\=delayed-open\\]\\:animate-scale-in[data-state=delayed-open]{animation:scale-in var(--duration-base) var(--ease-out) forwards}@keyframes dialog-in{0%{opacity:0;transform:translate(-50%,calc(-50% + 12px)) scale(.985)}to{opacity:1;transform:translate(-50%,-50%) scale(1)}}.data-\\[state\\=open\\]\\:animate-dialog-in[data-state=open]{animation:dialog-in var(--duration-base) var(--ease-out) forwards}@keyframes fade-in{0%{opacity:0}to{opacity:1}}.data-\\[state\\=open\\]\\:animate-fade-in[data-state=open]{animation:fade-in var(--duration-base) var(--ease-out) forwards}@keyframes scale-in{0%{opacity:0;transform:scale(.98)}to{opacity:1;transform:scale(1)}}.data-\\[state\\=open\\]\\:animate-scale-in[data-state=open]{animation:scale-in var(--duration-base) var(--ease-out) forwards}@keyframes sheet-in-left{0%{transform:translate(-100%);opacity:.6}to{transform:translate(0);opacity:1}}.data-\\[state\\=open\\]\\:animate-sheet-in-left[data-state=open]{animation:sheet-in-left var(--duration-base) var(--ease-out) forwards}@keyframes sheet-in-right{0%{transform:translate(100%);opacity:.6}to{transform:translate(0)}}.data-\\[state\\=open\\]\\:animate-sheet-in-right[data-state=open]{animation:sheet-in-right var(--duration-base) var(--ease-out) forwards}.data-\\[range-end\\=true\\]\\:rounded-md[data-range-end=true]{border-radius:var(--radius-md)}.data-\\[range-middle\\=true\\]\\:rounded-none[data-range-middle=true]{border-radius:0}.data-\\[range-start\\=true\\]\\:rounded-md[data-range-start=true]{border-radius:var(--radius-md)}.data-\\[state\\=open\\]\\:border-line-focus[data-state=open]{--tw-border-opacity: 1;border-color:hsl(var(--border-focus) / var(--tw-border-opacity, 1))}.data-\\[highlighted\\]\\:bg-surface-2[data-highlighted]{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.data-\\[range-end\\=true\\]\\:bg-brand[data-range-end=true]{--tw-bg-opacity: 1;background-color:hsl(var(--brand-hsl) / var(--tw-bg-opacity, 1))}.data-\\[range-middle\\=true\\]\\:bg-transparent[data-range-middle=true]{background-color:transparent}.data-\\[range-start\\=true\\]\\:bg-brand[data-range-start=true],.data-\\[selected-single\\=true\\]\\:bg-brand[data-selected-single=true]{--tw-bg-opacity: 1;background-color:hsl(var(--brand-hsl) / var(--tw-bg-opacity, 1))}.data-\\[state\\=selected\\]\\:bg-brand-subtle[data-state=selected]{background-color:var(--brand-subtle)}.data-\\[today\\=true\\]\\:font-semibold[data-today=true]{font-weight:600}.data-\\[highlighted\\]\\:text-text-primary[data-highlighted]{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.data-\\[placeholder\\]\\:text-text-tertiary[data-placeholder]{--tw-text-opacity: 1;color:hsl(var(--text-tertiary) / var(--tw-text-opacity, 1))}.data-\\[range-end\\=true\\]\\:text-brand-fg[data-range-end=true]{color:var(--brand-fg)}.data-\\[range-middle\\=true\\]\\:text-text-primary[data-range-middle=true]{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.data-\\[range-start\\=true\\]\\:text-brand-fg[data-range-start=true],.data-\\[selected-single\\=true\\]\\:text-brand-fg[data-selected-single=true]{color:var(--brand-fg)}.data-\\[state\\=active\\]\\:text-text-primary[data-state=active],.data-\\[state\\=checked\\]\\:text-text-primary[data-state=checked]{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.data-\\[today\\=true\\]\\:text-brand[data-today=true]{--tw-text-opacity: 1;color:hsl(var(--brand-hsl) / var(--tw-text-opacity, 1))}.data-\\[disabled\\]\\:opacity-50[data-disabled]{opacity:.5}.data-\\[state\\=open\\]\\:shadow-focus[data-state=open]{--tw-shadow: var(--shadow-focus);--tw-shadow-colored: var(--shadow-focus);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.data-\\[range-end\\=true\\]\\:hover\\:bg-brand-hover:hover[data-range-end=true],.data-\\[range-start\\=true\\]\\:hover\\:bg-brand-hover:hover[data-range-start=true],.data-\\[selected-single\\=true\\]\\:hover\\:bg-brand-hover:hover[data-selected-single=true]{background-color:var(--brand-hover)}.data-\\[range-end\\=true\\]\\:hover\\:text-brand-fg:hover[data-range-end=true],.data-\\[range-start\\=true\\]\\:hover\\:text-brand-fg:hover[data-range-start=true],.data-\\[selected-single\\=true\\]\\:hover\\:text-brand-fg:hover[data-selected-single=true]{color:var(--brand-fg)}@media(prefers-reduced-motion:reduce){.motion-reduce\\:animate-none{animation:none}}@media(min-width:640px){.sm\\:flex-row{flex-direction:row}.sm\\:items-start{align-items:flex-start}.sm\\:items-center{align-items:center}.sm\\:justify-end{justify-content:flex-end}.sm\\:justify-between{justify-content:space-between}.sm\\:gap-2\\.5{gap:10px}.sm\\:p-5{padding:20px}.sm\\:p-6{padding:24px}.sm\\:text-2xl{font-size:clamp(26px,24.78px + .327vw,30px);line-height:1.308}.sm\\:text-\\[2\\.1rem\\]{font-size:33.6px}}@media(min-width:768px){.md\\:flex{display:flex}.md\\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\\:grid-cols-\\[minmax\\(0\\,1fr\\)_minmax\\(260px\\,340px\\)\\]{grid-template-columns:minmax(0,1fr) minmax(260px,340px)}}.\\[\\&\\>\\*\\:not\\(summary\\)\\]\\:pl-4>*:not(summary){padding-left:16px}.\\[\\&\\>span\\]\\:line-clamp-1>span{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1}.\\[\\&\\>tr\\]\\:last\\:border-b-0:last-child>tr{border-bottom-width:0px}.\\[\\&\\[data-state\\=open\\]\\>span\\[data-chevron\\]\\]\\:rotate-180[data-state=open]>span[data-chevron]{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group.toaster .group-\\[\\.toaster\\]\\:\\[\\&_\\[data-icon\\]\\]\\:text-danger [data-icon]{--tw-text-opacity: 1;color:hsl(var(--danger) / var(--tw-text-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:\\[\\&_\\[data-icon\\]\\]\\:text-info [data-icon]{--tw-text-opacity: 1;color:hsl(var(--info) / var(--tw-text-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:\\[\\&_\\[data-icon\\]\\]\\:text-success [data-icon]{--tw-text-opacity: 1;color:hsl(var(--success) / var(--tw-text-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:\\[\\&_\\[data-icon\\]\\]\\:text-warning [data-icon]{--tw-text-opacity: 1;color:hsl(var(--warning) / var(--tw-text-opacity, 1))}.\\[\\&_tr\\]\\:border-b tr{border-bottom-width:1px}.\\[\\&_tr\\]\\:border-line-subtle tr{--tw-border-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-border-opacity, 1))}li .\\[li_\\&\\]\\:pl-5{padding-left:20px}`;
39411
40250
  const sdkShadowCSS = ".issue-sticker-marker{pointer-events:auto}@keyframes highlight-pulse{0%,to{transform:scale(1);box-shadow:var(--shadow-md)}50%{transform:scale(1.3);box-shadow:var(--shadow-focus),var(--shadow-lg)}}.issue-sticker-marker.highlight-animation{animation:highlight-pulse .7s ease-in-out 3}";
39412
40251
  const contentPageCSS = `@layer issue-sticker-mode{html:has(>body.issue-sticker-mode-active),body.issue-sticker-mode-active,body.issue-sticker-mode-active *{cursor:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32'%3E%3Cpath d='M3 29 C3 22 3 20 3 15 a12 12 0 0 1 12-12 h2 a12 12 0 0 1 12 12 a12 12 0 0 1-12 12 H12 Z' fill='%231C221F' fill-opacity='0.35' stroke='%23ffffff' stroke-width='2' stroke-linejoin='round'/%3E%3C/svg%3E") 3 29,crosshair!important}body.issue-sticker-mode-active iframe{pointer-events:none!important}body.issue-sticker-mode-active .issue-sticker-mode-highlight{outline:2px solid hsl(160 64% 34%)!important;outline-offset:2px!important;background-color:#1f8e691f!important;transition:outline .1s ease,background-color .1s ease!important}}`;
39413
40252
  const PAGE_STYLE_ID = "issue-sticker-page-style";
39414
40253
  let root = null;
39415
40254
  let mountContainer = null;
40255
+ let detachHostGuards = null;
39416
40256
  function renderWidget(config) {
39417
40257
  if (!root || !mountContainer) {
39418
40258
  return;
@@ -39443,8 +40283,13 @@ function mountWidget(config) {
39443
40283
  }
39444
40284
  document.body.appendChild(hostElement);
39445
40285
  const shadow = createShadowRoot(hostElement, [globalsCSS, sdkShadowCSS]);
39446
- attachHostTypingGuard(shadow.shadowRoot);
39447
- attachHostPointerGuard(shadow.shadowRoot);
40286
+ detachHostGuards == null ? void 0 : detachHostGuards();
40287
+ const guards = [
40288
+ attachHostTypingGuard(shadow.shadowRoot),
40289
+ attachHostPointerGuard(shadow.shadowRoot),
40290
+ attachHostInteractionGuard(shadow.shadowRoot)
40291
+ ];
40292
+ detachHostGuards = () => guards.forEach((detach) => detach());
39448
40293
  mountContainer = shadow.mountContainer;
39449
40294
  mountContainer.style.width = "0";
39450
40295
  mountContainer.style.height = "0";
@@ -39460,6 +40305,8 @@ function unmountWidget() {
39460
40305
  root = null;
39461
40306
  }
39462
40307
  mountContainer = null;
40308
+ detachHostGuards == null ? void 0 : detachHostGuards();
40309
+ detachHostGuards = null;
39463
40310
  const host = document.getElementById(ISSUE_STICKER_HOST_ELEMENT_ID);
39464
40311
  if (host && getIssueStickerRuntimeOwner(host) !== ISSUE_STICKER_RUNTIME_OWNER.extension) {
39465
40312
  host.remove();