@bongos/core 1.19.93 → 1.19.95

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/.bongos-core.json CHANGED
@@ -2,22 +2,22 @@
2
2
  "artifact": "bongos-core",
3
3
  "manifest_schema": 1,
4
4
  "generator": "scripts/gds/package-core.js",
5
- "core_version": "1.19.93",
6
- "core_contract": "1.19.93",
7
- "source_commit": "21cae5c4c8e6499adcb4ae4287a8c54338e4a7eb",
5
+ "core_version": "1.19.95",
6
+ "core_contract": "1.19.95",
7
+ "source_commit": "e6e718ed6edb6fc01565339760fd0fd14cffa2fc",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-08-13T19:19:24.577Z",
9
+ "built_at": "2026-08-13T21:44:20.853Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
- "docs_redacted": 299,
12
+ "docs_redacted": 298,
13
13
  "agent_docs_stubbed": 21,
14
- "functional_verbatim": 1242,
14
+ "functional_verbatim": 1230,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 1562,
20
- "tree_sha256": "3af671c8c0888aff0b695438f56df0dd20a3bd543318b7e6bd9fd287e51050fb",
19
+ "file_count": 1549,
20
+ "tree_sha256": "cb89e58c1034e1681a97ac75f367935e745215ceb83b1aa393639904a0d861db",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/blocker-review/SKILL.md",
@@ -1227,7 +1227,7 @@
1227
1227
  {
1228
1228
  "path": "docs/adr/0152-landing-gate-served-by-the-core-app.md",
1229
1229
  "mode": "0000644",
1230
- "sha256": "fab7be360b4c2cf74cf4e86599af5a0574716bb3df619514afda36e904305f4e"
1230
+ "sha256": "b7375d8e9367b348a0f085df14ca719a26dafb67029f0cbe0218ad30efef7743"
1231
1231
  },
