@fabrika/builder 0.2.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/BUILD-CONTRACT.md +41 -0
- package/LICENSE +21 -0
- package/README.md +100 -0
- package/cli/src/bin.ts +66 -0
- package/cli/src/build-status.ts +109 -0
- package/cli/src/catalog.ts +42 -0
- package/cli/src/commands/advance.ts +73 -0
- package/cli/src/commands/build.ts +215 -0
- package/cli/src/commands/ensure-daemon.ts +25 -0
- package/cli/src/commands/gate.ts +165 -0
- package/cli/src/commands/induce.ts +61 -0
- package/cli/src/commands/init.ts +91 -0
- package/cli/src/commands/next.ts +31 -0
- package/cli/src/config.ts +47 -0
- package/cli/src/context.ts +28 -0
- package/cli/src/daemon-client.ts +280 -0
- package/cli/src/daemon-lifecycle.ts +171 -0
- package/cli/src/derive.ts +109 -0
- package/cli/src/index.ts +38 -0
- package/cli/src/induce.ts +151 -0
- package/cli/src/inject-skills.ts +313 -0
- package/cli/src/issues.ts +75 -0
- package/cli/src/layout.ts +124 -0
- package/cli/src/packet.ts +108 -0
- package/cli/src/phase-state.ts +19 -0
- package/cli/src/phases.ts +131 -0
- package/cli/src/skeletons.ts +104 -0
- package/daemon/src/bin.ts +70 -0
- package/daemon/src/build-stream.ts +38 -0
- package/daemon/src/handlers.ts +472 -0
- package/daemon/src/index.ts +123 -0
- package/daemon/src/paths.ts +30 -0
- package/daemon/src/phases.ts +221 -0
- package/daemon/src/static.ts +72 -0
- package/decisions/README.md +133 -0
- package/decisions/patterns/app-deploy.md +55 -0
- package/decisions/patterns/auth-password-kv.md +45 -0
- package/decisions/patterns/count-store.md +44 -0
- package/decisions/patterns/di-container.md +47 -0
- package/decisions/patterns/feature-architecture.md +48 -0
- package/decisions/patterns/lib-publish.md +46 -0
- package/decisions/patterns/monorepo.md +47 -0
- package/decisions/patterns/named-seeds.md +45 -0
- package/decisions/patterns/observability.md +45 -0
- package/decisions/patterns/single-worker-spa.md +74 -0
- package/decisions/patterns/structured-logging.md +49 -0
- package/decisions/patterns/typed-worker-rpc.md +87 -0
- package/decisions/patterns/vozka-app-deploy.md +74 -0
- package/decisions/patterns/write-split.md +47 -0
- package/decisions/technologies/analytics-engine.md +44 -0
- package/decisions/technologies/bindx.md +55 -0
- package/decisions/technologies/bun.md +51 -0
- package/decisions/technologies/buzola.md +80 -0
- package/decisions/technologies/cloudflare-access.md +50 -0
- package/decisions/technologies/cloudflare-vite-plugin.md +43 -0
- package/decisions/technologies/cloudflare.md +50 -0
- package/decisions/technologies/contember.md +65 -0
- package/decisions/technologies/cron-triggers.md +46 -0
- package/decisions/technologies/d1.md +51 -0
- package/decisions/technologies/durable-objects.md +44 -0
- package/decisions/technologies/kv.md +42 -0
- package/decisions/technologies/lopata.md +55 -0
- package/decisions/technologies/nuasite.md +50 -0
- package/decisions/technologies/oblaka.md +48 -0
- package/decisions/technologies/opice.md +40 -0
- package/decisions/technologies/pletivo.md +53 -0
- package/decisions/technologies/postgres.md +47 -0
- package/decisions/technologies/preact.md +44 -0
- package/decisions/technologies/propustka.md +137 -0
- package/decisions/technologies/queue.md +55 -0
- package/decisions/technologies/r2.md +41 -0
- package/decisions/technologies/react-router.md +47 -0
- package/decisions/technologies/react.md +37 -0
- package/decisions/technologies/roj.md +51 -0
- package/decisions/technologies/sentry.md +49 -0
- package/decisions/technologies/tailwind.md +45 -0
- package/decisions/technologies/vite.md +47 -0
- package/decisions/technologies/vozka.md +122 -0
- package/decisions/technologies/workers-ai.md +59 -0
- package/decisions/technologies/wrangler.md +41 -0
- package/decisions/tooling/deptective.md +45 -0
- package/decisions/tooling/edvabe.md +45 -0
- package/index.ts +2 -0
- package/package.json +51 -0
- package/skill/SKILL.md +76 -0
- package/skill/agents/build-orchestrator.md +170 -0
- package/skill/agents/flows-author.md +65 -0
- package/skill/agents/implementer.md +37 -0
- package/skill/agents/openai.yaml +4 -0
- package/skill/agents/opice-skeleton-author.md +68 -0
- package/skill/gates/brief.jsx +20 -0
- package/skill/gates/build.jsx +20 -0
- package/skill/gates/design.jsx +22 -0
- package/skill/gates/flows.jsx +14 -0
- package/skill/gates/review-grid.jsx +32 -0
- package/skill/gates/tech.jsx +17 -0
- package/skill/gates/vision.jsx +27 -0
- package/stacks/README.md +44 -0
- package/stacks/cf-fat.md +64 -0
- package/stacks/cf-internal-app.md +125 -0
- package/stacks/cf-lightweight.md +54 -0
- package/stacks/contember-app.md +53 -0
- package/stacks/content-site.md +45 -0
- package/ui/dist/assets/annotations-runtime.js +1 -0
- package/ui/dist/assets/client.css +2 -0
- package/ui/dist/assets/client.fhjppkvr.js +214 -0
- package/ui/dist/assets/components.js +63 -0
- package/ui/dist/assets/preact-compat.js +1 -0
- package/ui/dist/assets/react-jsx-dev-runtime.js +3 -0
- package/ui/dist/assets/react-jsx-runtime.js +3 -0
- package/ui/dist/assets/runtime.js +1 -0
- package/ui/dist/index.html +36 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: cf-internal-app
|
|
3
|
+
title: "Full internal Cloudflare app — Buzola SPA + Propustka + Vozka"
|
|
4
|
+
goal: "From empty to a full internal app: Buzola SPA + Worker API, D1/R2/Queue/Cron/Workers AI, local Vite+lopata, native Propustka login, Vozka-managed production deploy, and green opice tests."
|
|
5
|
+
decisions:
|
|
6
|
+
- cloudflare
|
|
7
|
+
- lopata
|
|
8
|
+
- oblaka
|
|
9
|
+
- vozka
|
|
10
|
+
- wrangler
|
|
11
|
+
- vite
|
|
12
|
+
- react
|
|
13
|
+
- buzola
|
|
14
|
+
- tailwind
|
|
15
|
+
- d1
|
|
16
|
+
- r2
|
|
17
|
+
- queue
|
|
18
|
+
- cron-triggers
|
|
19
|
+
- workers-ai
|
|
20
|
+
- propustka
|
|
21
|
+
- bun
|
|
22
|
+
- named-seeds
|
|
23
|
+
- single-worker-spa
|
|
24
|
+
- typed-worker-rpc
|
|
25
|
+
- vozka-app-deploy
|
|
26
|
+
- opice
|
|
27
|
+
build: "see §build recipe below"
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
# Stack: cf-internal-app
|
|
31
|
+
|
|
32
|
+
The production-grade **internal-app shape**: an internal, client-rendered Buzola/React
|
|
33
|
+
SPA backed by a Cloudflare Worker. It runs as a Vite SPA plus a lopata Worker
|
|
34
|
+
locally, ships as one Worker with static assets in production, logs people in
|
|
35
|
+
through native Propustka, and delegates the whole production lifecycle to Vozka.
|
|
36
|
+
|
|
37
|
+
Use `cf-lightweight` when an app only needs SPA + D1 and does not belong to the
|
|
38
|
+
Vozka/Propustka ecosystem. This preset deliberately includes the operational
|
|
39
|
+
primitives available to the stack: R2, Queue, Cron and Workers AI. Drop any of those at the
|
|
40
|
+
tech gate when the product has no corresponding workload.
|
|
41
|
+
|
|
42
|
+
## Composition
|
|
43
|
+
|
|
44
|
+
Frontend: `react` + `buzola` + `vite` + `tailwind`. Runtime/data: `cloudflare` +
|
|
45
|
+
`d1` + `r2` + `queue` + `cron-triggers` + `workers-ai`. Local development:
|
|
46
|
+
`lopata` + `oblaka` + `bun`. Authentication/authorization: native `propustka`.
|
|
47
|
+
Deployment: `vozka` + `wrangler` through the `vozka-app-deploy` pattern. Delivery:
|
|
48
|
+
`single-worker-spa` + `typed-worker-rpc`. Acceptance: `named-seeds` + `opice`.
|
|
49
|
+
|
|
50
|
+
requires-closure holds:
|
|
51
|
+
|
|
52
|
+
- `vozka` → {`cloudflare`, `oblaka`, `wrangler`, `propustka`};
|
|
53
|
+
- `propustka` → {`cloudflare`, `d1`};
|
|
54
|
+
- `single-worker-spa` → {`cloudflare`, `vite`, `buzola`};
|
|
55
|
+
- `typed-worker-rpc` → {`cloudflare`, `buzola`};
|
|
56
|
+
- `vozka-app-deploy` → {`vozka`, `propustka`, `oblaka`, `wrangler`};
|
|
57
|
+
- `buzola` → `react`; all CF resources → `cloudflare`; `named-seeds` → `d1`.
|
|
58
|
+
|
|
59
|
+
One per exclusive slot: platform, deploy-control-plane, app-delivery-shape,
|
|
60
|
+
client-worker-api, cf-dev-runtime, bundler, ui-framework, router, styling,
|
|
61
|
+
relational-db, ai-runtime, authz, js-runtime, seed-strategy and e2e-tests. There
|
|
62
|
+
is **no Cloudflare Access**
|
|
63
|
+
and no app-owned password auth; native Propustka owns both login and permission
|
|
64
|
+
resolution.
|
|
65
|
+
|
|
66
|
+
**Maturity:** Vozka, Propustka, Buzola, lopata and opice are 0.x/WIP. This is a
|
|
67
|
+
curated internal production stack, not the generic default. Pin the working
|
|
68
|
+
versions, read installed sources/types before scaffolding, and typecheck the
|
|
69
|
+
actual resource/auth config.
|
|
70
|
+
|
|
71
|
+
## Required application shape
|
|
72
|
+
|
|
73
|
+
### Local vs production delivery
|
|
74
|
+
|
|
75
|
+
- `bun run dev` starts the Vite/Buzola SPA (normally `:5173`).
|
|
76
|
+
- `bunx lopata dev` starts the Worker and local bindings (normally `:8787`).
|
|
77
|
+
- Vite proxies `/rpc`, `/api` and `/internal` to the Worker.
|
|
78
|
+
- Production builds the SPA to `dist/`; a Worker `ASSETS` binding with SPA
|
|
79
|
+
fallback serves non-API paths. Selective `run_worker_first` patterns send only
|
|
80
|
+
API namespaces through Worker code; those paths return their own 404s and
|
|
81
|
+
never fall through to `index.html`.
|
|
82
|
+
|
|
83
|
+
### Native Propustka login
|
|
84
|
+
|
|
85
|
+
- Define one app id and one action/scope vocabulary shared by runtime auth and
|
|
86
|
+
the schema in `vozka.config.ts`.
|
|
87
|
+
- Off-local, bind `IAM` to `propustka-worker` and inject `PROPUSTKA_URL` as the
|
|
88
|
+
token issuer/login origin.
|
|
89
|
+
- Pass ordered `AppGates` to `PropustkaAuth`: normally a `service` rule followed
|
|
90
|
+
by a `human` rule for each protected API path. Unmatched paths fail closed.
|
|
91
|
+
- Return `loginUrl` to the SPA on a human auth miss, redirect the browser to it,
|
|
92
|
+
and attach every returned `setCookie` to the response.
|
|
93
|
+
- Local/opice runs synthesize a deterministic operator `AuthContext`; current
|
|
94
|
+
`FakeIamClient` is for management methods, not request authentication.
|
|
95
|
+
|
|
96
|
+
### Typed Worker API
|
|
97
|
+
|
|
98
|
+
- Compose a validated method map behind one `/rpc` endpoint and export its
|
|
99
|
+
inferred contract to the SPA through a type-only import.
|
|
100
|
+
- Authenticate once at the dispatcher, then require each feature handler to
|
|
101
|
+
enforce its concrete `auth.can(...)` capability.
|
|
102
|
+
- Preserve Propustka's `loginUrl`/`setCookie` across the RPC response seam.
|
|
103
|
+
|
|
104
|
+
### Vozka deploy
|
|
105
|
+
|
|
106
|
+
- `vozka.config.ts` is the production source of truth: stable `id`, exported
|
|
107
|
+
resource builder, schema and pipeline.
|
|
108
|
+
- `ctx.domain` owns the custom domain; `pipeline.secrets` lists required runtime
|
|
109
|
+
secrets. Do not recreate deployment in `.github/workflows/deploy.yml`.
|
|
110
|
+
- A thin `oblaka.ts` reuses the resource builder only for the local lopata loop.
|
|
111
|
+
- The account operator registers repo/env/domain/trigger/secrets in Vozka. That
|
|
112
|
+
is a deployment prerequisite, not a scaffold-time Cloudflare mutation.
|
|
113
|
+
|
|
114
|
+
## Build recipe (per-issue verify stage)
|
|
115
|
+
|
|
116
|
+
1. install with Bun; generate local `wrangler.jsonc` from the shared resource
|
|
117
|
+
graph and apply D1 migrations + the issue's named seeds through lopata.
|
|
118
|
+
2. keep the lopata Worker and Vite SPA running as two background processes.
|
|
119
|
+
3. run the targeted opice scenario against the Vite URL; auth is the deterministic
|
|
120
|
+
dev operator and external AI/providers use deterministic fakes.
|
|
121
|
+
4. run unit tests + `bun run typecheck` + the SPA production build. Validate
|
|
122
|
+
`vozka.config.ts`/`oblaka.ts` through their real config-generation command when
|
|
123
|
+
their raw-source type graph is intentionally excluded from `tsc`.
|
|
124
|
+
5. collapse the result. Production deployment remains Vozka's responsibility;
|
|
125
|
+
do not deploy from an issue implementation loop.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: cf-lightweight
|
|
3
|
+
title: "buzola SPA + lopata + D1 on Cloudflare — lightweight, no SSR (preferred default)"
|
|
4
|
+
goal: "From empty to a buzola SPA on a local lopata CF dev server with a D1 binding and a green opice test — no SSR, local-first."
|
|
5
|
+
decisions:
|
|
6
|
+
- cloudflare
|
|
7
|
+
- lopata
|
|
8
|
+
- oblaka
|
|
9
|
+
- vite
|
|
10
|
+
- react
|
|
11
|
+
- buzola
|
|
12
|
+
- d1
|
|
13
|
+
- bun
|
|
14
|
+
- tailwind
|
|
15
|
+
- named-seeds
|
|
16
|
+
- opice
|
|
17
|
+
build: "see §build recipe below"
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# Stack: cf-lightweight
|
|
21
|
+
|
|
22
|
+
The **preferred default**: a client-rendered **buzola SPA** (no SSR) on **lopata**
|
|
23
|
+
(local-first CF runtime, Bun) with a single **D1** database, styled with Tailwind,
|
|
24
|
+
tested by opice. Lighter than `cf-fat` — no SSR machinery, no workerd, the bun/lopata
|
|
25
|
+
path. Replaces the old `cf-local` (which was RRv7 SSR); the SPA-no-SSR shape is the
|
|
26
|
+
lightweight one.
|
|
27
|
+
|
|
28
|
+
## Composition
|
|
29
|
+
|
|
30
|
+
`cloudflare` (platform) · `lopata` (cf-dev-runtime, local-first) · `oblaka` (iac) ·
|
|
31
|
+
`buzola` (SPA router — **no SSR**) + `react` · `d1` (relational-db) · `tailwind`
|
|
32
|
+
(styling) · `bun` (toolchain) · `named-seeds` · `opice`.
|
|
33
|
+
|
|
34
|
+
requires-closure holds: `lopata`→{cloudflare,vite}, `buzola`→react, `d1`/`oblaka`→cloudflare,
|
|
35
|
+
`named-seeds`→d1 — all present. One per slot (cf-dev-runtime=lopata, router=buzola,
|
|
36
|
+
ui-framework=react, relational-db=d1, …). **No `react-router`** — buzola is the SPA router.
|
|
37
|
+
|
|
38
|
+
**Note on vite vs the non-vite preference:** `lopata` is a vite plugin, so this stack
|
|
39
|
+
_does_ run vite for the local CF dev server, and buzola integrates via `@buzola/vite-plugin`.
|
|
40
|
+
The Bun-native `@buzola/bun-plugin` (true non-vite) path is for setups that don't use
|
|
41
|
+
lopata's vite dev server — see `buzola` / `bun`.
|
|
42
|
+
|
|
43
|
+
Read first: **`buzola`** (`.route()` is the URL, Navigation-API-only, codegen), **`lopata`**
|
|
44
|
+
(local D1 + the dev server), **`d1`** (idempotent migrations).
|
|
45
|
+
|
|
46
|
+
## Build recipe (per-issue verify stage)
|
|
47
|
+
|
|
48
|
+
1. start the lopata dev server (Bun) → the buzola SPA at the local URL.
|
|
49
|
+
2. `bunx lopata d1 migrations apply` + apply the relevant `named-seeds` (idempotent).
|
|
50
|
+
3. `bun test` the targeted opice scenarios against the running SPA.
|
|
51
|
+
4. collapse the result (steps + invariants); between iterations the app stays up, reseed idempotently.
|
|
52
|
+
|
|
53
|
+
Scaffold: write artifacts from sources → `bun install` → `bunx oblaka oblaka.ts` (→ wrangler.jsonc
|
|
54
|
+
with the D1 binding) → migrate → seed → lopata dev → buzola SPA renders D1 data → `bun test` green.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: contember-app
|
|
3
|
+
title: "Contember (Postgres GraphQL CMS) + buzola/React admin SPA + bindx + Tailwind"
|
|
4
|
+
goal: "From empty to a Contember backend (Postgres) with a typed schema + ACL, and a buzola/React admin SPA bound to it via bindx, green opice tests against the running SPA."
|
|
5
|
+
decisions:
|
|
6
|
+
- contember
|
|
7
|
+
- postgres
|
|
8
|
+
- bindx
|
|
9
|
+
- react
|
|
10
|
+
- buzola
|
|
11
|
+
- tailwind
|
|
12
|
+
- bun
|
|
13
|
+
- opice
|
|
14
|
+
build: "see §build recipe below"
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# Stack: contember-app
|
|
18
|
+
|
|
19
|
+
A **data-driven admin app**: a Contember backend (GraphQL CMS + ACL + tenancy on
|
|
20
|
+
**Postgres**) with a **buzola/React SPA** front end bound to it through **bindx**
|
|
21
|
+
(typed data binding + form state). The opposite deployment shape to the cf-* stacks
|
|
22
|
+
— a **Node backend + Postgres** plus a **static SPA**, not Workers + D1.
|
|
23
|
+
|
|
24
|
+
## Composition
|
|
25
|
+
|
|
26
|
+
Backend: `contember` (backend slot) → `postgres` (relational-db). Frontend:
|
|
27
|
+
`react` + `buzola` (SPA router, bundled via `@buzola/bun-plugin` — the non-vite path)
|
|
28
|
+
|
|
29
|
+
- `bindx` (data-binding over Contember's GraphQL) + `tailwind` (styling). Toolchain:
|
|
30
|
+
`bun`. Tests: `opice`.
|
|
31
|
+
|
|
32
|
+
requires-closure holds: `contember`→postgres, `bindx`→{contember,react},
|
|
33
|
+
`buzola`→react — all present. One per slot (backend, relational-db, router,
|
|
34
|
+
ui-framework, data-binding, styling). **No `cloudflare`/`d1`** — Contember is a Node
|
|
35
|
+
service on Postgres; `d1` would collide with `postgres` on `relational-db`.
|
|
36
|
+
|
|
37
|
+
Read first: **`contember`** (schema-as-code DSL, predicate ACL, Postgres + migrations),
|
|
38
|
+
**`bindx`** (the codegen step + implicit selection over-fetch gotcha), **`buzola`**
|
|
39
|
+
(`.route()` is the URL, Navigation-API-only, bun-plugin).
|
|
40
|
+
|
|
41
|
+
**Maturity:** `bindx` and `buzola` are `wip` (0.x) — the binding/router pieces still
|
|
42
|
+
churn; pin versions. The Contember backend itself is stable.
|
|
43
|
+
|
|
44
|
+
## Build recipe (per-issue verify stage)
|
|
45
|
+
|
|
46
|
+
1. start Postgres + the Contember engine (`docker-compose`); apply Contember migrations.
|
|
47
|
+
2. `generate:schema` (bindx generator: Contember model → typed entities) — commit the output.
|
|
48
|
+
3. start the buzola SPA dev server (`@buzola/bun-plugin`) → it queries the Contember GraphQL API.
|
|
49
|
+
4. `bun test` the targeted opice scenarios against the running SPA.
|
|
50
|
+
5. between iterations: backend + SPA stay up; reseed Contember data idempotently.
|
|
51
|
+
|
|
52
|
+
Scaffold: declare the Contember schema + ACL → bring up Postgres/engine → migrate →
|
|
53
|
+
`generate:schema` → wire `ContemberBindxProvider` + buzola routes → opice green.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: content-site
|
|
3
|
+
title: "pletivo (Bun-native SSG) + Preact islands + Tailwind — static content site"
|
|
4
|
+
goal: "From empty to a statically-generated content site (file-based pages + content collections) with Preact islands and Tailwind, served locally, green opice smoke test."
|
|
5
|
+
decisions:
|
|
6
|
+
- pletivo
|
|
7
|
+
- preact
|
|
8
|
+
- tailwind
|
|
9
|
+
- bun
|
|
10
|
+
- opice
|
|
11
|
+
build: "see §build recipe below"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Stack: content-site
|
|
15
|
+
|
|
16
|
+
A **static content site** (marketing / docs / blog) built the Bun-native way —
|
|
17
|
+
`pletivo` (Astro-compatible SSG on Bun) with **Preact islands** for the bits that
|
|
18
|
+
need interactivity, **Tailwind** styling, tested by `opice`. Squarely on the team's
|
|
19
|
+
**bun / non-vite** preference: no app server, no vite pipeline, just a Bun build.
|
|
20
|
+
|
|
21
|
+
## Composition
|
|
22
|
+
|
|
23
|
+
`pletivo` (site-framework) · `preact` (islands — `ui-framework`) · `tailwind`
|
|
24
|
+
(styling) · `bun` (runtime/build) · `opice` (tests).
|
|
25
|
+
|
|
26
|
+
requires-closure holds: `pletivo`→bun; `pletivo` recommends `preact`+`tailwind`
|
|
27
|
+
(both present). One per slot (site-framework, ui-framework, styling, js-runtime,
|
|
28
|
+
e2e-tests). **No `react`** — pletivo's islands are Preact (and `preact` conflicts
|
|
29
|
+
`react`). **No SSR** — it's static; pre-declare dynamic routes via `getStaticPaths`.
|
|
30
|
+
|
|
31
|
+
Read first: **`pletivo`** (island registry reset, JSON-serializable island props,
|
|
32
|
+
`getStaticPaths` for dynamic routes), **`preact`** (DOM-standard event names).
|
|
33
|
+
|
|
34
|
+
**Maturity:** `pletivo` is `experimental` (0.1.x) — fine for a static site you control,
|
|
35
|
+
but pin the version; it's the bun-native bet, not a settled default.
|
|
36
|
+
|
|
37
|
+
## Build recipe (per-issue verify stage)
|
|
38
|
+
|
|
39
|
+
1. `bun install`; author `.astro`/`.tsx` pages under `src/pages/` + islands under `src/islands/`.
|
|
40
|
+
2. run the pletivo dev server (Bun) and/or `pletivo build` → static HTML.
|
|
41
|
+
3. serve the output locally; `bun test` the targeted opice scenarios against it.
|
|
42
|
+
4. between iterations: keep the dev server up; content is file-based (no DB to seed).
|
|
43
|
+
|
|
44
|
+
Hosting is flexible (any static host / Cloudflare Pages); add `cloudflare` only if the
|
|
45
|
+
site grows into needing Workers/bindings — at which point reconsider a Workers SSR stack.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createContext as t,useContext as o}from"react";var i=t(null),r=t(""),e=t({sessionId:"",isShared:!1,fsAvailable:!1,uploadUrl:null});function s(){let n=o(i);if(!n)throw Error("useAnnotations requires an AnnotationProvider");return n}function l(){return o(e)}export{s as useAnnotations,l as useAnnotationHost,r as SessionContext,e as AnnotationHostContext,i as AnnotationCtx};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
2
|
+
@import "https://fonts.googleapis.com/css2?family=Instrument+Serif&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap";@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-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-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}}}@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:"JetBrains Mono", "Fira Code", Consolas, monospace;--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-2xl:42rem;--container-3xl:48rem;--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-medium:500;--font-weight-semibold:600;--tracking-wide:.025em;--tracking-wider:.05em;--tracking-widest:.1em;--leading-snug:1.375;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--ease-out:cubic-bezier(0, 0, .2, 1);--animate-ping:ping 1s cubic-bezier(0, 0, .2, 1) infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--blur-sm:8px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-bg-base:#161510;--color-bg-surface:#1c1b15;--color-bg-elevated:#25241d;--color-bg-code:#1c1b16;--color-bg-input:#fff8dc08;--color-bg-elevated-half:#25241d80;--color-text-primary:#e8e4df;--color-text-secondary:#a09a92;--color-text-tertiary:#847d75;--color-text-code:#c4beb7;--color-text-disabled:#4a463f;--color-text-inverse:#1a1a1a;--color-border-subtle:#fff8f00f;--color-border-medium:#fff8f017;--color-border-hover:#fff8f024;--color-border-strong:#fff8f02e;--color-accent-green:#4a9e6d;--color-accent-green-muted:#4a9e6d24;--color-accent-amber:#c49a3a;--color-accent-amber-muted:#c49a3a24;--color-accent-red:#c45a5a;--color-accent-red-muted:#c45a5a24;--color-accent-blue:#5a8ec4;--color-accent-blue-muted:#5a8ec41f;--color-highlight-bg:#ffdc6426;--color-highlight-border:#ffdc6459;--color-highlight-annotation:#ffdc642e;--color-highlight-active:#ffdc6459;--color-highlight-selected:#ffdc641a;--color-badge-bg:#c49a3a33;--color-btn-primary:#e8e4df;--color-btn-primary-text:#1a1a1a;--font-heading:"Instrument Serif", Georgia, "Times New Roman", serif;--font-body:"Inter", -apple-system, BlinkMacSystemFont, sans-serif;--text-section:1.5rem;--text-section--line-height:1.25;--text-section--letter-spacing:-.01em;--text-task-label:.9375rem;--text-task-label--line-height:1.4;--text-body:.875rem;--text-body--line-height:1.6;--text-code:.8125rem;--text-code--line-height:1.55;--text-meta:.75rem;--text-meta--line-height:1.4;--text-meta--letter-spacing:.02em;--text-tiny:.6875rem;--text-tiny--line-height:1.4}}@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,-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;-webkit-text-decoration: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]){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}body{background-color:var(--color-bg-base);color:var(--color-text-primary);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:Inter,-apple-system,BlinkMacSystemFont,sans-serif}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:0}.top-1{top:var(--spacing)}.top-1\.5{top:calc(var(--spacing) * 1.5)}.top-1\/2{top:50%}.top-2{top:calc(var(--spacing) * 2)}.right-0{right:0}.right-1{right:var(--spacing)}.right-1\.5{right:calc(var(--spacing) * 1.5)}.right-3{right:calc(var(--spacing) * 3)}.-left-8{left:calc(var(--spacing) * -8)}.left-0{left:0}.z-10{z-index:10}.z-50{z-index:50}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.-mx-4{margin-inline:calc(var(--spacing) * -4)}.mx-0\.5{margin-inline:calc(var(--spacing) * .5)}.mx-4{margin-inline:calc(var(--spacing) * 4)}.mx-auto{margin-inline:auto}.my-1{margin-block:var(--spacing)}.my-2{margin-block:calc(var(--spacing) * 2)}.my-3{margin-block:calc(var(--spacing) * 3)}.-mt-0\.5{margin-top:calc(var(--spacing) * -.5)}.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-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-5{margin-top:calc(var(--spacing) * 5)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-\[7px\]{margin-top:7px}.mt-px{margin-top:1px}.mb-1{margin-bottom:var(--spacing)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-8{margin-bottom:calc(var(--spacing) * 8)}.ml-2{margin-left:calc(var(--spacing) * 2)}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.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-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-11{height:calc(var(--spacing) * 11)}.h-64{height:calc(var(--spacing) * 64)}.h-\[16px\]{height:16px}.h-\[18px\]{height:18px}.h-auto{height:auto}.h-full{height:100%}.h-screen{height:100vh}.min-h-0{min-height:0}.min-h-\[20px\]{min-height:20px}.min-h-\[40px\]{min-height:40px}.min-h-\[60px\]{min-height:60px}.min-h-screen{min-height:100vh}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-2{width:calc(var(--spacing) * 2)}.w-2\.5{width:calc(var(--spacing) * 2.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-72{width:calc(var(--spacing) * 72)}.w-80{width:calc(var(--spacing) * 80)}.w-full{width:100%}.w-px{width:1px}.w-screen{width:100vw}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-full{max-width:100%}.min-w-0{min-width:0}.min-w-\[16px\]{min-width:16px}.min-w-\[18px\]{min-width:18px}.flex-1{flex:1}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.-rotate-90{rotate:-90deg}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-ping{animation:var(--animate-ping)}.animate-pulse{animation:var(--animate-pulse)}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize{resize:both}.resize-none{resize:none}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-\[5rem_1fr\]{grid-template-columns:5rem 1fr}.grid-cols-\[14rem_minmax\(0\,1fr\)_22rem_auto\]{grid-template-columns:14rem minmax(0,1fr) 22rem auto}.grid-cols-\[14rem_minmax\(0\,1fr\)_auto\]{grid-template-columns:14rem minmax(0,1fr) auto}.grid-rows-\[minmax\(0\,1fr\)\]{grid-template-rows:minmax(0,1fr)}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.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)}: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)))}.gap-x-3{column-gap:calc(var(--spacing) * 3)}.gap-y-1{row-gap:var(--spacing)}.gap-y-1\.5{row-gap:calc(var(--spacing) * 1.5)}.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-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-\[1\.5px\]{border-style:var(--tw-border-style);border-width:1.5px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-none{--tw-border-style:none;border-style:none}.border-accent-amber{border-color:var(--color-accent-amber)}.border-border-hover{border-color:var(--color-border-hover)}.border-border-medium{border-color:var(--color-border-medium)}.border-border-strong{border-color:var(--color-border-strong)}.border-border-subtle{border-color:var(--color-border-subtle)}.border-highlight-border{border-color:var(--color-highlight-border)}.\!bg-transparent{background-color:#0000!important}.bg-accent-amber{background-color:var(--color-accent-amber)}.bg-accent-amber-muted{background-color:var(--color-accent-amber-muted)}.bg-accent-amber-muted\/60{background-color:#c49a3a16}@supports (color:color-mix(in lab, red, red)){.bg-accent-amber-muted\/60{background-color:color-mix(in oklab, var(--color-accent-amber-muted) 60%, transparent)}}.bg-accent-amber\/60{background-color:#c49a3a99}@supports (color:color-mix(in lab, red, red)){.bg-accent-amber\/60{background-color:color-mix(in oklab, var(--color-accent-amber) 60%, transparent)}}.bg-accent-blue{background-color:var(--color-accent-blue)}.bg-accent-blue-muted{background-color:var(--color-accent-blue-muted)}.bg-accent-green{background-color:var(--color-accent-green)}.bg-accent-green-muted{background-color:var(--color-accent-green-muted)}.bg-accent-green\/60{background-color:#4a9e6d99}@supports (color:color-mix(in lab, red, red)){.bg-accent-green\/60{background-color:color-mix(in oklab, var(--color-accent-green) 60%, transparent)}}.bg-accent-red{background-color:var(--color-accent-red)}.bg-accent-red-muted{background-color:var(--color-accent-red-muted)}.bg-accent-red\/60{background-color:#c45a5a99}@supports (color:color-mix(in lab, red, red)){.bg-accent-red\/60{background-color:color-mix(in oklab, var(--color-accent-red) 60%, transparent)}}.bg-badge-bg{background-color:var(--color-badge-bg)}.bg-bg-base{background-color:var(--color-bg-base)}.bg-bg-code{background-color:var(--color-bg-code)}.bg-bg-elevated{background-color:var(--color-bg-elevated)}.bg-bg-input{background-color:var(--color-bg-input)}.bg-bg-surface{background-color:var(--color-bg-surface)}.bg-bg-surface\/30{background-color:#1c1b154d}@supports (color:color-mix(in lab, red, red)){.bg-bg-surface\/30{background-color:color-mix(in oklab, var(--color-bg-surface) 30%, transparent)}}.bg-black\/40{background-color:#0006}@supports (color:color-mix(in lab, red, red)){.bg-black\/40{background-color:color-mix(in oklab, var(--color-black) 40%, 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-border-hover{background-color:var(--color-border-hover)}.bg-border-medium{background-color:var(--color-border-medium)}.bg-border-subtle{background-color:var(--color-border-subtle)}.bg-btn-primary{background-color:var(--color-btn-primary)}.bg-highlight-bg{background-color:var(--color-highlight-bg)}.bg-highlight-selected{background-color:var(--color-highlight-selected)}.bg-text-inverse{background-color:var(--color-text-inverse)}.bg-text-tertiary{background-color:var(--color-text-tertiary)}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.p-0{padding:0}.p-0\.5{padding:calc(var(--spacing) * .5)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.px-0{padding-inline:0}.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-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.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-4{padding-block:calc(var(--spacing) * 4)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-6{padding-block:calc(var(--spacing) * 6)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-px{padding-block:1px}.pt-3{padding-top:calc(var(--spacing) * 3)}.pb-1{padding-bottom:var(--spacing)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pl-2{padding-left:calc(var(--spacing) * 2)}.pl-5{padding-left:calc(var(--spacing) * 5)}.text-center{text-align:center}.text-left{text-align:left}.font-body{font-family:var(--font-body)}.font-heading{font-family:var(--font-heading)}.font-mono{font-family:var(--font-mono)}.text-meta{font-size:var(--text-meta);line-height:var(--tw-leading,var(--text-meta--line-height));letter-spacing:var(--tw-tracking,var(--text-meta--letter-spacing))}.text-section{font-size:var(--text-section);line-height:var(--tw-leading,var(--text-section--line-height));letter-spacing:var(--tw-tracking,var(--text-section--letter-spacing))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-body{font-size:var(--text-body);line-height:var(--tw-leading,var(--text-body--line-height))}.text-code{font-size:var(--text-code);line-height:var(--tw-leading,var(--text-code--line-height))}.text-task-label{font-size:var(--text-task-label);line-height:var(--tw-leading,var(--text-task-label--line-height))}.text-tiny{font-size:var(--text-tiny);line-height:var(--tw-leading,var(--text-tiny--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-\[9px\]{font-size:9px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.break-words{overflow-wrap:break-word}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-accent-amber{color:var(--color-accent-amber)}.text-accent-blue{color:var(--color-accent-blue)}.text-accent-green{color:var(--color-accent-green)}.text-accent-red{color:var(--color-accent-red)}.text-btn-primary-text{color:var(--color-btn-primary-text)}.text-text-code{color:var(--color-text-code)}.text-text-disabled{color:var(--color-text-disabled)}.text-text-primary{color:var(--color-text-primary)}.text-text-secondary{color:var(--color-text-secondary)}.text-text-tertiary{color:var(--color-text-tertiary)}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.line-through{text-decoration-line:line-through}.underline{text-decoration-line:underline}.decoration-border-hover{-webkit-text-decoration-color:var(--color-border-hover);-webkit-text-decoration-color:var(--color-border-hover);text-decoration-color:var(--color-border-hover)}.opacity-0{opacity:0}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-55{opacity:.55}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow-lg{--tw-shadow:0 8px 24px var(--tw-shadow-color,var(--color-shadow));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 12px var(--tw-shadow-color,var(--color-shadow));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-none{--tw-shadow:0 0 #0000;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 2px var(--tw-shadow-color,var(--color-shadow));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)}.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-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{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))}.duration-100{--tw-duration:.1s;transition-duration:.1s}.duration-150{--tw-duration:.15s;transition-duration:.15s}.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}.select-text{-webkit-user-select:text;user-select:text}.\[writing-mode\:vertical-rl\]{writing-mode:vertical-rl}@media (hover:hover){.group-hover\/ann\:opacity-100:is(:where(.group\/ann):hover *){opacity:1}.group-hover\/code\:opacity-60:is(:where(.group\/code):hover *),.group-hover\/diff\:opacity-60:is(:where(.group\/diff):hover *){opacity:.6}.group-hover\/section\:opacity-20:is(:where(.group\/section):hover *){opacity:.2}.group-hover\/section\:opacity-50:is(:where(.group\/section):hover *){opacity:.5}.group-hover\/task\:opacity-100:is(:where(.group\/task):hover *){opacity:1}}.placeholder\:text-text-disabled::placeholder{color:var(--color-text-disabled)}.placeholder\:text-text-tertiary::placeholder{color:var(--color-text-tertiary)}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.odd\:bg-bg-elevated-half:nth-child(odd){background-color:var(--color-bg-elevated-half)}@media (hover:hover){.hover\:-translate-y-px:hover{--tw-translate-y:-1px;translate:var(--tw-translate-x) var(--tw-translate-y)}.hover\:border-border-hover:hover{border-color:var(--color-border-hover)}.hover\:bg-accent-red-muted:hover{background-color:var(--color-accent-red-muted)}.hover\:bg-bg-elevated:hover{background-color:var(--color-bg-elevated)}.hover\:bg-bg-elevated-half:hover{background-color:var(--color-bg-elevated-half)}.hover\:bg-bg-input:hover{background-color:var(--color-bg-input)}.hover\:bg-bg-surface\/50:hover{background-color:#1c1b1580}@supports (color:color-mix(in lab, red, red)){.hover\:bg-bg-surface\/50:hover{background-color:color-mix(in oklab, var(--color-bg-surface) 50%, transparent)}}.hover\:bg-border-medium:hover{background-color:var(--color-border-medium)}.hover\:bg-border-subtle:hover{background-color:var(--color-border-subtle)}.hover\:text-accent-red:hover{color:var(--color-accent-red)}.hover\:text-text-primary:hover{color:var(--color-text-primary)}.hover\:text-text-secondary:hover{color:var(--color-text-secondary)}.hover\:text-text-tertiary:hover{color:var(--color-text-tertiary)}.hover\:underline:hover{text-decoration-line:underline}.hover\:\!opacity-80:hover{opacity:.8!important}.hover\:\!opacity-100:hover{opacity:1!important}.hover\:opacity-90:hover{opacity:.9}.hover\:opacity-100:hover{opacity:1}}.focus\:border-none:focus{--tw-border-style:none;border-style:none}.focus\:border-border-hover:focus{border-color:var(--color-border-hover)}.focus\:ring-0:focus{--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)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}}mark[data-annotation-id]{background:var(--color-highlight-annotation);border-bottom:1.5px solid var(--color-highlight-bg);cursor:pointer;color:inherit;border-radius:2px;padding:1px 0;transition:background .15s,box-shadow .15s}mark[data-annotation-id].mark-active{background:var(--color-highlight-active);border-bottom:2px solid var(--color-highlight-border);box-shadow:0 0 0 1px var(--color-highlight-border)}@keyframes ann-flash{0%,to{background:var(--color-highlight-annotation)}25%,75%{background:var(--color-highlight-active)}}.ann-flash{animation:.6s 2 ann-flash}[data-annotation-image]{cursor:crosshair;touch-action:none;position:relative}.ann-region{background:var(--color-highlight-annotation);border:2px solid var(--color-highlight-bg);cursor:pointer;border-radius:2px;transition:background .15s,box-shadow .15s;position:absolute}.ann-region[data-annotation-region=ellipse]{border-radius:50%}.ann-region.mark-active{background:var(--color-highlight-active);border-color:var(--color-highlight-border);box-shadow:0 0 0 1px var(--color-highlight-border)}.ann-region-draft{pointer-events:none;border-style:dashed;border-color:var(--color-highlight-border)}.annotation-highlight{background:var(--color-highlight-bg);border-bottom:2px solid var(--color-highlight-border);cursor:pointer;border-radius:2px;transition:background .15s}.annotation-highlight:hover,.annotation-highlight.active{background:var(--color-highlight-active)}:root{--color-shadow:#00000040;--color-scrollbar:#fff8f00f;--color-scrollbar-hover:#fff8f01f;--color-folder-fill:#c49a3a26;--color-folder-fill-active:#c49a3a40;--color-folder-stroke:#c49a3a80;--color-folder-stroke-active:#c49a3a;--color-hljs-base:#c4beb7;--color-hljs-keyword:#d2a6ff;--color-hljs-string:#a8cc8c;--color-hljs-comment:#847d75;--color-hljs-number:#e8a87c;--color-hljs-variable:#7ec8e3;--color-hljs-function:#dbc074;--color-hljs-punctuation:#a09a92}[data-theme=light]{--color-bg-base:#faf5e6;--color-bg-surface:#f3edd8;--color-bg-elevated:#ebe4ce;--color-bg-code:#f0ebd5;--color-bg-input:#503c0a0d;--color-bg-elevated-half:#ebe4ce80;--color-text-primary:#2a2520;--color-text-secondary:#5a5550;--color-text-tertiary:#9a9390;--color-text-code:#4a4540;--color-text-disabled:#c5c0b8;--color-text-inverse:#faf6ef;--color-border-subtle:#503c1e14;--color-border-medium:#503c1e1a;--color-border-hover:#503c1e26;--color-border-strong:#503c1e33;--color-accent-green:#3a8a5a;--color-accent-green-muted:#3a8a5a1a;--color-accent-amber:#a07820;--color-accent-amber-muted:#a078201a;--color-accent-red:#b84a4a;--color-accent-red-muted:#b84a4a14;--color-accent-blue:#4a7eb4;--color-accent-blue-muted:#4a7eb414;--color-highlight-bg:#c8a01e26;--color-highlight-border:#c8a01e66;--color-highlight-annotation:#c8a01e2e;--color-highlight-active:#c8a01e59;--color-highlight-selected:#c8a01e1a;--color-badge-bg:#a0782026;--color-btn-primary:#2a2520;--color-btn-primary-text:#faf6ef;--color-shadow:#503c1e1a;--color-scrollbar:#503c1e14;--color-scrollbar-hover:#503c1e26;--color-folder-fill:#a078201f;--color-folder-fill-active:#a0782038;--color-folder-stroke:#a0782066;--color-folder-stroke-active:#a07820;--color-hljs-base:#4a4540;--color-hljs-keyword:#7c3aed;--color-hljs-string:#2e7d32;--color-hljs-comment:#9a9390;--color-hljs-number:#c45a20;--color-hljs-variable:#1976d2;--color-hljs-function:#8a6d00;--color-hljs-punctuation:#6b6560}code,pre,.font-mono{font-family:JetBrains Mono,Fira Code,Consolas,monospace}::selection{background-color:var(--color-highlight-active);color:var(--color-text-primary)}:focus-visible{outline:2px solid var(--color-accent-blue);outline-offset:2px;border-radius:4px}textarea:focus-visible{outline:none}button,a,[role=button]{transition:all .15s}.diff-added{background-color:#4a9e6d40}@supports (color:color-mix(in lab, red, red)){.diff-added{background-color:color-mix(in srgb, var(--color-accent-green) 25%, transparent)}}.diff-added{border-radius:2px;padding-block:1px;padding-inline:1px}.diff-removed{background-color:#c45a5a40}@supports (color:color-mix(in lab, red, red)){.diff-removed{background-color:color-mix(in srgb, var(--color-accent-red) 25%, transparent)}}.diff-removed{text-decoration:line-through;-webkit-text-decoration-color:var(--color-accent-red);-webkit-text-decoration-color:var(--color-accent-red);text-decoration-color:var(--color-accent-red);border-radius:2px;padding-block:1px;padding-inline:1px}.diff-block-removed{background:#c45a5a14}@supports (color:color-mix(in lab, red, red)){.diff-block-removed{background:color-mix(in srgb, var(--color-accent-red) 8%, transparent)}}.diff-block-removed{border-left:3px solid var(--color-accent-red);opacity:.7}.diff-block-added{background:#4a9e6d14}@supports (color:color-mix(in lab, red, red)){.diff-block-added{background:color-mix(in srgb, var(--color-accent-green) 8%, transparent)}}.diff-block-added{border-left:3px solid var(--color-accent-green)}.diff-row-removed td{background:#c45a5a1a}@supports (color:color-mix(in lab, red, red)){.diff-row-removed td{background:color-mix(in srgb, var(--color-accent-red) 10%, transparent)}}.diff-row-removed td{text-decoration:line-through;-webkit-text-decoration-color:var(--color-accent-red);-webkit-text-decoration-color:var(--color-accent-red);text-decoration-color:var(--color-accent-red);opacity:.6}.diff-row-added td{background:#4a9e6d1a}@supports (color:color-mix(in lab, red, red)){.diff-row-added td{background:color-mix(in srgb, var(--color-accent-green) 10%, transparent)}}.diff-item-removed{background:#c45a5a1a}@supports (color:color-mix(in lab, red, red)){.diff-item-removed{background:color-mix(in srgb, var(--color-accent-red) 10%, transparent)}}.diff-item-removed{text-decoration:line-through;-webkit-text-decoration-color:var(--color-accent-red);-webkit-text-decoration-color:var(--color-accent-red);text-decoration-color:var(--color-accent-red);opacity:.6}.diff-item-added{background:#4a9e6d1a}@supports (color:color-mix(in lab, red, red)){.diff-item-added{background:color-mix(in srgb, var(--color-accent-green) 10%, transparent)}}.diff-block-label{text-transform:uppercase;pointer-events:none;border-radius:3px;padding:1px 6px;font-size:10px;font-weight:500;position:absolute;top:4px;right:8px}@keyframes plan-refresh{0%{opacity:.6}to{opacity:1}}.plan-updated{animation:.3s plan-refresh}input[type=range]{appearance:none;background:0 0;height:20px}input[type=range]::-webkit-slider-thumb{appearance:none;background:var(--color-accent-amber);cursor:pointer;border:2px solid var(--color-bg-base);border-radius:50%;width:14px;height:14px;margin-top:-5px}input[type=range]::-moz-range-thumb{background:var(--color-accent-amber);cursor:pointer;border:2px solid var(--color-bg-base);border-radius:50%;width:14px;height:14px}input[type=range]::-webkit-slider-runnable-track{background:0 0;border-radius:2px;height:4px}input[type=range]::-moz-range-track{background:0 0;border-radius:2px;height:4px}.prose-canvas{color:var(--color-text-secondary);font-size:.875rem;line-height:1.7}.prose-canvas h1,.prose-canvas h2,.prose-canvas h3,.prose-canvas h4,.prose-canvas h5,.prose-canvas h6{color:var(--color-text-primary);margin-top:1.5em;margin-bottom:.5em;font-weight:600;line-height:1.3}.prose-canvas h1{font-size:1.5rem}.prose-canvas h2{font-size:1.25rem}.prose-canvas h3{font-size:1.1rem}.prose-canvas h1:first-child,.prose-canvas h2:first-child,.prose-canvas h3:first-child{margin-top:0}.prose-canvas p{margin-top:.75em;margin-bottom:.75em}.prose-canvas p:first-child{margin-top:0}.prose-canvas a{color:var(--color-accent-blue);-webkit-text-decoration:underline #5a8ec466;text-decoration:underline #5a8ec466}@supports (color:color-mix(in lab, red, red)){.prose-canvas a{-webkit-text-decoration-color:color-mix(in srgb, var(--color-accent-blue) 40%, transparent);-webkit-text-decoration-color:color-mix(in srgb, var(--color-accent-blue) 40%, transparent);text-decoration-color:color-mix(in srgb, var(--color-accent-blue) 40%, transparent)}}.prose-canvas a{text-underline-offset:2px}.prose-canvas a:hover{-webkit-text-decoration-color:var(--color-accent-blue);-webkit-text-decoration-color:var(--color-accent-blue);text-decoration-color:var(--color-accent-blue)}.prose-canvas strong{color:var(--color-text-primary);font-weight:600}.prose-canvas code{background:var(--color-bg-code);color:var(--color-text-code);border-radius:4px;padding:.15em .4em;font-size:.8em}.prose-canvas pre{background:var(--color-bg-code);border-radius:8px;margin-top:.75em;margin-bottom:.75em;padding:1em;overflow-x:auto}.prose-canvas pre code{background:0 0;padding:0;font-size:.8rem}.prose-canvas ul,.prose-canvas ol{margin-top:.5em;margin-bottom:.5em;padding-left:1.5em}.prose-canvas ul{list-style-type:disc}.prose-canvas ol{list-style-type:decimal}.prose-canvas li{margin-top:.25em}.prose-canvas li>ul,.prose-canvas li>ol{margin-top:.25em;margin-bottom:0}.prose-canvas blockquote{border-left:3px solid var(--color-border-strong);color:var(--color-text-tertiary);margin-top:.75em;margin-bottom:.75em;margin-left:0;padding-left:1em}.prose-canvas hr{border:none;border-top:1px solid var(--color-border-subtle);margin:1.5em 0}.prose-canvas table{border-collapse:collapse;width:100%;margin-top:.75em;margin-bottom:.75em;font-size:.8rem}.prose-canvas th,.prose-canvas td{border:1px solid var(--color-border-subtle);text-align:left;padding:.5em .75em}.prose-canvas th{background:var(--color-bg-elevated);color:var(--color-text-primary);font-weight:600}.prose-canvas img{border-radius:8px;max-width:100%;margin-top:.75em;margin-bottom:.75em}.hljs{color:var(--color-hljs-base)!important;background:0 0!important}pre code.hljs{padding:0!important}.hljs-keyword,.hljs-selector-tag,.hljs-built_in,.hljs-name,.hljs-tag{color:var(--color-hljs-keyword)!important}.hljs-string,.hljs-title,.hljs-section,.hljs-attribute,.hljs-literal,.hljs-template-tag,.hljs-template-variable,.hljs-type{color:var(--color-hljs-string)!important}.hljs-comment,.hljs-quote,.hljs-deletion,.hljs-meta{color:var(--color-hljs-comment)!important}.hljs-number,.hljs-regexp,.hljs-addition{color:var(--color-hljs-number)!important}.hljs-variable,.hljs-attr{color:var(--color-hljs-variable)!important}.hljs-function .hljs-title,.hljs-title.function_{color:var(--color-hljs-function)!important}.hljs-params{color:var(--color-hljs-base)!important}.hljs-property{color:var(--color-hljs-variable)!important}.hljs-punctuation{color:var(--color-hljs-punctuation)!important}html,body{font-family:var(--font-body);background:var(--color-bg-base);color:var(--color-text-primary);-webkit-font-smoothing:antialiased;margin:0}body{font-size:var(--text-body);line-height:var(--text-body--line-height)}::-webkit-scrollbar{width:9px;height:9px}::-webkit-scrollbar-thumb{background:var(--color-border-medium);border-radius:5px}::-webkit-scrollbar-thumb:hover{background:var(--color-border-hover)}::-webkit-scrollbar-track{background:0 0}@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-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-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 ping{75%,to{opacity:0;transform:scale(2)}}@keyframes pulse{50%{opacity:.5}}
|