myapikey 0.34.6 → 0.36.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myapikey",
3
- "version": "0.34.6",
3
+ "version": "0.36.0",
4
4
  "type": "module",
5
5
  "description": "Personal LLM API gateway & proxy — one address + one API key for all your models. Forwards OpenAI & Anthropic calls to your backends with failover and a circuit breaker. Pure passthrough, no format translation. Self-hosted (CLI + web UI).",
6
6
  "keywords": [
@@ -283,6 +283,69 @@ model.command("remove <name>").description("remove a model entirely (both format
283
283
  console.log(`Removed ${name}.`);
284
284
  });
285
285
 
286
+ model
287
+ .command("debug <name> [action] [index]")
288
+ .description("debug capture: on|off toggles recording of the last 50 actual requests/responses (failed attempts are ALWAYS recorded in a global net, last 50); show lists everything (add an index to dump one in full)")
289
+ .action(async (name: string, action = "show", indexRaw?: string) => {
290
+ const path = `/admin/models/${encodeURIComponent(name)}/debug`;
291
+ if (action === "on" || action === "off") {
292
+ const r = (await api(ctx(), "PUT", path, { enabled: action === "on" })) as { enabled: boolean };
293
+ console.log(
294
+ r.enabled
295
+ ? `Debug capture ON for ${name} — the last 50 upstream attempts (request + response) are recorded in memory; turn off to clear.`
296
+ : `Debug capture OFF for ${name}; captured content cleared (auto-recorded failures stay until they age out of the net).`,
297
+ );
298
+ return;
299
+ }
300
+ if (action !== "show") throw new Error(`Unknown action '${action}' (use on | off | show).`);
301
+ const r = (await api(ctx(), "GET", path)) as { enabled: boolean; captures: any[]; failures: any[] };
302
+ if (!r.enabled) console.log(`Debug capture is OFF for ${name} (failed calls are still auto-recorded).`);
303
+ const isFail = (c: any) => c.status >= 400 || c.status === 0;
304
+ // One newest-first timeline; a failure made while the switch was on sits
305
+ // in both server buffers — show it once, from the net, tagged AUTO.
306
+ const rows = [
307
+ ...(r.failures ?? []).map((c) => ({ c, auto: true })),
308
+ ...(r.captures ?? []).filter((c) => !isFail(c)).map((c) => ({ c, auto: false })),
309
+ ].sort((a, b) => b.c.ts - a.c.ts);
310
+ if (indexRaw !== undefined) {
311
+ const row = rows[Number(indexRaw) - 1];
312
+ if (!row) throw new Error(`No capture #${indexRaw} (list is newest-first, ${rows.length} recorded).`);
313
+ const c = row.c;
314
+ console.log(`#${Number(indexRaw)}${row.auto ? " (auto)" : ""} ${new Date(c.ts).toLocaleTimeString("en-GB", { hour12: false })} ${c.provider} [${c.format}] status=${c.status} ${c.ms}ms${c.upstreamModel ? ` model=${c.upstreamModel}` : ""}${c.error ? ` error=${c.error}` : ""}${c.truncated ? " (truncated)" : ""}`);
315
+ console.log("\n--- request (forwarded verbatim) ---");
316
+ console.log(prettyJson(c.request));
317
+ if (c.response !== undefined) {
318
+ console.log("\n--- response (upstream) ---");
319
+ console.log(prettyJson(c.response));
320
+ }
321
+ return;
322
+ }
323
+ if (!rows.length) return console.log("Nothing recorded yet — call the model, then `show` again (or pass an index to dump one).");
324
+ console.log(`${name}: ${rows.length} recorded (newest first; AUTO = failed attempt, always recorded)`);
325
+ for (let i = 0; i < rows.length; i++) {
326
+ const { c, auto } = rows[i];
327
+ console.log(
328
+ ` #${i + 1}${auto ? " AUTO" : " "} ${new Date(c.ts).toLocaleTimeString("en-GB", { hour12: false })} ${c.provider} [${c.format}] status=${c.status} ${c.ms}ms ${fmtBytes(c.request)} req / ${fmtBytes(c.response ?? "")} resp${c.upstreamModel ? ` model=${c.upstreamModel}` : ""}${c.error ? ` ${c.error}` : ""}`,
329
+ );
330
+ }
331
+ console.log("\nDump one: myapikey model debug <name> show <index>");
332
+ });
333
+
334
+ /** Pretty-print when the text parses as JSON (a forwarded request body, a JSON
335
+ * response); raw text (e.g. an SSE stream) goes through untouched. */
336
+ function prettyJson(s: string): string {
337
+ try {
338
+ return JSON.stringify(JSON.parse(s), null, 2);
339
+ } catch {
340
+ return s;
341
+ }
342
+ }
343
+
344
+ function fmtBytes(s: string): string {
345
+ const n = Buffer.byteLength(s, "utf8");
346
+ return n >= 1024 ? `${(n / 1024).toFixed(1)}KB` : `${n}B`;
347
+ }
348
+
286
349
  // ---------------------------------------------------------------------------
287
350
  // call
288
351
  // ---------------------------------------------------------------------------
@@ -163,6 +163,7 @@ function projectModel(name: string, e: ModelEntry, byId: Map<string, Provider>)
163
163
  anthropic: proj(e.anthropic),
164
164
  responses: proj(e.responses),
165
165
  paceRpm: e.paceRpm ?? 0,
166
+ debugCapture: e.debugCapture === true,
166
167
  };
167
168
  }
168
169
 
@@ -566,6 +567,7 @@ export function adminApi(store: Store, auth: MiddlewareHandler, openai: Hono, an
566
567
  const cfg = store.get();
567
568
  if (!cfg.models[name]) return c.json({ error: { message: "model not found" } }, 404);
568
569
  if (cfg.models[next]) return c.json({ error: { message: `model already exists: ${next}` } }, 409);
570
+ store.clearCaptures(name); // captured bodies keyed by the old name are unreachable now
569
571
  await store.update((d) => {
570
572
  const rebuilt: typeof d.models = {};
571
573
  for (const [k, v] of Object.entries(d.models)) rebuilt[k === name ? next : k] = v;
@@ -692,12 +694,56 @@ export function adminApi(store: Store, auth: MiddlewareHandler, openai: Hono, an
692
694
  errStatus = 404;
693
695
  return;
694
696
  }
695
- if (rpm) entry.paceRpm = rpm;
696
- else delete entry.paceRpm;
697
- });
698
- if (errStatus === 404) return c.json({ error: { message: "model not found" } }, 404);
699
- return c.json({ ok: true, paceRpm: rpm ?? 0 });
697
+ if (rpm) entry.paceRpm = rpm;
698
+ else delete entry.paceRpm;
699
+ });
700
+ if (errStatus === 404) return c.json({ error: { message: "model not found" } }, 404);
701
+ return c.json({ ok: true, paceRpm: rpm ?? 0 });
702
+ });
703
+
704
+ // Debug capture switch (pure debugging aid — see ModelEntry.debugCapture).
705
+ // ON: dispatch records every upstream attempt for this model (exact forwarded
706
+ // request + response bodies) into an in-memory ring buffer, last 50. OFF: the
707
+ // buffer is cleared immediately — captured conversations shouldn't outlive the
708
+ // debugging session. Independently, FAILED attempts always land in a global
709
+ // in-memory net (last 50 across all models) so an error can be inspected even
710
+ // when the switch was never on; `failures` here is that net filtered to this
711
+ // model. The switch is config state (persists in data.json); the captured
712
+ // bodies do not (in-memory only, restart clears them too).
713
+ app.get("/models/:name/debug", (c) => {
714
+ const name = c.req.param("name");
715
+ if (!store.get().models[name]) return c.json({ error: { message: "model not found" } }, 404);
716
+ return c.json({ enabled: store.isDebug(name), captures: store.getCaptures(name), failures: store.getFailCaptures(name) });
717
+ });
718
+
719
+ app.put("/models/:name/debug", async (c) => {
720
+ const name = c.req.param("name");
721
+ const body = await readJson<{ enabled?: unknown }>(c.req.raw);
722
+ const enabled = body?.enabled === true;
723
+ let errStatus = 0;
724
+ await store.update((d) => {
725
+ const entry = d.models[name];
726
+ if (!entry) {
727
+ errStatus = 404;
728
+ return;
729
+ }
730
+ if (enabled) entry.debugCapture = true;
731
+ else delete entry.debugCapture;
700
732
  });
733
+ if (errStatus === 404) return c.json({ error: { message: "model not found" } }, 404);
734
+ if (!enabled) store.clearCaptures(name);
735
+ return c.json({ ok: true, enabled });
736
+ });
737
+
738
+ // Manual scrub of this model's rows in the always-on failure net (the switch
739
+ // only governs the per-model buffer; failures age out of the global ring on
740
+ // their own — this drops them now).
741
+ app.delete("/models/:name/debug/fails", (c) => {
742
+ const name = c.req.param("name");
743
+ if (!store.get().models[name]) return c.json({ error: { message: "model not found" } }, 404);
744
+ store.clearFailCaptures(name);
745
+ return c.json({ ok: true });
746
+ });
701
747
 