1232
1232
  {
1233
1233
  "path": "docs/adr/0152-metic-task-abandonment.md",
@@ -4127,12 +4127,12 @@
4127
4127
  {
4128
4128
  "path": "package-lock.json",
4129
4129
  "mode": "0000644",
4130
- "sha256": "e4d2e6a8bbc8133d751d3de125ceae75921e7f70767ed1924e13d5290dd09a3a"
4130
+ "sha256": "0ebfc1c5594ff344281bfc92013bb8cf8f871c7f02152c91ae847cde36f2de3d"
4131
4131
  },
4132
4132
  {
4133
4133
  "path": "package.json",
4134
4134
  "mode": "0000644",
4135
- "sha256": "64a4916ec09c80ea3c845e65fa9e31551091509c86438e180b488154d70e4249"
4135
+ "sha256": "b192fbde61e9428fff6a9b7077f341abe1f6d930e009496b93c6fb44eca2a45c"
4136
4136
  },
4137
4137
  {
4138
4138
  "path": "public-docs/index.html",
@@ -4372,7 +4372,7 @@
4372
4372
  {
4373
4373
  "path": "public-landing/assets/cosmos.css",
4374
4374
  "mode": "0000644",
4375
- "sha256": "85fbc4908f2a288b324e58beb490df67d44df294da1a3422347795077770ee1c"
4375
+ "sha256": "106a8508531bbb320f72e76e22bffdb4249c2024a9a3711050ba5c54a966ce7e"
4376
4376
  },
4377
4377
  {
4378
4378
  "path": "public-landing/buddha.png",
@@ -4384,80 +4384,15 @@
4384
4384
  "mode": "0000644",
4385
4385
  "sha256": "efcc1894c06d1f4755028ae554707a61f8ef6e6753d7d7554f94e3748910f155"
4386
4386
  },
4387
- {
4388
- "path": "public-landing/gate/README.md",
4389
- "mode": "0000644",
4390
- "sha256": "8c6d2ce03e2a3169d7d7bb4a8b5f42c955e0bb64e14d42161dcdd9109e9f9ed5"
4391
- },
4392
- {
4393
- "path": "public-landing/gate/dispmaps/bar.png",
4394
- "mode": "0000644",
4395
- "sha256": "35c4fc1ca951f1425b29e76ed370e9802e8f1f343f51070d0ee8bac06be70cb2"
4396
- },
4397
- {
4398
- "path": "public-landing/gate/dispmaps/flat.png",
4399
- "mode": "0000644",
4400
- "sha256": "32b7f0f2ea60904b8dd151425eed3459b2fdfca540c579def212a5cf01b406e2"
4401
- },
4402
- {
4403
- "path": "public-landing/gate/dispmaps/key.png",
4404
- "mode": "0000644",
4405
- "sha256": "5c6f1444e1b71b8edac5479ef347f390114ccad4a34339e8ab68924c9060ba6d"
4406
- },
4407
- {
4408
- "path": "public-landing/gate/index.html",
4409
- "mode": "0000644",
4410
- "sha256": "4fabebb37446cb55803332d89909dd25b905ebbff5f3dd7d607d383122095800"
4411
- },
4412
- {
4413
- "path": "public-landing/gate/previews/cloud-bongos.jpg",
4414
- "mode": "0000644",
4415
- "sha256": "d38164c645c07f9248655f5474d354778c93c35807b4a4180489039edbf3f5ea"
4416
- },
4417
- {
4418
- "path": "public-landing/gate/previews/off-the-boats.jpg",
4419
- "mode": "0000644",
4420
- "sha256": "64f4fa38ca377df8d1ee5b814bde5c245b5bd3e83cf537ad15ac92f0e95b7ecd"
4421
- },
4422
- {
4423
- "path": "public-landing/gate/space.css",
4424
- "mode": "0000644",
4425
- "sha256": "e6ab371ee547125e6d28098ee367a9021bff4d2dde566448ef014c08a6ebed44"
4426
- },
4427
- {
4428
- "path": "public-landing/gate/space.js",
4429
- "mode": "0000644",
4430
- "sha256": "68a14a35d2dc4f801c265208c1899da1d072f5274e68c965e37aff88fb2abf2e"
4431
- },
4432
- {
4433
- "path": "public-landing/gate/sprites/enter.png",
4434
- "mode": "0000644",
4435
- "sha256": "d60776f6f6eb8daf645d85edfeb46d7173a88122d9fe669f9e24103d3133d7d8"
4436
- },
4437
- {
4438
- "path": "public-landing/gate/sprites/search.png",
4439
- "mode": "0000644",
4440
- "sha256": "6b80ae019451264d1cd187268a45caa53f013c727331442f65034fddcb135665"
4441
- },
4442
- {
4443
- "path": "public-landing/gate/sprites/terminal-blank.png",
4444
- "mode": "0000644",
4445
- "sha256": "1417e50ec3da32018c76a9dc41be5cfb87c663b61935f9d18ca88d67373aa4f8"
4446
- },
4447
- {
4448
- "path": "public-landing/gate/sprites/tile.png",
4449
- "mode": "0000644",
4450
- "sha256": "f101868879d35002b92a294a5572003fb4e8954aa9cba5db4b7791e297c32abf"
4451
- },
4452
4387
  {
4453
4388
  "path": "public-landing/index.html",
4454
4389
  "mode": "0000644",
4455
- "sha256": "38292de5ed174fad5fe806a9adf369cf6ecd7df9377e79e05d2834e48a21b766"
4390
+ "sha256": "bddfd05afb8b2c93155efff9bfc6812a7e213ed7f4d66a8d4cd2db3c899d0088"
4456
4391
  },
4457
4392
  {
4458
4393
  "path": "public-landing/projects.html",
4459
4394
  "mode": "0000644",
4460
- "sha256": "1bced570761a901c4dab1247f160ca9230e38762058c000e75c955783c11ed09"
4395
+ "sha256": "6edf67839bd80ba22672ebfb9f5c38a2bbce980157024b50d71ce9bae211c8db"
4461
4396
  },
4462
4397
  {
4463
4398
  "path": "public-landing/world/assets/clouds/cloud-island-large.png",
@@ -5182,7 +5117,7 @@
5182
5117
  {
5183
5118
  "path": "scripts/gds/run-unit-tests.js",
5184
5119
  "mode": "0000644",
5185
- "sha256": "ce8856194aa805d1f62d47d4b04bac05168c0c748f869a094476a28b9db2a30a"
5120
+ "sha256": "5ff111416adc80a4513bc91488185f5f47800f16c1fe8b746e0982ee9b4ffeb9"
5186
5121
  },
5187
5122
  {
5188
5123
  "path": "scripts/gds/sandbox-stage.js",
@@ -5602,7 +5537,7 @@
5602
5537
  {
5603
5538
  "path": "src/bongos/module-scope-map.js",
5604
5539
  "mode": "0000644",
5605
- "sha256": "a5c939de500ba39d12f09e053e3b9079510716cb4af18cad0e13482952b02904"
5540
+ "sha256": "61817b4fa47b01ae1df612fcb7b82a28ff23b6265f6513eb0a28bc3aca6ea8d3"
5606
5541
  },
5607
5542
  {
5608
5543
  "path": "src/bongos/module-submissions.js",
@@ -5767,7 +5702,7 @@
5767
5702
  {
5768
5703
  "path": "src/module-api.js",
5769
5704
  "mode": "0000644",
5770
- "sha256": "02e87e7c76ab2408a3607fb48e52ba81b5123d9563bff1b05fdfa2f7ff7ac0e3"
5705
+ "sha256": "401742f230ab25aa8dbb410a1b8bf1bfbe02257a360362fa7488f3965a88c5ca"
5771
5706
  },
5772
5707
  {
5773
5708
  "path": "src/module-loader/loader.js",
@@ -5802,7 +5737,7 @@
5802
5737
  {
5803
5738
  "path": "src/platform-server.js",
5804
5739
  "mode": "0000644",
5805
- "sha256": "2f43ee21d8268940b0f76e20db0fcaf1220f1cb264363fc61c2856d9943b14c9"
5740
+ "sha256": "181c9850c374b653136a259d80e276ac285d2ee5f55fadeb9109aef418190d91"
5806
5741
  },
5807
5742
  {
5808
5743
  "path": "src/ui/adapters/claude-design/index.js",
@@ -6539,11 +6474,6 @@
6539
6474
  "mode": "0000644",
6540
6475
  "sha256": "df503f198853f6fe3497a2e0317d90bee69cc275f024027ad569ee1cef0155b6"
6541
6476
  },
6542
- {
6543
- "path": "tests/gate_launch_metadata.mjs",
6544
- "mode": "0000644",
6545
- "sha256": "235dfa1ea341b596ea4981fe8375650150387a60c9093c3a1a7b761222ee259a"
6546
- },
6547
6477
  {
6548
6478
  "path": "tests/gate_review.mjs",
6549
6479
  "mode": "0000644",
@@ -6922,7 +6852,7 @@
6922
6852
  {
6923
6853
  "path": "tests/known_failing_ratchet.mjs",
6924
6854
  "mode": "0000644",
6925
- "sha256": "0afe138db1e80744f79a352c7ac5e0e998c05d07885aa23d047b9da2ed4be237"
6855
+ "sha256": "256e691e969fbdb1f023b4673f181ced0663f344ed3a307c40e6531eb6585735"
6926
6856
  },
6927
6857
  {
6928
6858
  "path": "tests/land_watch.mjs",
@@ -6932,7 +6862,7 @@
6932
6862
  {
6933
6863
  "path": "tests/landing_page.mjs",
6934
6864
  "mode": "0000644",
6935
- "sha256": "0399fd099ef383f1bb174e9997bccdc5c4e5cd26cdef94f56131641ecbde05c8"
6865
+ "sha256": "be7364e749e00920e65f16ca685bee1e068d62adf9dc9ed0817381f2d0af4c7e"
6936
6866
  },
6937
6867
  {
6938
6868
  "path": "tests/lifecycle-card.mjs",
@@ -7279,6 +7209,11 @@
7279
7209
  "mode": "0000644",
7280
7210
  "sha256": "dc354f082204073409aea90df9b91f149d474c6930b761ed5c9928bb76e1358f"
7281
7211
  },
7212
+ {
7213
+ "path": "tests/projects_hub.mjs",
7214
+ "mode": "0000644",
7215
+ "sha256": "e3e3e3f8164da3f897166dca2b0502fac2519483b36282e91ef5645cfef6c18c"
7216
+ },
7282
7217
  {
7283
7218
  "path": "tests/provision.mjs",
7284
7219
  "mode": "0000644",
@@ -1,6 +1,6 @@
1
1
  # 0152 — The landing gate is served by the core app, not by the front proxy
2
2
 
3
- - **Status:** Accepted
3
+ - **Status:** Accepted — *amended 2026-08-13 at launch, see [§Amendment](#<redacted>)*
4
4
  - **Date:** 2026-07-27
5
5
  - **Deciders:** Lars (owner / Archon), Claude
6
6
  - **Track:** `internal` (platform serving / instance infra)
@@ -54,6 +54,19 @@ example.com, www.example.com {
54
54
  }
55
55
  ```
56
56
 
57
+ ### Amendment — 2026-08-13: the lock and the apex `redir` come out at launch
58
+
59
+ The block above carries a comment that was true when written and is **no longer true**: *"The app issues the same 302, so this line stays correct after the lock is removed."* The gate-navigation work ([#1002743](https://example.com/builders#/task/1002743)) retired the entry ritual — `/gate/` is gone, `GET /` on the apex now serves the landing page directly (200, not a 302), and the projects hub at `/projects` became the signed-in front door. So at launch, `redir / /gate/ 302` does not "stay correct": it points the apex at a path the app now 404s.
60
+
61
+ **The launch edit is therefore a delete of the whole `@gate` block, not just its `basic_auth`.** Both the lock *and* the `redir` come out together, and every path the matcher covered (`/`, `/gate`, `/gate/*`, `/world/*`) falls through to the catch-all — which is byte-identical in its proxy config, so the only behavioural deltas are "no password" and "no apex redirect". Performed on the reference instance 2026-08-13 ([#1002768](https://example.com/builders#/task/1002768), closing blocker [#1000111](https://example.com/builders#/blocker/1000111)).
62
+
63
+ Two things about §Decision survive the amendment intact, and they are why the edit was a one-block delete rather than a re-plumb:
64
+
65
+ - **The `@hidden` guard is independent.** It is matched *above* the gate block, so removing the gate does not widen exposure — `*.md`/`*.json`/`*.py` and the `proof`/`refs`/`references` paths still `respond 404`.
66
+ - **"Lifting the lock is a one-block delete, not a re-plumb"** — the load-bearing claim of §Decision — held exactly as designed. What changed is only its *scope*: the delete takes the `redir` with it.
67
+
68
+ Generalised for any instance: the pre-launch lock and the apex redirect are **one removable unit**, and an instance that has retired its gate must drop both at once. Leaving the `redir` behind after the app stops serving `/gate/` sends every apex visitor to a 404 — the exact failure this amendment records.
69
+
57
70
  Three consequences follow, and they are the actual decision:
58
71
 
59
72
  **1. There is no separate Caddy config to "give" the landing.** The block above is the *generic* instance block — the same pure-proxy shape `provision.js`'s `caddyBlock()` already writes for every provisioned instance ([ADR 0111](<redacted>.md) §4). The landing stopped being a special case in the proxy the moment the app could serve it. Host-specific values (hostnames, cert paths, whether a lock exists) stay on the host, per [ADR 0062](<redacted>.md) §7 — the core ships the *shape*, not an instance's hostnames.
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.93",
3
+ "version": "1.19.95",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.93",
9
+ "version": "1.19.95",
10
10
  "license": "AGPL-3.0-or-later",
11
11
  "dependencies": {
12
12
  "express": "^4.21.2",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.93",
3
+ "version": "1.19.95",
4
4
  "description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "main": "src/platform-server.js",
@@ -293,6 +293,12 @@ h1{font-family:var(--font-display);font-weight:700;font-size:clamp(24px,3.4vw,32
293
293
  .confirmRow{display:flex;gap:10px;align-items:center;flex-wrap:wrap;font-size:13.5px;font-weight:700;color:var(--err);}
294
294
  .confirmRow .plain{font-weight:600;color:var(--ink-soft);}
295
295
  .finePrint{font-size:12.5px;color:var(--ink-faint);font-weight:600;margin-top:16px;line-height:1.5;}
296
+ /* a link in fine print still has to look like one — without this it fell back to
297
+ the browser's default blue, the one colour in the page that is not ours */
298
+ .finePrint a{color:var(--accent-deep);font-weight:700;text-decoration:none;}
299
+ .finePrint a:hover{text-decoration:underline;}
300
+ :root[data-mode="dark"] .finePrint a{color:var(--sun-lit);}
301
+ @media (prefers-color-scheme: dark){:root:not([data-mode="light"]) .finePrint a{color:var(--sun-lit);}}
296
302
 
297
303
  /* ── progress strips: one visual, per-flow vocabularies (direction §4).
298
304
  Default = the sea-blue "working" look; .isLive = verified-landed green;
@@ -359,6 +365,240 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
359
365
  .foot{position:relative;z-index:1;text-align:center;font-size:13px;font-weight:600;color:var(--foot-ink);padding:0 20px 34px;text-shadow:var(--chrome-glow);}
360
366
  .foot code{font-family:var(--font-mono);color:var(--foot-code);}
361
367
 
368
+ /* ═══════════════════════════════════════════════════════════════════════
369
+ THE PROJECT HUB (task 1002743, direction docs/design/projects-hub-direction.md §6)
370
+
371
+ /projects is one surface holding three views — map (sky | table), your
372
+ projects, new project — under a persistent shell. These components were
373
+ authored page-locally in the approved mock and GRADUATE here, so the hub
374
+ and the manage cards are one system rather than two that resemble each
375
+ other. Every dark override is written TWICE (the explicit hook and the
376
+ prefers-color-scheme twin); tests/cosmos_token_contrast.mjs compares them.
377
+ ═══════════════════════════════════════════════════════════════════════ */
378
+
379
+ /* ── the persistent shell header. It belongs to the PAGE, not to the orb
380
+ field, so it never drifts, scales or pans with the sky — and it renders in
381
+ EVERY view including the table, which is what the gate's
382
+ `body:not(.view-space)` rule got wrong. ── */
383
+ .shell{position:relative;z-index:40;display:flex;align-items:center;justify-content:space-between;
384
+ gap:clamp(10px,2vw,22px);padding:16px clamp(16px,4vw,34px);flex-wrap:wrap;}
385
+ /* The wordmark TYPES, so its width changes every ~190ms. As the middle child of
386
+ a space-between row the nav would slide with it. Equal `flex:1 1 0` flanks
387
+ compute to the same width regardless of content, which pins the nav dead
388
+ centre and lets the mark change width inside its own box. Measured: 0px drift
389
+ across seven typed states. */
390
+ .shell .mark{flex:1 1 0;min-width:0;
391
+ font-family:var(--font-display);font-weight:700;font-size:22px;letter-spacing:-0.5px;
392
+ text-transform:lowercase;color:var(--chrome-ink);text-decoration:none;text-shadow:var(--chrome-glow);
393
+ white-space:nowrap;}
394
+ .shell .mark span{color:var(--chrome-accent);}
395
+ .shell .caret{color:var(--chrome-accent);animation:cbBlink 1s step-end infinite;font-weight:400;}
396
+ @keyframes cbBlink{50%{opacity:0;}}
397
+ .shell-right{flex:1 1 0;display:flex;align-items:center;justify-content:flex-end;
398
+ gap:clamp(8px,1.6vw,14px);flex-wrap:wrap;}
399
+
400
+ /* the three destinations, in the .btn-space glass voice so the nav and the
401
+ chrome buttons read as one family */
402
+ .navrow{flex:0 0 auto;display:flex;align-items:center;gap:4px;background:var(--glass-bg);
403
+ border:1.5px solid var(--glass-border);border-radius:999px;padding:4px;backdrop-filter:blur(4px);}
404
+ .navrow a{font-family:var(--font-display);font-weight:700;font-size:13.5px;color:var(--glass-ink);
405
+ text-decoration:none;padding:7px 15px;border-radius:999px;white-space:nowrap;text-shadow:var(--chrome-glow);}
406
+ .navrow a:hover{color:var(--chrome-accent);}
407
+ .navrow a[aria-current="page"]{background:var(--accent);color:#fff;text-shadow:none;}
408
+ /* Home LEAVES the hub, so it never takes the current-view pill and a hairline
409
+ sets it apart. Without it the header offered no way back to the landing page
410
+ except the wordmark, which nothing announced as a link. */
411
+ .navrow .navHome{color:var(--glass-ink);opacity:.9;}
412
+ .navrow .navHome:hover{opacity:1;color:var(--chrome-accent);}
413
+ .navSep{width:1px;align-self:stretch;margin:4px 3px;background:var(--glass-border);flex:0 0 auto;}
414
+ /* below the mid breakpoint the three groups stack and centre rather than
415
+ fighting for one row — equal flanks only buy a centred nav while it fits */
416
+ @media (max-width:820px){
417
+ .shell{justify-content:center;}
418
+ .shell .mark,.shell-right{flex:0 0 auto;}
419
+ .navrow{order:3;flex-basis:100%;justify-content:center;}
420
+ }
421
+
422
+ /* ── the per-view control strip: Sky|Table, the only-mine filter, search ── */
423
+ .toolbar{position:relative;z-index:30;display:flex;align-items:center;gap:12px;flex-wrap:wrap;
424
+ padding:0 clamp(16px,4vw,34px) 16px;}
425
+ .seg{display:inline-flex;background:var(--glass-bg);border:1.5px solid var(--glass-border);
426
+ border-radius:999px;padding:4px;gap:4px;backdrop-filter:blur(4px);}
427
+ .seg button{font-family:var(--font-display);font-weight:700;font-size:13px;color:var(--glass-ink);
428
+ background:none;border:none;cursor:pointer;padding:7px 16px;border-radius:999px;text-shadow:var(--chrome-glow);}
429
+ .seg button[aria-pressed="true"]{background:var(--accent);color:#fff;text-shadow:none;}
430
+
431
+ .search{flex:1;min-width:190px;max-width:460px;display:flex;align-items:center;gap:9px;
432
+ background:var(--glass-bg);border:1.5px solid var(--glass-border);border-radius:999px;
433
+ padding:8px 16px;backdrop-filter:blur(4px);}
434
+ .search svg{width:15px;height:15px;fill:none;stroke:var(--glass-ink);stroke-width:2;flex:0 0 auto;}
435
+ .search input{flex:1;min-width:0;background:none;border:none;outline:none;font-family:var(--font-mono);
436
+ font-size:13px;color:var(--chrome-ink);}
437
+ .search input::placeholder{color:var(--glass-ink);opacity:.75;}
438
+ .search .count{font-family:var(--font-mono);font-size:11.5px;color:var(--glass-ink);white-space:nowrap;}
439
+
440
+ /* the only-mine filter — a real two-state control, not a link: it filters the
441
+ SAME data in place rather than navigating */
442
+ .switch{display:inline-flex;align-items:center;gap:9px;cursor:pointer;background:var(--glass-bg);
443
+ border:1.5px solid var(--glass-border);border-radius:999px;padding:7px 15px 7px 11px;
444
+ font-family:var(--font-display);font-weight:700;font-size:13px;color:var(--glass-ink);
445
+ backdrop-filter:blur(4px);text-shadow:var(--chrome-glow);white-space:nowrap;}
446
+ .switch input{position:absolute;opacity:0;pointer-events:none;}
447
+ .switch .track{width:32px;height:18px;border-radius:999px;background:color-mix(in srgb,var(--ink) 22%,transparent);
448
+ position:relative;flex:0 0 auto;transition:background .16s ease;}
449
+ .switch .track::after{content:"";position:absolute;top:2px;left:2px;width:14px;height:14px;border-radius:50%;
450
+ background:#fff;transition:transform .16s ease;box-shadow:0 1px 3px rgba(0,0,0,.3);}
451
+ .switch input:checked + .track{background:var(--accent);}
452
+ .switch input:checked + .track::after{transform:translateX(14px);}
453
+ .switch input:focus-visible + .track{outline:2px solid var(--accent);outline-offset:2px;}
454
+ .switch:has(input:checked){color:var(--chrome-accent);border-color:var(--accent);}
455
+
456
+ /* ── the orb field. Ground B (owner, 2026-08-13): dual-mode full-bleed — the
457
+ sky IS the page and follows light/dark, which is what --sky-bg/--star-display
458
+ already encode. Deliberately STATIC: where things sit is this pass, how the
459
+ sky moves is not. Component-scoped custom properties only — a page-local or
460
+ component `:root` would outrank the branding pack spliced after this file. ── */
461
+ .fieldWrap{padding:0;max-width:none;}
462
+ .field{position:relative;height:calc(100vh - 232px);min-height:340px;overflow:hidden;
463
+ --grid-ink:color-mix(in srgb,var(--rule) 92%,transparent);}
464
+ .field .ground{position:absolute;inset:0;background:var(--sky-bg);}
465
+ /* the gravity sheet, flattened: two gradients under a radial mask */
466
+ .field .ground::after{content:"";position:absolute;inset:-10%;
467
+ background-image:linear-gradient(var(--grid-ink) 1px,transparent 1px),
468
+ linear-gradient(90deg,var(--grid-ink) 1px,transparent 1px);
469
+ background-size:74px 74px;
470
+ -webkit-mask-image:radial-gradient(ellipse 62% 58% at 50% 50%,#000 12%,transparent 78%);
471
+ mask-image:radial-gradient(ellipse 62% 58% at 50% 50%,#000 12%,transparent 78%);}
472
+ .field .stars{position:absolute;inset:0;display:var(--star-display);}
473
+ .field .stars i{position:absolute;width:2px;height:2px;border-radius:50%;background:#fff;opacity:.55;}
474
+
475
+ /* a project rendered as an orb. Colour comes from tokens, never a literal, so
476
+ every accent follows a re-theme. */
477
+ .worldOrb{position:absolute;transform:translate(-50%,-50%);display:flex;flex-direction:column;
478
+ align-items:center;gap:9px;cursor:pointer;background:none;border:none;padding:0;text-decoration:none;}
479
+ .worldOrb .disc{width:var(--d,96px);height:var(--d,96px);border-radius:50%;position:relative;
480
+ background:radial-gradient(circle at 34% 28%,color-mix(in srgb,var(--acc) 22%,#fff) 0%,
481
+ var(--acc) 42%,color-mix(in srgb,var(--acc) 72%,#000) 100%);
482
+ box-shadow:0 0 34px -4px color-mix(in srgb,var(--acc) 55%,transparent);}
483
+ .worldOrb .disc::after{content:"";position:absolute;inset:0;border-radius:50%;
484
+ border:1.5px solid color-mix(in srgb,#fff 55%,transparent);}
485
+ .worldOrb .nm{font-family:var(--font-display);font-weight:700;font-size:14.5px;color:var(--chrome-ink);
486
+ text-shadow:var(--chrome-glow);white-space:nowrap;}
487
+ .worldOrb .sub{font-family:var(--font-mono);font-size:11.5px;color:var(--chrome-accent);
488
+ text-shadow:var(--chrome-glow);white-space:nowrap;}
489
+ /* z-index 3: the disc's ::after rim is positioned and later in DOM order, so
490
+ without this the badge is painted over by the orb it labels */
491
+ .worldOrb .badge{position:absolute;top:-11px;left:50%;transform:translateX(-50%);z-index:3;
492
+ font-family:var(--font-mono);font-size:10.5px;font-weight:500;padding:2px 9px;border-radius:999px;
493
+ white-space:nowrap;background:var(--gold-soft);color:#2b170e;
494
+ box-shadow:0 2px 8px color-mix(in srgb,var(--ink) 30%,transparent);}
495
+ /* the owned ring is ONE ring, not two — a second cream halo read as a defect */
496
+ .worldOrb[data-rel="owner"] .disc{box-shadow:0 0 34px -4px color-mix(in srgb,var(--acc) 55%,transparent),
497
+ 0 0 0 3px var(--accent);}
498
+ .worldOrb[data-rel="owner"] .badge{background:var(--accent);color:#fff;}
499
+ /* building, not owning — a lighter claim, said differently on purpose */
500
+ .worldOrb[data-rel="builder"] .disc{box-shadow:0 0 34px -4px color-mix(in srgb,var(--acc) 55%,transparent),
501
+ 0 0 0 2px color-mix(in srgb,var(--accent) 55%,transparent);}
502
+ .worldOrb[data-rel="builder"] .badge{background:var(--accent-soft);color:var(--accent-deep);
503
+ border:1px solid color-mix(in srgb,var(--accent) 45%,transparent);}
504
+ :root[data-mode="dark"] .worldOrb[data-rel="builder"] .badge{color:var(--sun-lit);}
505
+ @media (prefers-color-scheme: dark){:root:not([data-mode="light"]) .worldOrb[data-rel="builder"] .badge{color:var(--sun-lit);}}
506
+ .worldOrb .chip{transform:scale(.88);}
507
+
508
+ /* what the only-mine filter shows when you are connected to nothing. Not a
509
+ hypothetical — GET /my-projects returns { owned: [], building: [] } for the
510
+ platform's own archon today, so it is the DAY-ONE state. */
511
+ .fieldEmpty{position:absolute;inset:0;display:none;flex-direction:column;align-items:center;
512
+ justify-content:center;gap:14px;text-align:center;padding:26px;z-index:4;}
513
+ .field.isEmpty .fieldEmpty{display:flex;}
514
+ .fieldEmpty p{font-size:15px;font-weight:600;color:var(--chrome-ink);max-width:44ch;line-height:1.55;
515
+ text-shadow:var(--chrome-glow);}
516
+ .fieldEmpty .sub{font-size:13.5px;font-weight:600;color:var(--glass-ink);}
517
+
518
+ .hintbar{position:absolute;left:50%;bottom:14px;transform:translateX(-50%);display:flex;gap:8px;
519
+ flex-wrap:wrap;justify-content:center;font-family:var(--font-mono);font-size:11.5px;
520
+ color:var(--glass-ink);background:var(--glass-bg);border:1.5px solid var(--glass-border);
521
+ border-radius:999px;padding:7px 16px;backdrop-filter:blur(4px);max-width:92%;}
522
+ .hintbar kbd{font-family:var(--font-mono);background:color-mix(in srgb,var(--accent) 24%,transparent);
523
+ border-radius:5px;padding:1px 6px;font-size:11px;color:var(--chrome-accent);}
524
+
525
+ /* ── the directory table: the same rows, the same filter, and the shell is
526
+ still overhead — the gate's table dropped the projects door entirely. ── */
527
+ .tbl{width:100%;border-collapse:collapse;background:var(--card);border-radius:20px;overflow:hidden;
528
+ box-shadow:var(--shadow-card);}
529
+ .tbl th{text-align:left;font-family:var(--font-display);font-size:11.5px;font-weight:700;
530
+ letter-spacing:.9px;text-transform:uppercase;color:var(--ink-faint);padding:15px 18px;
531
+ border-bottom:2px solid var(--line);white-space:nowrap;}
532
+ .tbl td{padding:15px 18px;border-bottom:1px solid var(--line);font-size:14px;font-weight:600;
533
+ color:var(--ink-soft);vertical-align:middle;}
534
+ .tbl tr:last-child td{border-bottom:none;}
535
+ .tbl tr:hover td{background:color-mix(in srgb,var(--accent) 5%,transparent);}
536
+ .tbl .pname{display:flex;align-items:center;gap:11px;}
537
+ .tbl .pdot{width:11px;height:11px;border-radius:50%;background:var(--acc);flex:0 0 auto;
538
+ box-shadow:0 0 0 3px color-mix(in srgb,var(--acc) 22%,transparent);}
539
+ .tbl .ptitle{font-family:var(--font-display);font-weight:700;font-size:15px;color:var(--ink);}
540
+ .tbl .ptag{font-size:12.5px;color:var(--ink-faint);font-weight:600;}
541
+ .tbl .num{font-family:var(--font-mono);font-weight:500;}
542
+ .tbl .pill{display:inline-block;font-family:var(--font-mono);font-size:11px;padding:3px 10px;
543
+ border-radius:999px;background:var(--accent-soft);color:var(--accent-deep);}
544
+ :root[data-mode="dark"] .tbl .pill{color:var(--sun-lit);}
545
+ @media (prefers-color-scheme: dark){:root:not([data-mode="light"]) .tbl .pill{color:var(--sun-lit);}}
546
+ .tbl .tags{display:flex;gap:6px;flex-wrap:wrap;}
547
+ .tbl .tags span{font-family:var(--font-mono);font-size:10.5px;padding:2px 8px;border-radius:999px;
548
+ border:1px solid var(--line);color:var(--ink-faint);}
549
+ .tbl td.right{text-align:right;}
550
+ /* Nothing in an action or status cell may wrap: "Manage →" breaking after the
551
+ space and the chip's recency dropping to a second line both read as a broken
552
+ control rather than a tight column. Those two cells claim their width and the
553
+ name cell absorbs the slack. */
554
+ .tbl .btn,.tbl .chip{white-space:nowrap;}
555
+ .tbl .colStatus{width:1%;white-space:nowrap;}
556
+ .tbl .colGo{width:1%;white-space:nowrap;padding-left:8px;}
557
+ .tbl .colName{width:auto;}
558
+ @media (max-width:780px){.tbl .colTags,.tbl .colType{display:none;}}
559
+
560
+ /* ── your projects: search + a state filter whose pills carry live counts, so
561
+ a state with nothing in it reads 0 before you click it. The filter GROUPS
562
+ the chip outcomes; it never relabels them — "Live" holds both `live` and
563
+ `live?` because neither is known-down, and each card still says which. ── */
564
+ .mineBar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:20px;}
565
+ .mineBar .search{background:var(--bg-card);border-color:var(--line);}
566
+ .mineBar .search input{color:var(--ink);}
567
+ .mineBar .search input::placeholder{color:var(--ink-faint);}
568
+ .mineBar .search svg{stroke:var(--ink-faint);}
569
+ .mineBar .search .count{color:var(--ink-faint);}
570
+ .filters{display:flex;gap:7px;flex-wrap:wrap;}
571
+ .filters button{display:inline-flex;align-items:center;gap:7px;cursor:pointer;font-family:var(--font-display);
572
+ font-weight:700;font-size:13px;color:var(--ink-soft);background:var(--bg-card);
573
+ border:2px solid var(--line);border-radius:999px;padding:7px 14px;white-space:nowrap;}
574
+ .filters button:hover{border-color:var(--accent);color:var(--accent-deep);}
575
+ .filters button[aria-pressed="true"]{background:var(--accent);border-color:var(--accent);color:#fff;}
576
+ .filters .n{font-family:var(--font-mono);font-weight:500;font-size:11.5px;opacity:.75;}
577
+ .mineEmpty{font-size:14.5px;font-weight:600;color:var(--ink-soft);padding:10px 0 22px;}
578
+
579
+ /* ── the create rail ── */
580
+ .steps{display:flex;align-items:flex-start;margin-bottom:26px;}
581
+ .steps li{flex:1;display:flex;flex-direction:column;align-items:center;gap:8px;position:relative;list-style:none;}
582
+ .steps li:not(:first-child)::before{content:"";position:absolute;top:15px;right:50%;width:100%;height:3px;
583
+ background:var(--line);margin-right:15px;border-radius:2px;}
584
+ .steps li.done:not(:first-child)::before{background:var(--accent);}
585
+ .steps .snum{width:32px;height:32px;border-radius:50%;border:3px solid var(--line);background:var(--bg-card);
586
+ display:flex;align-items:center;justify-content:center;font-family:var(--font-display);font-weight:700;
587
+ font-size:14px;color:var(--ink-faint);position:relative;z-index:1;}
588
+ .steps li.done .snum{border-color:var(--accent);background:var(--accent);color:#fff;}
589
+ .steps li.now .snum{border-color:var(--accent);color:var(--accent-deep);}
590
+ :root[data-mode="dark"] .steps li.now .snum{color:var(--sun-lit);}
591
+ @media (prefers-color-scheme: dark){:root:not([data-mode="light"]) .steps li.now .snum{color:var(--sun-lit);}}
592
+ .steps .slbl{font-size:12.5px;font-weight:700;color:var(--ink-faint);}
593
+ .steps li.now .slbl,.steps li.done .slbl{color:var(--ink-soft);}
594
+ .createSplit{display:grid;grid-template-columns:1fr 300px;gap:var(--pad-md);align-items:center;}
595
+ @media (max-width:840px){.createSplit{grid-template-columns:1fr;}.createSplit .bongoCell{display:none;}}
596
+
597
+ /* ── the honest empty / not-available-here state. A child instance does not
598
+ provision, so it must SAY so rather than fall into the generic alert. ── */
599
+ .fieldNote{border:2px dashed var(--line);border-radius:18px;padding:24px;text-align:center;
600
+ font-size:14.5px;font-weight:600;color:var(--ink-soft);line-height:1.55;}
601
+
362
602
  @media (max-width:640px){
363
603
  .projHead{gap:8px;}
364
604
  .actions{flex-direction:column;align-items:stretch;}
@@ -233,7 +233,7 @@ h1.big{font-family:var(--font-display);font-weight:700;font-size:clamp(33px,5.4v
233
233
  <div class="wrap">
234
234
  <section class="lead">
235
235
  <div>
236
- <p class="eyebrow">type <span class="typed" id="typed">bongos</span><span class="caret" aria-hidden="true">▌</span> in any AI</p>
236
+ <p class="eyebrow">type <b>bongos</b> in <span class="typed" id="typed">any AI</span><span class="caret" aria-hidden="true">▌</span></p>
237
237
  <h1 class="big" data-brand-tagline>Building software is laying&nbsp;down a beat.</h1>
238
238
  <p class="lede">AI agents claim tasks and ship the code; you set the direction and the gates.</p>
239
239
  <div class="cta-row">
@@ -556,9 +556,15 @@ h1.big{font-family:var(--font-display);font-weight:700;font-size:clamp(33px,5.4v
556
556
  var reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
557
557
 
558
558
  /* ── the self-typing eyebrow. DECORATION over real text: the markup already
559
- says "bongos" and stays correct with scripting off or motion reduced. ── */
559
+ reads "type bongos in any AI" and stays correct with scripting off or
560
+ motion reduced.
561
+
562
+ `bongos` is the CONSTANT and the ASSISTANT is what varies — that is the
563
+ whole claim: one word, and it works in any of them. Typing the first slot
564
+ instead produced "type claude in any AI", which nobody does and which
565
+ inverted the point of the line. ── */
560
566
  if (!reduce) {
561
- var el = document.getElementById('typed'), words = ['claude','gemini','chatgpt','bongos'], w = 0;
567
+ var el = document.getElementById('typed'), words = ['Claude','ChatGPT','Gemini','any AI'], w = 0;
562
568
  (function cycle(){
563
569
  var word = words[w++ % words.length], i = 0;
564
570
  el.textContent = '';
@@ -567,7 +573,7 @@ h1.big{font-family:var(--font-display);font-weight:700;font-size:clamp(33px,5.4v
567
573
  else setTimeout(function erase(){
568
574
  if (el.textContent.length) { el.textContent = el.textContent.slice(0, -1); setTimeout(erase, 38); }
569
575
  else cycle();
570
- }, word === 'bongos' ? 2100 : 900);
576
+ }, word === 'any AI' ? 2100 : 900);
571
577
  })();
572
578
  })();
573
579
  }