cubefarm 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Leon van Zyl
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # ✻ cubefarm
2
+
3
+ A cartoon 3D office where a team of Claude Code agents works through your GitHub issues. You walk the floors, look over their shoulders and watch their pull requests get tested and merged.
4
+
5
+ ## Get started
6
+
7
+ ```bash
8
+ npx cubefarm
9
+ ```
10
+
11
+ That's it. cubefarm checks your machine, starts the office and opens it in your browser. The first time, `npx` asks whether to install cubefarm: say yes.
12
+
13
+ ### What you need
14
+
15
+ - **Node.js 22 or newer**: [nodejs.org](https://nodejs.org)
16
+ - **git**
17
+ - **The GitHub CLI**, signed in: install it from [cli.github.com](https://cli.github.com), then run `gh auth login`
18
+ - **A Claude subscription**: sign in once with `npx cubefarm login`. Your agents use this login. You don't need to install Claude Code, because cubefarm brings its own.
19
+
20
+ Not sure you're ready? `npx cubefarm doctor` checks all four.
21
+
22
+ ### Just want to look around?
23
+
24
+ ```bash
25
+ npx cubefarm --demo
26
+ ```
27
+
28
+ Demo mode fakes GitHub and the agents, so it costs nothing and changes nothing.
29
+
30
+ ## Your first five minutes
31
+
32
+ 1. **Set up your company.** A short wizard asks your name, names your company and introduces your CEO.
33
+ 2. **Move in a project.** Pick one of your project folders or a GitHub repo, or start a new one. It gets its own floor. Every project needs to be on GitHub, because issues and pull requests are how the team works.
34
+ 3. **Let the CEO plan.** The CEO studies the project, writes its QA checklist, plans the work as GitHub issues and proposes who to hire. Press `P` for your phone to chat with them and approve hires.
35
+ 4. **Watch the work.** Developers pick up issues and open pull requests. QA testers review and test each one in a real browser, then post a report with screenshots. With auto-merge on, a pull request merges itself once QA passes and GitHub's checks are green.
36
+
37
+ ## What's in the office
38
+
39
+ - **Floors**: one per project. Ride the elevator between them.
40
+ - **Desks**: walk up behind an agent to watch their monitor. It streams their real terminal, and shows a live browser when they test the UI.
41
+ - **The QA lab**: every floor has at least one QA tester.
42
+ - **The whiteboard**: the Kanban board, from backlog to merged.
43
+ - **The lobby**: the manager's office, where you connect projects, hire, file issues and change settings, and the CEO's corner office.
44
+
45
+ ## Controls
46
+
47
+ | Key | Action |
48
+ | --- | --- |
49
+ | `W A S D` / arrows | walk |
50
+ | `Shift` | run |
51
+ | mouse | look around (click the view first) |
52
+ | `E` | use what you're looking at: a desk, the whiteboard, the elevator, the manager's computer |
53
+ | `P` | your phone |
54
+ | `H` | help |
55
+ | `Esc` | let go of the mouse, or close a panel |
56
+
57
+ ## Commands
58
+
59
+ | Command | What it does |
60
+ | --- | --- |
61
+ | `npx cubefarm` | start the office and open it in your browser |
62
+ | `npx cubefarm login` | sign in to Claude |
63
+ | `npx cubefarm doctor` | check that your machine is ready |
64
+ | `npx cubefarm --demo` | fake GitHub and fake agents |
65
+ | `npx cubefarm --port 4400` | use another port (the default is 4317) |
66
+ | `npx cubefarm --no-open` | don't open the browser |
67
+
68
+ ## Updating
69
+
70
+ `npx` keeps using the version it downloaded first. When a newer one is out, cubefarm tells you as it starts. To update:
71
+
72
+ ```bash
73
+ npx cubefarm@latest
74
+ ```
75
+
76
+ Prefer a permanent install? Run `npm install -g cubefarm`, then start it with `cubefarm`.
77
+
78
+ ## Good to know
79
+
80
+ - **It runs on your Claude subscription.** Every agent draws on the same usage limits. To cap how many work at once, set a session limit in the manager's console.
81
+ - **Agents work on your machine.** By default they can only edit files in their own copy of the repo, can't push to your main branch and can't merge. The office merges, after QA.
82
+ - **Your office lives in `~/.cubefarm`**: settings, clones of your repos and one working copy per agent. Set `SWARM_HOME` to use another folder.
83
+
84
+ ## Learn more
85
+
86
+ - [How it works](docs/how-it-works.md): the life of an issue, QA, auto-merge, models and usage, the safety model and floor previews
87
+ - [Contributing](CONTRIBUTING.md): run it from source, tests, architecture and publishing
88
+
89
+ ## License
90
+
91
+ [MIT](LICENSE)
@@ -0,0 +1,257 @@
1
+ #!/usr/bin/env node
2
+ // `npx cubefarm`: checks this machine is ready, starts the office and opens it in the browser.
3
+ import { spawn, spawnSync } from 'node:child_process';
4
+ import fs from 'node:fs';
5
+ import { createRequire } from 'node:module';
6
+ import net from 'node:net';
7
+ import path from 'node:path';
8
+ import { fileURLToPath, pathToFileURL } from 'node:url';
9
+ import { parseArgs } from 'node:util';
10
+
11
+ const MIN_NODE = 22;
12
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
13
+ const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
14
+
15
+ const HELP = `
16
+ cubefarm ${pkg.version}: a cartoon 3D office where a team of Claude Code agents works through your GitHub issues.
17
+
18
+ Usage
19
+ npx cubefarm start the office and open it in your browser
20
+ npx cubefarm login sign in to Claude with your subscription
21
+ npx cubefarm doctor check that this machine is ready
22
+
23
+ Options
24
+ --port <n> port for the office (default 4317)
25
+ --demo fake GitHub and fake agents: look around without spending any usage
26
+ --no-open don't open the browser
27
+ -v, --version
28
+ -h, --help
29
+
30
+ The office keeps its state and workspaces in ~/.cubefarm (set SWARM_HOME to use another folder).
31
+ `;
32
+
33
+ const color = process.stdout.isTTY && !process.env.NO_COLOR;
34
+ const paint = (open, close) => (s) => (color ? `\x1b[${open}m${s}\x1b[${close}m` : s);
35
+ const green = paint(32, 39);
36
+ const red = paint(31, 39);
37
+ const dim = paint(2, 22);
38
+ const fail = (msg) => {
39
+ console.error(`\n ${red('✗')} ${msg}\n`);
40
+ process.exit(1);
41
+ };
42
+
43
+ if (Number(process.versions.node.split('.')[0]) < MIN_NODE) fail(`cubefarm needs Node.js ${MIN_NODE} or newer (this is ${process.version}). Get it from https://nodejs.org`);
44
+
45
+ let args;
46
+ try {
47
+ args = parseArgs({
48
+ allowPositionals: true,
49
+ options: {
50
+ port: { type: 'string', short: 'p' },
51
+ demo: { type: 'boolean' },
52
+ 'no-open': { type: 'boolean' },
53
+ version: { type: 'boolean', short: 'v' },
54
+ help: { type: 'boolean', short: 'h' },
55
+ },
56
+ });
57
+ } catch (err) {
58
+ fail(`${err.message}\n Run npx cubefarm --help for the options.`);
59
+ }
60
+ const { values, positionals } = args;
61
+ const command = positionals[0] ?? 'start';
62
+
63
+ if (values.help) {
64
+ console.log(HELP);
65
+ process.exit(0);
66
+ }
67
+ if (values.version) {
68
+ console.log(pkg.version);
69
+ process.exit(0);
70
+ }
71
+
72
+ // ---------- Claude Code ----------
73
+
74
+ // The Agent SDK ships Claude Code as a per-platform package. The office runs its agents on it, so logging in with it
75
+ // is logging the agents in. Same lookup as the SDK's own.
76
+ function claudeBinary() {
77
+ let sdk;
78
+ try {
79
+ sdk = createRequire(import.meta.url).resolve('@anthropic-ai/claude-agent-sdk');
80
+ } catch {
81
+ return null;
82
+ }
83
+ const { platform, arch } = process;
84
+ const musl = platform === 'linux' && !process.report?.getReport?.()?.header?.glibcVersionRuntime;
85
+ const targets = platform === 'linux' ? (musl ? [`linux-${arch}-musl`, `linux-${arch}`] : [`linux-${arch}`, `linux-${arch}-musl`]) : [`${platform}-${arch}`];
86
+ const fromSdk = createRequire(sdk);
87
+ for (const target of targets) {
88
+ try {
89
+ return fromSdk.resolve(`@anthropic-ai/claude-agent-sdk-${target}/claude${platform === 'win32' ? '.exe' : ''}`);
90
+ } catch {
91
+ // not installed for this target
92
+ }
93
+ }
94
+ return null;
95
+ }
96
+
97
+ // Like the agents: no API keys or settings inherited from a Claude Code session this was started from.
98
+ function claudeEnv() {
99
+ const env = {};
100
+ for (const [k, v] of Object.entries(process.env)) if (!/^(ANTHROPIC_|CLAUDE)/i.test(k) || k === 'CLAUDE_CONFIG_DIR') env[k] = v;
101
+ return env;
102
+ }
103
+
104
+ // ---------- checks ----------
105
+
106
+ const sh = (cmd, cmdArgs, env) => spawnSync(cmd, cmdArgs, { encoding: 'utf8', windowsHide: true, timeout: 30_000, env });
107
+
108
+ /** What the office needs: { name, ok, detail, fix, required }. */
109
+ function checks() {
110
+ const out = [{ name: 'Node.js', ok: true, detail: process.version }];
111
+
112
+ const git = sh('git', ['--version']);
113
+ out.push({ name: 'git', ok: git.status === 0, detail: git.stdout?.trim().replace(/^git version /, ''), fix: 'install git: https://git-scm.com/downloads', required: true });
114
+
115
+ if (sh('gh', ['--version']).status !== 0) {
116
+ out.push({ name: 'GitHub CLI', ok: false, fix: 'install it from https://cli.github.com, then run: gh auth login' });
117
+ } else {
118
+ const ok = sh('gh', ['auth', 'status']).status === 0;
119
+ out.push({ name: 'GitHub CLI', ok, detail: ok ? 'signed in' : 'not signed in', fix: 'run: gh auth login' });
120
+ }
121
+
122
+ const claude = claudeBinary();
123
+ if (!claude) {
124
+ out.push({ name: 'Claude Code', ok: false, fix: `reinstall cubefarm: Claude Code for ${process.platform}-${process.arch} is missing` });
125
+ } else {
126
+ let status = null;
127
+ try {
128
+ status = JSON.parse(sh(claude, ['auth', 'status', '--json'], claudeEnv()).stdout);
129
+ } catch {
130
+ // unreadable: treated as signed out
131
+ }
132
+ const ok = status?.loggedIn === true;
133
+ out.push({ name: 'Claude', ok, detail: ok ? `signed in${status.subscriptionType ? ` (${status.subscriptionType})` : ''}` : 'not signed in', fix: 'run: npx cubefarm login' });
134
+ }
135
+ return out;
136
+ }
137
+
138
+ const printCheck = (c) => console.log(` ${c.ok ? green('✓') : red('✗')} ${c.name.padEnd(12)} ${c.detail ? dim(c.detail) : ''}${c.ok ? '' : ` → ${c.fix}`}`);
139
+
140
+ // ---------- helpers ----------
141
+
142
+ function portOpen(port) {
143
+ return new Promise((resolve) => {
144
+ const socket = net.connect({ port, host: '127.0.0.1' });
145
+ const done = (open) => {
146
+ socket.destroy();
147
+ resolve(open);
148
+ };
149
+ socket.once('connect', () => done(true));
150
+ socket.once('error', () => done(false));
151
+ socket.setTimeout(1000, () => done(false));
152
+ });
153
+ }
154
+
155
+ async function isOffice(url) {
156
+ try {
157
+ const res = await fetch(`${url}/api/state`, { signal: AbortSignal.timeout(3000) });
158
+ const data = await res.json();
159
+ return Array.isArray(data?.repos) && Array.isArray(data?.agents);
160
+ } catch {
161
+ return false;
162
+ }
163
+ }
164
+
165
+ function openBrowser(url) {
166
+ const [cmd, cmdArgs, opts] =
167
+ process.platform === 'win32'
168
+ ? ['cmd.exe', ['/d', '/s', '/c', `"start "" "${url}""`], { windowsVerbatimArguments: true }]
169
+ : process.platform === 'darwin'
170
+ ? ['open', [url], {}]
171
+ : ['xdg-open', [url], {}];
172
+ try {
173
+ spawn(cmd, cmdArgs, { ...opts, stdio: 'ignore', detached: true, windowsHide: true })
174
+ .on('error', () => undefined)
175
+ .unref();
176
+ } catch {
177
+ // no browser to open: the URL is printed anyway
178
+ }
179
+ }
180
+
181
+ const newer = (a, b) => {
182
+ const [x, y] = [a, b].map((v) => v.split('-')[0].split('.').map(Number));
183
+ for (let i = 0; i < 3; i++) if ((x[i] ?? 0) !== (y[i] ?? 0)) return (x[i] ?? 0) > (y[i] ?? 0);
184
+ return false;
185
+ };
186
+
187
+ async function checkForUpdate() {
188
+ if (process.env.CI) return;
189
+ try {
190
+ const res = await fetch(`https://registry.npmjs.org/${pkg.name}/latest`, { signal: AbortSignal.timeout(4000) });
191
+ const { version } = await res.json();
192
+ if (typeof version === 'string' && newer(version, pkg.version)) {
193
+ console.log(`\n cubefarm ${version} is out (this is ${pkg.version}). Start it with ${green('npx cubefarm@latest')} to update.\n`);
194
+ }
195
+ } catch {
196
+ // offline, or the registry is slow: try again next time
197
+ }
198
+ }
199
+
200
+ // ---------- commands ----------
201
+
202
+ if (command === 'doctor') {
203
+ console.log(`\n cubefarm ${pkg.version}\n`);
204
+ const results = checks();
205
+ results.forEach(printCheck);
206
+ const ok = results.every((c) => c.ok);
207
+ console.log(ok ? `\n All set. Start the office with: npx cubefarm\n` : '');
208
+ process.exit(ok ? 0 : 1);
209
+ }
210
+
211
+ if (command === 'login') {
212
+ const claude = claudeBinary();
213
+ if (!claude) fail(`Claude Code for ${process.platform}-${process.arch} is missing. Reinstall cubefarm.`);
214
+ console.log('\n Signing in to Claude. Your agents use this login and your subscription.\n');
215
+ const res = spawnSync(claude, ['auth', 'login'], { stdio: 'inherit', env: claudeEnv() });
216
+ process.exit(res.status ?? 1);
217
+ }
218
+
219
+ if (command !== 'start') fail(`Unknown command "${command}". Run npx cubefarm --help for the options.`);
220
+
221
+ const port = Number(values.port ?? process.env.SWARM_PORT ?? 4317);
222
+ if (!Number.isInteger(port) || port < 0 || port > 65535) fail(`"${values.port ?? process.env.SWARM_PORT}" isn't a port number.`);
223
+ const url = `http://localhost:${port}`;
224
+ const demo = values.demo || process.env.SWARM_DEMO === '1' || process.env.SWARM_DEMO === 'true';
225
+
226
+ // A second office on the same state would resume every agent's session a second time.
227
+ if (port !== 0 && (await portOpen(port))) {
228
+ if (!(await isOffice(`http://127.0.0.1:${port}`))) fail(`Something else is using port ${port}. Start cubefarm on another one: npx cubefarm --port 4400`);
229
+ console.log(`\n cubefarm is already running: ${url}\n`);
230
+ if (!values['no-open']) openBrowser(url);
231
+ process.exit(0);
232
+ }
233
+
234
+ if (!demo) {
235
+ const problems = checks().filter((c) => !c.ok);
236
+ if (problems.length) {
237
+ console.log('');
238
+ problems.forEach(printCheck);
239
+ if (problems.some((c) => c.required)) fail('cubefarm can’t run without these.');
240
+ console.log(dim('\n Starting anyway. Fix the above, and check again with: npx cubefarm doctor'));
241
+ }
242
+ }
243
+
244
+ const entry = path.join(root, 'dist-server', 'index.js');
245
+ if (!fs.existsSync(entry)) fail(`${path.relative(process.cwd(), entry) || entry} is missing. In a checkout of the repo, run npm run build first.`);
246
+
247
+ process.env.SWARM_PORT = String(port);
248
+ if (demo) process.env.SWARM_DEMO = '1';
249
+ process.setSourceMapsEnabled(true);
250
+ // The server loads its state, then listens.
251
+ await import(pathToFileURL(entry).href);
252
+
253
+ if (!values['no-open'] && port !== 0) {
254
+ for (let i = 0; i < 100 && !(await portOpen(port)); i++) await new Promise((r) => setTimeout(r, 200));
255
+ openBrowser(url);
256
+ }
257
+ void checkForUpdate();
@@ -0,0 +1,2 @@
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/rapier-DO_XuNdK.js","assets/index-uo74me0U.js","assets/index-8tdBZH9c.css"])))=>i.map(i=>d[i]);
2
+ import{A as e,C as t,D as n,E as r,N as i,O as a,S as o,T as s,_ as c,a as l,b as u,c as d,d as f,f as p,g as m,h,i as g,j as _,k as v,l as y,m as b,n as x,o as S,p as C,r as w,s as T,t as E,u as D,v as O,w as k,x as A,y as ee}from"./index-uo74me0U.js";import{a as te,i as j,n as ne,r as re,t as M}from"./rapier-DO_XuNdK.js";var N=i(_());function ie(e,t){if(typeof e!=`object`||!e)return e;var n=e[Symbol.toPrimitive];if(n!==void 0){var r=n.call(e,t||`default`);if(typeof r!=`object`)return r;throw TypeError(`@@toPrimitive must return a primitive value.`)}return(t===`string`?String:Number)(e)}function P(e){var t=ie(e,`string`);return typeof t==`symbol`?t:t+``}function F(e,t,n){return(t=P(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function ae(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function I(e){for(var t=1;t<arguments.length;t++){var n=arguments[t]==null?{}:arguments[t];t%2?ae(Object(n),!0).forEach(function(t){F(e,t,n[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):ae(Object(n)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}var oe=new n;new o;var se=new e;new r;var L=new k,R=new e,z=new n,B=new e,ce=t=>{let[n,r,i]=t;return new e(n,r,i)},le=({x:e,y:t,z:n,w:r})=>oe.set(e,t,n,r),ue=e=>{if(Array.isArray(e))return new j(e[0],e[1],e[2]);if(typeof e==`number`)return new j(e,e,e);{let t=e;return new j(t.x,t.y,t.z)}},de={fixed:1,dynamic:0,kinematicPosition:2,kinematicVelocity:3},fe=e=>de[e],pe=(e,t)=>{let n=Array.from(e);for(let r=0;r<e.length/3;r++)n[r*3]*=t.x,n[r*3+1]*=t.y,n[r*3+2]*=t.z;return n},me=t=>t?t instanceof n?[t.x,t.y,t.z,t.w]:t instanceof e||t instanceof o?[t.x,t.y,t.z]:Array.isArray(t)?t:[t]:[0];function V(e){let t=(0,N.useRef)(void 0);return t.current===void 0&&(t.current={value:typeof e==`function`?e():e}),t.current.value}var he=e=>{let t=(0,N.useRef)(e),n=(0,N.useRef)(0),r=(0,N.useRef)(0);(0,N.useEffect)(()=>{t.current=e},[e]),(0,N.useEffect)(()=>{let e=()=>{let i=performance.now(),a=i-r.current;n.current=requestAnimationFrame(e),t.current(a/1e3),r.current=i};return n.current=requestAnimationFrame(e),()=>cancelAnimationFrame(n.current)},[])},ge=({onStep:e,updatePriority:t})=>(b((t,n)=>{e(n)},t),null),_e=({onStep:e})=>(he(t=>{e(t)}),null),ve=(0,N.memo)(({onStep:e,type:t,updatePriority:n})=>t===`independent`?N.createElement(_e,{onStep:e}):N.createElement(ge,{onStep:e,updatePriority:n}));function ye(e,t){if(e==null)return{};var n={};for(var r in e)if({}.hasOwnProperty.call(e,r)){if(t.indexOf(r)!==-1)continue;n[r]=e[r]}return n}function be(e,t){if(e==null)return{};var n,r,i=ye(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r<a.length;r++)n=a[r],t.indexOf(n)===-1&&{}.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var xe=[`mass`,`linearDamping`,`angularDamping`,`type`,`onCollisionEnter`,`onCollisionExit`,`onIntersectionEnter`,`onIntersectionExit`,`onContactForce`,`children`,`canSleep`,`ccd`,`gravityScale`,`softCcdPrediction`,`ref`],Se=(e,t,n)=>{let r=t.slice();if(e===`heightfield`){let e=r[3];return e.x*=n.x,e.x*=n.y,e.x*=n.z,r}if(e===`trimesh`||e===`convexHull`)return r[0]=pe(r[0],n),r;let i=[n.x,n.y,n.z,n.x,n.x];return r.map((e,t)=>i[t]*e)},Ce=(e,t,n,r)=>{let i=Se(e.shape,e.args,n),a=re[e.shape](...i);return t.createCollider(a,r?.())},we=[`shape`,`args`],Te="Please pick ONLY ONE of the `density`, `mass` and `massProperties` options.",Ee=(e,t)=>{if(t.density!==void 0){if(t.mass!==void 0||t.massProperties!==void 0)throw Error(Te);e.setDensity(t.density);return}if(t.mass!==void 0){if(t.massProperties!==void 0)throw Error(Te);e.setMass(t.mass);return}t.massProperties!==void 0&&e.setMassProperties(t.massProperties.mass,t.massProperties.centerOfMass,t.massProperties.principalAngularInertia,t.massProperties.angularInertiaLocalFrame)},De={sensor:(e,t)=>{e.setSensor(t)},collisionGroups:(e,t)=>{e.setCollisionGroups(t)},solverGroups:(e,t)=>{e.setSolverGroups(t)},friction:(e,t)=>{e.setFriction(t)},frictionCombineRule:(e,t)=>{e.setFrictionCombineRule(t)},restitution:(e,t)=>{e.setRestitution(t)},restitutionCombineRule:(e,t)=>{e.setRestitutionCombineRule(t)},activeCollisionTypes:(e,t)=>{e.setActiveCollisionTypes(t)},contactSkin:(e,t)=>{e.setContactSkin(t)},quaternion:()=>{},position:()=>{},rotation:()=>{},scale:()=>{}},Oe=Object.keys(De),ke=(e,t,n)=>{let r=n.get(e.handle);if(r){let n=r.object.parent.getWorldScale(se),i=r.worldParent?.matrixWorld.clone().invert();r.object.updateWorldMatrix(!0,!1),L.copy(r.object.matrixWorld),i&&L.premultiply(i),L.decompose(R,z,B),e.parent()?(e.setTranslationWrtParent({x:R.x*n.x,y:R.y*n.y,z:R.z*n.z}),e.setRotationWrtParent(z)):(e.setTranslation({x:R.x*n.x,y:R.y*n.y,z:R.z*n.z}),e.setRotation(z)),Oe.forEach(n=>{if(n in t){let r=t[n];De[n](e,r,t)}}),Ee(e,t)}},Ae=(e,t,n)=>{let r=(0,N.useMemo)(()=>Oe.flatMap(e=>me(t[e])),[t]);(0,N.useEffect)(()=>{ke(e(),t,n)},[...r,e])},je=e=>{let t=!1;return e.traverseAncestors(e=>{e.userData.r3RapierType===`MeshCollider`&&(t=!0)}),t},Me=(e,t,n)=>({collider:e,worldParent:n||void 0,object:t}),Ne={cuboid:`cuboid`,ball:`ball`,hull:`convexHull`,trimesh:`trimesh`},Pe=({object:e,ignoreMeshColliders:t=!0,options:n})=>{let r=[];e.updateWorldMatrix(!0,!1);let i=e.matrixWorld.clone().invert(),a=e=>{if(`isMesh`in e){if(t&&je(e))return;let a=e.getWorldScale(B),s=Ne[n.colliders||`cuboid`];e.updateWorldMatrix(!0,!1),L.copy(e.matrixWorld).premultiply(i).decompose(R,z,B);let c=new o().setFromQuaternion(z,`XYZ`),{geometry:l}=e,{args:u,offset:d}=Fe(l,n.colliders||`cuboid`),f=I(I({},Re(n)),{},{args:u,shape:s,rotation:[c.x,c.y,c.z],position:[R.x+d.x*a.x,R.y+d.y*a.y,R.z+d.z*a.z],scale:[a.x,a.y,a.z]});r.push(f)}};return n.includeInvisible?e.traverse(a):e.traverseVisible(a),r},Fe=(t,n)=>{switch(n){case`cuboid`:{t.computeBoundingBox();let{boundingBox:n}=t,r=n.getSize(new e);return{args:[r.x/2,r.y/2,r.z/2],offset:n.getCenter(new e)}}case`ball`:{t.computeBoundingSphere();let{boundingSphere:e}=t;return{args:[e.radius],offset:e.center}}case`trimesh`:{let n=t.index?t.clone():C(t);return{args:[n.attributes.position.array,n.index?.array],offset:new e}}case`hull`:return{args:[t.clone().attributes.position.array],offset:new e}}return{args:[],offset:new e}},Ie=e=>({collision:!!(e!=null&&e.onCollisionEnter||e!=null&&e.onCollisionExit||e!=null&&e.onIntersectionEnter||e!=null&&e.onIntersectionExit),contactForce:!!(e!=null&&e.onContactForce)}),Le=(e,t,n,r={})=>{let{onCollisionEnter:i,onCollisionExit:a,onIntersectionEnter:o,onIntersectionExit:s,onContactForce:c}=t;(0,N.useEffect)(()=>{let l=e();if(l){let{collision:e,contactForce:u}=Ie(t),d=e||r.collision,f=u||r.contactForce;d&&f?l.setActiveEvents(M.COLLISION_EVENTS|M.CONTACT_FORCE_EVENTS):d?l.setActiveEvents(M.COLLISION_EVENTS):f&&l.setActiveEvents(M.CONTACT_FORCE_EVENTS),n.set(l.handle,{onCollisionEnter:i,onCollisionExit:a,onIntersectionEnter:o,onIntersectionExit:s,onContactForce:c})}return()=>{l&&n.delete(l.handle)}},[i,a,o,s,c,r])},Re=(e={})=>be(e,xe),ze=e=>{let t=(0,N.useRef)(e);return(0,N.useEffect)(()=>{t.current=e},[e]),t},H=()=>{let e=(0,N.useContext)(Ge);if(!e)throw Error(`react-three-rapier: useRapier must be used within <Physics />!`);return e},Be=e=>{let{beforeStepCallbacks:t}=H(),n=ze(e);(0,N.useEffect)(()=>(t.add(n),()=>{t.delete(n)}),[])},Ve=e=>{let{afterStepCallbacks:t}=H(),n=ze(e);(0,N.useEffect)(()=>(t.add(n),()=>{t.delete(n)}),[])},He=(e,t,n=!0)=>{let[r,i]=(0,N.useState)([]);return(0,N.useEffect)(()=>{e.current&&t.colliders!==!1&&i(Pe({object:e.current,options:t,ignoreMeshColliders:n}))},[t.colliders]),r},Ue=(0,N.memo)(()=>{let{world:e}=H(),t=(0,N.useRef)(null);return b(()=>{let n=t.current;if(!n)return;let r=e.debugRender(),i=new ee;i.setAttribute(`position`,new O(r.vertices,3)),i.setAttribute(`color`,new O(r.colors,4)),n.geometry.dispose(),n.geometry=i}),N.createElement(`group`,null,N.createElement(`lineSegments`,{ref:t,frustumCulled:!1},N.createElement(`lineBasicMaterial`,{color:16777215,vertexColors:!0}),N.createElement(`bufferGeometry`,null)))}),We=e=>{let t;return{proxy:new Proxy({},{get(n,r){return t||=e(),Reflect.get(t,r)},set(n,r,i){return t||=e(),Reflect.set(t,r,i)}}),reset:()=>{t=void 0},set:e=>{t=e}}},Ge=(0,N.createContext)(void 0),Ke=(e,t)=>({target:{rigidBody:e.rigidBody.object,collider:e.collider.object,colliderObject:e.collider.state?.object,rigidBodyObject:e.rigidBody.state?.object},other:{rigidBody:t.rigidBody.object,collider:t.collider.object,colliderObject:t.collider.state?.object,rigidBodyObject:t.rigidBody.state?.object},rigidBody:t.rigidBody.object,collider:t.collider.object,colliderObject:t.collider.state?.object,rigidBodyObject:t.rigidBody.state?.object}),qe=async()=>{let e=await f(()=>import(`./rapier-DO_XuNdK.js`).then(e=>e.o),__vite__mapDeps([0,1,2]));return await e.init(),e},Je=e=>{let{colliders:n=`cuboid`,children:r,timeStep:i=1/60,paused:a=!1,interpolate:o=!0,updatePriority:s,updateLoop:c=`follow`,debug:l=!1,gravity:u=[0,-9.81,0],allowedLinearError:d=.001,predictionDistance:f=.002,numSolverIterations:p=4,numInternalPgsIterations:g=1,minIslandSize:_=128,maxCcdSubsteps:v=1,contactNaturalFrequency:y=30,lengthUnit:b=1}=e,x=m(qe,[`@react-thee/rapier`,qe]),{invalidate:S}=h(),C=V(()=>new Map),w=V(()=>new Map),T=V(()=>new Map),E=V(()=>new Map),D=V(()=>new ne(!1)),O=V(()=>new Set),k=V(()=>new Set),A=V(()=>({filterContactPair:(...e)=>{for(let t of O){let n=t.current(...e);if(n!==null)return n}return null},filterIntersectionPair:(...e)=>{for(let t of k)if(t.current(...e)===!1)return!1;return!0}})),ee=V(()=>new Set),te=V(()=>new Set),{proxy:j,reset:re,set:M}=V(()=>We(()=>new x.World(ce(u))));(0,N.useEffect)(()=>()=>{j.free(),re()},[]),(0,N.useEffect)(()=>{j.gravity=ue(u),j.integrationParameters.numSolverIterations=p,j.integrationParameters.numInternalPgsIterations=g,j.integrationParameters.normalizedAllowedLinearError=d,j.integrationParameters.minIslandSize=_,j.integrationParameters.maxCcdSubsteps=v,j.integrationParameters.normalizedPredictionDistance=f,j.lengthUnit=b,j.integrationParameters.contact_natural_frequency=y},[j,...u,p,g,d,_,v,f,b,y]);let ie=(0,N.useCallback)(e=>{let t=j.getCollider(e),n=E.get(e),r=w.get(e),i=t?.parent()?.handle,a=i===void 0?void 0:j.getRigidBody(i),o=a&&i!==void 0?T.get(i):void 0,s=i===void 0?void 0:C.get(i);return{collider:{object:t,events:n,state:r},rigidBody:{object:a,events:o,state:s}}},[]),[P]=(0,N.useState)({previousState:{},accumulator:0}),F=(0,N.useCallback)(e=>{let n=j,r=i===`vary`,s=t.clamp(e,0,.5),c=e=>{ee.forEach(e=>{e.current(n)}),n.timestep=e;let t=O.size>0||k.size>0;n.step(D,t?A:void 0),te.forEach(e=>{e.current(n)})};if(r)c(s);else for(P.accumulator+=s;P.accumulator>=i;)o&&(P.previousState={},n.forEachRigidBody(e=>{P.previousState[e.handle]={position:e.translation(),rotation:e.rotation()}})),c(i),P.accumulator-=i;let l=r||!o||a?1:P.accumulator/i;C.forEach((e,t)=>{let r=n.getRigidBody(t),i=T.get(t);if(i!=null&&i.onSleep||i!=null&&i.onWake){if(r.isSleeping()&&!e.isSleeping){var a;i==null||(a=i.onSleep)==null||a.call(i)}if(!r.isSleeping()&&e.isSleeping){var o;i==null||(o=i.onWake)==null||o.call(i)}e.isSleeping=r.isSleeping()}if(!r||r.isSleeping()&&!(`isInstancedMesh`in e.object)||!e.setMatrix)return;let s=r.translation(),c=r.rotation(),u=P.previousState[t];u&&(L.compose(u.position,le(u.rotation),e.scale).premultiply(e.invertedWorldMatrix).decompose(R,z,B),e.meshType==`mesh`&&(e.object.position.copy(R),e.object.quaternion.copy(z))),L.compose(s,le(c),e.scale).premultiply(e.invertedWorldMatrix).decompose(R,z,B),e.meshType==`instancedMesh`?e.setMatrix(L):(e.object.position.lerp(R,l),e.object.quaternion.slerp(z,l))}),D.drainCollisionEvents((e,t,r)=>{let i=ie(e),a=ie(t);if(!(i!=null&&i.collider.object)||!(a!=null&&a.collider.object))return;let o=Ke(i,a),s=Ke(a,i);if(r)n.contactPair(i.collider.object,a.collider.object,(e,t)=>{var n,r,c,l,u,d,f,p;(n=i.rigidBody.events)==null||(r=n.onCollisionEnter)==null||r.call(n,I(I({},o),{},{manifold:e,flipped:t})),(c=a.rigidBody.events)==null||(l=c.onCollisionEnter)==null||l.call(c,I(I({},s),{},{manifold:e,flipped:t})),(u=i.collider.events)==null||(d=u.onCollisionEnter)==null||d.call(u,I(I({},o),{},{manifold:e,flipped:t})),(f=a.collider.events)==null||(p=f.onCollisionEnter)==null||p.call(f,I(I({},s),{},{manifold:e,flipped:t}))});else{var c,l,u,d,f,p,m,h;(c=i.rigidBody.events)==null||(l=c.onCollisionExit)==null||l.call(c,o),(u=a.rigidBody.events)==null||(d=u.onCollisionExit)==null||d.call(u,s),(f=i.collider.events)==null||(p=f.onCollisionExit)==null||p.call(f,o),(m=a.collider.events)==null||(h=m.onCollisionExit)==null||h.call(m,s)}if(r){if(n.intersectionPair(i.collider.object,a.collider.object)){var g,_,v,y,b,x,S,C;(g=i.rigidBody.events)==null||(_=g.onIntersectionEnter)==null||_.call(g,o),(v=a.rigidBody.events)==null||(y=v.onIntersectionEnter)==null||y.call(v,s),(b=i.collider.events)==null||(x=b.onIntersectionEnter)==null||x.call(b,o),(S=a.collider.events)==null||(C=S.onIntersectionEnter)==null||C.call(S,s)}}else{var w,T,E,D,O,k,A,ee;(w=i.rigidBody.events)==null||(T=w.onIntersectionExit)==null||T.call(w,o),(E=a.rigidBody.events)==null||(D=E.onIntersectionExit)==null||D.call(E,s),(O=i.collider.events)==null||(k=O.onIntersectionExit)==null||k.call(O,o),(A=a.collider.events)==null||(ee=A.onIntersectionExit)==null||ee.call(A,s)}}),D.drainContactForceEvents(e=>{var t,n,r,i,a,o,s,c;let l=ie(e.collider1()),u=ie(e.collider2());if(!(l!=null&&l.collider.object)||!(u!=null&&u.collider.object))return;let d=Ke(l,u),f=Ke(u,l);(t=l.rigidBody.events)==null||(n=t.onContactForce)==null||n.call(t,I(I({},d),{},{totalForce:e.totalForce(),totalForceMagnitude:e.totalForceMagnitude(),maxForceDirection:e.maxForceDirection(),maxForceMagnitude:e.maxForceMagnitude()})),(r=u.rigidBody.events)==null||(i=r.onContactForce)==null||i.call(r,I(I({},f),{},{totalForce:e.totalForce(),totalForceMagnitude:e.totalForceMagnitude(),maxForceDirection:e.maxForceDirection(),maxForceMagnitude:e.maxForceMagnitude()})),(a=l.collider.events)==null||(o=a.onContactForce)==null||o.call(a,I(I({},d),{},{totalForce:e.totalForce(),totalForceMagnitude:e.totalForceMagnitude(),maxForceDirection:e.maxForceDirection(),maxForceMagnitude:e.maxForceMagnitude()})),(s=u.collider.events)==null||(c=s.onContactForce)==null||c.call(s,I(I({},f),{},{totalForce:e.totalForce(),totalForceMagnitude:e.totalForceMagnitude(),maxForceDirection:e.maxForceDirection(),maxForceMagnitude:e.maxForceMagnitude()}))}),n.forEachActiveRigidBody(()=>{S()})},[a,i,o,j]),ae=(0,N.useMemo)(()=>({rapier:x,world:j,setWorld:e=>{M(e)},physicsOptions:{colliders:n,gravity:u},rigidBodyStates:C,colliderStates:w,rigidBodyEvents:T,colliderEvents:E,beforeStepCallbacks:ee,afterStepCallbacks:te,isPaused:a,isDebug:l,step:F,filterContactPairHooks:O,filterIntersectionPairHooks:k}),[a,F,l,n,u]),oe=(0,N.useCallback)(e=>{a||F(e)},[a,F]);return N.createElement(Ge.Provider,{value:ae},N.createElement(ve,{onStep:oe,type:c,updatePriority:s}),l&&N.createElement(Ue,null),r)};function U(){return U=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)({}).hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},U.apply(null,arguments)}var Ye=(e,t=null)=>{let n=(0,N.useRef)(t);return e&&typeof e!=`function`?(e.current||=n.current,e):n},Xe=(e,t,n)=>{let r=(0,N.useRef)(void 0),i=(0,N.useCallback)(()=>(r.current||=e(),r.current),n);return(0,N.useEffect)(()=>{let e=i(),n=()=>t(e);return()=>{n(),r.current=void 0}},[i]),i},Ze=e=>{let t=fe(e?.type||`dynamic`),n=new te(t);return n.canSleep=e?.canSleep??!0,n},Qe=({rigidBody:e,object:t,setMatrix:n,getMatrix:r,worldScale:i,meshType:a=`mesh`})=>(t.updateWorldMatrix(!0,!1),{object:t,rigidBody:e,invertedWorldMatrix:t.parent.matrixWorld.clone().invert(),setMatrix:n||(e=>{t.matrix.copy(e)}),getMatrix:r||(e=>e.copy(t.matrix)),scale:i||t.getWorldScale(B).clone(),isSleeping:!1,meshType:a}),$e=[`args`,`colliders`,`canSleep`],et={gravityScale:(e,t)=>{e.setGravityScale(t,!0)},additionalSolverIterations(e,t){e.setAdditionalSolverIterations(t)},linearDamping:(e,t)=>{e.setLinearDamping(t)},angularDamping:(e,t)=>{e.setAngularDamping(t)},dominanceGroup:(e,t)=>{e.setDominanceGroup(t)},enabledRotations:(e,[t,n,r])=>{e.setEnabledRotations(t,n,r,!0)},enabledTranslations:(e,[t,n,r])=>{e.setEnabledTranslations(t,n,r,!0)},lockRotations:(e,t)=>{e.lockRotations(t,!0)},lockTranslations:(e,t)=>{e.lockTranslations(t,!0)},angularVelocity:(e,[t,n,r])=>{e.setAngvel({x:t,y:n,z:r},!0)},linearVelocity:(e,[t,n,r])=>{e.setLinvel({x:t,y:n,z:r},!0)},ccd:(e,t)=>{e.enableCcd(t)},softCcdPrediction:(e,t)=>{e.setSoftCcdPrediction(t)},userData:(e,t)=>{e.userData=t},type(e,t){e.setBodyType(fe(t),!0)},position:()=>{},rotation:()=>{},quaternion:()=>{},scale:()=>{}},tt=Object.keys(et),nt=(e,t,n,r=!0)=>{if(!e)return;let i=n.get(e.handle);i&&(r&&(i.object.updateWorldMatrix(!0,!1),L.copy(i.object.matrixWorld).decompose(R,z,B),e.setTranslation(R,!1),e.setRotation(z,!1)),tt.forEach(n=>{n in t&&et[n](e,t[n])}))},rt=(e,t,n,r=!0)=>{let i=(0,N.useMemo)(()=>tt.flatMap(e=>me(t[e])),[t]);(0,N.useEffect)(()=>{nt(e(),t,n,r)},i)},it=(e,t,n)=>{let{onWake:r,onSleep:i,onCollisionEnter:a,onCollisionExit:o,onIntersectionEnter:s,onIntersectionExit:c,onContactForce:l}=t,u={onWake:r,onSleep:i,onCollisionEnter:a,onCollisionExit:o,onIntersectionEnter:s,onIntersectionExit:c,onContactForce:l};(0,N.useEffect)(()=>{let t=e();return n.set(t.handle,u),()=>{n.delete(t.handle)}},[r,i,a,o,s,c,l])},at=({x:t,y:n,z:r}={x:0,y:0,z:0})=>new e(t,n,r),W=(0,N.memo)(e=>{let{children:t,position:n,rotation:r,quaternion:i,scale:a,name:o}=e,{world:s,colliderEvents:c,colliderStates:l}=H(),u=_t(),d=Ye(e.ref),f=(0,N.useRef)(null),p=Xe(()=>{let t=f.current.getWorldScale(at()),n=Ce(e,s,t,u?.getRigidBody);return typeof e.ref==`function`&&e.ref(n),d.current=n,n},e=>{s.getCollider(e.handle)&&s.removeCollider(e,!0)},[...we.flatMap(t=>Array.isArray(e[t])?e[t]:[e[t]]),u]);(0,N.useEffect)(()=>{let e=p();return l.set(e.handle,Me(e,f.current,u?.ref.current)),()=>{l.delete(e.handle)}},[p]);let m=(0,N.useMemo)(()=>I(I({},Re(u?.options)),e),[e,u?.options]);return Ae(p,m,l),Le(p,m,c,Ie(u?.options)),N.createElement(`object3D`,{position:n,rotation:r,quaternion:i,scale:a,ref:f,name:o},t)}),G=N.forwardRef((e,t)=>N.createElement(W,U({},e,{shape:`cuboid`,ref:t})));G.displayName=`CuboidCollider`;var ot=N.forwardRef((e,t)=>N.createElement(W,U({},e,{shape:`roundCuboid`,ref:t})));ot.displayName=`RoundCuboidCollider`;var st=N.forwardRef((e,t)=>N.createElement(W,U({},e,{shape:`ball`,ref:t})));st.displayName=`BallCollider`;var ct=N.forwardRef((e,t)=>N.createElement(W,U({},e,{shape:`capsule`,ref:t})));ct.displayName=`CapsuleCollider`;var lt=N.forwardRef((e,t)=>N.createElement(W,U({},e,{shape:`heightfield`,ref:t})));lt.displayName=`HeightfieldCollider`;var ut=N.forwardRef((e,t)=>N.createElement(W,U({},e,{shape:`trimesh`,ref:t})));ut.displayName=`TrimeshCollider`;var dt=N.forwardRef((e,t)=>N.createElement(W,U({},e,{shape:`cone`,ref:t})));dt.displayName=`ConeCollider`;var ft=N.forwardRef((e,t)=>N.createElement(W,U({},e,{shape:`roundCone`,ref:t})));ft.displayName=`RoundConeCollider`;var pt=N.forwardRef((e,t)=>N.createElement(W,U({},e,{shape:`cylinder`,ref:t})));pt.displayName=`CylinderCollider`,pt.displayName=`RoundCylinderCollider`;var mt=N.forwardRef((e,t)=>N.createElement(W,U({},e,{shape:`convexHull`,ref:t})));mt.displayName=`ConvexHullCollider`;var ht=[`ref`,`children`,`type`,`position`,`rotation`,`scale`,`quaternion`,`transformState`],gt=(0,N.createContext)(void 0),_t=()=>(0,N.useContext)(gt),K=(0,N.memo)(e=>{let{ref:t,children:n,type:r,position:i,rotation:a,scale:o,quaternion:s,transformState:c}=e,l=be(e,ht),u=(0,N.useRef)(null),d=Ye(t),{world:f,rigidBodyStates:p,physicsOptions:m,rigidBodyEvents:h}=H(),g=(0,N.useMemo)(()=>I(I(I({},m),e),{},{children:void 0}),[m,e]),_=$e.flatMap(e=>Array.isArray(g[e])?[...g[e]]:g[e]),v=He(u,g),y=Xe(()=>{let e=Ze(g),n=f.createRigidBody(e);return typeof t==`function`&&t(n),d.current=n,n},e=>{f.getRigidBody(e.handle)&&f.removeRigidBody(e)},_);(0,N.useEffect)(()=>{let t=y(),n=Qe({rigidBody:t,object:u.current});return p.set(t.handle,e.transformState?e.transformState(n):n),()=>{p.delete(t.handle)}},[y]),rt(y,g,p),it(y,g,h);let b=(0,N.useMemo)(()=>({ref:u,getRigidBody:y,options:g}),[y]);return N.createElement(gt.Provider,{value:b},N.createElement(`object3D`,U({ref:u},l,{position:i,rotation:a,quaternion:s,scale:o}),n,v.map((e,t)=>N.createElement(W,U({key:t},e)))))});K.displayName=`RigidBody`;var vt=(0,N.memo)(e=>{let{children:t,type:n}=e,{physicsOptions:r}=H(),i=(0,N.useRef)(null),{options:a}=_t(),o=He(i,(0,N.useMemo)(()=>I(I(I({},r),a),{},{children:void 0,colliders:n}),[r,a]),!1);return N.createElement(`object3D`,{ref:i,userData:{r3RapierType:`MeshCollider`}},t,o.map((e,t)=>N.createElement(W,U({key:t},e))))});vt.displayName=`MeshCollider`;var yt=[`ref`],bt=[`children`,`instances`,`colliderNodes`,`position`,`rotation`,`quaternion`,`scale`],xt=(0,N.memo)(e=>{let{ref:t}=e,n=be(e,yt),r=Ye(t,[]),i=(0,N.useRef)(null),a=(0,N.useRef)(null),{children:o,instances:s,colliderNodes:c=[],position:l,rotation:u,quaternion:d,scale:f}=n,p=be(n,bt),m=He(i,I(I({},n),{},{children:void 0})),h=()=>{let e=a.current.children[0];if(e&&`isInstancedMesh`in e)return e};(0,N.useEffect)(()=>{let e=h();e?e.instanceMatrix.setUsage(A):console.warn(`InstancedRigidBodies expects exactly one child, which must be an InstancedMesh`)},[]);let g=(e,t)=>{let n=h();return n?I(I({},e),{},{getMatrix:e=>(n.getMatrixAt(t,e),e),setMatrix:e=>{n.setMatrixAt(t,e),n.instanceMatrix.needsUpdate=!0},meshType:`instancedMesh`}):e};return N.createElement(`object3D`,U({ref:i},p,{position:l,rotation:u,quaternion:d,scale:f}),N.createElement(`object3D`,{ref:a},o),s?.map((e,t)=>N.createElement(K,U({},p,e,{ref:e=>{r.current[t]=e},transformState:e=>g(e,t)}),N.createElement(N.Fragment,null,c.map((e,t)=>N.createElement(N.Fragment,{key:t},e)),m.map((e,t)=>N.createElement(W,U({key:t},e)))))))});xt.displayName=`InstancedRigidBodies`;var St=(e,t)=>(Ct(e)<<16)+(t===void 0?65535:Ct(t)),Ct=e=>[e].flat().reduce((e,t)=>e|1<<t,0),q=c(),wt=`#1f1d2b`,Tt={beach:{kind:`beach`,r:.4,restitution:.8,density:1.2,damping:.9},exercise:{kind:`exercise`,r:.45,restitution:.7,density:1.6,damping:.9},yarn:{kind:`yarn`,r:.33,restitution:.35,density:2.5,damping:1.3}},J=(e,t,n,r)=>({id:e,...Tt[t],start:{x:n,y:Tt[t].r+.02,z:r}}),Et={office:[J(`beach-ball`,`beach`,6.6,6.8),J(`exercise-ball`,`exercise`,9.8,8.6),J(`yarn-ball`,`yarn`,12.2,6.2)],lobby:[J(`beach-ball`,`beach`,-11,5.6),J(`exercise-ball`,`exercise`,-7.6,8.2),J(`yarn-ball`,`yarn`,-5.4,4.4)]},Dt=e=>Math.abs(e.x)>16||Math.abs(e.z)>12||e.y<-.5||e.y>6,Y=[`#ef476f`,`#ffffff`,`#ffd166`,`#ffffff`,`#118ab2`,`#ffffff`],X=null;function Ot(){if(X)return X;let e=new Uint8Array(Y.length*10*4);for(let t=0;t<10;t++)for(let n=0;n<Y.length;n++){let r=parseInt((t===0||t===9?`#ffffff`:Y[n]).slice(1),16),i=(t*Y.length+n)*4;e[i]=r>>16&255,e[i+1]=r>>8&255,e[i+2]=r&255,e[i+3]=255}return X=new u(e,Y.length,10,a),X.colorSpace=v,X.minFilter=s,X.magFilter=s,X.generateMipmaps=!1,X.needsUpdate=!0,X}var kt=`#8338ec`,At=`#ff70a6`,jt=[[0,0,0],[Math.PI/2,0,0],[.6,0,1.1],[-.7,.3,-1],[1.2,.9,.4]];function Mt({def:e}){let{r:t}=e;return e.kind===`beach`?(0,q.jsxs)(`mesh`,{castShadow:!0,material:g(`beach-ball`,Ot()),children:[(0,q.jsx)(`sphereGeometry`,{args:[t,24,16]}),(0,q.jsx)(p,{thickness:.02,color:wt})]}):e.kind===`exercise`?(0,q.jsxs)(`group`,{children:[(0,q.jsxs)(`mesh`,{castShadow:!0,material:w(kt),children:[(0,q.jsx)(`sphereGeometry`,{args:[t,24,16]}),(0,q.jsx)(p,{thickness:.02,color:wt})]}),(0,q.jsx)(`mesh`,{rotation:[Math.PI/2,0,0],material:w(x(kt,-.14)),children:(0,q.jsx)(`torusGeometry`,{args:[t*1.002,.018,6,40]})}),(0,q.jsx)(`mesh`,{position:[0,0,t],rotation:[Math.PI/2,0,0],material:w(`#f8f9fa`),children:(0,q.jsx)(`cylinderGeometry`,{args:[.045,.05,.04,12]})})]}):(0,q.jsxs)(`group`,{children:[(0,q.jsxs)(`mesh`,{castShadow:!0,material:w(At),children:[(0,q.jsx)(`sphereGeometry`,{args:[t,20,14]}),(0,q.jsx)(p,{thickness:.02,color:wt})]}),jt.map((e,n)=>(0,q.jsx)(`mesh`,{rotation:e,material:w(x(At,n%2?-.12:.08)),children:(0,q.jsx)(`torusGeometry`,{args:[t*1.005,.022,6,36]})},n)),(0,q.jsx)(`mesh`,{position:[t*.55,-t*.62,t*.45],rotation:[.4,0,.9],material:w(x(At,-.12)),children:(0,q.jsx)(`capsuleGeometry`,{args:[.022,.3,3,6]})})]})}var Nt=1/60,Z={building:0,doorway:1,pusher:2,toys:3},Pt=St(Z.doorway,[Z.toys]),Ft=St(Z.pusher,[Z.building,Z.toys]),It=St(Z.toys,[Z.building,Z.doorway,Z.pusher,Z.toys]),Lt=St(Z.building,[Z.pusher,Z.toys]),Q=T();function Rt({floor:e}){let t=(0,N.useMemo)(()=>e===`office`?y():d(),[e]);return(0,q.jsxs)(K,{type:`fixed`,colliders:!1,children:[(0,q.jsx)(G,{args:[17,.5,16],position:[0,-.5,2],friction:.8,restitution:.5,collisionGroups:Lt}),(0,q.jsx)(G,{args:[17,.5,16],position:[0,S+.5,2],collisionGroups:Lt}),(0,q.jsx)(G,{args:[(Q.maxX-Q.minX)/2,S/2,(Q.maxZ-Q.minZ)/2],position:[(Q.minX+Q.maxX)/2,S/2,(Q.minZ+Q.maxZ)/2],collisionGroups:Pt}),t.map((e,t)=>{let n=e.h??3.6;return(0,q.jsx)(G,{args:[(e.maxX-e.minX)/2,n/2,(e.maxZ-e.minZ)/2],position:[(e.minX+e.maxX)/2,n/2,(e.minZ+e.maxZ)/2],friction:.6,restitution:.5,collisionGroups:Lt},t)})]})}var zt={x:0,y:0,z:0},$={half:.62,y:.95,mass:4,maxSpeed:12,maxImpulse:600*Nt,teleport:1.5};function Bt(){let e=h(e=>e.camera),t=(0,N.useRef)(null),n=(0,N.useMemo)(()=>({x:0,y:$.y,z:0}),[]),r=(0,N.useMemo)(()=>({x:0,y:0,z:0}),[]),i=(0,N.useRef)({x:NaN,z:NaN});return Be(()=>{let a=t.current;if(!a)return;let{x:o,z:s}=e.position,c=i.current,l=o===c.x&&s===c.z;if(c.x=o,c.z=s,l&&a.isSleeping())return;let u=a.translation(),d=o-u.x,f=s-u.z,p=Math.hypot(d,f);if(p>$.teleport){n.x=o,n.z=s,a.setTranslation(n,!0),a.setLinvel(zt,!0);return}if(l&&p<.002){a.setLinvel(zt,!1);return}let m=$.maxImpulse*Math.min(1,Math.max(.1,(.7-p)/.4)),h=p>0?Math.min(1/Nt,$.maxSpeed/p):0,g=a.linvel();r.x=$.mass*(d*h-g.x),r.z=$.mass*(f*h-g.z);let _=Math.hypot(r.x,r.z);_>m&&(r.x*=m/_,r.z*=m/_),a.applyImpulse(r,!0)}),(0,q.jsx)(K,{ref:t,colliders:!1,position:[e.position.x,$.y,e.position.z],gravityScale:0,enabledTranslations:[!0,!1,!0],lockRotations:!0,children:(0,q.jsx)(ct,{args:[$.half,l],mass:$.mass,restitution:0,friction:.2,collisionGroups:Ft})})}var Vt={x:0,y:0,z:0,w:1};function Ht(e,t){e.setTranslation(t.start,!0),e.setRotation(Vt,!0),e.setLinvel(zt,!0),e.setAngvel(zt,!0)}function Ut({floor:e}){let t=Et[e],{world:n}=H(),r=(0,N.useRef)([]),i=(0,N.useMemo)(()=>t.map((e,t)=>e=>void(r.current[t]=e)),[t]);(0,N.useEffect)(()=>(E(()=>({bodies:n.bodies.len(),balls:t.map((e,t)=>{let n=r.current[t],i=n?n.translation():e.start;return{id:e.id,x:i.x,y:i.y,z:i.z,sleeping:!n||n.isSleeping()}})})),()=>E(null)),[n,t]);let a=(0,N.useRef)(0);return Ve((0,N.useCallback)(()=>{if(!(++a.current%15))for(let e=0;e<t.length;e++){let n=r.current[e];n&&!n.isSleeping()&&Dt(n.translation())&&Ht(n,t[e])}},[t])),t.map((e,t)=>(0,q.jsxs)(K,{ref:i[t],colliders:!1,position:[e.start.x,e.start.y,e.start.z],linearDamping:e.damping,angularDamping:e.damping,ccd:!0,userData:{toy:e.id},children:[(0,q.jsx)(st,{args:[e.r],restitution:e.restitution,friction:.7,density:e.density,collisionGroups:It}),(0,q.jsx)(Mt,{def:e})]},e.id))}function Wt({floor:e}){let t=D(e=>e.travel!==null);return(0,q.jsxs)(Je,{timeStep:Nt,paused:t,numSolverIterations:8,children:[(0,q.jsx)(Rt,{floor:e}),(0,q.jsx)(Bt,{}),(0,q.jsx)(Ut,{floor:e})]})}var Gt=(0,N.memo)(Wt);export{Gt as default};
@@ -0,0 +1 @@
1
+ :root{--ink:#1f1d2b;--paper:#fffdf6;--muted:#6c7086;--good:#2dc653;--bad:#ef476f;--warn:#ffb703;--accent:#ff8a5b;--font:Fredoka, "Segoe UI", system-ui, sans-serif;--mono:"JetBrains Mono", Consolas, monospace;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}*{box-sizing:border-box}html,body,#root{height:100%;font-family:var(--font);color:var(--ink);background:#bfe3ff;margin:0;overflow:hidden}canvas{display:block}a{color:#3a56d4}kbd{border:2px solid var(--ink);min-width:1.6em;font-family:var(--font);text-align:center;background:#fff;border-bottom-width:4px;border-radius:6px;margin:0 .1em;padding:0 .4em;font-size:.85em;font-weight:600;line-height:1.5;display:inline-block}code{font-family:var(--mono);background:#0000000f;border-radius:5px;padding:1px 5px;font-size:.85em}.muted{color:var(--muted)}.small{font-size:.85rem}.spacer{flex:1}.grow{flex:1;min-width:0}.nowrap{white-space:nowrap}.btn{font-family:var(--font);border:2.5px solid var(--ink);color:var(--ink);box-shadow:0 3px 0 var(--ink);cursor:pointer;white-space:nowrap;background:#fff;border-radius:12px;padding:8px 14px;font-size:.95rem;font-weight:600;transition:transform 60ms,box-shadow 60ms}.btn:hover:not(:disabled){box-shadow:0 4px 0 var(--ink);transform:translateY(-1px)}.btn:active:not(:disabled){box-shadow:0 1px 0 var(--ink);transform:translateY(2px)}.btn:disabled{opacity:.45;cursor:not-allowed}.btn-good{background:#80ed99}.btn-bad{background:#ff8fa3}.btn-ghost{box-shadow:none;background:0 0;border-color:#1f1d2b59}.btn-small{box-shadow:0 2px 0 var(--ink);border-width:2px;border-radius:9px;padding:4px 10px;font-size:.82rem}.btn-ghost.btn-small{box-shadow:none}.btn-big{background:var(--accent);color:#fff;text-shadow:0 2px #0003;box-shadow:0 5px 0 var(--ink);border-radius:18px;padding:14px 32px;font-size:1.3rem}a.btn{text-decoration:none;display:inline-block}input,textarea,select{font-family:var(--font);color:var(--ink);background:#fff;border:2px solid #1f1d2b40;border-radius:10px;outline:none;width:100%;padding:8px 10px;font-size:.95rem}input:focus,textarea:focus,select:focus{border-color:var(--accent)}input[type=checkbox],input[type=radio]{width:auto;accent-color:var(--accent)}input[type=color]{width:42px;height:34px;padding:2px}textarea{resize:vertical}input.inline{background:0 0;border-color:#0000;padding:4px 6px}input.inline:hover,input.inline:focus{background:#fff;border-color:#1f1d2b40}.row{align-items:center;gap:8px;margin:6px 0;display:flex}.row.wrap{flex-wrap:wrap}.toggle{cursor:pointer;align-items:center;gap:6px;font-size:.92rem;display:inline-flex}.toggle.block{align-items:flex-start;margin:8px 0;display:flex}.pill,.chip{border:2px solid var(--ink);background:#fff;border-radius:999px;align-items:center;gap:4px;padding:3px 10px;font-size:.8rem;font-weight:600;display:inline-flex}.chip{color:var(--ink);border-width:1.5px;padding:1px 8px;font-size:.72rem;text-decoration:none}.chip-good,.pill-ok{background:#b9fbc0}.pill-bad{background:#ffc2d1}.pill-demo{background:#ffd166}.dot{border:2px solid var(--ink);border-radius:50%;width:12px;height:12px;display:inline-block}.agent-chip{align-items:center;gap:5px;font-size:.85rem;font-weight:600;display:inline-flex}.status{border:2px solid var(--ink);text-transform:uppercase;letter-spacing:.03em;border-radius:999px;padding:1px 9px;font-size:.75rem;font-weight:600;display:inline-block}.status-idle{background:#e9ecef}.status-preparing{background:#ffe066}.status-working{background:#74c0fc}.status-done{background:#8ce99a}.status-error{background:#ffa8a8}.status-stopped{background:#dee2e6}.status-installing,.status-starting{background:#ffe066}.status-running{background:#8ce99a}.status-unconfigured{background:#e9ecef}.hud{pointer-events:none;z-index:10;position:fixed;inset:0}.hud>*{pointer-events:auto}.hud-floor{background:var(--paper);border:3px solid var(--ink);box-shadow:0 4px 0 var(--ink);border-radius:18px;align-items:center;gap:12px;max-width:min(520px,100vw - 32px);padding:8px 16px 8px 8px;display:flex;position:absolute;top:16px;left:16px}.floor-num{background:var(--accent);border:3px solid var(--ink);color:#fff;text-shadow:0 2px #00000040;border-radius:12px;flex:none;place-items:center;width:46px;height:46px;font-size:1.5rem;font-weight:700;display:grid}.floor-name{text-overflow:ellipsis;white-space:nowrap;font-size:1.1rem;font-weight:700;overflow:hidden}.floor-sub{color:var(--muted);font-size:.85rem}.hud-status{flex-wrap:wrap;justify-content:flex-end;gap:6px;max-width:50vw;display:flex;position:absolute;top:16px;right:16px}.hud-banner{border:3px solid var(--ink);background:#ffe066;border-radius:14px;max-width:90vw;padding:8px 16px;font-weight:600;position:absolute;top:84px;left:50%;transform:translate(-50%)}.crosshair{border:2px solid var(--ink);pointer-events:none;background:#fff;border-radius:50%;width:8px;height:8px;margin:-4px 0 0 -4px;transition:transform .15s;position:absolute;top:50%;left:50%}.crosshair-hot{background:var(--accent);transform:scale(1.8)}.hud-hint{background:var(--paper);border:3px solid var(--ink);box-shadow:0 4px 0 var(--ink);white-space:nowrap;pointer-events:none;border-radius:14px;padding:8px 16px;font-size:1.05rem;font-weight:600;position:absolute;bottom:22%;left:50%;transform:translate(-50%)}.hud-resume{color:#fff;text-shadow:0 2px 0 var(--ink), 0 0 6px var(--ink);pointer-events:none;font-weight:600;position:absolute;top:calc(50% + 26px);left:50%;transform:translate(-50%)}.workers{background:var(--paper);border:3px solid var(--ink);width:min(360px,100vw - 32px);max-height:calc(100vh - 180px);box-shadow:0 4px 0 var(--ink);border-radius:18px;flex-direction:column;display:flex;position:absolute;top:60px;right:16px;overflow:hidden}.workers-closed{width:auto}.wk-head{font-family:var(--font);letter-spacing:.08em;text-transform:uppercase;color:var(--ink);cursor:pointer;text-align:left;background:0 0;border:0;justify-content:space-between;align-items:center;gap:12px;padding:8px 14px;font-size:.8rem;font-weight:600;display:flex}.wk-head b{color:#1e9e45}.wk-chevron{color:var(--muted);letter-spacing:0;text-transform:none}.wk-list{padding:0 6px 6px;overflow-y:auto}.wk-empty{color:var(--muted);padding:4px 10px 8px;font-size:.85rem}.wk-row{width:100%;font-family:var(--font);color:var(--ink);text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;grid-template-columns:auto minmax(0,1fr);align-items:center;gap:10px;padding:5px 8px 5px 26px;display:grid}.wk-row:hover{background:#1f1d2b0f}.wk-dot{border:2px solid var(--ink);border-radius:50%;width:12px;height:12px}.wk-main{flex-direction:column;min-width:0;display:flex}.wk-name{white-space:nowrap;text-overflow:ellipsis;font-size:.95rem;font-weight:600;overflow:hidden}.wk-task{color:var(--muted);margin-left:6px;font-size:.78rem;font-weight:500}.wk-line{gap:6px;min-width:0;display:flex}.wk-last{min-width:0;font-family:var(--mono);color:var(--muted);white-space:nowrap;text-overflow:ellipsis;flex:auto;font-size:.72rem;overflow:hidden}.wk-last-text{font-family:var(--font);color:var(--ink);font-size:.8rem}.wk-last-thinking{font-style:italic}.wk-last-error{color:var(--bad)}.wk-ago{color:var(--muted);flex:none;font-size:.72rem}.wk-group+.wk-group{margin-top:4px}.wk-group-head{width:100%;font-family:var(--font);letter-spacing:.05em;text-transform:uppercase;color:var(--muted);text-align:left;cursor:pointer;background:0 0;border:0;border-radius:10px;align-items:center;gap:8px;padding:6px 8px 2px;font-size:.75rem;font-weight:600;display:flex}.wk-group-head:hover{color:var(--ink)}.wk-group-arrow{width:10px}.wk-swatch{border:2px solid var(--ink);border-radius:4px;flex:none;width:12px;height:12px}.wk-group-name{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--ink);overflow:hidden}.wk-group-count{color:var(--ink);background:#1f1d2b14;border-radius:999px;margin-left:auto;padding:0 7px}@media (width<=700px){.workers{display:none}}.hud-help{pointer-events:none;background:#fffdf6d9;border-radius:12px;padding:6px 10px;font-size:.85rem;position:absolute;bottom:14px;left:16px}.stats{z-index:60;font:10px/1.2 var(--mono);color:#7cffb2;white-space:nowrap;pointer-events:none;background:#15151fcc;border-bottom-right-radius:8px;padding:1px 8px;position:fixed;top:0;left:0}.fade{opacity:0;pointer-events:none;background:#15151f;place-items:center;transition:opacity .6s;display:grid;position:absolute;inset:0}.fade-in{opacity:1}.fade-label{color:#7cffb2;font-size:3rem;font-weight:700}.toasts{z-index:40;flex-direction:column;align-items:flex-end;gap:8px;max-width:min(420px,100vw - 32px);display:flex;position:absolute;bottom:16px;right:16px}.toast{background:var(--paper);border:3px solid var(--ink);box-shadow:0 4px 0 var(--ink);cursor:pointer;border-radius:14px;padding:10px 14px;font-weight:500;animation:.25s ease-out pop}.toast-success{background:#d8f9df}.toast-error{background:#ffd6de}@keyframes pop{0%{opacity:0;transform:translateY(12px)scale(.95)}}.start{z-index:50;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);background:radial-gradient(circle at 30% 20%,#ffd6a5bf,#74c0fccc);place-items:center;padding:16px;display:grid;position:fixed;inset:0}.start-card{background:var(--paper);border:4px solid var(--ink);width:min(640px,100%);box-shadow:0 8px 0 var(--ink);text-align:center;border-radius:28px;max-height:calc(100vh - 32px);padding:28px 32px;overflow:auto}.start-logo{color:var(--accent);font-size:4rem;line-height:1;animation:12s linear infinite spin;display:inline-block}@keyframes spin{to{transform:rotate(360deg)}}.start h1{margin:6px 0 4px;font-size:2.6rem}.start-tag{color:var(--muted);margin:0 0 16px;font-size:1.1rem}.start-list{text-align:left;gap:8px;margin:0 0 22px;padding:0;list-style:none;display:grid}.start-meta{color:var(--muted);flex-wrap:wrap;justify-content:center;align-items:center;gap:10px;margin-top:16px;display:flex}.overlay{z-index:30;background:#15151f59;place-items:center;padding:16px;animation:.15s ease-out fadein;display:grid;position:fixed;inset:0}@keyframes fadein{0%{opacity:0}}.panel{background:var(--paper);border:4px solid var(--ink);width:min(640px,100%);max-height:calc(100vh - 32px);box-shadow:0 8px 0 var(--ink);border-radius:24px;flex-direction:column;animation:.2s ease-out pop;display:flex;overflow:hidden}.panel-wide{width:min(1320px,100%);height:calc(100vh - 32px)}.panel-head{background:var(--accent);border-bottom:4px solid var(--ink);align-items:center;gap:10px;padding:12px 16px;display:flex}.panel-title{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;font-size:1.25rem;font-weight:700;overflow:hidden}.panel-title a{color:var(--ink)}.panel-x{border:3px solid var(--ink);cursor:pointer;background:#fff;border-radius:12px;flex:none;width:36px;height:36px;font-weight:700}.panel-body{flex-direction:column;flex:1;min-height:0;padding:16px;display:flex;overflow:auto}.help h3{margin:14px 0 4px}.help p{margin:0;line-height:1.55}.mouse-settings{border:2px solid var(--ink);background:#fff8ef;border-radius:12px;flex-direction:column;gap:8px;margin-top:10px;padding:10px 12px;display:flex}.mouse-sens{align-items:center;gap:10px;font-size:.92rem;display:flex}.mouse-sens input[type=range]{max-width:260px;accent-color:var(--accent,#ff8a5b);flex:1}.mouse-sens b{font-variant-numeric:tabular-nums;min-width:3.4em}.sound{gap:10px 18px}.sound-volume{flex:200px;align-items:center;gap:8px;max-width:340px;display:flex}.sound-volume input[type=range]{accent-color:var(--accent);background:0 0;border:0;flex:1;padding:0}.sound-pct{text-align:right;font-variant-numeric:tabular-nums;min-width:3.2em}.elevator{flex-direction:column;gap:10px;display:flex}.floor-btn{text-align:left;border:3px solid var(--ink);box-shadow:0 4px 0 var(--ink);cursor:pointer;font-family:var(--font);background:#fff;border-radius:16px;grid-template-rows:auto auto;grid-template-columns:54px 1fr;align-items:center;column-gap:12px;padding:10px;display:grid}.floor-btn:hover{transform:translateY(-1px)}.floor-btn-here{outline:3px dashed var(--accent);outline-offset:3px;background:#fff4e6}.floor-btn-num{border:3px solid var(--ink);background:var(--accent);color:#fff;text-shadow:0 2px #00000040;border-radius:50%;grid-row:span 2;place-items:center;width:54px;height:54px;font-size:1.5rem;font-weight:700;display:grid}.floor-btn-name{text-overflow:ellipsis;white-space:nowrap;font-size:1.1rem;font-weight:700;overflow:hidden}.floor-btn-meta{color:var(--muted);font-size:.85rem}.term-title{align-items:center;gap:10px;display:flex}.avatar{border:3px solid var(--ink);color:#fff;text-shadow:0 1px #0000004d;border-radius:50%;flex:none;place-items:center;width:34px;height:34px;font-weight:700;display:grid}.term-meta{flex-wrap:wrap;align-items:center;gap:6px 14px;margin-bottom:10px;font-size:.92rem;display:flex}.term-error{word-break:break-word;background:#ffe3e3;border:2px solid #ff8787;border-radius:10px;margin-bottom:8px;padding:6px 10px;font-size:.88rem}.term-split{flex:1;grid-template-columns:1fr;gap:12px;min-height:0;display:grid}.term-split-2{grid-template-columns:minmax(0,1.4fr) minmax(0,1fr)}.term{color:#e8e8f2;border:3px solid var(--ink);font-family:var(--mono);background:#1b1b29;border-radius:14px;min-height:200px;padding:12px 14px;font-size:13px;line-height:1.5;overflow:auto}.term-line{white-space:pre-wrap;word-break:break-word}.term-tool{color:#8be9fd}.term-result{color:#8d8da8}.term-system{color:#bd93f9}.term-error.term-line,.term-line.term-error{color:#ff6b6b;font-size:inherit;background:0 0;border:none;margin:0;padding:0}.term-manager{color:#ffb86c;font-weight:600}.term-done{color:#50fa7b;font-weight:600}.term-thinking{color:#f1fa8c;font-style:italic}.term-spin{color:#ff9e64;animation:1.4s ease-in-out infinite blink}@keyframes blink{50%{opacity:.55}}.browser{flex-direction:column;gap:6px;min-height:0;display:flex}.browser-bar{border:3px solid var(--ink);text-overflow:ellipsis;white-space:nowrap;background:#e9ecf2;border-bottom:none;border-radius:14px 14px 0 0;padding:6px 12px;font-size:.85rem;overflow:hidden}.browser-view{border:3px solid var(--ink);background:#fff;border-radius:0 0 14px 14px;flex:1;min-height:0;margin-top:-6px;overflow:auto}.browser-view img{width:100%;display:block}.term-input{gap:8px;margin-top:12px;display:flex}.term-actions{flex-wrap:wrap;align-items:center;gap:8px;margin-top:10px;display:flex}.term-actions select{width:auto;max-width:420px}.kanban-toolbar{flex-wrap:wrap;align-items:center;gap:12px;margin-bottom:12px;display:flex}.issue-form{background:#fff;border:3px dashed #1f1d2b4d;border-radius:16px;flex-direction:column;gap:8px;margin-bottom:12px;padding:12px;display:flex}.issue-form .row select{flex:1}.kanban{flex:1;grid-template-columns:repeat(4,minmax(220px,1fr));gap:12px;min-height:0;display:grid;overflow-x:auto}.kcol{border:3px solid var(--ink);background:#f4f1ea;border-radius:18px;flex-direction:column;min-height:0;display:flex;overflow:hidden}.kcol-head{border-bottom:3px solid var(--ink);padding:10px 12px;font-weight:700}.kcol-backlog .kcol-head{background:#ffd166}.kcol-progress .kcol-head{background:#4cc9f0}.kcol-review .kcol-head{background:#80ed99}.kcol-qa .kcol-head{background:#ff9f68}.kanban-5{grid-template-columns:repeat(5,minmax(210px,1fr))}.kcol-merged .kcol-head{background:#c77dff}.count{border:2px solid var(--ink);text-align:center;background:#fff;border-radius:999px;min-width:24px;padding:0 6px;font-size:.8rem;display:inline-block}.kcol-body{flex-direction:column;gap:8px;padding:10px;display:flex;overflow:auto}.kempty{padding:8px}.kcard{border:2.5px solid var(--ink);background:#fff;border-radius:12px;padding:8px 10px;box-shadow:0 3px #1f1d2bcc}.kcard-warn{background:#fff3d6}.kcard-bad{background:#ffe0e6}.kcard-good{background:#e3fbe7}.kcard-title{font-size:.92rem;font-weight:500;line-height:1.35}.kcard-title a{font-weight:700}.kcard-foot{align-items:center;gap:6px;margin-top:6px;display:flex}.kcard-foot select{width:auto;margin-left:auto;padding:3px 6px;font-size:.82rem}.kcard-foot-wrap{flex-wrap:wrap}.panel-app{width:100%;max-width:1600px}.panel-app .panel-body{padding:12px 16px}.app-title{align-items:center;gap:10px;min-width:0;display:flex}.app-title-name,.app-title-ref{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.app-title-name{flex:0 auto}.app-title-ref{color:var(--ink);opacity:.75;flex:0 2 auto;font-weight:500}.app-title .status{flex:none}.app-toolbar{flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:10px;display:flex}.app-ref{flex:0 auto;width:260px;min-width:0;padding:5px 8px;font-size:.88rem}.seg{border:2px solid var(--ink);box-shadow:0 2px 0 var(--ink);border-radius:9px;display:inline-flex;overflow:hidden}.seg-btn{font-family:var(--font);color:var(--ink);cursor:pointer;white-space:nowrap;background:#fff;border:0;padding:4px 10px;font-size:.82rem;font-weight:600}.seg-btn+.seg-btn{border-left:2px solid var(--ink)}.seg-btn.seg-on{background:var(--accent)}.seg-btn:focus-visible{outline-offset:-3px;outline:3px solid #3a56d4}.app-stage{border:3px solid var(--ink);background:repeating-linear-gradient(45deg,#efeadf 0 12px,#f6f2e9 12px 24px);border-radius:14px;flex:1;justify-content:center;min-height:280px;display:flex;position:relative;overflow:hidden}.app-frame{background:#fff;border:0;width:100%;height:100%;display:block}.app-stage-phone .app-frame{border-left:3px solid var(--ink);border-right:3px solid var(--ink);width:396px;max-width:100%}.app-frame-hint{border:2.5px solid var(--ink);width:min(560px,100% - 24px);box-shadow:0 3px 0 var(--ink);background:#fff3d6;border-radius:12px;padding:10px 14px;font-size:.9rem;line-height:1.4;position:absolute;bottom:16px;left:50%;transform:translate(-50%)}.app-foot{margin:8px 0 0;line-height:1.4}.app-state{text-align:center;background:#fff;border:3px dashed #1f1d2b4d;border-radius:14px;flex-direction:column;flex:1;align-items:center;gap:10px;min-height:0;padding:24px 16px;display:flex;overflow:auto}.app-state h3{margin:0}.app-state p{max-width:560px;margin:0}.app-state-left{text-align:left;align-items:stretch}.app-state-left p{max-width:none}.app-state-icon{font-size:2.6rem;line-height:1}.app-spin{animation:2.4s linear infinite spin}.app-steps{text-align:left;color:var(--muted);margin:0;padding:0;font-weight:500;list-style:none}.app-steps li{padding:2px 0}.app-step-done{color:#1b873a}.app-step-now{color:var(--ink);font-weight:700}.app-log{text-align:left;white-space:pre-wrap;word-break:break-word;width:100%;max-width:900px;min-height:0;max-height:320px;margin:0;font-size:12px}.app-state-left .app-log{flex:0 auto;max-width:none}.app-settings{width:min(640px,100%)}.preview-form{flex-direction:column;gap:2px;display:flex}.preview-form input,.preview-form textarea{font-family:var(--mono);font-size:.85rem}.preview-form p{margin:2px 0 4px}.preview-details summary{cursor:pointer;font-weight:600}.preview-details summary code{word-break:break-all;font-weight:400}.preview-details[open] summary{margin-bottom:6px}.preview-details .status{font-size:.68rem}@media (width<=520px){.panel-app .panel-body{padding:10px}.app-ref{flex:100%;max-width:none}.app-toolbar .spacer,.app-title-ref{display:none}}.tabs{flex-wrap:wrap;gap:6px;margin-bottom:12px;display:flex}.tab{font-family:var(--font);border:2.5px solid var(--ink);cursor:pointer;background:#fff;border-radius:12px;padding:8px 14px;font-size:.95rem;font-weight:600}.tab-on{background:var(--accent);color:#fff;text-shadow:0 1px #00000040;box-shadow:0 3px 0 var(--ink)}.tab-body{flex:1;min-height:0;overflow:auto}.tab-grid{grid-template-columns:minmax(0,1.2fr) minmax(0,1fr);align-items:start;gap:16px;display:grid}@media (width<=900px){.tab-grid{grid-template-columns:1fr}.kanban,.kanban-5{grid-template-columns:repeat(5,250px)}.term-split-2{grid-template-rows:minmax(200px,1fr) auto;grid-template-columns:1fr}}.section{margin:0 0 10px}.card{border:3px solid var(--ink);background:#fff;border-radius:18px;flex-direction:column;gap:6px;margin-bottom:12px;padding:12px 14px;display:flex}.card h3{margin:0 0 4px}.floor-card{border-left:12px solid var(--accent)}.floor-badge{background:var(--accent);border:2.5px solid var(--ink);color:#fff;text-shadow:0 1px #00000040;border-radius:10px;flex:none;place-items:center;width:36px;height:36px;font-weight:700;display:grid}.repo-list{border:2px solid #1f1d2b1f;border-radius:12px;flex-direction:column;gap:4px;max-height:260px;padding:6px;display:flex;overflow:auto}.repo-list.tall{max-height:520px}.repo-row{border-radius:10px;justify-content:space-between;align-items:center;gap:10px;padding:6px 8px;display:flex}.repo-row:hover{background:#fff4e6}.repo-row select{width:auto}.links{font-size:.85rem}.team{border-collapse:collapse;width:100%}.team td{vertical-align:middle;border-top:1.5px solid #1f1d2b14;padding:4px 6px}.team select{width:auto;padding:4px 6px}.field{flex-direction:column;gap:4px;margin:6px 0;font-size:.9rem;font-weight:600;display:flex}.wizard{text-align:left;width:min(680px,100%)}.wizard h1,.wizard .start-logo,.wizard .start-tag,.wizard h2,.wizard-icon{text-align:center}.wizard h2{margin:4px 0 6px;font-size:1.9rem}.wizard-icon{font-size:3rem;line-height:1.1}.wizard .start-logo{width:fit-content;margin:0 auto;display:block}.wizard .btn-big{margin:0 auto;display:block}.wizard-steps{justify-content:center;gap:8px;margin-bottom:10px;display:flex}.wizard-dot{border:2px solid var(--ink);background:#fff;border-radius:50%;width:12px;height:12px}.wizard-dot-done{background:#80ed99}.wizard-dot-on{background:var(--accent);transform:scale(1.25)}.wizard-nav{align-items:center;gap:10px;margin-top:18px;display:flex}.wizard-done{text-align:center;padding:10px 0}.wizard-done p{margin:6px 0}.ceo-setup{align-items:center;gap:18px;display:flex}.ceo-portrait{flex:none;width:120px;height:120px}.swatch{border:2.5px solid var(--ink);cursor:pointer;border-radius:50%;width:24px;height:24px;padding:0}.swatch-on{box-shadow:0 0 0 3px #fff, 0 0 0 5px var(--ink)}.picker{flex-direction:column;gap:8px;display:flex}.picker-tabs{flex-wrap:wrap;gap:6px;display:flex}.picker-tab{font-family:var(--font);border:2.5px solid var(--ink);cursor:pointer;white-space:nowrap;background:#fff;border-radius:12px;flex:1;padding:8px 10px;font-size:.88rem;font-weight:600}.picker-tab-on{background:var(--accent);color:#fff;text-shadow:0 1px #00000040;box-shadow:0 3px 0 var(--ink)}.picker-mode{flex-direction:column;gap:6px;display:flex}.picker-mode .row{margin:0}.folder-list{max-height:240px}.chip-warn{background:#fff3bf}.tour{z-index:45;background:var(--paper);border:3px solid var(--ink);border-left:10px solid var(--accent);width:min(390px,100vw - 32px);box-shadow:0 5px 0 var(--ink);border-radius:18px;padding:10px 14px;animation:.25s ease-out pop;position:fixed;bottom:16px;left:16px}.tour-cheer{border-left-color:var(--good);background:#eafbee}.tour-head{align-items:center;display:flex}.tour-count{color:var(--muted);font-size:.78rem;font-weight:700}.tour-title{margin:2px 0 4px;font-size:1.1rem;font-weight:700}.tour-body{font-size:.9rem;line-height:1.5}.tour-nav{align-items:center;margin-top:8px;display:flex}.ceo-term{flex:none;height:340px}.job-brief{margin:0 0 8px}.job-brief summary{cursor:pointer;font-weight:600}.job-brief .md{margin-top:4px;line-height:1.45}.confirm-overlay{z-index:60;background:#15151f73;place-items:center;padding:16px;animation:.12s ease-out fadein;display:grid;position:fixed;inset:0}.confirm{background:var(--paper);border:4px solid var(--ink);width:min(440px,100%);box-shadow:0 8px 0 var(--ink);text-align:center;border-radius:24px;padding:20px 22px 14px;animation:.18s ease-out pop}.confirm-danger{border-top:12px solid var(--bad)}.confirm-warn{border-top:12px solid var(--warn)}.confirm-good{border-top:12px solid var(--good)}.confirm-icon{font-size:2.4rem;line-height:1}.confirm h3{margin:8px 0 6px;font-size:1.25rem}.confirm-body{color:#4a4e69;margin-bottom:4px;line-height:1.5}.confirm-actions{justify-content:center;gap:10px;margin-top:14px;display:flex}.confirm-keys{margin-top:10px}.btn-warn{background:#ffd166}.badge{background:var(--bad);color:#fff;border:2px solid var(--ink);border-radius:999px;place-items:center;min-width:20px;height:20px;padding:0 6px;font-size:.72rem;font-weight:700;line-height:1;display:inline-grid}.toasts{bottom:88px}.phone-btn{font-family:var(--font);color:var(--ink);background:var(--paper);border:3px solid var(--ink);box-shadow:0 4px 0 var(--ink);cursor:pointer;border-radius:18px;align-items:center;gap:8px;padding:6px 14px 6px 8px;font-size:.9rem;font-weight:600;display:flex;position:absolute;bottom:16px;right:16px}.phone-btn:hover{transform:translateY(-1px)}.phone-btn-icon{font-size:1.7rem;line-height:1;display:inline-block}.phone-btn-ring .phone-btn-icon{animation:2.4s ease-in-out infinite ring}@keyframes ring{0%,72%,to{transform:rotate(0)}76%{transform:rotate(-16deg)}80%{transform:rotate(14deg)}84%{transform:rotate(-10deg)}88%{transform:rotate(8deg)}}.phone-btn-badge{position:absolute;top:-8px;left:26px}.phone-overlay{background:#15151f47;place-items:end;padding:16px 24px}.phone{border:4px solid var(--ink);width:min(400px,100%);height:min(760px,100vh - 32px);box-shadow:0 10px 0 var(--ink), inset 0 0 0 3px #3b3f5c;background:#23263a;border-radius:38px;flex-direction:column;padding:10px 10px 8px;animation:.22s ease-out slideup;display:flex}@keyframes slideup{0%{opacity:0;transform:translateY(40px)}}.phone-status{color:#e9e9f5;justify-content:space-between;align-items:center;padding:2px 16px 8px;font-size:.8rem;font-weight:600;display:flex}.phone-notch{background:#15151f;border-radius:999px;width:90px;height:18px}.phone-screen{background:var(--paper);border-radius:26px 26px 12px 12px;flex-direction:column;flex:1;min-height:0;display:flex;overflow:hidden}.phone-tabs{gap:4px;padding:8px 4px 2px;display:flex}.phone-tab{color:#b8bbd6;font-family:var(--font);cursor:pointer;background:0 0;border:none;border-radius:14px;flex-direction:column;flex:1;align-items:center;gap:2px;padding:6px 4px;font-size:.78rem;font-weight:600;display:flex}.phone-tab-on{color:#fff;background:#3b3f5c}.phone-tab-icon{font-size:1.3rem;position:relative}.badge-dot{min-width:18px;height:18px;font-size:.65rem;position:absolute;top:-6px;right:-14px}.phone-hint{text-align:center;color:#8e92b3;padding-top:4px;font-size:.72rem}.phone-hint kbd{font-size:.7rem}.phone-scroll{flex:1;padding:12px;overflow:auto}.phone-h{align-items:center;gap:6px;margin:10px 2px 8px;font-size:1rem;display:flex}.phone-h:first-child{margin-top:0}.phone-empty{padding:10px 6px;line-height:1.5}.linkish{color:#3a56d4;font-family:var(--font);cursor:pointer;text-align:left;background:0 0;border:none;padding:0;font-weight:600}.phone-chat{flex-direction:column;flex:1;min-height:0;display:flex}.chat-head{background:#fff;border-bottom:2px solid #1f1d2b14;align-items:center;gap:10px;padding:10px 12px;display:flex}.presence-busy{color:#d9480f;font-weight:600}.chat-log{background:#f4f1ea;flex-direction:column;flex:1;gap:8px;padding:10px;display:flex;overflow:auto}.bubble-row{display:flex}.bubble-row-me{justify-content:flex-end}.bubble{border:2px solid var(--ink);border-radius:16px;max-width:86%;padding:8px 11px 5px;font-size:.9rem;line-height:1.4}.bubble-them{background:#fff;border-bottom-left-radius:4px}.bubble-me{background:#ffd6a5;border-bottom-right-radius:4px}.bubble-from{color:var(--muted);font-size:.72rem;font-weight:700}.bubble-text{white-space:pre-wrap;overflow-wrap:anywhere}.bubble:has(.md pre,.md-table){width:86%}.md{overflow-wrap:anywhere;min-width:0}.md>:first-child{margin-top:0}.md>:last-child{margin-bottom:0}.md p,.md ul,.md ol,.md pre,.md blockquote,.md .md-table{margin:0 0 .55em}.md ul,.md ol{padding-left:1.3em}.md li>ul,.md li>ol{margin:.15em 0 .2em}.md li+li{margin-top:.15em}.md li>p{margin:0}.md h1,.md h2,.md h3,.md h4,.md h5,.md h6{margin:.7em 0 .3em;font-size:1em;line-height:1.25}.md h1{font-size:1.15em}.md h2{font-size:1.08em}.md h5,.md h6{color:var(--muted)}.md a{color:#3a56d4;overflow-wrap:anywhere;font-weight:600}.md a:focus-visible{outline:2px solid var(--accent);outline-offset:1px;border-radius:3px}.md-deadlink{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.md code{font-family:var(--mono);background:#1f1d2b14;border-radius:5px;padding:.1em .3em;font-size:.86em}.md pre,.md-table{contain:inline-size;overflow-x:auto}.md pre{background:#1f1d2b14;border-radius:8px;padding:7px 9px;line-height:1.35}.md pre code{white-space:pre;overflow-wrap:normal;background:0 0;padding:0}.md blockquote{color:#4a4e69;border-left:3px solid #1f1d2b40;padding:0 0 0 9px}.md hr{border:none;border-top:1.5px solid #1f1d2b26;margin:.6em 0}.md table{border-collapse:collapse;font-size:.9em}.md th,.md td{text-align:left;overflow-wrap:normal;border:1px solid #1f1d2b33;padding:3px 7px}.md th{background:#1f1d2b0f}.md td code{white-space:nowrap}.md input[type=checkbox]{vertical-align:middle;margin:0 4px 0 0}.md li:has(>input[type=checkbox]){margin-left:-1.1em;list-style:none}.bubble-time{text-align:right;color:var(--muted);margin-top:2px;font-size:.65rem}.bubble-office{text-align:center;color:#4a4e69;background:#1f1d2b12;border-radius:999px;align-self:center;max-width:92%;padding:3px 10px;font-size:.76rem}.bubble-typing{gap:4px;padding:10px 12px;display:flex}.bubble-typing span{background:var(--muted);border-radius:50%;width:7px;height:7px;animation:1.2s infinite typing}.bubble-typing span:nth-child(2){animation-delay:.15s}.bubble-typing span:nth-child(3){animation-delay:.3s}@keyframes typing{0%,60%,to{opacity:.5;transform:translateY(0)}30%{opacity:1;transform:translateY(-4px)}}.quick{background:#fff;gap:6px;padding:6px 10px 0;display:flex;overflow-x:auto}.quick-chip{font-family:var(--font);cursor:pointer;background:#fff;border:1.5px solid #1f1d2b4d;border-radius:999px;flex:none;padding:4px 10px;font-size:.75rem;font-weight:600}.quick-chip:hover{border-color:var(--accent)}.chat-input{background:#fff;gap:6px;padding:8px 10px 10px;display:flex}.resume{border:3px solid var(--ink);color:var(--ink);background:#fff;border-radius:16px;flex-direction:column;gap:6px;margin-bottom:10px;padding:10px 12px;display:flex}.bubble .resume{border-width:2px;margin:6px 0 2px}.resume-letgo{background:#fff4f2}.resume-hot{box-shadow:0 0 0 4px var(--accent)}.resume-done{opacity:.75}.resume-head{align-items:center;gap:10px;display:flex}.resume-name{font-size:1.05rem;font-weight:700}.resume-title{color:var(--muted);font-size:.85rem;font-weight:600}.resume-meta{flex-wrap:wrap;align-items:center;gap:6px;display:flex}.resume-reason{border-left:3px solid #1f1d2b33;padding-left:9px;font-size:.88rem;line-height:1.45}.resume-brief{background:#f6f3ec;border-radius:10px;margin:0;padding:8px;font-size:.82rem;line-height:1.45}.tiles{grid-template-columns:repeat(3,1fr);gap:8px;display:grid}.tile{border:2.5px solid var(--ink);text-align:center;background:#fff;border-radius:14px;padding:8px}.tile-value{white-space:nowrap;font-size:1.15rem;font-weight:700}.tile-label{color:var(--muted);font-size:.7rem;font-weight:600}.report{flex-direction:column;gap:6px;margin:0;padding:0;list-style:none;display:flex}.report li{background:#fff;border:2px solid #1f1d2b1f;border-radius:12px;align-items:flex-start;gap:8px;padding:7px 10px;font-size:.86rem;line-height:1.4;display:flex}.report li.report-good{background:#e7fbe9;border-color:#80ed99}.report li.report-warn{background:#fff4d6;border-color:#ffd166}.proj{border:2.5px solid var(--ink);border-left:10px solid var(--accent);background:#fff;border-radius:14px;margin-bottom:8px;padding:6px 10px 8px}.proj .row{margin:2px 0}.proj .floor-badge{width:30px;height:30px}.proj-name{text-overflow:ellipsis;white-space:nowrap;display:block;overflow:hidden}.proj-sum{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.proj-stats{flex-wrap:wrap;gap:10px;font-weight:600;display:flex}.proj-ready{color:#1b873a}@media (width<=520px){.phone-overlay{place-items:center;padding:8px}.phone-btn-label{display:none}}