kanon-cli 0.1.1 → 0.1.2

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.
@@ -4,8 +4,8 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Kanon Dashboard</title>
7
- <script type="module" crossorigin src="/assets/index-7UwkIyFn.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-DBQ473Y5.css">
7
+ <script type="module" crossorigin src="/assets/index-DrHjrBfj.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-ClOAcx9M.css">
9
9
  </head>
10
10
  <body class="h-full text-gray-100">
11
11
  <div id="root" class="h-full"></div>
@@ -10,8 +10,12 @@ export function createProxyRoutes() {
10
10
  const serverUrl = getServerUrl();
11
11
 
12
12
  try {
13
+ if (!config.token) {
14
+ return res.status(401).json({ error: 'Agent not configured. Set up your agent on the Credentials page first.' });
15
+ }
16
+
13
17
  const headers = { 'Content-Type': 'application/json' };
14
- if (config.token) headers['Authorization'] = `Bearer ${config.token}`;
18
+ headers['Authorization'] = `Bearer ${config.token}`;
15
19
 
16
20
  const opts = { method, headers };
17
21
  if (req.body && Object.keys(req.body).length) {
@@ -208,13 +208,17 @@ export function createSettingsRoutes() {
208
208
  router.put('/credentials', (req, res) => {
209
209
  try {
210
210
  const current = loadGlobalConfig();
211
- const update = { ...current };
212
-
213
- if (req.body.server_url) update.server_url = req.body.server_url;
214
- if (req.body.email) update.email = req.body.email;
215
- if (req.body.password) update.password = req.body.password;
216
-
217
- saveGlobalConfig(update);
211
+ const email = req.body.email ?? current.email ?? '';
212
+ // If email is cleared, also clear auth fields
213
+ const clearAuth = !email;
214
+ saveGlobalConfig({
215
+ server_url: req.body.server_url ?? current.server_url ?? '',
216
+ email,
217
+ password: clearAuth ? '' : (req.body.password ?? current.password ?? ''),
218
+ token: clearAuth ? '' : (req.body.token ?? current.token ?? ''),
219
+ user_id: clearAuth ? '' : (req.body.user_id ?? current.user_id ?? ''),
220
+ user_name: clearAuth ? '' : (req.body.user_name ?? current.user_name ?? ''),
221
+ });
218
222
  res.json({ ok: true });
219
223
  } catch (err) {
220
224
  res.status(500).json({ error: err.message });
package/src/lib/admin.js CHANGED
@@ -1,9 +1,21 @@
1
1
  import chalk from 'chalk';
2
2
  import fs from 'fs';
3
- import { getStatusPath } from './config.js';
3
+ import { getStatusPath, getServerUrl, getToken } from './config.js';
4
4
  import { getActiveWorkers, killWorker, spawnClaude } from './claude.js';
5
5
  import { buildBundlePrompt } from '../prompts/templates.js';
6
6
 
7
+ /** Fire-and-forget POST to server for agent status changes */
8
+ function _notifyAgentStatus(cardId, boardId, action, extra = {}) {
9
+ const serverUrl = getServerUrl();
10
+ const token = getToken();
11
+ if (!serverUrl || !token || !boardId) return;
12
+ fetch(`${serverUrl}/api/agents/status`, {
13
+ method: 'POST',
14
+ headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
15
+ body: JSON.stringify({ cardId, boardId, action, ...extra }),
16
+ }).catch(() => {});
17
+ }
18
+
7
19
  export class AgentController {
8
20
  constructor(config = {}) {
9
21
  this.checkInterval = (config.check_interval_seconds || 60) * 1000;
@@ -113,6 +125,7 @@ export class AgentController {
113
125
  }
114
126
 
115
127
  this.queue.push({ cardId, prompt, config: claudeConfig, priority, addedAt: Date.now(), card, extraPrompt, boardId });
128
+ _notifyAgentStatus(cardId, boardId, 'queue');
116
129
  // Sort by priority (higher first), then by time (older first)
117
130
  this.queue.sort((a, b) => b.priority - a.priority || a.addedAt - b.addedAt);
118
131
 
@@ -202,8 +215,16 @@ export class AgentController {
202
215
  }
203
216
 
204
217
  this.log('info', null, `Starting bundled worker for ${tasks.length} cards: ${cardIds.map(id => id.substring(0, 8)).join(', ')}`);
205
- const worker = spawnClaude(bundleId, combinedPrompt, tasks[0].config);
218
+ const worker = spawnClaude(bundleId, combinedPrompt, { ...tasks[0].config, boardId: tasks[0].boardId, outputCardId: tasks[0].cardId });
206
219
  if (worker?.process) {
220
+ // Notify primary card as active, others as bundled
221
+ for (const t of tasks) {
222
+ const isPrimary = t === tasks[0];
223
+ _notifyAgentStatus(t.cardId, t.boardId, 'start', {
224
+ bundleId,
225
+ primaryCardId: isPrimary ? null : tasks[0].cardId,
226
+ });
227
+ }
207
228
  worker.process.on('close', (code) => {
208
229
  this._decBoard(boardKey);
209
230
  this.completedWorkers.push({
@@ -217,6 +238,9 @@ export class AgentController {
217
238
  output: worker.output.slice(-50),
218
239
  });
219
240
  if (this.completedWorkers.length > 10) this.completedWorkers.shift();
241
+ for (const cid of cardIds) {
242
+ _notifyAgentStatus(cid, tasks[0].boardId, 'stop', { exitCode: code });
243
+ }
220
244
  this.log(code === 0 ? 'info' : 'warn', null, `Bundled worker finished (exit ${code}, ${Math.round((Date.now() - worker.startedAt) / 1000)}s) — cards: ${cardIds.map(id => id.substring(0, 8)).join(', ')}`);
221
245
  this._processQueue();
222
246
  });
@@ -225,8 +249,9 @@ export class AgentController {
225
249
 
226
250
  _startSingleWorker(task, boardKey) {
227
251
  this.log('info', task.cardId, 'Starting worker from queue');
228
- const worker = spawnClaude(task.cardId, task.prompt, task.config);
252
+ const worker = spawnClaude(task.cardId, task.prompt, { ...task.config, boardId: task.boardId });
229
253
  if (worker?.process) {
254
+ _notifyAgentStatus(task.cardId, task.boardId, 'start');
230
255
  worker.process.on('close', (code) => {
231
256
  if (boardKey) this._decBoard(boardKey);
232
257
  this.completedWorkers.push({
@@ -238,6 +263,7 @@ export class AgentController {
238
263
  output: worker.output.slice(-50),
239
264
  });
240
265
  if (this.completedWorkers.length > 10) this.completedWorkers.shift();
266
+ _notifyAgentStatus(task.cardId, task.boardId, 'stop', { exitCode: code });
241
267
  this.log(code === 0 ? 'info' : 'warn', task.cardId, `Worker finished (exit ${code}, ${Math.round((Date.now() - worker.startedAt) / 1000)}s)`);
242
268
  this._processQueue();
243
269
  });
package/src/lib/api.js CHANGED
@@ -219,6 +219,61 @@ class KanonAPI {
219
219
  }
220
220
  return res.json();
221
221
  }
222
+ // --- Subcards ---
223
+
224
+ async setParent(cardId, parentCardId) {
225
+ return this.request('PUT', `/cards/${cardId}/set-parent`, { parentCardId });
226
+ }
227
+
228
+ // --- Sheets ---
229
+
230
+ async getSheet(cardId) {
231
+ return this.request('GET', `/cards/${cardId}/sheet`);
232
+ }
233
+
234
+ async createSheet(cardId) {
235
+ return this.request('POST', `/cards/${cardId}/sheet`);
236
+ }
237
+
238
+ async updateSheet(cardId, data) {
239
+ return this.request('PUT', `/cards/${cardId}/sheet`, { data });
240
+ }
241
+
242
+ async updateSheetCell(cardId, { tab, row, col, value }) {
243
+ return this.request('PUT', `/cards/${cardId}/sheet/cell`, { tab, row, col, value });
244
+ }
245
+
246
+ async deleteSheet(cardId) {
247
+ return this.request('DELETE', `/cards/${cardId}/sheet`);
248
+ }
249
+
250
+ // --- Notes (plaintext) ---
251
+
252
+ async getNoteText(cardId) {
253
+ return this.request('GET', `/cards/${cardId}/note/text`);
254
+ }
255
+
256
+ async setNoteText(cardId, text) {
257
+ return this.request('PUT', `/cards/${cardId}/note/text`, { text });
258
+ }
259
+
260
+ async appendNoteText(cardId, text) {
261
+ return this.request('POST', `/cards/${cardId}/note/text/append`, { text });
262
+ }
263
+
264
+ async createNote(cardId) {
265
+ return this.request('POST', `/cards/${cardId}/note`);
266
+ }
267
+
268
+ async deleteNote(cardId) {
269
+ return this.request('DELETE', `/cards/${cardId}/note`);
270
+ }
271
+
272
+ // --- Canvas/Whiteboard ---
273
+
274
+ async getCanvasSummary(cardId) {
275
+ return this.request('GET', `/cards/${cardId}/whiteboard/summary`);
276
+ }
222
277
  }
223
278
 
224
279
  export const api = new KanonAPI();
package/src/lib/claude.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import { spawn } from 'child_process';
2
+ import fs from 'fs';
2
3
  import path from 'path';
3
4
  import { fileURLToPath } from 'url';
4
5
  import chalk from 'chalk';
6
+ import { getServerUrl, getToken } from './config.js';
5
7
 
6
8
  // Resolve the bin/ directory so `kanon` is in PATH for spawned Claude sessions
7
9
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -10,6 +12,19 @@ const kanonBinDir = path.resolve(__dirname, '../../bin');
10
12
  /** Active Claude processes indexed by cardId */
11
13
  const activeWorkers = new Map();
12
14
 
15
+ /** Fire-and-forget POST to server for agent output streaming */
16
+ function _flushToServer(cardId, boardId, lines, config) {
17
+ const serverUrl = getServerUrl();
18
+ const token = getToken();
19
+ if (!serverUrl || !token) return;
20
+ fetch(`${serverUrl}/api/agents/output`, {
21
+ method: 'POST',
22
+ headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
23
+ body: JSON.stringify({ cardId, boardId, lines }),
24
+ }).catch(() => {}); // fire-and-forget
25
+ }
26
+
27
+
13
28
  /**
14
29
  * Spawn a Claude Code process for a card.
15
30
  * Returns a WorkerInfo object tracked by the agent controller.
@@ -35,6 +50,16 @@ export function spawnClaude(cardId, prompt, config = {}) {
35
50
 
36
51
  const projectDir = config.project_dir || process.cwd();
37
52
 
53
+ // Write a temporary kanon.config.yaml so `kanon board/cards/list` work inside the agent
54
+ let tempConfigPath = null;
55
+ if (config.boardId) {
56
+ const configPath = path.join(projectDir, 'kanon.config.yaml');
57
+ if (!fs.existsSync(configPath)) {
58
+ tempConfigPath = configPath;
59
+ fs.writeFileSync(configPath, `board_id: "${config.boardId}"\n`);
60
+ }
61
+ }
62
+
38
63
  const worker = {
39
64
  cardId,
40
65
  startedAt: Date.now(),
@@ -42,6 +67,10 @@ export function spawnClaude(cardId, prompt, config = {}) {
42
67
  output: [],
43
68
  exitCode: null,
44
69
  process: null,
70
+ _pendingServerLines: [],
71
+ _boardId: config.boardId || null,
72
+ _outputCardId: config.outputCardId || null,
73
+ _tempConfigPath: tempConfigPath,
45
74
  };
46
75
 
47
76
  const proc = spawn(claudeCmd, args, {
@@ -53,6 +82,14 @@ export function spawnClaude(cardId, prompt, config = {}) {
53
82
 
54
83
  worker.process = proc;
55
84
 
85
+ // Flush pending output to server every 3 seconds + poll for kill requests
86
+ worker._flushTimer = setInterval(() => {
87
+ if (worker._pendingServerLines.length > 0 && worker._boardId) {
88
+ const lines = worker._pendingServerLines.splice(0);
89
+ _flushToServer(worker._outputCardId || worker.cardId, worker._boardId, lines, config);
90
+ }
91
+ }, 3000);
92
+
56
93
  // Buffer for incomplete JSON lines from stream-json output
57
94
  let stdoutBuf = '';
58
95
 
@@ -73,7 +110,8 @@ export function spawnClaude(cardId, prompt, config = {}) {
73
110
  // Not valid JSON – store raw line as-is
74
111
  worker.lastOutput = Date.now();
75
112
  worker.output.push(trimmed);
76
- if (worker.output.length > 100) worker.output.shift();
113
+ worker._pendingServerLines.push(trimmed);
114
+ if (worker.output.length > 500) worker.output.shift();
77
115
  continue;
78
116
  }
79
117
 
@@ -84,21 +122,28 @@ export function spawnClaude(cardId, prompt, config = {}) {
84
122
  for (const block of parsed.message.content) {
85
123
  if (block.type === 'text' && block.text) {
86
124
  worker.output.push(block.text);
125
+ worker._pendingServerLines.push(block.text);
87
126
  } else if (block.type === 'tool_use') {
88
127
  const name = block.name || 'unknown';
89
128
  const inputPreview = block.input
90
129
  ? JSON.stringify(block.input).substring(0, 120)
91
130
  : '';
92
- worker.output.push(`[Tool: ${name}] ${inputPreview}`);
131
+ const toolLine = `[Tool: ${name}] ${inputPreview}`;
132
+ worker.output.push(toolLine);
133
+ worker._pendingServerLines.push(toolLine);
93
134
  }
94
- if (worker.output.length > 100) worker.output.shift();
135
+ if (worker.output.length > 500) worker.output.shift();
95
136
  }
96
137
  } else if (parsed.type === 'result' && parsed.result) {
97
- worker.output.push(`[Result] ${parsed.result.substring(0, 200)}`);
98
- if (worker.output.length > 100) worker.output.shift();
138
+ const resultLine = `[Result] ${parsed.result.substring(0, 200)}`;
139
+ worker.output.push(resultLine);
140
+ worker._pendingServerLines.push(resultLine);
141
+ if (worker.output.length > 500) worker.output.shift();
99
142
  } else if (parsed.type === 'error') {
100
- worker.output.push(`[Error] ${parsed.error?.message || JSON.stringify(parsed)}`);
101
- if (worker.output.length > 100) worker.output.shift();
143
+ const errorLine = `[Error] ${parsed.error?.message || JSON.stringify(parsed)}`;
144
+ worker.output.push(errorLine);
145
+ worker._pendingServerLines.push(errorLine);
146
+ if (worker.output.length > 500) worker.output.shift();
102
147
  }
103
148
  // Silently skip other message types (system, etc.)
104
149
  }
@@ -107,12 +152,23 @@ export function spawnClaude(cardId, prompt, config = {}) {
107
152
  proc.stderr.on('data', (data) => {
108
153
  const line = data.toString();
109
154
  worker.lastOutput = Date.now();
110
- worker.output.push(`[stderr] ${line}`);
111
- if (worker.output.length > 100) worker.output.shift();
155
+ const stderrLine = `[stderr] ${line}`;
156
+ worker.output.push(stderrLine);
157
+ worker._pendingServerLines.push(stderrLine);
158
+ if (worker.output.length > 500) worker.output.shift();
112
159
  });
113
160
 
114
161
  proc.on('close', (code) => {
115
162
  worker.exitCode = code;
163
+ clearInterval(worker._flushTimer);
164
+ // Final flush
165
+ if (worker._pendingServerLines.length > 0 && worker._boardId) {
166
+ _flushToServer(worker._outputCardId || worker.cardId, worker._boardId, worker._pendingServerLines.splice(0), config);
167
+ }
168
+ // Clean up temp config
169
+ if (worker._tempConfigPath) {
170
+ try { fs.unlinkSync(worker._tempConfigPath); } catch {}
171
+ }
116
172
  activeWorkers.delete(cardId);
117
173
  console.log(chalk.dim(`Worker for card ${cardId} exited with code ${code}`));
118
174
  });
@@ -5,31 +5,44 @@
5
5
 
6
6
  export const DEFAULT_SYSTEM = `You are a Kanon board agent. You interact with the board using the \`kanon\` CLI which is in your PATH. The CLI is already authenticated.
7
7
 
8
+ The card details below already contain the full card content — do NOT re-read the card with \`kanon card <id>\` unless you need to refresh after making changes.
9
+
8
10
  ## Key Commands
9
11
  \`\`\`
10
- kanon card <id> Read card details
11
- kanon card create "title" "list" [opts] Create card
12
- kanon card update <id> [opts]Update card (--comment, --add-label, --move, --done, etc.)
13
- kanon board — Board context (lists, labels, members)
14
- kanon cards List all cards
15
- kanon help-all Full command reference with all options
12
+ kanon card update <id> --comment "text" Add a comment
13
+ kanon card update <id> --move "List" Move card to a list (by name)
14
+ kanon card update <id> --doneMark card as done
15
+ kanon card update <id> --description "x" — Set description
16
+ kanon card update <id> --add-label "x" Add a label
17
+ kanon card <id> Re-read card (only if needed)
18
+ kanon board — Board context (only if you need list names or members)
19
+ kanon cards — List all cards (only if task requires it)
20
+ kanon subcard <parentId> — List subcards
21
+ kanon subcard <parentId> nest <cardId> — Nest card under parent
22
+ kanon subcard <parentId> unnest <cardId> — Unnest card from parent
23
+ kanon card create <title> <list> --parent <id> — Create as subcard
24
+ kanon sheet <id> — Read sheet data
25
+ kanon sheet <id> set <tab> <row> <col> "val" — Set cell (0-indexed)
26
+ kanon sheet <id> export — CSV output
27
+ kanon note <id> — Read note text
28
+ kanon note <id> set "text" — Set note text
29
+ kanon note <id> append "text" — Append to note
30
+ kanon canvas <id> — Read canvas objects & connections
31
+ kanon help-all — Full command reference
16
32
  \`\`\`
17
33
 
18
- Run \`kanon help-all\` to see all available commands including checklists, labels, lists, and attachments.
19
34
  Be concise and fast. Do not explain what you will do — just do it.`;
20
35
 
21
- export const DEFAULT_TASK = `When you receive a card:
22
- 1. Read the title, description, labels, and comments to understand what needs to be done.
23
- 2. If the card references other cards, use \`kanon card <id>\` to read them.
24
- 3. If you need board context (available lists, labels, members), use \`kanon board\`.
25
- 4. Execute the task. If it involves code changes, implement them, then commit and push.
26
- 5. When you start working, move the card to an "In Progress" list if one exists.
27
- 6. When done, add a brief comment summarizing what you did.
28
- 7. When the task is fully complete, move the card to a "Review" list if one exists and mark it as done.
29
- 8. If something is unclear, add a comment with your questions instead of guessing.
36
+ export const DEFAULT_TASK = `## Instructions
37
+ 1. The card details above are already complete start working immediately.
38
+ 2. If the task involves code changes, implement them, then commit and push.
39
+ 3. When done, add a brief comment summarizing what you did.
40
+ 4. Move the card to "Review" and mark it done:
41
+ \`kanon card update <id> --move "Review" --done\`
42
+ 5. If something is unclear, add a comment with your questions instead of guessing.
30
43
 
31
- Do not modify cards you were not asked to work on.
32
- Always read the full card before taking action check comments for recent feedback.`;
44
+ Do not run \`kanon board\`, \`kanon cards\`, or \`kanon help-all\` unless the task specifically requires that information.
45
+ Do not modify cards you were not asked to work on.`;
33
46
 
34
47
  export const DEFAULT_BUNDLE = `You have multiple cards to work on. Review each card below and process them.
35
48
  Use sub-agents (the Agent tool) to work on cards in parallel where possible.
@@ -40,7 +53,10 @@ For each card, follow the task instructions above.`;
40
53
  * Format card details into a readable block.
41
54
  */
42
55
  export function formatCard(card) {
43
- const parts = [`Card ID: ${card.id}`, `Title: ${card.title}`];
56
+ const parts = [`Card ID: ${card.id}`];
57
+ if (card.boardId) parts.push(`Board ID: ${card.boardId}`);
58
+ if (card.list_name) parts.push(`List: ${card.list_name}`);
59
+ parts.push(`Title: ${card.title}`);
44
60
 
45
61
  if (card.description) {
46
62
  parts.push(`Description: ${card.description}`);
@@ -60,6 +76,26 @@ export function formatCard(card) {
60
76
  }
61
77
  }
62
78
 
79
+ if (card.subcards?.length) {
80
+ parts.push(`\nSubcards:`);
81
+ for (const sc of card.subcards) {
82
+ const done = sc.is_done ? ' [done]' : '';
83
+ parts.push(`- ${sc.title} (${sc.id})${done}`);
84
+ }
85
+ }
86
+
87
+ if (card.parent_card_id) {
88
+ parts.push(`Parent card: ${card.parent_card_id}`);
89
+ }
90
+
91
+ const attachments = [];
92
+ if (card.has_sheet) attachments.push('sheet');
93
+ if (card.has_note) attachments.push('note');
94
+ if (card.has_whiteboard) attachments.push('canvas');
95
+ if (attachments.length) {
96
+ parts.push(`Attachments: ${attachments.join(', ')} (use \`kanon ${attachments[0]} ${card.id}\` to read)`);
97
+ }
98
+
63
99
  if (card.comments?.length) {
64
100
  parts.push(`\nComments (newest first):`);
65
101
  for (const c of card.comments.slice(0, 10)) {
@@ -1 +0,0 @@
1
- *,: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:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";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:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,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}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:#374151;border-radius:3px}::-webkit-scrollbar-thumb:hover{background:#4b5563}.pointer-events-none{pointer-events:none}.\!visible{visibility:visible!important}.visible{visibility:visible}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.-right-1{right:-.25rem}.-right-2{right:-.5rem}.-top-1{top:-.25rem}.left-0\.5{left:.125rem}.left-3{left:.75rem}.left-\[18px\]{left:18px}.right-2{right:.5rem}.right-3{right:.75rem}.top-0\.5{top:.125rem}.top-1\/2{top:50%}.top-8{top:2rem}.top-full{top:100%}.z-50{z-index:50}.col-span-2{grid-column:span 2 / span 2}.mx-2{margin-left:.5rem;margin-right:.5rem}.mb-0\.5{margin-bottom:.125rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-full{height:100%}.max-h-40{max-height:10rem}.max-h-48{max-height:12rem}.max-h-60{max-height:15rem}.max-h-64{max-height:16rem}.max-h-\[60vh\]{max-height:60vh}.max-h-\[70vh\]{max-height:70vh}.w-10{width:2.5rem}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-20{width:5rem}.w-3{width:.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-56{width:14rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-full{width:100%}.min-w-0{min-width:0px}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.-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))}.rotate-180{--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))}.rotate-90{--tw-rotate: 90deg;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))}.scale-110{--tw-scale-x: 1.1;--tw-scale-y: 1.1;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}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.cursor-text{cursor:text}.select-text{-webkit-user-select:text;-moz-user-select:text;user-select:text}.resize-y{resize:vertical}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-r{border-right-width:1px}.border-r-2{border-right-width:2px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-gray-600{--tw-border-opacity: 1;border-color:rgb(75 85 99 / var(--tw-border-opacity, 1))}.border-gray-700{--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity, 1))}.border-gray-700\/30{border-color:#3741514d}.border-gray-700\/50{border-color:#37415180}.border-gray-800{--tw-border-opacity: 1;border-color:rgb(31 41 55 / var(--tw-border-opacity, 1))}.border-gray-800\/50{border-color:#1f293780}.border-green-500\/30{border-color:#22c55e4d}.border-kanon-500{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.border-kanon-500\/30{border-color:#3b82f64d}.border-red-500\/30{border-color:#ef44444d}.border-red-800\/40{border-color:#991b1b66}.border-transparent{border-color:transparent}.border-yellow-500\/30{border-color:#eab3084d}.bg-black\/50{background-color:#00000080}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-cyan-500\/10{background-color:#06b6d41a}.bg-gray-500{--tw-bg-opacity: 1;background-color:rgb(107 114 128 / var(--tw-bg-opacity, 1))}.bg-gray-700{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}.bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.bg-gray-800\/20{background-color:#1f293733}.bg-gray-800\/30{background-color:#1f29374d}.bg-gray-800\/50{background-color:#1f293780}.bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity, 1))}.bg-gray-950{--tw-bg-opacity: 1;background-color:rgb(3 7 18 / var(--tw-bg-opacity, 1))}.bg-gray-950\/50{background-color:#03071280}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-green-500\/10{background-color:#22c55e1a}.bg-green-500\/20{background-color:#22c55e33}.bg-green-600\/20{background-color:#16a34a33}.bg-kanon-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-kanon-500\/10{background-color:#3b82f61a}.bg-kanon-500\/20{background-color:#3b82f633}.bg-kanon-500\/5{background-color:#3b82f60d}.bg-kanon-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.bg-orange-500{--tw-bg-opacity: 1;background-color:rgb(249 115 22 / var(--tw-bg-opacity, 1))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-red-500\/10{background-color:#ef44441a}.bg-red-500\/20{background-color:#ef444433}.bg-red-600\/20{background-color:#dc262633}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-yellow-500{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity, 1))}.bg-yellow-500\/10{background-color:#eab3081a}.bg-yellow-500\/15{background-color:#eab30826}.bg-yellow-500\/20{background-color:#eab30833}.bg-yellow-600\/20{background-color:#ca8a0433}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.p-1{padding:.25rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-3{padding-bottom:.75rem}.pl-3{padding-left:.75rem}.pr-16{padding-right:4rem}.pt-2{padding-top:.5rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-\[10px\]{font-size:10px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.italic{font-style:italic}.leading-tight{line-height:1.25}.tracking-wider{letter-spacing:.05em}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-cyan-400{--tw-text-opacity: 1;color:rgb(34 211 238 / var(--tw-text-opacity, 1))}.text-gray-100{--tw-text-opacity: 1;color:rgb(243 244 246 / var(--tw-text-opacity, 1))}.text-gray-200{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-green-400\/60{color:#4ade8099}.text-green-400\/70{color:#4ade80b3}.text-kanon-300{--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity, 1))}.text-kanon-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-kanon-400\/60{color:#60a5fa99}.text-purple-400{--tw-text-opacity: 1;color:rgb(192 132 252 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-transparent{color:transparent}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-400{--tw-text-opacity: 1;color:rgb(250 204 21 / var(--tw-text-opacity, 1))}.placeholder-gray-600::-moz-placeholder{--tw-placeholder-opacity: 1;color:rgb(75 85 99 / var(--tw-placeholder-opacity, 1))}.placeholder-gray-600::placeholder{--tw-placeholder-opacity: 1;color:rgb(75 85 99 / var(--tw-placeholder-opacity, 1))}.caret-gray-200{caret-color:#e5e7eb}.opacity-0{opacity:0}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.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-white{--tw-ring-opacity: 1;--tw-ring-color: rgb(255 255 255 / var(--tw-ring-opacity, 1))}.ring-offset-2{--tw-ring-offset-width: 2px}.ring-offset-gray-900{--tw-ring-offset-color: #111827}.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)}.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}.last\:border-0:last-child{border-width:0px}.hover\:scale-110:hover{--tw-scale-x: 1.1;--tw-scale-y: 1.1;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))}.hover\:border-gray-600\/50:hover{border-color:#4b556380}.hover\:border-kanon-500\/50:hover{border-color:#3b82f680}.hover\:bg-gray-700\/50:hover{background-color:#37415180}.hover\:bg-gray-800:hover{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-800\/30:hover{background-color:#1f29374d}.hover\:bg-gray-800\/50:hover{background-color:#1f293780}.hover\:bg-gray-800\/80:hover{background-color:#1f2937cc}.hover\:bg-green-600\/30:hover{background-color:#16a34a4d}.hover\:bg-kanon-500:hover{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.hover\:bg-kanon-500\/20:hover{background-color:#3b82f633}.hover\:bg-kanon-500\/5:hover{background-color:#3b82f60d}.hover\:bg-red-500\/10:hover{background-color:#ef44441a}.hover\:bg-red-500\/20:hover{background-color:#ef444433}.hover\:bg-red-600\/30:hover{background-color:#dc26264d}.hover\:bg-yellow-600\/30:hover{background-color:#ca8a044d}.hover\:text-gray-200:hover{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.hover\:text-gray-300:hover{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.hover\:text-gray-400:hover{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.hover\:text-green-300:hover{--tw-text-opacity: 1;color:rgb(134 239 172 / var(--tw-text-opacity, 1))}.hover\:text-kanon-300:hover{--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity, 1))}.hover\:text-kanon-400:hover{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.hover\:text-red-300:hover{--tw-text-opacity: 1;color:rgb(252 165 165 / var(--tw-text-opacity, 1))}.hover\:text-red-400:hover{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.focus\:border-kanon-500:focus{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.focus\:border-kanon-500\/50:focus{border-color:#3b82f680}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--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)}.focus\:ring-kanon-500\/30:focus{--tw-ring-color: rgb(59 130 246 / .3)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:opacity-100{opacity:1}@media(min-width:768px){.md\:block{display:block}}