conductor-remote 1.5.0 → 1.7.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/dist/index.html CHANGED
@@ -11,8 +11,8 @@
11
11
  <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
12
12
  <link rel="icon" type="image/svg+xml" href="/icon.svg" />
13
13
  <title>Conductor Remote</title>
14
- <script type="module" crossorigin src="/assets/index-Dza-b9zD.js"></script>
15
- <link rel="stylesheet" crossorigin href="/assets/index-BcgS8jNR.css">
14
+ <script type="module" crossorigin src="/assets/index-McPNxH2y.js"></script>
15
+ <link rel="stylesheet" crossorigin href="/assets/index-Gog06HCM.css">
16
16
  <link rel="manifest" href="/manifest.webmanifest"></head>
17
17
  <body>
18
18
  <div id="root"></div>
package/dist/sw.js CHANGED
@@ -1 +1 @@
1
- if(!self.define){let e,i={};const n=(n,s)=>(n=new URL(n+".js",s).href,i[n]||new Promise(i=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=i,document.head.appendChild(e)}else e=n,importScripts(n),i()}).then(()=>{let e=i[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(s,o)=>{const r=e||("document"in self?document.currentScript.src:"")||location.href;if(i[r])return;let c={};const t=e=>n(e,r),l={module:{uri:r},exports:c,require:t};i[r]=Promise.all(s.map(e=>l[e]||t(e))).then(e=>(o(...e),c))}}define(["./workbox-9c191d2f"],function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"index.html",revision:"679c71c87637a7da381d770ba23bd4e3"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/index-Dza-b9zD.js",revision:null},{url:"assets/index-BcgS8jNR.css",revision:null},{url:"apple-touch-icon.png",revision:"2b9301416b880d45d4bb655f2600d1f2"},{url:"icon-192.png",revision:"c5e01ac58768627e18ee7b8b6a9239ef"},{url:"icon-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon-maskable-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon.svg",revision:"c1aee186821798733dd477e69a0ef243"},{url:"manifest.webmanifest",revision:"cf88fbc5755108a7fe0616fa160a8a15"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/index.html"),{denylist:[/^\/api\//]}))});
1
+ if(!self.define){let e,i={};const n=(n,s)=>(n=new URL(n+".js",s).href,i[n]||new Promise(i=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=i,document.head.appendChild(e)}else e=n,importScripts(n),i()}).then(()=>{let e=i[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(s,o)=>{const r=e||("document"in self?document.currentScript.src:"")||location.href;if(i[r])return;let c={};const t=e=>n(e,r),l={module:{uri:r},exports:c,require:t};i[r]=Promise.all(s.map(e=>l[e]||t(e))).then(e=>(o(...e),c))}}define(["./workbox-9c191d2f"],function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"index.html",revision:"9b2a23f2db3c39277979e2a1d9c43902"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/index-McPNxH2y.js",revision:null},{url:"assets/index-Gog06HCM.css",revision:null},{url:"apple-touch-icon.png",revision:"2b9301416b880d45d4bb655f2600d1f2"},{url:"icon-192.png",revision:"c5e01ac58768627e18ee7b8b6a9239ef"},{url:"icon-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon-maskable-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon.svg",revision:"c1aee186821798733dd477e69a0ef243"},{url:"manifest.webmanifest",revision:"cf88fbc5755108a7fe0616fa160a8a15"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/index.html"),{denylist:[/^\/api\//]}))});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "conductor-remote",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "type": "module",
5
5
  "packageManager": "yarn@4.15.0",
6
6
  "description": "Phone control panel for local Conductor agents. Reads ride SQLite + git; prompts ride Conductor's own dispatch path.",
package/scripts/qr.ts CHANGED
@@ -381,8 +381,13 @@ function render(m: boolean[][], indent: string): string[] {
381
381
  return lines
382
382
  }
383
383
 
384
+ /** Encode `text` into its QR module matrix (`true` = dark). Shared by the terminal and web SVG renderers. */
385
+ export function qrMatrix(text: string): boolean[][] {
386
+ const { version, bits } = encodeText(text)
387
+ return buildMatrix(version, bits)
388
+ }
389
+
384
390
  /** Encode `text` and return the terminal lines that draw its QR code (each already indented). */
385
391
  export function qrLines(text: string, indent = ' '): string[] {
386
- const { version, bits } = encodeText(text)
387
- return render(buildMatrix(version, bits), indent)
392
+ return render(qrMatrix(text), indent)
388
393
  }
@@ -81,11 +81,20 @@ function buildPlist(): string {
81
81
  const proj = xml(projectDir)
82
82
  const out = xml(path.join(logDir, 'relay.log'))
83
83
  const err = xml(path.join(logDir, 'relay.err.log'))
84
- // Optional overrides carried into the daemon's environment.
85
- const envEntries: Array<[string, string]> = [['PATH', '/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin']]
84
+ // node's own dir leads so the daemon can find `npm` (adjacent to node) for self-update under launchd's
85
+ // bare PATH; Homebrew's bin is appended for tailscale/node on Apple Silicon.
86
+ const nodeDir = path.dirname(process.execPath)
87
+ const daemonPath = `${nodeDir}:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/homebrew/bin`
88
+ // MANAGED marks this as the launchd-supervised instance: autoupdate.ts only self-restarts (exit →
89
+ // KeepAlive respawn) when it sees this, so a dev `yarn start` or worktree run never auto-updates.
90
+ const envEntries: Array<[string, string]> = [
91
+ ['PATH', daemonPath],
92
+ ['CONDUCTOR_REMOTE_MANAGED', '1']
93
+ ]
86
94
  if (process.env.WRITE_STRATEGY) envEntries.push(['WRITE_STRATEGY', process.env.WRITE_STRATEGY])
87
95
  if (process.env.RELAY_HOST) envEntries.push(['RELAY_HOST', process.env.RELAY_HOST])
88
96
  if (process.env.RELAY_PORT) envEntries.push(['RELAY_PORT', process.env.RELAY_PORT])
97
+ if (process.env.AUTO_UPDATE) envEntries.push(['AUTO_UPDATE', process.env.AUTO_UPDATE])
89
98
  const envXml = envEntries.map(([k, v]) => `\t\t<key>${xml(k)}</key>\n\t\t<string>${xml(v)}</string>`).join('\n')
90
99
  return `<?xml version="1.0" encoding="UTF-8"?>
91
100
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Self-update for the globally-installed relay daemon.
3
+ *
4
+ * The relay ships as a global npm package driven by a KeepAlive LaunchAgent. Without this, staying
5
+ * current means the user manually re-running `npm i -g conductor-remote && conductor-remote service
6
+ * install`. Here the running daemon periodically asks the npm registry for the latest published
7
+ * version and, when it's newer, runs `npm i -g conductor-remote@latest` and exits — launchd's
8
+ * KeepAlive restarts it into the freshly-installed code (the plist's baked `bin/cli.js` path is stable
9
+ * across a global reinstall, so no re-`service install` is needed).
10
+ *
11
+ * Two hard gates keep this from firing where it shouldn't:
12
+ * - CONDUCTOR_REMOTE_MANAGED=1 — set only by `service install` in the plist, so it proves we are the
13
+ * launchd-managed daemon and that exit()→KeepAlive-restart is a safe way to reload.
14
+ * - projectDir has no `.git` — proves we're the published tarball, not a dev worktree. A worktree's
15
+ * LaunchAgent runs from the worktree path, so `npm i -g` wouldn't even swap its code; never touch it.
16
+ *
17
+ * `AUTO_UPDATE` overrides the default: `off` disables entirely; `check` polls and reports availability
18
+ * (via /api/state and the log) but never installs; `on` forces auto when the gates allow it.
19
+ *
20
+ * Stdlib + global fetch only — no runtime deps, no transform-requiring syntax (keeps the relay strip-clean).
21
+ */
22
+ import { execFile } from 'node:child_process'
23
+ import fs from 'node:fs'
24
+ import path from 'node:path'
25
+ import { promisify } from 'node:util'
26
+
27
+ const execFileP = promisify(execFile)
28
+
29
+ const NAME = 'conductor-remote'
30
+ const projectDir = path.resolve(import.meta.dirname, '..')
31
+ const REGISTRY = process.env.NPM_REGISTRY ?? 'https://registry.npmjs.org'
32
+ const CHECK_INTERVAL_MS = 6 * 60 * 60 * 1000
33
+ const FIRST_DELAY_MS = 90 * 1000
34
+
35
+ export type UpdateMode = 'off' | 'check' | 'auto'
36
+
37
+ export interface UpdateStatus {
38
+ /** Version this process is running (from the package's own package.json). */
39
+ current: string
40
+ /** Latest version seen on the registry, or null before the first successful check. */
41
+ latest: string | null
42
+ /** True when `latest` is a strictly higher release than `current`. */
43
+ available: boolean
44
+ /** Epoch ms of the last successful registry check, or null. */
45
+ checkedAt: number | null
46
+ /** Effective mode after the gates are applied. */
47
+ mode: UpdateMode
48
+ /** Last check/install error message, or null. */
49
+ lastError: string | null
50
+ }
51
+
52
+ function readVersion(): string {
53
+ try {
54
+ const pkg = JSON.parse(fs.readFileSync(path.join(projectDir, 'package.json'), 'utf8')) as { version?: string }
55
+ return pkg.version ?? '0.0.0'
56
+ } catch {
57
+ return '0.0.0'
58
+ }
59
+ }
60
+
61
+ const CURRENT = readVersion()
62
+
63
+ const status: UpdateStatus = {
64
+ current: CURRENT,
65
+ latest: null,
66
+ available: false,
67
+ checkedAt: null,
68
+ mode: 'off',
69
+ lastError: null
70
+ }
71
+
72
+ /** Snapshot of the updater state, surfaced on /api/state so the phone can show the version and any update. */
73
+ export function updateStatus(): UpdateStatus {
74
+ return { ...status }
75
+ }
76
+
77
+ /** Parse `x.y.z` (ignoring any prerelease/build suffix) into a comparable tuple; null if unparseable. */
78
+ function parseVersion(v: string): [number, number, number] | null {
79
+ const m = /^(\d+)\.(\d+)\.(\d+)/.exec(v.trim())
80
+ return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null
81
+ }
82
+
83
+ /** True when `candidate` is a strictly higher release than `base` (prereleases collapse to their x.y.z). */
84
+ function isNewer(candidate: string, base: string): boolean {
85
+ const a = parseVersion(candidate)
86
+ const b = parseVersion(base)
87
+ if (!a || !b) return false
88
+ for (let i = 0; i < 3; i++) {
89
+ if (a[i] > b[i]) return true
90
+ if (a[i] < b[i]) return false
91
+ }
92
+ return false
93
+ }
94
+
95
+ async function fetchLatest(): Promise<string | null> {
96
+ // The `/latest` sub-endpoint serves the full manifest as application/json; the abbreviated
97
+ // `vnd.npm.install-v1+json` media type is only valid on the packument root and 406s here.
98
+ const res = await fetch(`${REGISTRY}/${NAME}/latest`, {
99
+ headers: { accept: 'application/json' },
100
+ signal: AbortSignal.timeout(10_000)
101
+ })
102
+ if (!res.ok) throw new Error(`registry ${res.status}`)
103
+ const body = (await res.json()) as { version?: string }
104
+ return body.version ?? null
105
+ }
106
+
107
+ /** Resolve npm next to the running node (Homebrew/nvm keep them in one bin dir); fall back to PATH. */
108
+ function npmBin(): string {
109
+ const adjacent = path.join(path.dirname(process.execPath), 'npm')
110
+ return fs.existsSync(adjacent) ? adjacent : 'npm'
111
+ }
112
+
113
+ async function installLatest(): Promise<void> {
114
+ await execFileP(npmBin(), ['install', '-g', `${NAME}@latest`], {
115
+ timeout: 300_000,
116
+ env: { ...process.env, npm_config_yes: 'true' }
117
+ })
118
+ }
119
+
120
+ function log(msg: string): void {
121
+ console.info(`[auto-update] ${msg}`)
122
+ }
123
+
124
+ /**
125
+ * Effective mode. Precedence: explicit AUTO_UPDATE > gated default.
126
+ * off — disabled. check — poll + report, never install. auto — poll + install + self-restart.
127
+ * `auto` (default when unset, or when AUTO_UPDATE=on) requires BOTH gates; without them, an explicit
128
+ * `on` degrades to `check` (visibility without an unsafe install) and an unset default degrades to `off`
129
+ * (a dev `yarn start` or worktree daemon stays silent).
130
+ */
131
+ function resolveMode(): UpdateMode {
132
+ const raw = process.env.AUTO_UPDATE?.trim().toLowerCase()
133
+ if (raw === 'off' || raw === 'false' || raw === '0') return 'off'
134
+ if (raw === 'check' || raw === 'notify') return 'check'
135
+ const managed = process.env.CONDUCTOR_REMOTE_MANAGED === '1'
136
+ const published = !fs.existsSync(path.join(projectDir, '.git'))
137
+ const canAuto = managed && published
138
+ if (raw === 'on' || raw === 'auto' || raw === '1' || raw === 'true') return canAuto ? 'auto' : 'check'
139
+ return canAuto ? 'auto' : 'off'
140
+ }
141
+
142
+ let inFlight = false
143
+
144
+ async function runCheck(mode: 'check' | 'auto'): Promise<void> {
145
+ if (inFlight) return
146
+ inFlight = true
147
+ try {
148
+ const latest = await fetchLatest()
149
+ status.latest = latest
150
+ status.checkedAt = Date.now()
151
+ status.available = latest != null && isNewer(latest, CURRENT)
152
+ status.lastError = null
153
+ if (!status.available || latest == null) return
154
+ if (mode === 'check') {
155
+ log(`update available: ${CURRENT} → ${latest} (AUTO_UPDATE=check — not installing)`)
156
+ return
157
+ }
158
+ log(`updating ${CURRENT} → ${latest} via \`npm i -g ${NAME}@latest\`…`)
159
+ await installLatest()
160
+ log(`installed ${latest}; restarting to apply (launchd KeepAlive brings the relay back).`)
161
+ // Let the log line flush, then exit; KeepAlive respawns us into the new code.
162
+ setTimeout(() => process.exit(0), 500).unref()
163
+ } catch (err) {
164
+ status.lastError = err instanceof Error ? err.message : String(err)
165
+ log(`check/update failed: ${status.lastError}`)
166
+ } finally {
167
+ inFlight = false
168
+ }
169
+ }
170
+
171
+ /** Start the periodic self-updater. Safe to call unconditionally — it no-ops unless the gates pass. */
172
+ export function startAutoUpdate(): void {
173
+ const mode = resolveMode()
174
+ status.mode = mode
175
+ if (mode === 'off') return
176
+ log(`enabled (mode=${mode}, current=${CURRENT}); first check in ${FIRST_DELAY_MS / 1000}s, then every 6h.`)
177
+ setTimeout(() => void runCheck(mode), FIRST_DELAY_MS).unref()
178
+ setInterval(() => void runCheck(mode), CHECK_INTERVAL_MS).unref()
179
+ }
package/src/server.ts CHANGED
@@ -2,11 +2,12 @@ import crypto from 'node:crypto'
2
2
  import fs from 'node:fs'
3
3
  import http from 'node:http'
4
4
  import path from 'node:path'
5
+ import { startAutoUpdate, updateStatus } from './autoupdate.ts'
5
6
  import { loadConfig } from './config.ts'
6
7
  import { ConductorDb } from './db.ts'
7
8
  import { workspaceDiff } from './git.ts'
8
9
  import { Reads } from './reads.ts'
9
- import { describeActuator, pickActuator } from './writes.ts'
10
+ import { describeActuator, newChat, pickActuator } from './writes.ts'
10
11
 
11
12
  const cfg = loadConfig()
12
13
  const db = new ConductorDb(cfg.dbPath)
@@ -92,9 +93,12 @@ const server = http.createServer(async (req, res) => {
92
93
  try {
93
94
  // GET /api/state — workspace list with active-session status
94
95
  if (req.method === 'GET' && pathname === '/api/state') {
96
+ const update = updateStatus()
95
97
  return json(res, 200, {
96
98
  workspaces: reads.listWorkspaces(),
97
- actuator: await describeActuator(actuator)
99
+ actuator: await describeActuator(actuator),
100
+ version: update.current,
101
+ update
98
102
  })
99
103
  }
100
104
 
@@ -117,6 +121,23 @@ const server = http.createServer(async (req, res) => {
117
121
  return json(res, 200, { sessions: reads.listSessions(decodeURIComponent(m[1])) })
118
122
  }
119
123
 
124
+ // POST /api/workspaces/:id/sessions — open a new chat (Cmd+T) in the workspace
125
+ if (req.method === 'POST' && m) {
126
+ const workspaceId = decodeURIComponent(m[1])
127
+ const ws = reads.getWorkspace(workspaceId)
128
+ if (!ws) return json(res, 404, { error: 'workspace not found' })
129
+ const before = new Set(reads.listSessions(workspaceId).map(s => s.id))
130
+ const result = await newChat(ws)
131
+ if (!result.ok) return json(res, 502, result)
132
+ // The new session lands in the DB a beat after Cmd+T — poll for the fresh id.
133
+ let sessionId: string | null = null
134
+ for (let i = 0; i < 12 && !sessionId; i++) {
135
+ await new Promise(r => setTimeout(r, 500))
136
+ sessionId = reads.listSessions(workspaceId).find(s => !before.has(s.id))?.id ?? null
137
+ }
138
+ return json(res, 200, { ok: true, sessionId })
139
+ }
140
+
120
141
  // GET /api/workspaces/:id/diff
121
142
  m = pathname.match(/^\/api\/workspaces\/([^/]+)\/diff$/)
122
143
  if (req.method === 'GET' && m) {
@@ -168,4 +189,6 @@ server.listen(cfg.port, cfg.host, () => {
168
189
  ' Phone: fronted by `tailscale funnel`/`serve` — run `yarn service status` for the HTTPS URL'
169
190
  ].join('\n')
170
191
  )
192
+ // Keep the managed global daemon current — no-ops for dev checkouts / unmanaged runs (see autoupdate.ts).
193
+ startAutoUpdate()
171
194
  })
package/src/writes.ts CHANGED
@@ -61,6 +61,26 @@ export class SidecarActuator implements Actuator {
61
61
  }
62
62
  }
63
63
 
64
+ // AppleScript steps that focus a workspace via Conductor's command palette
65
+ // (Esc → Cmd+K → branch → Enter). The branch is read from RELAY_WS_QUERY at run
66
+ // time to dodge AppleScript escaping; the timing delays are load-bearing.
67
+ const FOCUS_WORKSPACE_STEPS = `
68
+ key code 53
69
+ delay 0.25
70
+ keystroke "k" using {command down}
71
+ delay 0.7
72
+ keystroke (system attribute "RELAY_WS_QUERY")
73
+ delay 0.9
74
+ key code 36
75
+ delay 1.3`
76
+
77
+ /** Conductor's command palette matches workspaces by branch — its unique key. A
78
+ * looser query (directory name) can match a command like unarchive, so prefer
79
+ * branch and only fall back when it's absent. */
80
+ function focusQuery(ws: Workspace): string {
81
+ return ws.branch || ws.workspace_name || ws.directory_name || ''
82
+ }
83
+
64
84
  /**
65
85
  * Drives Conductor's real send path via macOS Accessibility (AppleScript): focus
66
86
  * the target workspace, paste the prompt, press Enter. Uses whatever model /
@@ -78,22 +98,8 @@ export class AppleScriptActuator implements Actuator {
78
98
  readonly precise = true
79
99
 
80
100
  async send(target: SendTarget, text: string): Promise<SendResult> {
81
- // The branch is Conductor's unique per-workspace key in the command palette;
82
- // anything less specific can match a *command* (e.g. unarchive) instead of a
83
- // workspace, so prefer branch and pass it via env to dodge AppleScript escaping.
84
- const ws = target.workspace
85
- const navQuery = ws.branch || ws.workspace_name || ws.directory_name || ''
86
- const navigate = navQuery
87
- ? `
88
- key code 53
89
- delay 0.25
90
- keystroke "k" using {command down}
91
- delay 0.7
92
- keystroke (system attribute "RELAY_WS_QUERY")
93
- delay 0.9
94
- key code 36
95
- delay 1.3`
96
- : ''
101
+ const navQuery = focusQuery(target.workspace)
102
+ const navigate = navQuery ? FOCUS_WORKSPACE_STEPS : ''
97
103
  // Paste beats keystroke for long/multibyte prompts. Stash the clipboard,
98
104
  // focus the target workspace, paste, send, and restore.
99
105
  const script = `
@@ -133,6 +139,31 @@ set the clipboard to savedClipboard
133
139
  }
134
140
  }
135
141
 
142
+ /**
143
+ * Open a new chat in the target workspace — Conductor's "New chat, same files"
144
+ * (Cmd+T). Focuses the workspace first (command palette → branch), then Cmd+T; the
145
+ * caller detects the freshly-created session id from the DB.
146
+ */
147
+ export async function newChat(workspace: Workspace): Promise<SendResult> {
148
+ const navQuery = focusQuery(workspace)
149
+ if (!navQuery) return { ok: false, strategy: 'applescript', error: 'workspace has no branch to focus' }
150
+ const script = `
151
+ tell application "Conductor" to activate
152
+ delay 0.4
153
+ tell application "System Events"${FOCUS_WORKSPACE_STEPS}
154
+ keystroke "t" using {command down}
155
+ end tell`.trim()
156
+ try {
157
+ await exec('osascript', ['-e', script], {
158
+ env: { ...process.env, RELAY_WS_QUERY: navQuery },
159
+ timeout: 15000
160
+ })
161
+ return { ok: true, strategy: 'applescript' }
162
+ } catch (err) {
163
+ return { ok: false, strategy: 'applescript', error: err instanceof Error ? err.message : String(err) }
164
+ }
165
+ }
166
+
136
167
  export type WriteStrategy = 'applescript' | 'sidecar'
137
168
 
138
169
  export function pickActuator(strategy: WriteStrategy): Actuator {
@@ -1 +0,0 @@
1
- @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-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, -apple-system, "SF Pro Text", "Segoe UI", Roboto, sans-serif;--font-mono:ui-monospace, "SF Mono", "JetBrains Mono", "Fira Code", Menlo, monospace;--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--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;--font-weight-bold:700;--tracking-wide:.025em;--leading-tight:1.25;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--radius-3xl:1.5rem;--ease-out:cubic-bezier(0, 0, .2, 1);--animate-spin:spin 1s linear infinite;--blur-xl:24px;--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);--color-bg:#0a0b0e;--color-surface:#14161b;--color-surface-2:#1b1e26;--color-border:#262a33;--color-border-soft:#1e222a;--color-text:#e7e9ee;--color-muted:#8b909d;--color-faint:#5c616d;--color-accent:#8b7dff;--color-accent-soft:#2a2650;--color-working:#f5a623;--color-idle:#3ecf8e;--color-done:#5b9dff;--color-add:#3ecf8e;--color-del:#ff6b6b}}@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%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*{-webkit-tap-highlight-color:transparent}html,body,#root{height:100%}body{background:var(--color-bg);color:var(--color-text);font-family:var(--font-sans);-webkit-font-smoothing:antialiased;overscroll-behavior-y:none;margin:0;overflow-x:hidden}button{font:inherit;color:inherit;cursor:pointer}::-webkit-scrollbar{width:0;height:0}}@layer components{.card{align-items:center;gap:calc(var(--spacing) * 3);border-radius:var(--radius-2xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border);background-color:var(--color-surface);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3.5);text-align:left;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));display:flex}.card:active{background-color:var(--color-surface-2);scale:.985}.pill{padding-inline:calc(var(--spacing) * 3.5);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-muted);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));border-radius:3.40282e38px}.pill-active{background-color:var(--color-surface-2);color:var(--color-text)}.dot{width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5);background-color:var(--color-faint);border-radius:3.40282e38px;flex-shrink:0}.dot-working{background:var(--color-working);box-shadow:0 0 #f5a62399}@supports (color:color-mix(in lab,red,red)){.dot-working{box-shadow:0 0 color-mix(in srgb,var(--color-working) 60%,transparent)}}.dot-working{animation:1.6s ease-out infinite pulse}.dot-idle{background:var(--color-idle)}.dot-done{background:var(--color-done)}.md>:first-child{margin-top:0}.md>:last-child{margin-bottom:0}.md p{margin:.5em 0}.md h1,.md h2,.md h3,.md h4,.md h5,.md h6{margin:.9em 0 .4em;font-weight:600;line-height:1.3}.md h1{font-size:1.25em}.md h2{font-size:1.15em}.md h3{font-size:1.05em}.md ul,.md ol{margin:.5em 0;padding-left:1.4em}.md ul{list-style:outside}.md ol{list-style:decimal}.md li,.md li>ul,.md li>ol{margin:.2em 0}.md code{background:#1b1e26cc;border-radius:.3rem}@supports (color:color-mix(in lab,red,red)){.md code{background:color-mix(in srgb,var(--color-surface-2) 80%,transparent)}}.md code{font-family:var(--font-mono);padding:.1em .35em;font-size:.85em}.md pre{border:1px solid var(--color-border-soft);background:var(--color-bg);border-radius:.6rem;margin:.5em 0;padding:.6em .75em;overflow-x:auto}.md pre code{background:0 0;padding:0;font-size:12px;line-height:1.5}.md a{color:var(--color-accent);text-underline-offset:2px;text-decoration:underline}.md blockquote{border-left:2px solid var(--color-border);color:var(--color-muted);margin:.5em 0;padding-left:.75em}.md hr{border:0;border-top:1px solid var(--color-border-soft);margin:.75em 0}.md table{border-collapse:collapse;max-width:100%;margin:.5em 0;font-size:.9em;display:block;overflow-x:auto}.md th,.md td{border:1px solid var(--color-border);text-align:left;padding:.3em .6em}.md th{background:var(--color-surface-2);font-weight:600}.md strong{font-weight:600}.md input[type=checkbox]{margin-right:.4em}}@layer utilities{.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:0}.inset-y-0{inset-block:0}.top-0{top:0}.top-1\.5{top:calc(var(--spacing) * 1.5)}.top-full{top:100%}.-right-0\.5{right:calc(var(--spacing) * -.5)}.right-1\.5{right:calc(var(--spacing) * 1.5)}.right-2{right:calc(var(--spacing) * 2)}.-bottom-0\.5{bottom:calc(var(--spacing) * -.5)}.left-0{left:0}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.mx-auto{margin-inline:auto}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.-mr-1{margin-right:calc(var(--spacing) * -1)}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1{margin-bottom:var(--spacing)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.-ml-1{margin-left:calc(var(--spacing) * -1)}.ml-auto{margin-left:auto}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.size-1\.5{width:calc(var(--spacing) * 1.5);height:calc(var(--spacing) * 1.5)}.size-1\.5\!{width:calc(var(--spacing) * 1.5)!important;height:calc(var(--spacing) * 1.5)!important}.size-2{width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.size-8{width:calc(var(--spacing) * 8);height:calc(var(--spacing) * 8)}.size-9{width:calc(var(--spacing) * 9);height:calc(var(--spacing) * 9)}.size-20{width:calc(var(--spacing) * 20);height:calc(var(--spacing) * 20)}.size-full{width:100%;height:100%}.h-full{height:100%}.max-h-40{max-height:calc(var(--spacing) * 40)}.w-1{width:var(--spacing)}.w-64{width:calc(var(--spacing) * 64)}.w-\[85\%\]{width:85%}.w-full{width:100%}.max-w-36{max-width:calc(var(--spacing) * 36)}.max-w-80{max-width:calc(var(--spacing) * 80)}.max-w-\[85\%\]{max-width:85%}.max-w-\[92\%\]{max-width:92%}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:0}.min-w-5{min-width:calc(var(--spacing) * 5)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.-translate-x-full{--tw-translate-x:-100%;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-0{--tw-translate-x:0;translate:var(--tw-translate-x) var(--tw-translate-y)}.-rotate-90{rotate:-90deg}.animate-spin{animation:var(--animate-spin)}.resize-none{resize:none}.flex-col{flex-direction:column}.place-items-center{place-items:center}.items-center{align-items:center}.items-end{align-items:flex-end}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.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-5{gap:calc(var(--spacing) * 5)}.self-start{align-self:flex-start}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-3xl{border-radius:var(--radius-3xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.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-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-accent\/50{border-color:#8b7dff80}@supports (color:color-mix(in lab,red,red)){.border-accent\/50{border-color:color-mix(in oklab,var(--color-accent) 50%,transparent)}}.border-border{border-color:var(--color-border)}.border-border-soft{border-color:var(--color-border-soft)}.border-faint{border-color:var(--color-faint)}.border-t-accent{border-top-color:var(--color-accent)}.bg-accent{background-color:var(--color-accent)}.bg-accent-soft{background-color:var(--color-accent-soft)}.bg-bg{background-color:var(--color-bg)}.bg-bg\/80{background-color:#0a0b0ecc}@supports (color:color-mix(in lab,red,red)){.bg-bg\/80{background-color:color-mix(in oklab,var(--color-bg) 80%,transparent)}}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.bg-black\/50{background-color:color-mix(in oklab,var(--color-black) 50%,transparent)}}.bg-del\/10{background-color:#ff6b6b1a}@supports (color:color-mix(in lab,red,red)){.bg-del\/10{background-color:color-mix(in oklab,var(--color-del) 10%,transparent)}}.bg-idle\/10{background-color:#3ecf8e1a}@supports (color:color-mix(in lab,red,red)){.bg-idle\/10{background-color:color-mix(in oklab,var(--color-idle) 10%,transparent)}}.bg-surface{background-color:var(--color-surface)}.bg-surface-2{background-color:var(--color-surface-2)}.bg-surface\/60{background-color:#14161b99}@supports (color:color-mix(in lab,red,red)){.bg-surface\/60{background-color:color-mix(in oklab,var(--color-surface) 60%,transparent)}}.bg-transparent{background-color:#0000}.bg-working\/10{background-color:#f5a6231a}@supports (color:color-mix(in lab,red,red)){.bg-working\/10{background-color:color-mix(in oklab,var(--color-working) 10%,transparent)}}.object-contain{object-fit:contain}.p-3{padding:calc(var(--spacing) * 3)}.p-6{padding:calc(var(--spacing) * 6)}.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-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.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-3{padding-block:calc(var(--spacing) * 3)}.py-16{padding-block:calc(var(--spacing) * 16)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-2\.5{padding-bottom:calc(var(--spacing) * 2.5)}.pl-2{padding-left:calc(var(--spacing) * 2)}.text-center{text-align:center}.font-mono{font-family:var(--font-mono)}.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-\[11\.5px\]{font-size:11.5px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[14px\]{font-size:14px}.text-\[15px\]{font-size:15px}.leading-\[1\.5\]{--tw-leading:1.5;line-height:1.5}.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-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.\[overflow-wrap\:anywhere\]{overflow-wrap:anywhere}.whitespace-pre{white-space:pre}.whitespace-pre-wrap{white-space:pre-wrap}.text-accent{color:var(--color-accent)}.text-add{color:var(--color-add)}.text-del{color:var(--color-del)}.text-faint{color:var(--color-faint)}.text-idle{color:var(--color-idle)}.text-muted{color:var(--color-muted)}.text-text{color:var(--color-text)}.text-white{color:var(--color-white)}.text-working{color:var(--color-working)}.capitalize{text-transform:capitalize}.uppercase{text-transform:uppercase}.italic{font-style:italic}.\[color-scheme\:dark\]{color-scheme:dark}.opacity-60{opacity:.6}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px 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-2{--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)}.ring-surface{--tw-ring-color:var(--color-surface)}.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-xl{--tw-backdrop-blur:blur(var(--blur-xl));-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-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-200{--tw-duration:.2s;transition-duration:.2s}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.outline-none{--tw-outline-style:none;outline-style:none}.placeholder\:text-faint::placeholder{color:var(--color-faint)}.focus-within\:border-accent\/60:focus-within{border-color:#8b7dff99}@supports (color:color-mix(in lab,red,red)){.focus-within\:border-accent\/60:focus-within{border-color:color-mix(in oklab,var(--color-accent) 60%,transparent)}}.active\:scale-90:active{--tw-scale-x:90%;--tw-scale-y:90%;--tw-scale-z:90%;scale:var(--tw-scale-x) var(--tw-scale-y)}.active\:bg-surface-2:active,.disabled\:bg-surface-2:disabled{background-color:var(--color-surface-2)}.disabled\:text-faint:disabled{color:var(--color-faint)}.disabled\:opacity-50:disabled{opacity:.5}@media(min-width:48rem){.md\:static{position:static}.md\:z-auto{z-index:auto}.md\:block{display:block}.md\:hidden{display:none}.md\:w-72{width:calc(var(--spacing) * 72)}.md\:max-w-none{max-width:none}.md\:shrink-0{flex-shrink:0}.md\:translate-x-0{--tw-translate-x:0;translate:var(--tw-translate-x) var(--tw-translate-y)}.md\:transition-none{transition-property:none}}@media(min-width:64rem){.lg\:static{position:static}.lg\:z-auto{z-index:auto}.lg\:w-80{width:calc(var(--spacing) * 80)}.lg\:w-\[380px\]{width:380px}.lg\:shrink-0{flex-shrink:0}.lg\:border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.lg\:border-border-soft{border-color:var(--color-border-soft)}}@media(min-width:80rem){.xl\:w-\[460px\]{width:460px}}}@keyframes pulse{50%{opacity:.5}}@keyframes fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}.fade-in{animation:.18s ease-out fade-in}.pt-safe{padding-top:max(env(safe-area-inset-top),.5rem)}.pb-safe{padding-bottom:max(env(safe-area-inset-bottom),.5rem)}@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-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@keyframes spin{to{transform:rotate(360deg)}}