702
748
  // Set (or clear) the upstream-model mapping for ONE chain slot (addressed by
703
749
  // `index`). An empty `model` clears it (back to identity — send the public
@@ -904,6 +950,7 @@ export function adminApi(store: Store, auth: MiddlewareHandler, openai: Hono, an
904
950
  await store.update((d) => {
905
951
  delete d.models[name];
906
952
  });
953
+ store.clearCaptures(name);
907
954
  return c.json({ ok: true });
908
955
  });
909
956
 
@@ -1,7 +1,7 @@
1
1
  import { Hono, type Context, type MiddlewareHandler } from "hono";
2
2
  import { trimBase } from "../shared/config";
3
- import type { Format, Provider, RouteKey, Usage } from "../shared/types";
4
- import type { Store } from "./store";
3
+ import type { DebugCapture, Format, Provider, RouteKey, Usage } from "../shared/types";
4
+ import { CAPTURE_BODY_MAX, type Store } from "./store";
5
5
  import { UsageCollector } from "./tokens";
6
6
 
7
7
  /** HTTP statuses that should trigger failover to the next provider. 401/403
@@ -342,6 +342,10 @@ function observedBody(
342
342
  /** The original request's `messages`, used only to estimate prompt tokens
343
343
  * on the openai-chat-stream fallback path (see tokens.ts). */
344
344
  requestMessages?: unknown;
345
+ /** Called with each decoded text chunk AS IT FLOWS — the debug capture's
346
+ * tee point (the proxy keeps forwarding bytes verbatim regardless).
347
+ * Optional: absent = zero capture overhead. */
348
+ onText?: (txt: string) => void;
345
349
  onSettle: (info: SettleInfo) => void;
346
350
  },
