team-anya-cli 0.1.3 → 0.1.5

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.
@@ -265,13 +265,39 @@ export class FeishuSender {
265
265
  return response.data?.message_id ?? 'unknown';
266
266
  }
267
267
  // ── 表情回复(Reaction)──
268
+ /** "处理中"指示 emoji,收到消息时添加,回复后移除 */
269
+ static TYPING_EMOJI = 'OneSecond';
270
+ /** 待清除的 typing reaction,key 为 chatId */
271
+ pendingTypingReactions = new Map();
272
+ /**
273
+ * 给消息添加"处理中"表情,并记录到 pending 映射
274
+ *
275
+ * 收到消息后立即调用,大模型回复或出错时通过 clearTypingReaction 移除。
276
+ */
277
+ async addTypingReaction(chatId, messageId) {
278
+ const reactionId = await this.addReaction(messageId, FeishuSender.TYPING_EMOJI);
279
+ if (reactionId) {
280
+ this.pendingTypingReactions.set(chatId, { messageId, reactionId });
281
+ }
282
+ }
283
+ /**
284
+ * 清除指定群聊的"处理中"表情
285
+ *
286
+ * 在大模型回复、冷启动回复、或出错回复时调用。
287
+ */
288
+ async clearTypingReaction(chatId) {
289
+ const pending = this.pendingTypingReactions.get(chatId);
290
+ if (!pending)
291
+ return;
292
+ this.pendingTypingReactions.delete(chatId);
293
+ await this.deleteReaction(pending.messageId, pending.reactionId);
294
+ }
268
295
  /**
269
296
  * 给消息添加表情回复
270
297
  *
271
- * 用于收到用户消息后立即反馈"已收到",无需等待 Agent 处理完成。
272
298
  * 调用飞书 POST /im/v1/messages/{message_id}/reactions
273
299
  */