347
351
  ): ReadableStream<Uint8Array> {
@@ -385,6 +389,7 @@ function observedBody(
385
389
  }
386
390
  const txt = dec.decode(value, { stream: true });
387
391
  usage.feed(txt, { stream: opts.stream, key: opts.key });
392
+ opts.onText?.(txt);
388
393
  if (!terminal && markers.length) {
389
394
  const win = tail + txt;
390
395
  if (markers.some((m) => win.includes(m))) terminal = true;
@@ -608,15 +613,47 @@ export function proxyApi(
608
613
  // Count this attempt toward the source's RPM window — but not for a pinned
609
614
  // probe, which (like circuit state) takes no routing side-effects.
610
615
  if (pinIndex == null) store.recordDispatch(provider.id);
616
+ // Debug capture: EVERY upstream attempt is offered to the store —
617
+ // failed attempts always land in the global failure net (the safety
618
+ // net for after-the-fact debugging), everything lands in the model's
619
+ // own buffer while its switch is on. The row carries the exact
620
+ // forwarded body (this slot's model rewrite + thinking injection are
621
+ // already applied) and the response as it flowed. UI probes are
622
+ // excluded (not real conversations). One entry per attempt: a
623
+ // failover chain writes several, each showing what THAT source
624
+ // actually received.
625
+ const attemptStart = Date.now();
626
+ const reqText = JSON.stringify(body);
627
+ const capture = (status: number, response: string | undefined, truncated: boolean, error?: string) => {
628
+ if (isProbe) return;
629
+ const row: DebugCapture = {
630
+ ts: Date.now(),
631
+ model,
632
+ provider: provider.name,
633
+ providerId: provider.id,
634
+ format: wire,
635
+ status,
636
+ ms: Date.now() - attemptStart,
637
+ stream,
638
+ request: reqText,
639
+ ...(upstreamModel ? { upstreamModel } : {}),
640
+ ...(think ? { thinking: think } : {}),
641
+ ...(response ? { response } : {}),
642
+ ...(truncated ? { truncated: true } : {}),
643
+ ...(error ? { error } : {}),
644
+ };
645
+ store.pushCapture(model, row);
646
+ };
611
647
  let upstream: Response;
612
648
  try {
613
649
  upstream = await fetch(upstreamTarget(provider, key).url, {
614
650
  method: "POST",
615
651
  headers: upstreamHeaders(provider, wire, clientVersion),
616
- body: JSON.stringify(body),
652
+ body: reqText,
617
653
  });
618
654
  } catch {
619
655
  // Network error / DNS / timeout → try next provider.
656
+ capture(0, undefined, false, "network error");
620
657
  lastStatus = 502;
621
658
  lastErr = "network error";
622
659
  if (pinIndex != null) break; // per-source probe: fail fast, no circuit impact.
@@ -642,20 +679,34 @@ export function proxyApi(
642
679
  // the body's end (so the row reflects the real outcome, not just the
643
680
  // headers). See observedBody() for the detection rules.
644
681
  const ttfb = Date.now() - start;
682
+ // Debug capture tee: accumulate the decoded chunks into a bounded
683
+ // string (the proxy keeps forwarding bytes verbatim regardless).
684
+ const capAcc = { text: "", truncated: false };
645
685
  const out = observedBody(upstream, {
646
686
  stream,
647
687
  key,
648
688
  requestMessages: body.messages,
689
+ onText: (txt: string) => {
690
+ const room = CAPTURE_BODY_MAX - capAcc.text.length;
691
+ if (room <= 0) {
692
+ capAcc.truncated = true;
693
+ return;
694
+ }
695
+ if (txt.length > room) capAcc.truncated = true;
696
+ capAcc.text += txt.slice(0, room);
697
+ },
649
698
  onSettle: (info) => {
650
699
  if (info.ok) {
651
700
  store.recordCircuitSuccess(provider.id);
652
701
  store.pushLog({ ts: Date.now(), model, upstreamModel, provider: provider.name, providerId: provider.id, format: wire, status: 200, ms: ttfb, stream, thinking: think, usage: info.usage });
702
+ capture(200, capAcc.text, capAcc.truncated);
653
703
  } else {
654
704
  // A pinned per-source probe takes no circuit side-effects (a manual
655
705
  // test must not trip the breaker) — mirrors the retryable branch.
656
706
  if (pinIndex == null) store.recordCircuitFailure(provider.id, info.status, info.error || "stream failed");
657
707
  if (!isProbe) rt.warn(`proxy stream failed: provider '${provider.name}' status=${info.status} (${info.error || "stream failed"})`);
658
708
  store.pushLog({ ts: Date.now(), model, upstreamModel, provider: provider.name, providerId: provider.id, format: wire, status: info.status, ms: ttfb, stream, thinking: think, error: info.error });
709
+ capture(info.status, capAcc.text, capAcc.truncated, info.error);
659
710
  }
660
711
  },
661
712
  });
@@ -667,6 +718,7 @@ export function proxyApi(
667
718
  // reason for the log (this branch never streams back to the client).
668
719
  const txt = await upstream.text().catch(() => "");
669
720
  lastErr = shortError(txt) || `HTTP ${upstream.status}`;
721
+ capture(upstream.status, txt, false, lastErr);
670
722
  if (pinIndex != null) break; // per-source probe: fail fast, no circuit impact.
671
723
  sayFailover(provider, `HTTP ${lastStatus} (${lastErr})`);
672
724
  // A 429/overloaded upstream usually carries Retry-After; honoring it
@@ -687,6 +739,7 @@ export function proxyApi(
687
739
  // error text off a CLONE so the original body still streams back.
688
740
  const errText = await upstream.clone().text().catch(() => "");
689
741
  store.pushLog({ ts: Date.now(), model, upstreamModel, provider: provider.name, providerId: provider.id, format: wire, status: upstream.status, ms: Date.now() - start, stream, thinking: think, error: shortError(errText) || `HTTP ${upstream.status}` });
742
+ capture(upstream.status, errText, false, shortError(errText) || `HTTP ${upstream.status}`);
690
743
  return passThrough(upstream, isProbe ? provider.name : undefined);
691
744
  }
692
745
 
@@ -1,7 +1,7 @@
1
1
  import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, statSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { defaultConfig, newApiKey, CONFIG_VERSION } from "../shared/config";
4
- import type { GateConfig, LogEntry, Provider } from "../shared/types";
4
+ import type { DebugCapture, GateConfig, LogEntry, Provider } from "../shared/types";
5
5
  import { createLogger, type Logger } from "./logger";
6
6
 
7
7
  /** Call-log retention: the log is bounded two ways — never older than this, and
@@ -45,6 +45,20 @@ const RPM_WINDOW_MS = 60_000;
45
45
  * time and the queue depth (at N rpm / 60s wait, at most ~N requests queue). */
46
46
  const PACE_MAX_WAIT_MS = 60_000;
47
47
 
48
+ /** Debug capture (in-memory ring buffers, never persisted). Two tiers:
49
+ * - FAIL_NET (global): the last CAPTURE_FAIL_MAX FAILED upstream attempts
50
+ * across ALL models, recorded ALWAYS (that's the safety net — errors are
51
+ * debugged after the fact, when nobody pre-armed a switch). 50 × 2 × 256KB
52
+ * ≈ 25MB worst case, typical far less (failed responses are tiny).
53
+ * - per-model switch buffer: last CAPTURE_MAX attempts of a model whose
54
+ * ModelEntry.debugCapture is on — success AND failure.
55
+ * Bodies (conversations) are sensitive: everything lives only in memory,
56
+ * never touches disk, ages out of the rings, and the switch-off / clear
57
+ * endpoints drop it on demand. */
58
+ export const CAPTURE_MAX = 50;
59
+ export const CAPTURE_FAIL_MAX = 50;
60
+ export const CAPTURE_BODY_MAX = 256 * 1024;
61
+
48
62
  /** Per-provider circuit state (in-memory, never persisted). */
49
63
  interface CircuitEntry {
50
64
  fails: number;
@@ -191,6 +205,15 @@ export class Store {
191
205
  /** Per-model even-pacing queue: model name -> epoch ms of the next free
192
206
  * release slot. In-memory, NOT persisted (resets on restart). */
193
207
  private pace = new Map<string, number>();
208
+ /** Per-model debug-capture ring buffers: model name -> last CAPTURE_MAX
209
+ * upstream attempts (oldest first) while the model's switch is on.
210
+ * In-memory, NOT persisted — bodies are conversation content, so they live
211
+ * only while the switch is on and evaporate on restart or toggle-off. */
212
+ private captures = new Map<string, DebugCapture[]>();
213
+ /** The always-on failure net: last CAPTURE_FAIL_MAX FAILED upstream attempts
214
+ * across all models (oldest first), regardless of any switch. In-memory,
215
+ * NOT persisted; entries age out as new failures push them off. */
216
+ private failCaptures: DebugCapture[] = [];
194
217
 
195
218
  constructor(dataDir: string, opts: { logger?: Logger } = {}) {
196
219
  this.dataDir = dataDir;
@@ -649,6 +672,68 @@ export class Store {
649
672
  return wait;
650
673
  }
651
674
 
675
+ // --- debug capture (per model, in-memory ring buffer; never persisted) ---
676
+
677
+ /** Whether a model's debug capture switch is on (config state, persisted). */
678
+ isDebug(model: string): boolean {
679
+ return !!this.data.models[model]?.debugCapture;
680
+ }
681
+
682
+ /** Record one upstream attempt. Routing: FAILED attempts (4xx/5xx/network)
683
+ * always land in the global failure net (the safety net for after-the-fact
684
+ * debugging); EVERYTHING lands in the model's own buffer while its switch
685
+ * is on. When the switch is off, successes are dropped. Bodies are capped
686
+ * at CAPTURE_BODY_MAX chars each (flagging `truncated`). */
687
+ pushCapture(model: string, entry: DebugCapture): void {
688
+ const e = this.capped({ ...entry, model }); // row's model = the routing key
689
+ const failed = e.status >= 400 || e.status === 0;
690
+ if (failed) {
691
+ this.failCaptures.push(e);
692
+ if (this.failCaptures.length > CAPTURE_FAIL_MAX) this.failCaptures.splice(0, this.failCaptures.length - CAPTURE_FAIL_MAX);
693
+ }
694
+ if (!this.isDebug(model)) return;
695
+ let arr = this.captures.get(model);
696
+ if (!arr) this.captures.set(model, (arr = []));
697
+ arr.push(e);
698
+ if (arr.length > CAPTURE_MAX) arr.splice(0, arr.length - CAPTURE_MAX);
699
+ }
700
+
701
+ /** Copy of an entry with both bodies clamped to CAPTURE_BODY_MAX. */
702
+ private capped(entry: DebugCapture): DebugCapture {
703
+ const e = { ...entry };
704
+ if (e.request.length > CAPTURE_BODY_MAX) {
705
+ e.request = e.request.slice(0, CAPTURE_BODY_MAX);
706
+ e.truncated = true;
707
+ }
708
+ if (e.response !== undefined && e.response.length > CAPTURE_BODY_MAX) {
709
+ e.response = e.response.slice(0, CAPTURE_BODY_MAX);
710
+ e.truncated = true;
711
+ }
712
+ return e;
713
+ }
714
+
715
+ /** The model's switch-captured attempts, newest first. */
716
+ getCaptures(model: string): DebugCapture[] {
717
+ const arr = this.captures.get(model);
718
+ return arr ? [...arr].reverse() : [];
719
+ }
720
+
721
+ /** The model's always-recorded failed attempts (from the global net),
722
+ * newest first. */
723
+ getFailCaptures(model: string): DebugCapture[] {
724
+ return this.failCaptures.filter((c) => c.model === model).reverse();
725
+ }
726
+
727
+ /** Drop a model's switch-capture buffer (toggle-off, model delete/rename). */
728
+ clearCaptures(model: string): void {
729
+ this.captures.delete(model);
730
+ }
731
+
732
+ /** Scrub a model's entries from the global failure net (manual clear). */
733
+ clearFailCaptures(model: string): void {
734
+ this.failCaptures = this.failCaptures.filter((c) => c.model !== model);
735
+ }
736
+
652
737
  /** Snapshot of every configured provider's circuit state for GET /admin/circuit.
653
738
  * Healthy providers appear as state "open"; a provider deleted while cooling
654
739
  * simply drops out (we iterate the live config, not the map). */
@@ -100,6 +100,14 @@ export interface ModelEntry {
100
100
  * independent of Provider.rpm (that one skips a busy source and fails over);
101
101
  * this one paces the model across all three route slots. Absent = unlimited. */
102
102
  paceRpm?: number;
103
+ /** Debug capture (pure debugging aid, never on by default). When true, dispatch
104
+ * records every UPSTREAM ATTEMPT for this model — the exact forwarded request
105
+ * body and the response body — into an in-memory ring buffer (last 50, see
106
+ * Store.pushCapture). Turning it off clears the buffer; restarts clear it too.
107
+ * Independently of this switch, FAILED attempts always land in a global
108
+ * in-memory net (last 50 across all models) so an error can be inspected
109
+ * after the fact. Nothing here ever touches logs.jsonl. */
110
+ debugCapture?: boolean;
103
111
  }
104
112
 
105
113
  export interface Account {
@@ -181,3 +189,38 @@ export interface LogEntry {
181
189
  cooldownMs?: number;
182
190
  fails?: number;
183
191
  }
192
+
193
+ /** One debug-captured upstream attempt (in-memory ring buffer ONLY — never
194
+ * persisted to logs.jsonl or data.json; see ModelEntry.debugCapture). One
195
+ * client call that fails over produces several entries, one per attempt,
196
+ * because each attempt's forwarded body can differ (per-slot model rewrite +
197
+ * thinking injection). Bodies are captured VERBATIM: `request` is the exact
198
+ * JSON string sent upstream, `response` the upstream body as it flowed (raw
199
+ * SSE text for streams). Headers are never captured — provider api keys stay
200
+ * out of the buffer. */
201
+ export interface DebugCapture {
202
+ ts: number;
203
+ model: string;
204
+ provider: string;
205
+ providerId: string;
206
+ format: Format;
207
+ /** The upstream model name actually sent this attempt (post per-slot
208
+ * rewrite). Absent when the public name went through verbatim. */
209
+ upstreamModel?: string;
210
+ /** Upstream HTTP status (0 = network error / never reached). */
211
+ status: number;
212
+ /** Latency of THIS attempt (ms). */
213
+ ms: number;
214
+ stream: boolean;
215
+ /** The exact forwarded request body (JSON text). */
216
+ request: string;
217
+ /** The upstream response body as it flowed. Absent when nothing was read
218
+ * (network error) or the client cancelled before the body flowed. */
219
+ response?: string;
220
+ /** True when a body was cut at the capture size cap. */
221
+ truncated?: boolean;
222
+ /** Short failure reason (mirrors LogEntry.error). */
223
+ error?: string;
224
+ /** The thinking level this attempt ran with (same shape as LogEntry.thinking). */
225
+ thinking?: { value: string; from: "client" | "default" };
226
+ }
@@ -0,0 +1 @@
1
+ @keyframes row-flash-35cb6f16{0%{background-color:color-mix(in oklab,var(--primary) 16%,transparent)}to{background-color:transparent}}.row-flash[data-v-35cb6f16]{animation:row-flash-35cb6f16 1.4s ease-out}/*! tailwindcss v4.3.3 | 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-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--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-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction: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-outline-style:solid;--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;--tw-ease:initial;--tw-animation-delay:0s;--tw-animation-direction:normal;--tw-animation-duration:initial;--tw-animation-fill-mode:none;--tw-animation-iteration-count:1;--tw-enter-blur:0;--tw-enter-opacity:1;--tw-enter-rotate:0;--tw-enter-scale:1;--tw-enter-translate-x:0;--tw-enter-translate-y:0;--tw-exit-blur:0;--tw-exit-opacity:1;--tw-exit-rotate:0;--tw-exit-scale:1;--tw-exit-translate-x:0;--tw-exit-translate-y:0}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, 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-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-orange-600:oklch(64.6% .222 41.116);--color-orange-700:oklch(55.3% .195 38.402);--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-amber-600:oklch(66.6% .179 58.318);--color-amber-700:oklch(55.5% .163 48.998);--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-700:oklch(50.8% .118 165.612);--color-teal-400:oklch(77.7% .152 181.912);--color-teal-500:oklch(70.4% .14 182.503);--color-teal-600:oklch(60% .118 184.704);--color-cyan-400:oklch(78.9% .154 211.53);--color-cyan-500:oklch(71.5% .143 215.221);--color-cyan-600:oklch(60.9% .126 221.723);--color-sky-400:oklch(74.6% .16 232.661);--color-sky-500:oklch(68.5% .169 237.323);--color-sky-600:oklch(58.8% .158 241.966);--color-blue-500:oklch(62.3% .214 259.815);--color-indigo-400:oklch(67.3% .182 276.935);--color-indigo-500:oklch(58.5% .233 277.117);--color-indigo-600:oklch(51.1% .262 276.966);--color-violet-300:oklch(81.1% .111 293.571);--color-violet-400:oklch(70.2% .183 293.541);--color-violet-500:oklch(60.6% .25 292.717);--color-violet-600:oklch(54.1% .281 293.009);--color-violet-700:oklch(49.1% .27 292.581);--color-fuchsia-400:oklch(74% .238 322.16);--color-fuchsia-500:oklch(66.7% .295 322.15);--color-fuchsia-600:oklch(59.1% .293 322.896);--color-pink-400:oklch(71.8% .202 349.761);--color-pink-500:oklch(65.6% .241 354.308);--color-pink-600:oklch(59.2% .249 .584);--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-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-md:28rem;--container-lg:32rem;--container-xl:36rem;--container-3xl:48rem;--container-5xl:64rem;--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 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--tracking-tight:-.025em;--tracking-wide:.025em;--leading-tight:1.25;--leading-relaxed:1.625;--ease-in:cubic-bezier(.4, 0, 1, 1);--ease-out:cubic-bezier(0, 0, .2, 1);--animate-spin:spin 1s linear infinite;--animate-ping:ping 1s cubic-bezier(0, 0, .2, 1) infinite;--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,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, 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:where(:not(iframe)){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}*{border-color:hsl(var(--border))}body{background-color:hsl(var(--background));color:hsl(var(--foreground));-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-feature-settings:"rlig" 1,"calt" 1}}@layer components;@layer utilities{.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.inset-x-0{inset-inline:0}.top-0{top:0}.top-1{top:var(--spacing)}.top-1\/2{top:50%}.top-4{top:calc(var(--spacing) * 4)}.top-\[50\%\]{top:50%}.right-1{right:var(--spacing)}.right-2{right:calc(var(--spacing) * 2)}.right-4{right:calc(var(--spacing) * 4)}.bottom-0{bottom:0}.bottom-full{bottom:100%}.left-1\/2{left:50%}.left-2{left:calc(var(--spacing) * 2)}.left-2\.5{left:calc(var(--spacing) * 2.5)}.left-\[50\%\]{left:50%}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.z-\[60\]{z-index:60}.-mx-1{margin-inline:calc(var(--spacing) * -1)}.mx-auto{margin-inline:auto}.my-1{margin-block:var(--spacing)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-4{margin-top:calc(var(--spacing) * 4)}.-mr-1{margin-right:calc(var(--spacing) * -1)}.mb-1{margin-bottom:var(--spacing)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.ml-1\.5{margin-left:calc(var(--spacing) * 1.5)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-flex{display:inline-flex}.table{display:table}.size-3{width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.size-6{width:calc(var(--spacing) * 6);height:calc(var(--spacing) * 6)}.size-8{width:calc(var(--spacing) * 8);height:calc(var(--spacing) * 8)}.size-9{width:calc(var(--spacing) * 9);height:calc(var(--spacing) * 9)}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-2\.5{height:calc(var(--spacing) * 2.5)}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.h-11{height:calc(var(--spacing) * 11)}.h-14{height:calc(var(--spacing) * 14)}.h-40{height:calc(var(--spacing) * 40)}.h-full{height:100%}.h-px{height:1px}.max-h-56{max-height:calc(var(--spacing) * 56)}.max-h-72{max-height:calc(var(--spacing) * 72)}.max-h-80{max-height:calc(var(--spacing) * 80)}.max-h-96{max-height:calc(var(--spacing) * 96)}.max-h-\[90vh\]{max-height:90vh}.min-h-0{min-height:0}.min-h-screen{min-height:100vh}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-1\/3{width:33.3333%}.w-2{width:calc(var(--spacing) * 2)}.w-2\.5{width:calc(var(--spacing) * 2.5)}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-6{width:calc(var(--spacing) * 6)}.w-7{width:calc(var(--spacing) * 7)}.w-8{width:calc(var(--spacing) * 8)}.w-9{width:calc(var(--spacing) * 9)}.w-11{width:calc(var(--spacing) * 11)}.w-12{width:calc(var(--spacing) * 12)}.w-28{width:calc(var(--spacing) * 28)}.w-32{width:calc(var(--spacing) * 32)}.w-36{width:calc(var(--spacing) * 36)}.w-72{width:calc(var(--spacing) * 72)}.w-\[128px\]{width:128px}.w-\[168px\]{width:168px}.w-auto{width:auto}.w-full{width:100%}.w-px{width:1px}.max-w-3xl{max-width:var(--container-3xl)}.max-w-5xl{max-width:var(--container-5xl)}.max-w-40{max-width:calc(var(--spacing) * 40)}.max-w-\[120px\]{max-width:120px}.max-w-\[160px\]{max-width:160px}.max-w-\[300px\]{max-width:300px}.max-w-full{max-width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.max-w-xl{max-width:var(--container-xl)}.min-w-0{min-width:0}.min-w-4{min-width:calc(var(--spacing) * 4)}.min-w-\[8rem\]{min-width:8rem}.min-w-\[180px\]{min-width:180px}.min-w-\[var\(--reka-select-trigger-width\)\]{min-width:var(--reka-select-trigger-width)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.caption-bottom{caption-side:bottom}.-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-\[-50\%\]{--tw-translate-x:-50%;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-2{--tw-translate-y:calc(var(--spacing) * 2);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-\[-50\%\]{--tw-translate-y:-50%;translate:var(--tw-translate-x) var(--tw-translate-y)}.rotate-180{rotate:180deg}.animate-ping{animation:var(--animate-ping)}.animate-spin{animation:var(--animate-spin)}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.resize{resize:both}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-\[3\.5rem_1fr\]{grid-template-columns:3.5rem 1fr}.grid-cols-\[auto_1fr\]{grid-template-columns:auto 1fr}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.items-stretch{align-items:stretch}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0{gap:0}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.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)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-px{gap:1px}:where(.space-y-0>:not(:last-child)){--tw-space-y-reverse:0;margin-block:0}: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(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * 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-2\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2.5) * 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(.space-y-3\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-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)))}.gap-x-1\.5{column-gap:calc(var(--spacing) * 1.5)}.gap-x-2{column-gap:calc(var(--spacing) * 2)}.gap-x-4{column-gap:calc(var(--spacing) * 4)}.gap-y-0\.5{row-gap:calc(var(--spacing) * .5)}.gap-y-1{row-gap:var(--spacing)}.gap-y-1\.5{row-gap:calc(var(--spacing) * 1.5)}: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)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-\[4px\]{border-radius:4px}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.rounded-xl{border-radius:calc(var(--radius) + 4px)}.rounded-t-sm{border-top-left-radius:calc(var(--radius) - 4px);border-top-right-radius:calc(var(--radius) - 4px)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-amber-500\/30{border-color:#f99c004d}@supports (color:color-mix(in lab,red,red)){.border-amber-500\/30{border-color:color-mix(in oklab,var(--color-amber-500) 30%,transparent)}}.border-border{border-color:hsl(var(--border))}.border-destructive\/40{border-color:hsl(var(--destructive))}@supports (color:color-mix(in lab,red,red)){.border-destructive\/40{border-color:color-mix(in oklab,hsl(var(--destructive)) 40%,transparent)}}.border-emerald-500\/30{border-color:#00bb7f4d}@supports (color:color-mix(in lab,red,red)){.border-emerald-500\/30{border-color:color-mix(in oklab,var(--color-emerald-500) 30%,transparent)}}.border-emerald-500\/40{border-color:#00bb7f66}@supports (color:color-mix(in lab,red,red)){.border-emerald-500\/40{border-color:color-mix(in oklab,var(--color-emerald-500) 40%,transparent)}}.border-input{border-color:hsl(var(--input))}.border-orange-500\/30{border-color:#fe6e004d}@supports (color:color-mix(in lab,red,red)){.border-orange-500\/30{border-color:color-mix(in oklab,var(--color-orange-500) 30%,transparent)}}.border-primary,.border-primary\/40{border-color:hsl(var(--primary))}@supports (color:color-mix(in lab,red,red)){.border-primary\/40{border-color:color-mix(in oklab,hsl(var(--primary)) 40%,transparent)}}.border-transparent{border-color:#0000}.border-violet-500\/30{border-color:#8d54ff4d}@supports (color:color-mix(in lab,red,red)){.border-violet-500\/30{border-color:color-mix(in oklab,var(--color-violet-500) 30%,transparent)}}.bg-accent{background-color:hsl(var(--accent))}.bg-amber-500{background-color:var(--color-amber-500)}.bg-amber-500\/5{background-color:#f99c000d}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/5{background-color:color-mix(in oklab,var(--color-amber-500) 5%,transparent)}}.bg-amber-500\/15{background-color:#f99c0026}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/15{background-color:color-mix(in oklab,var(--color-amber-500) 15%,transparent)}}.bg-background,.bg-background\/50{background-color:hsl(var(--background))}@supports (color:color-mix(in lab,red,red)){.bg-background\/50{background-color:color-mix(in oklab,hsl(var(--background)) 50%,transparent)}}.bg-background\/60{background-color:hsl(var(--background))}@supports (color:color-mix(in lab,red,red)){.bg-background\/60{background-color:color-mix(in oklab,hsl(var(--background)) 60%,transparent)}}.bg-background\/80{background-color:hsl(var(--background))}@supports (color:color-mix(in lab,red,red)){.bg-background\/80{background-color:color-mix(in oklab,hsl(var(--background)) 80%,transparent)}}.bg-black\/80{background-color:#000c}@supports (color:color-mix(in lab,red,red)){.bg-black\/80{background-color:color-mix(in oklab,var(--color-black) 80%,transparent)}}.bg-border{background-color:hsl(var(--border))}.bg-card{background-color:hsl(var(--card))}.bg-current{background-color:currentColor}.bg-cyan-500{background-color:var(--color-cyan-500)}.bg-cyan-500\/15{background-color:#00b7d726}@supports (color:color-mix(in lab,red,red)){.bg-cyan-500\/15{background-color:color-mix(in oklab,var(--color-cyan-500) 15%,transparent)}}.bg-destructive,.bg-destructive\/10{background-color:hsl(var(--destructive))}@supports (color:color-mix(in lab,red,red)){.bg-destructive\/10{background-color:color-mix(in oklab,hsl(var(--destructive)) 10%,transparent)}}.bg-destructive\/15{background-color:hsl(var(--destructive))}@supports (color:color-mix(in lab,red,red)){.bg-destructive\/15{background-color:color-mix(in oklab,hsl(var(--destructive)) 15%,transparent)}}.bg-destructive\/70{background-color:hsl(var(--destructive))}@supports (color:color-mix(in lab,red,red)){.bg-destructive\/70{background-color:color-mix(in oklab,hsl(var(--destructive)) 70%,transparent)}}.bg-emerald-500{background-color:var(--color-emerald-500)}.bg-emerald-500\/5{background-color:#00bb7f0d}@supports (color:color-mix(in lab,red,red)){.bg-emerald-500\/5{background-color:color-mix(in oklab,var(--color-emerald-500) 5%,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-500\/15{background-color:#00bb7f26}@supports (color:color-mix(in lab,red,red)){.bg-emerald-500\/15{background-color:color-mix(in oklab,var(--color-emerald-500) 15%,transparent)}}.bg-emerald-500\/70{background-color:#00bb7fb3}@supports (color:color-mix(in lab,red,red)){.bg-emerald-500\/70{background-color:color-mix(in oklab,var(--color-emerald-500) 70%,transparent)}}.bg-fuchsia-500{background-color:var(--color-fuchsia-500)}.bg-fuchsia-500\/15{background-color:#e12afb26}@supports (color:color-mix(in lab,red,red)){.bg-fuchsia-500\/15{background-color:color-mix(in oklab,var(--color-fuchsia-500) 15%,transparent)}}.bg-indigo-500{background-color:var(--color-indigo-500)}.bg-indigo-500\/15{background-color:#625fff26}@supports (color:color-mix(in lab,red,red)){.bg-indigo-500\/15{background-color:color-mix(in oklab,var(--color-indigo-500) 15%,transparent)}}.bg-muted,.bg-muted\/20{background-color:hsl(var(--muted))}@supports (color:color-mix(in lab,red,red)){.bg-muted\/20{background-color:color-mix(in oklab,hsl(var(--muted)) 20%,transparent)}}.bg-muted\/30{background-color:hsl(var(--muted))}@supports (color:color-mix(in lab,red,red)){.bg-muted\/30{background-color:color-mix(in oklab,hsl(var(--muted)) 30%,transparent)}}.bg-muted\/40{background-color:hsl(var(--muted))}@supports (color:color-mix(in lab,red,red)){.bg-muted\/40{background-color:color-mix(in oklab,hsl(var(--muted)) 40%,transparent)}}.bg-muted\/50{background-color:hsl(var(--muted))}@supports (color:color-mix(in lab,red,red)){.bg-muted\/50{background-color:color-mix(in oklab,hsl(var(--muted)) 50%,transparent)}}.bg-muted\/60{background-color:hsl(var(--muted))}@supports (color:color-mix(in lab,red,red)){.bg-muted\/60{background-color:color-mix(in oklab,hsl(var(--muted)) 60%,transparent)}}.bg-orange-500{background-color:var(--color-orange-500)}.bg-orange-500\/5{background-color:#fe6e000d}@supports (color:color-mix(in lab,red,red)){.bg-orange-500\/5{background-color:color-mix(in oklab,var(--color-orange-500) 5%,transparent)}}.bg-orange-500\/15{background-color:#fe6e0026}@supports (color:color-mix(in lab,red,red)){.bg-orange-500\/15{background-color:color-mix(in oklab,var(--color-orange-500) 15%,transparent)}}.bg-pink-500{background-color:var(--color-pink-500)}.bg-pink-500\/15{background-color:#f6339a26}@supports (color:color-mix(in lab,red,red)){.bg-pink-500\/15{background-color:color-mix(in oklab,var(--color-pink-500) 15%,transparent)}}.bg-popover,.bg-popover\/95{background-color:hsl(var(--popover))}@supports (color:color-mix(in lab,red,red)){.bg-popover\/95{background-color:color-mix(in oklab,hsl(var(--popover)) 95%,transparent)}}.bg-primary,.bg-primary\/5{background-color:hsl(var(--primary))}@supports (color:color-mix(in lab,red,red)){.bg-primary\/5{background-color:color-mix(in oklab,hsl(var(--primary)) 5%,transparent)}}.bg-primary\/15{background-color:hsl(var(--primary))}@supports (color:color-mix(in lab,red,red)){.bg-primary\/15{background-color:color-mix(in oklab,hsl(var(--primary)) 15%,transparent)}}.bg-primary\/50{background-color:hsl(var(--primary))}@supports (color:color-mix(in lab,red,red)){.bg-primary\/50{background-color:color-mix(in oklab,hsl(var(--primary)) 50%,transparent)}}.bg-rose-500{background-color:var(--color-rose-500)}.bg-rose-500\/15{background-color:#ff235726}@supports (color:color-mix(in lab,red,red)){.bg-rose-500\/15{background-color:color-mix(in oklab,var(--color-rose-500) 15%,transparent)}}.bg-secondary{background-color:hsl(var(--secondary))}.bg-sky-500{background-color:var(--color-sky-500)}.bg-sky-500\/15{background-color:#00a5ef26}@supports (color:color-mix(in lab,red,red)){.bg-sky-500\/15{background-color:color-mix(in oklab,var(--color-sky-500) 15%,transparent)}}.bg-teal-500{background-color:var(--color-teal-500)}.bg-teal-500\/15{background-color:#00baa726}@supports (color:color-mix(in lab,red,red)){.bg-teal-500\/15{background-color:color-mix(in oklab,var(--color-teal-500) 15%,transparent)}}.bg-transparent{background-color:#0000}.bg-violet-500{background-color:var(--color-violet-500)}.bg-violet-500\/5{background-color:#8d54ff0d}@supports (color:color-mix(in lab,red,red)){.bg-violet-500\/5{background-color:color-mix(in oklab,var(--color-violet-500) 5%,transparent)}}.bg-violet-500\/15{background-color:#8d54ff26}@supports (color:color-mix(in lab,red,red)){.bg-violet-500\/15{background-color:color-mix(in oklab,var(--color-violet-500) 15%,transparent)}}.p-0\.5{padding:calc(var(--spacing) * .5)}.p-1{padding:var(--spacing)}.p-1\.5{padding:calc(var(--spacing) * 1.5)}.p-2{padding:calc(var(--spacing) * 2)}.p-2\.5{padding:calc(var(--spacing) * 2.5)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.px-0\.5{padding-inline:calc(var(--spacing) * .5)}.px-1{padding-inline:var(--spacing)}.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-4{padding-inline:calc(var(--spacing) * 4)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0{padding-block:0}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.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)}.py-6{padding-block:calc(var(--spacing) * 6)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-10{padding-block:calc(var(--spacing) * 10)}.py-12{padding-block:calc(var(--spacing) * 12)}.pt-0{padding-top:0}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-9{padding-top:calc(var(--spacing) * 9)}.pr-1{padding-right:var(--spacing)}.pr-1\.5{padding-right:calc(var(--spacing) * 1.5)}.pr-8{padding-right:calc(var(--spacing) * 8)}.pr-9{padding-right:calc(var(--spacing) * 9)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pl-2{padding-left:calc(var(--spacing) * 2)}.pl-2\.5{padding-left:calc(var(--spacing) * 2.5)}.pl-6{padding-left:calc(var(--spacing) * 6)}.pl-7{padding-left:calc(var(--spacing) * 7)}.pl-8{padding-left:calc(var(--spacing) * 8)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.align-middle{vertical-align:middle}.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-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--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}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.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-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-accent-foreground{color:hsl(var(--accent-foreground))}.text-amber-600{color:var(--color-amber-600)}.text-amber-700{color:var(--color-amber-700)}.text-blue-500{color:var(--color-blue-500)}.text-card-foreground{color:hsl(var(--card-foreground))}.text-current{color:currentColor}.text-cyan-600{color:var(--color-cyan-600)}.text-destructive{color:hsl(var(--destructive))}.text-destructive-foreground{color:hsl(var(--destructive-foreground))}.text-emerald-500{color:var(--color-emerald-500)}.text-emerald-600{color:var(--color-emerald-600)}.text-emerald-700{color:var(--color-emerald-700)}.text-foreground,.text-foreground\/80{color:hsl(var(--foreground))}@supports (color:color-mix(in lab,red,red)){.text-foreground\/80{color:color-mix(in oklab,hsl(var(--foreground)) 80%,transparent)}}.text-fuchsia-600{color:var(--color-fuchsia-600)}.text-indigo-600{color:var(--color-indigo-600)}.text-muted-foreground,.text-muted-foreground\/40{color:hsl(var(--muted-foreground))}@supports (color:color-mix(in lab,red,red)){.text-muted-foreground\/40{color:color-mix(in oklab,hsl(var(--muted-foreground)) 40%,transparent)}}.text-muted-foreground\/50{color:hsl(var(--muted-foreground))}@supports (color:color-mix(in lab,red,red)){.text-muted-foreground\/50{color:color-mix(in oklab,hsl(var(--muted-foreground)) 50%,transparent)}}.text-muted-foreground\/60{color:hsl(var(--muted-foreground))}@supports (color:color-mix(in lab,red,red)){.text-muted-foreground\/60{color:color-mix(in oklab,hsl(var(--muted-foreground)) 60%,transparent)}}.text-muted-foreground\/70{color:hsl(var(--muted-foreground))}@supports (color:color-mix(in lab,red,red)){.text-muted-foreground\/70{color:color-mix(in oklab,hsl(var(--muted-foreground)) 70%,transparent)}}.text-orange-600{color:var(--color-orange-600)}.text-orange-700{color:var(--color-orange-700)}.text-pink-600{color:var(--color-pink-600)}.text-popover-foreground{color:hsl(var(--popover-foreground))}.text-primary{color:hsl(var(--primary))}.text-primary-foreground{color:hsl(var(--primary-foreground))}.text-red-500{color:var(--color-red-500)}.text-rose-600{color:var(--color-rose-600)}.text-secondary-foreground{color:hsl(var(--secondary-foreground))}.text-sky-600{color:var(--color-sky-600)}.text-teal-600{color:var(--color-teal-600)}.text-violet-600{color:var(--color-violet-600)}.text-violet-700{color:var(--color-violet-700)}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.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,)}.underline-offset-4{text-underline-offset:4px}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px 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)}.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)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px 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)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px 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)}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-0{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-primary\/60{--tw-ring-color:hsl(var(--primary))}@supports (color:color-mix(in lab,red,red)){.ring-primary\/60{--tw-ring-color:color-mix(in oklab, hsl(var(--primary)) 60%, transparent)}}.ring-offset-background{--tw-ring-offset-color:hsl(var(--background))}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.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{--tw-backdrop-blur:blur(8px);-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,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.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))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.ease-in{--tw-ease:var(--ease-in);transition-timing-function:var(--ease-in)}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.running{animation-play-state:running}@media (hover:hover){.group-hover\:block:is(:where(.group):hover *){display:block}.group-hover\:bg-destructive:is(:where(.group):hover *){background-color:hsl(var(--destructive))}.group-hover\:bg-emerald-500:is(:where(.group):hover *){background-color:var(--color-emerald-500)}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.placeholder\:text-muted-foreground::placeholder{color:hsl(var(--muted-foreground))}.focus-within\:opacity-100:focus-within{opacity:1}@media (hover:hover){.hover\:bg-accent:hover{background-color:hsl(var(--accent))}.hover\:bg-amber-500\/10:hover{background-color:#f99c001a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-amber-500\/10:hover{background-color:color-mix(in oklab,var(--color-amber-500) 10%,transparent)}}.hover\:bg-destructive\/10:hover{background-color:hsl(var(--destructive))}@supports (color:color-mix(in lab,red,red)){.hover\:bg-destructive\/10:hover{background-color:color-mix(in oklab,hsl(var(--destructive)) 10%,transparent)}}.hover\:bg-destructive\/90:hover{background-color:hsl(var(--destructive))}@supports (color:color-mix(in lab,red,red)){.hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab,hsl(var(--destructive)) 90%,transparent)}}.hover\:bg-muted:hover,.hover\:bg-muted\/30:hover{background-color:hsl(var(--muted))}@supports (color:color-mix(in lab,red,red)){.hover\:bg-muted\/30:hover{background-color:color-mix(in oklab,hsl(var(--muted)) 30%,transparent)}}.hover\:bg-muted\/40:hover{background-color:hsl(var(--muted))}@supports (color:color-mix(in lab,red,red)){.hover\:bg-muted\/40:hover{background-color:color-mix(in oklab,hsl(var(--muted)) 40%,transparent)}}.hover\:bg-muted\/50:hover{background-color:hsl(var(--muted))}@supports (color:color-mix(in lab,red,red)){.hover\:bg-muted\/50:hover{background-color:color-mix(in oklab,hsl(var(--muted)) 50%,transparent)}}.hover\:bg-primary\/90:hover{background-color:hsl(var(--primary))}@supports (color:color-mix(in lab,red,red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab,hsl(var(--primary)) 90%,transparent)}}.hover\:bg-secondary\/80:hover{background-color:hsl(var(--secondary))}@supports (color:color-mix(in lab,red,red)){.hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab,hsl(var(--secondary)) 80%,transparent)}}.hover\:bg-transparent:hover{background-color:#0000}.hover\:text-accent-foreground:hover{color:hsl(var(--accent-foreground))}.hover\:text-destructive:hover{color:hsl(var(--destructive))}.hover\:text-foreground:hover{color:hsl(var(--foreground))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}}.focus\:bg-accent:focus{background-color:hsl(var(--accent))}.focus\:bg-destructive\/10:focus{background-color:hsl(var(--destructive))}@supports (color:color-mix(in lab,red,red)){.focus\:bg-destructive\/10:focus{background-color:color-mix(in oklab,hsl(var(--destructive)) 10%,transparent)}}.focus\:text-accent-foreground:focus{color:hsl(var(--accent-foreground))}.focus\:text-destructive:focus{color:hsl(var(--destructive))}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-ring:focus{--tw-ring-color:hsl(var(--ring))}.focus\:ring-offset-1:focus{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus\:ring-offset-background:focus{--tw-ring-offset-color:hsl(var(--background))}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:opacity-100:focus-visible{opacity:1}.focus-visible\:ring-1:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color:hsl(var(--ring))}.focus-visible\:ring-offset-1:focus-visible{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus-visible\:ring-offset-background:focus-visible{--tw-ring-offset-color:hsl(var(--background))}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[inset\]\:pl-8[data-inset]{padding-left:calc(var(--spacing) * 8)}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y:var(--spacing);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y:calc(2*var(--spacing)*-1)}.data-\[side\=left\]\:translate-x-1[data-side=left]{--tw-translate-x:var(--spacing);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x:calc(2*var(--spacing))}.data-\[side\=right\]\:-translate-x-1[data-side=right]{--tw-translate-x:calc(var(--spacing) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.data-\[side\=top\]\:-translate-y-1[data-side=top]{--tw-translate-y:calc(var(--spacing) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y:calc(2*var(--spacing))}.data-\[state\=active\]\:bg-background[data-state=active]{background-color:hsl(var(--background))}.data-\[state\=active\]\:text-foreground[data-state=active]{color:hsl(var(--foreground))}.data-\[state\=active\]\:shadow[data-state=active]{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px 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)}.data-\[state\=checked\]\:translate-x-4[data-state=checked]{--tw-translate-x:calc(var(--spacing) * 4);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[state\=checked\]\:bg-primary[data-state=checked]{background-color:hsl(var(--primary))}.data-\[state\=checked\]\:text-primary-foreground[data-state=checked]{color:hsl(var(--primary-foreground))}.data-\[state\=closed\]\:animate-out[data-state=closed]{animation:exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity:0}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale:.95}.data-\[state\=open\]\:animate-in[data-state=open]{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity:0}.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale:.95}.data-\[state\=selected\]\:bg-muted[data-state=selected]{background-color:hsl(var(--muted))}.data-\[state\=unchecked\]\:translate-x-0[data-state=unchecked]{--tw-translate-x:0px;translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[state\=unchecked\]\:bg-input[data-state=unchecked]{background-color:hsl(var(--input))}@media (min-width:40rem){.sm\:max-w-sm{max-width:var(--container-sm)}.sm\:translate-x-2{--tw-translate-x:calc(var(--spacing) * 2);translate:var(--tw-translate-x) var(--tw-translate-y)}.sm\:translate-y-0{--tw-translate-y:0px;translate:var(--tw-translate-x) var(--tw-translate-y)}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-\[auto_1fr_auto_1fr\]{grid-template-columns:auto 1fr auto 1fr}.sm\:flex-row{flex-direction:row}.sm\:items-end{align-items:flex-end}.sm\:justify-end{justify-content:flex-end}.sm\:rounded-lg{border-radius:var(--radius)}}@media (min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:64rem){.lg\:col-span-2{grid-column:span 2/span 2}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}}.dark\:text-amber-300:is(.dark *){color:var(--color-amber-300)}.dark\:text-amber-400:is(.dark *){color:var(--color-amber-400)}.dark\:text-cyan-400:is(.dark *){color:var(--color-cyan-400)}.dark\:text-emerald-300:is(.dark *){color:var(--color-emerald-300)}.dark\:text-emerald-400:is(.dark *){color:var(--color-emerald-400)}.dark\:text-fuchsia-400:is(.dark *){color:var(--color-fuchsia-400)}.dark\:text-indigo-400:is(.dark *){color:var(--color-indigo-400)}.dark\:text-orange-400:is(.dark *){color:var(--color-orange-400)}.dark\:text-pink-400:is(.dark *){color:var(--color-pink-400)}.dark\:text-rose-400:is(.dark *){color:var(--color-rose-400)}.dark\:text-sky-400:is(.dark *){color:var(--color-sky-400)}.dark\:text-teal-400:is(.dark *){color:var(--color-teal-400)}.dark\:text-violet-300:is(.dark *){color:var(--color-violet-300)}.dark\:text-violet-400:is(.dark *){color:var(--color-violet-400)}.\[\&_svg\]\:size-4 svg{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.\[\&_tr\]\:border-b tr{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.\[\&_tr\:last-child\]\:border-0 tr:last-child{border-style:var(--tw-border-style);border-width:0}.\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role=checkbox]){padding-right:0}.\[\&\>span\]\:line-clamp-1>span{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}}@property --tw-animation-delay{syntax:"*";inherits:false;initial-value:0s}@property --tw-animation-direction{syntax:"*";inherits:false;initial-value:normal}@property --tw-animation-duration{syntax:"*";inherits:false}@property --tw-animation-fill-mode{syntax:"*";inherits:false;initial-value:none}@property --tw-animation-iteration-count{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-blur{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-blur{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-translate-y{syntax:"*";inherits:false;initial-value:0}:root{--background:0 0% 100%;--foreground:240 10% 3.9%;--card:0 0% 100%;--card-foreground:240 10% 3.9%;--popover:0 0% 100%;--popover-foreground:240 10% 3.9%;--primary:222 89% 55%;--primary-foreground:0 0% 100%;--secondary:240 4.8% 95.9%;--secondary-foreground:240 5.9% 10%;--muted:240 4.8% 95.9%;--muted-foreground:240 3.8% 46.1%;--accent:240 4.8% 95.9%;--accent-foreground:240 5.9% 10%;--destructive:0 72% 51%;--destructive-foreground:0 0% 98%;--border:240 5.9% 90%;--input:240 5.9% 90%;--ring:222 89% 55%;--radius:.6rem}.dark{--background:222 22% 7%;--foreground:210 20% 96%;--card:222 20% 9%;--card-foreground:210 20% 96%;--popover:222 20% 9%;--popover-foreground:210 20% 96%;--primary:217 91% 60%;--primary-foreground:222 47% 11%;--secondary:222 16% 16%;--secondary-foreground:210 20% 96%;--muted:222 16% 14%;--muted-foreground:215 16% 62%;--accent:222 16% 16%;--accent-foreground:210 20% 96%;--destructive:0 62% 45%;--destructive-foreground:0 0% 98%;--border:222 14% 18%;--input:222 14% 18%;--ring:217 91% 60%}*{scrollbar-width:thin;scrollbar-color:hsl(var(--border)) transparent}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@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-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{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-outline-style{syntax:"*";inherits:false;initial-value:solid}@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}@property --tw-ease{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes ping{75%,to{opacity:0;transform:scale(2)}}@keyframes enter{0%{opacity:var(--tw-enter-opacity,1);transform:translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0)scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1))rotate(var(--tw-enter-rotate,0));filter:blur(var(--tw-enter-blur,0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity,1);transform:translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0)scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1))rotate(var(--tw-exit-rotate,0));filter:blur(var(--tw-exit-blur,0))}}