274
- async addReaction(messageId, emojiType = 'OnIt') {
300
+ async addReaction(messageId, emojiType) {
275
301
  try {
276
302
  const response = await this.client.im.messageReaction.create({
277
303
  path: { message_id: messageId },
@@ -290,6 +316,24 @@ export class FeishuSender {
290
316
  return null;
291
317
  }
292
318
  }
319
+ /**
320
+ * 删除消息上的表情回复
321
+ *
322
+ * 调用飞书 DELETE /im/v1/messages/{message_id}/reactions/{reaction_id}
323
+ */
324
+ async deleteReaction(messageId, reactionId) {
325
+ try {
326
+ const response = await this.client.im.messageReaction.delete({
327
+ path: { message_id: messageId, reaction_id: reactionId },
328
+ });
329
+ if (response.code !== 0) {
330
+ console.warn(`[feishu-sender] 删除表情回复失败: ${response.msg || `code ${response.code}`}`);
331
+ }
332
+ }
333
+ catch (err) {
334
+ console.warn('[feishu-sender] 删除表情回复异常:', err);
335
+ }
336
+ }
293
337
  // ── 出站场景 ──
294
338
  async sendBlockedAlert(params) {
295
339
  // 用自然语言,不用制式模板
@@ -25,6 +25,7 @@ export async function parseFeishuMessage(event, eventId, downloader) {
25
25
  message_id: msg.message_id,
26
26
  message_type: msg.message_type,
27
27
  source_type: 'feishu',
28
+ ...(event.header?.create_time ? { event_create_time: event.header.create_time } : {}),
28
29
  ...(msg.parent_id ? { parent_message_id: msg.parent_id } : {}),
29
30
  ...(msg.root_id ? { root_message_id: msg.root_id } : {}),
30
31
  },
@@ -180,10 +181,10 @@ export class FeishuWSClient {
180
181
  return;
181
182
  const eventId = event.header?.event_id;
182
183
  const message = await parseFeishuMessage(event, eventId, this.mediaDownloader);
183
- // 立即添加表情回复(fire-and-forget,不阻断消息处理)
184
- if (this.feishuSender && event.message.message_id) {
185
- this.feishuSender.addReaction(event.message.message_id, 'OnIt').catch(err => {
186
- this.log.warn({ err }, '表情回复失败');
184
+ // 立即添加"处理中"表情(fire-and-forget,大模型回复后会自动移除)
185
+ if (this.feishuSender && event.message.message_id && event.message.chat_id) {
186
+ this.feishuSender.addTypingReaction(event.message.chat_id, event.message.message_id).catch(err => {
187
+ this.log.warn({ err }, 'typing 表情添加失败');
187
188
  });
188
189
  }
189
190
  // 解析发送者名称(异步,不阻断消息处理)
@@ -10,9 +10,34 @@ import { Clarifier } from '../loid/clarifier.js';
10
10
  import { OpportunityManager } from '../loid/opportunity-manager.js';
11
11
  import { SelfCalibrator } from '../loid/self-calibrator.js';
12
12
  import { parseFeishuMessage, resolveFeishuUser } from './feishu-ws.js';
13
+ /**
14
+ * 将 SQLite datetime('now') 产生的 'YYYY-MM-DD HH:MM:SS'(隐含 UTC)
15
+ * 统一转为 ISO 8601 'YYYY-MM-DDTHH:MM:SSZ',确保前端正确按 UTC 解析。
16
+ */
17
+ const UTC_DATETIME_RE = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
18
+ function normalizeTimestamps(obj) {
19
+ if (typeof obj === 'string') {
20
+ return UTC_DATETIME_RE.test(obj) ? obj.replace(' ', 'T') + 'Z' : obj;
21
+ }
22
+ if (Array.isArray(obj)) {
23
+ return obj.map(normalizeTimestamps);
24
+ }
25
+ if (obj !== null && typeof obj === 'object') {
26
+ const result = {};
27
+ for (const [key, value] of Object.entries(obj)) {
28
+ result[key] = normalizeTimestamps(value);
29
+ }
30
+ return result;
31
+ }
32
+ return obj;
33
+ }
13
34
  export async function registerRoutes(app, deps) {
14
35
  const { db, broker } = deps;
15
36
  const clarifier = deps.clarifier ?? new Clarifier({ db });
37
+ // 统一序列化钩子:API 响应中的 UTC 时间戳标准化
38
+ app.addHook('preSerialization', async (_request, _reply, payload) => {
39
+ return normalizeTimestamps(payload);
40
+ });
16
41
  // POST /api/tasks — 创建任务
17
42
  app.post('/api/tasks', async (request, reply) => {
18
43
  const body = request.body;
@@ -31,14 +31,19 @@ class EventDedup {
31
31
  }
32
32
  }
33
33
  }
34
+ // ── MessageQueue ──
35
+ /** 消息最大年龄(毫秒),超过则丢弃。默认 5 分钟。 */
36
+ const DEFAULT_MAX_AGE_MS = 5 * 60 * 1000;
34
37
  export class MessageQueue {
35
38
  handler;
36
39
  dedup;
40
+ maxAgeMs;
37
41
  sources = new Map();
38
42
  logger;
39
43
  constructor(config) {
40
44
  this.handler = config.handler;
41
45
  this.dedup = new EventDedup(config.dedupTtlMs ?? 5 * 60 * 1000);
46
+ this.maxAgeMs = config.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
42
47
  this.logger = config.logger ?? { info: console.log, error: console.error };
43
48
  }
44
49
  enqueue(msg) {
@@ -47,6 +52,15 @@ export class MessageQueue {
47
52
  // 重复事件,静默跳过
48
53
  return;
49
54
  }
55
+ // 丢弃过旧消息(飞书重连后可能重发很久以前的事件)
56
+ const createTime = msg.metadata?.event_create_time;
57
+ if (createTime) {
58
+ const ageMs = Date.now() - parseInt(createTime, 10);
59
+ if (ageMs > this.maxAgeMs) {
60
+ this.logger.warn?.(`[MessageQueue] 丢弃过旧消息: event_id=${eventId}, age=${Math.round(ageMs / 1000)}s, max=${Math.round(this.maxAgeMs / 1000)}s`);
61
+ return;
62
+ }
63
+ }
50
64
  const key = this.deriveSourceKey(msg);
51
65
  let sq = this.sources.get(key);
52
66
  if (!sq) {
@@ -45,6 +45,7 @@ export class LoidBrain {
45
45
  logger: this.config.logger,
46
46
  }, {
47
47
  db: this.deps.mcpDeps.db,
48
+ feishuSender: this.deps.feishuSender,
48
49
  });
49
50
  this.ready = true;
50
51
  this.config.logger?.info('LoidBrain 已初始化 (Session 模式)');
@@ -9,11 +9,60 @@ export class LoidSessionManager {
9
9
  config;
10
10
  deps;
11
11
  logger;
12
+ /** 冷启动时随机回复的话术池 */
13
+ static COLD_START_REPLIES = [
14
+ '稍等,我先理一下思路',
15
+ '好,我想想',
16
+ '让我看看',
17
+ '收到,稍等一下',
18
+ '我先看看上下文',
19
+ '好的,等我一下',
20
+ ];
21
+ lastColdStartIndex = -1;
22
+ /** 崩溃恢复中的话术池(像同事重启电脑一样自然) */
23
+ static CRASH_RECOVERY_REPLIES = [
24
+ '抱歉,刚才脑子卡了一下,我重新捋一下',
25
+ '不好意思,刚才断片了,我重新接上',
26
+ '刚才出了点状况,我重新来过',
27
+ '抱歉打断了,我重新整理一下思路',
28
+ '不好意思,刚才掉线了,马上接上',
29
+ ];
30
+ lastCrashRecoveryIndex = -1;
31
+ /** 崩溃恢复失败 / 启动失败的话术池 */
32
+ static STARTUP_FAILURE_REPLIES = [
33
+ '抱歉,我这边遇到了点问题暂时处理不了,稍后我再试试,或者你可以再发一次',
34
+ '不好意思,出了点技术问题我暂时响应不了,你可以稍后再@我试试',
35
+ '抱歉,我这边卡住了,短时间内可能没法响应,你可以过一会儿再找我',
36
+ ];
37
+ lastStartupFailureIndex = -1;
12
38
  constructor(config, deps) {
13
39
  this.config = config;
14
40
  this.deps = deps;
15
41
  this.logger = config.logger ?? { info: console.log, error: console.error };
16
42
  }
43
+ /** 从话术池随机取一句,避免连续重复 */
44
+ pickRandom(pool, lastIndex) {
45
+ let idx;
46
+ do {
47
+ idx = Math.floor(Math.random() * pool.length);
48
+ } while (idx === lastIndex && pool.length > 1);
49
+ return { text: pool[idx], index: idx };
50
+ }
51
+ pickColdStartReply() {
52
+ const { text, index } = this.pickRandom(LoidSessionManager.COLD_START_REPLIES, this.lastColdStartIndex);
53
+ this.lastColdStartIndex = index;
54
+ return text;
55
+ }
56
+ pickCrashRecoveryReply() {
57
+ const { text, index } = this.pickRandom(LoidSessionManager.CRASH_RECOVERY_REPLIES, this.lastCrashRecoveryIndex);
58
+ this.lastCrashRecoveryIndex = index;
59
+ return text;
60
+ }
61
+ pickStartupFailureReply() {
62
+ const { text, index } = this.pickRandom(LoidSessionManager.STARTUP_FAILURE_REPLIES, this.lastStartupFailureIndex);
63
+ this.lastStartupFailureIndex = index;
64
+ return text;
65
+ }
17
66
  // ── 公开方法 ──
18
67
  /**
19
68
  * 处理新消息:有活跃 session 就 enqueue,没有就创建
@@ -31,10 +80,32 @@ export class LoidSessionManager {
31
80
  // session 已销毁,删除并新建
32
81
  this.sessions.delete(key);
33
82
  }
83
+ // 冷启动:先回复一句话,让用户知道系统在处理
84
+ const messageId = ctx.message.metadata?.message_id;
85
+ if (this.deps.feishuSender && messageId) {
86
+ const reply = this.pickColdStartReply();
87
+ this.deps.feishuSender.sendReply({
88
+ text: reply,
89
+ replyToMessageId: messageId,
90
+ }).then(() => {
91
+ // 冷启动回复成功后,移除"处理中"表情
92
+ this.deps.feishuSender.clearTypingReaction(key).catch(() => { });
93
+ }).catch(err => {
94
+ this.logger.error('[SessionManager] 冷启动回复失败:', err);
95
+ // 即使回复失败也清除 typing reaction
96
+ this.deps.feishuSender.clearTypingReaction(key).catch(() => { });
97
+ });
98
+ }
34
99
  // 新建 session
35
- const session = await this.createSession(key);
36
- const contextPrompt = formatMessageContextPrompt(ctx);
37
- session.enqueue({ type: 'message', prompt: contextPrompt, enqueuedAt: new Date() });
100
+ try {
101
+ const session = await this.createSession(key);
102
+ const contextPrompt = formatMessageContextPrompt(ctx);
103
+ session.enqueue({ type: 'message', prompt: contextPrompt, enqueuedAt: new Date() });
104
+ }
105
+ catch (err) {
106
+ this.logger.error(`[SessionManager] 创建 session 失败 (${key}):`, err);
107
+ this.sendChatNotification(key, this.pickStartupFailureReply());
108
+ }
38
109
  }
39
110
  /**
40
111
  * 处理 Yor 交付验收:查找来源 session,活着就复用,否则新建
@@ -54,13 +125,22 @@ export class LoidSessionManager {
54
125
  }
55
126
  // 来源 session 已回收,新建独立 session
56
127
  const key = `delivery:${ctx.taskId}`;
57
- const session = await this.createSession(key);
58
- const protocol = this.config.protocols['review'] ?? '';
59
- const prompt = [
60
- protocol ? protocol + '\n\n---\n\n' : '',
61
- formatDeliveryContextPrompt(ctx),
62
- ].join('');
63
- session.enqueue({ type: 'delivery', prompt, enqueuedAt: new Date() });
128
+ try {
129
+ const session = await this.createSession(key);
130
+ const protocol = this.config.protocols['review'] ?? '';
131
+ const prompt = [
132
+ protocol ? protocol + '\n\n---\n\n' : '',
133
+ formatDeliveryContextPrompt(ctx),
134
+ ].join('');
135
+ session.enqueue({ type: 'delivery', prompt, enqueuedAt: new Date() });
136
+ }
137
+ catch (err) {
138
+ this.logger.error(`[SessionManager] 创建 delivery session 失败 (${key}):`, err);
139
+ // delivery 场景通知来源群
140
+ if (ctx.sourceChatId) {
141
+ this.sendChatNotification(ctx.sourceChatId, this.pickStartupFailureReply());
142
+ }
143
+ }
64
144
  }
65
145
  /**
66
146
  * 获取当前活跃 session 数量
@@ -166,12 +246,17 @@ export class LoidSessionManager {
166
246
  return null;
167
247
  }
168
248
  /**
169
- * Session 崩溃恢复:删旧 session,新建处理孤儿消息
249
+ * Session 崩溃恢复:删旧 session,通知用户,新建处理孤儿消息
170
250
  */
171
251
  async onSessionCrash(key, orphanedMessages) {
172
252
  this.logger.error(`[SessionManager] session 崩溃: ${key} (${orphanedMessages.length} 条孤儿消息)`);
173
253
  // 删除旧 session
174
254
  this.sessions.delete(key);
255
+ // 通知用户:正在恢复
256
+ const chatId = this.extractChatId(key);
257
+ if (chatId) {
258
+ this.sendChatNotification(chatId, this.pickCrashRecoveryReply());
259
+ }
175
260
  // 从 DB 加载活跃任务状态作为恢复上下文
176
261
  const activeTasks = this.getActiveTasks();
177
262
  const recoveryPrompt = formatRecoveryPrompt(activeTasks);
@@ -186,8 +271,38 @@ export class LoidSessionManager {
186
271
  }
187
272
  catch (err) {
188
273
  this.logger.error(`[SessionManager] 崩溃恢复失败 (${key}):`, err);
274
+ // 恢复失败:告知用户
275
+ if (chatId) {
276
+ this.sendChatNotification(chatId, this.pickStartupFailureReply());
277
+ }
189
278
  }
190
279
  }
280
+ /**
281
+ * 从 session key 提取 chatId(过滤掉 delivery: 等前缀的内部 key)
282
+ * 只有来自用户对话的 session 才需要通知
283
+ */
284
+ extractChatId(key) {
285
+ // delivery:ANYA-xxx 等内部 session 不直接通知用户
286
+ if (key.startsWith('delivery:'))
287
+ return null;
288
+ return key;
289
+ }
290
+ /**
291
+ * 向飞书群发送通知(fire-and-forget),同时清除"处理中"表情
292
+ */
293
+ sendChatNotification(chatId, text) {
294
+ if (!this.deps.feishuSender)
295
+ return;
296
+ // 清除 typing reaction(通知本身就是反馈)
297
+ this.deps.feishuSender.clearTypingReaction(chatId).catch(() => { });
298
+ this.deps.feishuSender.sendText({
299
+ receiveIdType: 'chat_id',
300
+ receiveId: chatId,
301
+ text,
302
+ }).catch(err => {
303
+ this.logger.error('[SessionManager] 崩溃通知发送失败:', err);
304
+ });
305
+ }
191
306
  /**
192
307
  * 获取活跃任务列表(用于崩溃恢复上下文)
193
308
  */
@@ -4,7 +4,7 @@ import { join, resolve } from 'node:path';
4
4
  import Fastify from 'fastify';
5
5
  import fastifyCors from '@fastify/cors';
6
6
  import fastifyStatic from '@fastify/static';
7
- import { createDB } from '@team-anya/db';
7
+ import { createDB, getTask as dbGetTask } from '@team-anya/db';
8
8
  import { memoryRemember, auditAppend, ChannelRegistry, FeishuAdapter } from '@team-anya/mcp-tools';
9
9
  import { ensureOffice, TaskStatus } from '@team-anya/core';
10
10
  import { loadConfig } from './config.js';
@@ -166,6 +166,7 @@ export async function buildServer() {
166
166
  logger: app.log,
167
167
  }, {
168
168
  broker,
169
+ feishuSender: feishuSender ?? undefined,
169
170
  mcpDeps: {
170
171
  db,
171
172
  channelRegistry,
@@ -174,6 +175,9 @@ export async function buildServer() {
174
175
  yorOrchestrator,
175
176
  getProjectConfig: (projectId) => worktreeManager.getProjectConfig(projectId),
176
177
  onProjectChanged: () => worktreeManager.invalidateCache(),
178
+ onMessageSent: feishuSender
179
+ ? (chatId) => { feishuSender.clearTypingReaction(chatId).catch(() => { }); }
180
+ : undefined,
177
181
  },
178
182
  });
179
183
  await loidBrain.init();
@@ -202,6 +206,21 @@ export async function buildServer() {
202
206
  app.log.error({ taskId, error }, 'Yor 实例崩溃');
203
207
  // 崩溃 → BLOCKED(非终态才变更)
204
208
  yorOrchestrator.transitionTask(taskId, TaskStatus.BLOCKED, 'system', `Yor 实例崩溃: ${error}`);
209
+ // 通知用户:任务执行遇到问题
210
+ if (feishuSender) {
211
+ const task = dbGetTask(db, taskId);
212
+ const chatId = task?.source_chat_id;
213
+ if (chatId) {
214
+ const title = task?.title ? `「${task.title}」` : taskId;
215
+ feishuSender.sendText({
216
+ receiveIdType: 'chat_id',
217
+ receiveId: chatId,
218
+ text: `${title} 执行过程中出了点问题,我先看看怎么回事`,
219
+ }).catch(err => {
220
+ app.log.error({ err, taskId }, 'Yor 崩溃通知发送失败');
221
+ });
222
+ }
223
+ }
205
224
  const ctx = contextBuilder.buildDeliveryContext(taskId, 1);
206
225
  loidBrain.handleDelivery(ctx).catch(err => {
207
226
  app.log.error({ err, taskId }, 'Yor 崩溃后验收触发失败');
@@ -0,0 +1 @@
1
+ /*! tailwindcss v4.2.1 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial}}}@layer tailwind{@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-orange-400:oklch(75% .183 55.934);--color-orange-500:oklch(70.5% .213 47.604);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-500:oklch(79.5% .184 86.047);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-emerald-300:oklch(84.5% .143 164.978);--color-emerald-400:oklch(76.5% .177 163.223);--color-emerald-500:oklch(69.6% .17 162.48);--color-emerald-600:oklch(59.6% .145 163.225);--color-emerald-900:oklch(37.8% .077 168.94);--color-teal-200:oklch(91% .096 180.426);--color-teal-300:oklch(85.5% .138 181.071);--color-teal-400:oklch(77.7% .152 181.912);--color-teal-500:oklch(70.4% .14 182.503);--color-teal-900:oklch(38.6% .063 188.416);--color-teal-950:oklch(27.7% .046 192.524);--color-cyan-300:oklch(86.5% .127 207.078);--color-cyan-400:oklch(78.9% .154 211.53);--color-cyan-500:oklch(71.5% .143 215.221);--color-cyan-700:oklch(52% .105 223.128);--color-sky-400:oklch(74.6% .16 232.661);--color-blue-300:oklch(80.9% .105 251.813);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-900:oklch(37.9% .146 265.522);--color-indigo-50:oklch(96.2% .018 272.314);--color-indigo-600:oklch(51.1% .262 276.966);--color-violet-400:oklch(70.2% .183 293.541);--color-violet-500:oklch(60.6% .25 292.717);--color-purple-400:oklch(71.4% .203 305.504);--color-purple-500:oklch(62.7% .265 303.9);--color-rose-200:oklch(89.2% .058 10.001);--color-rose-300:oklch(81% .117 11.638);--color-rose-400:oklch(71.2% .194 13.428);--color-rose-500:oklch(64.5% .246 16.439);--color-rose-600:oklch(58.6% .253 17.585);--color-rose-900:oklch(41% .159 10.272);--color-rose-950:oklch(27.1% .105 12.094);--color-slate-200:oklch(92.9% .013 255.508);--color-slate-300:oklch(86.9% .022 252.894);--color-slate-500:oklch(55.4% .046 257.417);--color-slate-700:oklch(37.2% .044 257.287);--color-slate-900:oklch(20.8% .042 265.755);--color-zinc-50:oklch(98.5% 0 0);--color-zinc-100:oklch(96.7% .001 286.375);--color-zinc-200:oklch(92% .004 286.32);--color-zinc-300:oklch(87.1% .006 286.286);--color-zinc-400:oklch(70.5% .015 286.067);--color-zinc-500:oklch(55.2% .016 285.938);--color-zinc-600:oklch(44.2% .017 285.786);--color-zinc-700:oklch(37% .013 285.805);--color-zinc-800:oklch(27.4% .006 286.033);--color-zinc-900:oklch(21% .006 285.885);--color-zinc-950:oklch(14.1% .005 285.823);--spacing:.25rem;--container-3xl:48rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--tracking-wide:.025em;--tracking-wider:.05em;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--blur-sm:8px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}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;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.start{inset-inline-start:var(--spacing)}.end{inset-inline-end:var(--spacing)}.right-6{right:calc(var(--spacing) * 6)}.bottom-4{bottom:calc(var(--spacing) * 4)}.left-0{left:calc(var(--spacing) * 0)}.m-0{margin:calc(var(--spacing) * 0)}.mx-auto{margin-inline:auto}.my-2{margin-block:calc(var(--spacing) * 2)}.my-4{margin-block:calc(var(--spacing) * 4)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mr-1{margin-right:calc(var(--spacing) * 1)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-0\.5{margin-left:calc(var(--spacing) * .5)}.ml-1{margin-left:calc(var(--spacing) * 1)}.ml-3{margin-left:calc(var(--spacing) * 3)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.hidden{display:none}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-full{height:100%}.max-h-60{max-height:calc(var(--spacing) * 60)}.max-h-80{max-height:calc(var(--spacing) * 80)}.max-h-96{max-height:calc(var(--spacing) * 96)}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-10{width:calc(var(--spacing) * 10)}.w-16{width:calc(var(--spacing) * 16)}.w-full{width:100%}.max-w-3xl{max-width:var(--container-3xl)}.max-w-\[85\%\]{max-width:85%}.max-w-\[200px\]{max-width:200px}.min-w-0{min-width:calc(var(--spacing) * 0)}.flex-1{flex:1}.flex-shrink-0{flex-shrink:0}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-pointer{cursor:pointer}.list-inside{list-style-position:inside}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.flex-col{flex-direction:column}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * .5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * .5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-zinc-800\/50>:not(:last-child)){border-color:#27272a80}@supports (color:color-mix(in lab,red,red)){:where(.divide-zinc-800\/50>:not(:last-child)){border-color:color-mix(in oklab,var(--color-zinc-800) 50%,transparent)}}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-br-md{border-bottom-right-radius:var(--radius-md)}.rounded-bl-md{border-bottom-left-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-y{border-block-style:var(--tw-border-style);border-block-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-amber-500\/20{border-color:#f99c0033}@supports (color:color-mix(in lab,red,red)){.border-amber-500\/20{border-color:color-mix(in oklab,var(--color-amber-500) 20%,transparent)}}.border-blue-500\/20{border-color:#3080ff33}@supports (color:color-mix(in lab,red,red)){.border-blue-500\/20{border-color:color-mix(in oklab,var(--color-blue-500) 20%,transparent)}}.border-blue-900\/30{border-color:#1c398e4d}@supports (color:color-mix(in lab,red,red)){.border-blue-900\/30{border-color:color-mix(in oklab,var(--color-blue-900) 30%,transparent)}}.border-cyan-500\/20{border-color:#00b7d733}@supports (color:color-mix(in lab,red,red)){.border-cyan-500\/20{border-color:color-mix(in oklab,var(--color-cyan-500) 20%,transparent)}}.border-emerald-500{border-color:var(--color-emerald-500)}.border-emerald-500\/20{border-color:#00bb7f33}@supports (color:color-mix(in lab,red,red)){.border-emerald-500\/20{border-color:color-mix(in oklab,var(--color-emerald-500) 20%,transparent)}}.border-rose-500{border-color:var(--color-rose-500)}.border-rose-500\/20{border-color:#ff235733}@supports (color:color-mix(in lab,red,red)){.border-rose-500\/20{border-color:color-mix(in oklab,var(--color-rose-500) 20%,transparent)}}.border-rose-900\/30{border-color:#8b08364d}@supports (color:color-mix(in lab,red,red)){.border-rose-900\/30{border-color:color-mix(in oklab,var(--color-rose-900) 30%,transparent)}}.border-slate-500\/20{border-color:#62748e33}@supports (color:color-mix(in lab,red,red)){.border-slate-500\/20{border-color:color-mix(in oklab,var(--color-slate-500) 20%,transparent)}}.border-slate-700\/50{border-color:#31415880}@supports (color:color-mix(in lab,red,red)){.border-slate-700\/50{border-color:color-mix(in oklab,var(--color-slate-700) 50%,transparent)}}.border-teal-500\/20{border-color:#00baa733}@supports (color:color-mix(in lab,red,red)){.border-teal-500\/20{border-color:color-mix(in oklab,var(--color-teal-500) 20%,transparent)}}.border-teal-900\/30{border-color:#0b4f4a4d}@supports (color:color-mix(in lab,red,red)){.border-teal-900\/30{border-color:color-mix(in oklab,var(--color-teal-900) 30%,transparent)}}.border-violet-500\/20{border-color:#8d54ff33}@supports (color:color-mix(in lab,red,red)){.border-violet-500\/20{border-color:color-mix(in oklab,var(--color-violet-500) 20%,transparent)}}.border-zinc-600{border-color:var(--color-zinc-600)}.border-zinc-700{border-color:var(--color-zinc-700)}.border-zinc-700\/30{border-color:#3f3f464d}@supports (color:color-mix(in lab,red,red)){.border-zinc-700\/30{border-color:color-mix(in oklab,var(--color-zinc-700) 30%,transparent)}}.border-zinc-700\/50{border-color:#3f3f4680}@supports (color:color-mix(in lab,red,red)){.border-zinc-700\/50{border-color:color-mix(in oklab,var(--color-zinc-700) 50%,transparent)}}.border-zinc-800{border-color:var(--color-zinc-800)}.border-zinc-800\/60{border-color:#27272a99}@supports (color:color-mix(in lab,red,red)){.border-zinc-800\/60{border-color:color-mix(in oklab,var(--color-zinc-800) 60%,transparent)}}.bg-amber-500\/10{background-color:#f99c001a}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/10{background-color:color-mix(in oklab,var(--color-amber-500) 10%,transparent)}}.bg-blue-500\/10{background-color:#3080ff1a}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/10{background-color:color-mix(in oklab,var(--color-blue-500) 10%,transparent)}}.bg-blue-500\/20{background-color:#3080ff33}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/20{background-color:color-mix(in oklab,var(--color-blue-500) 20%,transparent)}}.bg-blue-900\/20{background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.bg-blue-900\/20{background-color:color-mix(in oklab,var(--color-blue-900) 20%,transparent)}}.bg-cyan-500{background-color:var(--color-cyan-500)}.bg-cyan-500\/10{background-color:#00b7d71a}@supports (color:color-mix(in lab,red,red)){.bg-cyan-500\/10{background-color:color-mix(in oklab,var(--color-cyan-500) 10%,transparent)}}.bg-cyan-500\/20{background-color:#00b7d733}@supports (color:color-mix(in lab,red,red)){.bg-cyan-500\/20{background-color:color-mix(in oklab,var(--color-cyan-500) 20%,transparent)}}.bg-cyan-700\/50{background-color:#00749280}@supports (color:color-mix(in lab,red,red)){.bg-cyan-700\/50{background-color:color-mix(in oklab,var(--color-cyan-700) 50%,transparent)}}.bg-emerald-500\/10{background-color:#00bb7f1a}@supports (color:color-mix(in lab,red,red)){.bg-emerald-500\/10{background-color:color-mix(in oklab,var(--color-emerald-500) 10%,transparent)}}.bg-emerald-900\/20{background-color:#004e3b33}@supports (color:color-mix(in lab,red,red)){.bg-emerald-900\/20{background-color:color-mix(in oklab,var(--color-emerald-900) 20%,transparent)}}.bg-green-500{background-color:var(--color-green-500)}.bg-green-500\/20{background-color:#00c75833}@supports (color:color-mix(in lab,red,red)){.bg-green-500\/20{background-color:color-mix(in oklab,var(--color-green-500) 20%,transparent)}}.bg-indigo-600\/80{background-color:#4f39f6cc}@supports (color:color-mix(in lab,red,red)){.bg-indigo-600\/80{background-color:color-mix(in oklab,var(--color-indigo-600) 80%,transparent)}}.bg-orange-500\/20{background-color:#fe6e0033}@supports (color:color-mix(in lab,red,red)){.bg-orange-500\/20{background-color:color-mix(in oklab,var(--color-orange-500) 20%,transparent)}}.bg-purple-500{background-color:var(--color-purple-500)}.bg-purple-500\/20{background-color:#ac4bff33}@supports (color:color-mix(in lab,red,red)){.bg-purple-500\/20{background-color:color-mix(in oklab,var(--color-purple-500) 20%,transparent)}}.bg-red-500\/20{background-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/20{background-color:color-mix(in oklab,var(--color-red-500) 20%,transparent)}}.bg-rose-500\/10{background-color:#ff23571a}@supports (color:color-mix(in lab,red,red)){.bg-rose-500\/10{background-color:color-mix(in oklab,var(--color-rose-500) 10%,transparent)}}.bg-rose-900\/20{background-color:#8b083633}@supports (color:color-mix(in lab,red,red)){.bg-rose-900\/20{background-color:color-mix(in oklab,var(--color-rose-900) 20%,transparent)}}.bg-rose-950\/20{background-color:#4d021833}@supports (color:color-mix(in lab,red,red)){.bg-rose-950\/20{background-color:color-mix(in oklab,var(--color-rose-950) 20%,transparent)}}.bg-rose-950\/30{background-color:#4d02184d}@supports (color:color-mix(in lab,red,red)){.bg-rose-950\/30{background-color:color-mix(in oklab,var(--color-rose-950) 30%,transparent)}}.bg-slate-500\/10{background-color:#62748e1a}@supports (color:color-mix(in lab,red,red)){.bg-slate-500\/10{background-color:color-mix(in oklab,var(--color-slate-500) 10%,transparent)}}.bg-slate-900\/50{background-color:#0f172b80}@supports (color:color-mix(in lab,red,red)){.bg-slate-900\/50{background-color:color-mix(in oklab,var(--color-slate-900) 50%,transparent)}}.bg-teal-500\/10{background-color:#00baa71a}@supports (color:color-mix(in lab,red,red)){.bg-teal-500\/10{background-color:color-mix(in oklab,var(--color-teal-500) 10%,transparent)}}.bg-teal-950\/30{background-color:#022f2e4d}@supports (color:color-mix(in lab,red,red)){.bg-teal-950\/30{background-color:color-mix(in oklab,var(--color-teal-950) 30%,transparent)}}.bg-transparent{background-color:#0000}.bg-violet-500{background-color:var(--color-violet-500)}.bg-violet-500\/10{background-color:#8d54ff1a}@supports (color:color-mix(in lab,red,red)){.bg-violet-500\/10{background-color:color-mix(in oklab,var(--color-violet-500) 10%,transparent)}}.bg-yellow-500\/20{background-color:#edb20033}@supports (color:color-mix(in lab,red,red)){.bg-yellow-500\/20{background-color:color-mix(in oklab,var(--color-yellow-500) 20%,transparent)}}.bg-zinc-200\/90{background-color:#e4e4e7e6}@supports (color:color-mix(in lab,red,red)){.bg-zinc-200\/90{background-color:color-mix(in oklab,var(--color-zinc-200) 90%,transparent)}}.bg-zinc-500\/20{background-color:#71717b33}@supports (color:color-mix(in lab,red,red)){.bg-zinc-500\/20{background-color:color-mix(in oklab,var(--color-zinc-500) 20%,transparent)}}.bg-zinc-700{background-color:var(--color-zinc-700)}.bg-zinc-700\/50{background-color:#3f3f4680}@supports (color:color-mix(in lab,red,red)){.bg-zinc-700\/50{background-color:color-mix(in oklab,var(--color-zinc-700) 50%,transparent)}}.bg-zinc-800\/30{background-color:#27272a4d}@supports (color:color-mix(in lab,red,red)){.bg-zinc-800\/30{background-color:color-mix(in oklab,var(--color-zinc-800) 30%,transparent)}}.bg-zinc-800\/40{background-color:#27272a66}@supports (color:color-mix(in lab,red,red)){.bg-zinc-800\/40{background-color:color-mix(in oklab,var(--color-zinc-800) 40%,transparent)}}.bg-zinc-800\/80{background-color:#27272acc}@supports (color:color-mix(in lab,red,red)){.bg-zinc-800\/80{background-color:color-mix(in oklab,var(--color-zinc-800) 80%,transparent)}}.bg-zinc-900{background-color:var(--color-zinc-900)}.bg-zinc-900\/50{background-color:#18181b80}@supports (color:color-mix(in lab,red,red)){.bg-zinc-900\/50{background-color:color-mix(in oklab,var(--color-zinc-900) 50%,transparent)}}.bg-zinc-900\/70{background-color:#18181bb3}@supports (color:color-mix(in lab,red,red)){.bg-zinc-900\/70{background-color:color-mix(in oklab,var(--color-zinc-900) 70%,transparent)}}.bg-zinc-900\/80{background-color:#18181bcc}@supports (color:color-mix(in lab,red,red)){.bg-zinc-900\/80{background-color:color-mix(in oklab,var(--color-zinc-900) 80%,transparent)}}.bg-zinc-950{background-color:var(--color-zinc-950)}.p-0{padding:calc(var(--spacing) * 0)}.p-1{padding:calc(var(--spacing) * 1)}.p-1\.5{padding:calc(var(--spacing) * 1.5)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-4{padding-block:calc(var(--spacing) * 4)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pr-3{padding-right:calc(var(--spacing) * 3)}.pl-3{padding-left:calc(var(--spacing) * 3)}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-pre{white-space:pre}.whitespace-pre-wrap{white-space:pre-wrap}.text-amber-200{color:var(--color-amber-200)}.text-amber-300{color:var(--color-amber-300)}.text-amber-400{color:var(--color-amber-400)}.text-amber-400\/90{color:#fcbb00e6}@supports (color:color-mix(in lab,red,red)){.text-amber-400\/90{color:color-mix(in oklab,var(--color-amber-400) 90%,transparent)}}.text-blue-300{color:var(--color-blue-300)}.text-blue-400{color:var(--color-blue-400)}.text-cyan-300{color:var(--color-cyan-300)}.text-cyan-400{color:var(--color-cyan-400)}.text-emerald-300{color:var(--color-emerald-300)}.text-emerald-400{color:var(--color-emerald-400)}.text-emerald-600{color:var(--color-emerald-600)}.text-green-400{color:var(--color-green-400)}.text-indigo-50{color:var(--color-indigo-50)}.text-orange-400{color:var(--color-orange-400)}.text-purple-400{color:var(--color-purple-400)}.text-red-400{color:var(--color-red-400)}.text-rose-200\/80{color:#ffccd3cc}@supports (color:color-mix(in lab,red,red)){.text-rose-200\/80{color:color-mix(in oklab,var(--color-rose-200) 80%,transparent)}}.text-rose-300{color:var(--color-rose-300)}.text-rose-400{color:var(--color-rose-400)}.text-rose-400\/90{color:#ff667fe6}@supports (color:color-mix(in lab,red,red)){.text-rose-400\/90{color:color-mix(in oklab,var(--color-rose-400) 90%,transparent)}}.text-rose-500\/70{color:#ff2357b3}@supports (color:color-mix(in lab,red,red)){.text-rose-500\/70{color:color-mix(in oklab,var(--color-rose-500) 70%,transparent)}}.text-rose-600{color:var(--color-rose-600)}.text-sky-400{color:var(--color-sky-400)}.text-slate-200{color:var(--color-slate-200)}.text-slate-300{color:var(--color-slate-300)}.text-slate-500{color:var(--color-slate-500)}.text-teal-200\/80{color:#96f7e4cc}@supports (color:color-mix(in lab,red,red)){.text-teal-200\/80{color:color-mix(in oklab,var(--color-teal-200) 80%,transparent)}}.text-teal-300{color:var(--color-teal-300)}.text-teal-400{color:var(--color-teal-400)}.text-teal-400\/90{color:#00d3bde6}@supports (color:color-mix(in lab,red,red)){.text-teal-400\/90{color:color-mix(in oklab,var(--color-teal-400) 90%,transparent)}}.text-teal-500\/70{color:#00baa7b3}@supports (color:color-mix(in lab,red,red)){.text-teal-500\/70{color:color-mix(in oklab,var(--color-teal-500) 70%,transparent)}}.text-violet-400{color:var(--color-violet-400)}.text-violet-400\/70{color:#a685ffb3}@supports (color:color-mix(in lab,red,red)){.text-violet-400\/70{color:color-mix(in oklab,var(--color-violet-400) 70%,transparent)}}.text-yellow-400{color:var(--color-yellow-400)}.text-zinc-50{color:var(--color-zinc-50)}.text-zinc-100{color:var(--color-zinc-100)}.text-zinc-200{color:var(--color-zinc-200)}.text-zinc-300{color:var(--color-zinc-300)}.text-zinc-400{color:var(--color-zinc-400)}.text-zinc-500{color:var(--color-zinc-500)}.text-zinc-600{color:var(--color-zinc-600)}.text-zinc-900{color:var(--color-zinc-900)}.uppercase{text-transform:uppercase}.italic{font-style:italic}.line-through{text-decoration-line:line-through}.underline{text-decoration-line:underline}.underline-offset-2{text-underline-offset:2px}.opacity-0{opacity:0}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-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-sm{--tw-backdrop-blur:blur(var(--blur-sm));-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-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-300{--tw-duration:.3s;transition-duration:.3s}.select-none{-webkit-user-select:none;user-select:none}@media(hover:hover){.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}@media(hover:hover){.hover\:bg-amber-500\/15:hover{background-color:#f99c0026}@supports (color:color-mix(in lab,red,red)){.hover\:bg-amber-500\/15:hover{background-color:color-mix(in oklab,var(--color-amber-500) 15%,transparent)}}.hover\:bg-rose-500\/15:hover{background-color:#ff235726}@supports (color:color-mix(in lab,red,red)){.hover\:bg-rose-500\/15:hover{background-color:color-mix(in oklab,var(--color-rose-500) 15%,transparent)}}.hover\:bg-slate-500\/15:hover{background-color:#62748e26}@supports (color:color-mix(in lab,red,red)){.hover\:bg-slate-500\/15:hover{background-color:color-mix(in oklab,var(--color-slate-500) 15%,transparent)}}.hover\:bg-teal-500\/15:hover{background-color:#00baa726}@supports (color:color-mix(in lab,red,red)){.hover\:bg-teal-500\/15:hover{background-color:color-mix(in oklab,var(--color-teal-500) 15%,transparent)}}.hover\:bg-zinc-100:hover{background-color:var(--color-zinc-100)}.hover\:bg-zinc-700\/50:hover{background-color:#3f3f4680}@supports (color:color-mix(in lab,red,red)){.hover\:bg-zinc-700\/50:hover{background-color:color-mix(in oklab,var(--color-zinc-700) 50%,transparent)}}.hover\:bg-zinc-800:hover{background-color:var(--color-zinc-800)}.hover\:bg-zinc-800\/20:hover{background-color:#27272a33}@supports (color:color-mix(in lab,red,red)){.hover\:bg-zinc-800\/20:hover{background-color:color-mix(in oklab,var(--color-zinc-800) 20%,transparent)}}.hover\:bg-zinc-800\/30:hover{background-color:#27272a4d}@supports (color:color-mix(in lab,red,red)){.hover\:bg-zinc-800\/30:hover{background-color:color-mix(in oklab,var(--color-zinc-800) 30%,transparent)}}.hover\:text-cyan-300:hover{color:var(--color-cyan-300)}.hover\:text-zinc-300:hover{color:var(--color-zinc-300)}.hover\:underline:hover{text-decoration-line:underline}}}}@layer tailwind.base;.session-viewer pre{white-space:pre-wrap;word-break:break-word;margin:0}.session-viewer code{font-family:SF Mono,Fira Code,Fira Mono,Roboto Mono,monospace}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}