@bongos/core 1.19.725 → 1.19.727

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.
Files changed (229) hide show
  1. package/.bongos-core.json +179 -494
  2. package/.devcontainer/Dockerfile +1 -1
  3. package/.devcontainer/devcontainer.json +2 -2
  4. package/.devcontainer/healthcheck.sh +1 -1
  5. package/.devcontainer/init-firewall.sh +1 -1
  6. package/.devcontainer/post-create.sh +2 -2
  7. package/.env.local.example +1 -1
  8. package/Dockerfile +1 -1
  9. package/docs/copy-inventory.md +1 -1
  10. package/docs/copy-registry.json +9 -9
  11. package/docs/module-api-changelog.md +4 -0
  12. package/docs/recipes/devbox-app-release.md +4 -4
  13. package/modules/dev-box/app/scripts/dmgbuild-settings.py +5 -5
  14. package/modules/dev-box/app/scripts/gen-brand.js +1 -1
  15. package/modules/dev-box/app/src/brand.js +1 -1
  16. package/modules/dev-box/app/test/unit.mjs +16 -16
  17. package/modules/dev-box/box-access.js +1 -1
  18. package/modules/dev-box/routes/box.js +1 -1
  19. package/modules/discord/discord-approvals.js +1 -1
  20. package/modules/grading/grader-model.js +1 -1
  21. package/modules/grading/grader-prompt.js +1 -1
  22. package/modules/grading/grader-workers/quality.js +1 -1
  23. package/modules/hall-ui/module.json +1 -1
  24. package/modules/hall-ui/public/brand-apply.js +1 -1
  25. package/modules/hall-ui/public/shell.js +1 -1
  26. package/modules/hall-ui/public/work.js +1 -1
  27. package/modules/lifecycle/conflict-resolve.js +1 -1
  28. package/modules/lifecycle/db-analytics.js +1 -1
  29. package/modules/lifecycle/routes/analytics.js +1 -1
  30. package/modules/onboarding/approval-broadcast.js +1 -1
  31. package/modules/onboarding/onboarding.js +1 -1
  32. package/modules/provisioning/tests/liveness.mjs +1 -1
  33. package/modules/public-landing/public/index.html +1 -1
  34. package/modules/public-landing/public/projects-floor.states.json +2 -2
  35. package/modules/public-landing/public/projects.probes.json +4 -4
  36. package/modules/public-landing/public/projects.states.json +2 -2
  37. package/modules/status-ui/module.json +1 -1
  38. package/modules/status-ui/public/status.js +4 -4
  39. package/modules/status-ui/public/style.css +1 -1
  40. package/modules/ui-design/kit/fixtures/cost-summary.json +1 -1
  41. package/modules/ui-design/kit/fixtures/featured-hub.json +1 -1
  42. package/modules/ui-design/kit/fixtures/featured.json +1 -1
  43. package/modules/ui-design/kit/fixtures/leaderboard.json +1 -1
  44. package/modules/ui-design/kit/fixtures/my-projects.json +1 -1
  45. package/modules/ui-design/kit/fixtures/progress.json +1 -1
  46. package/modules/ui-design/kit/fixtures/recent-shipped.json +1 -1
  47. package/package-lock.json +2 -2
  48. package/package.json +1 -1
  49. package/scripts/cron/style_guide_audit.sh +1 -1
  50. package/scripts/discord/lib.js +1 -1
  51. package/scripts/discord/post.js +1 -1
  52. package/scripts/gds/audit-authorship.sh +1 -1
  53. package/scripts/gds/audit-replay.sh +29 -2
  54. package/scripts/gds/audit-ships.js +1 -1
  55. package/scripts/gds/backfill-sessions.js +2 -2
  56. package/scripts/gds/cf-tunnel.js +4 -4
  57. package/scripts/gds/claude-materialize.js +2 -2
  58. package/scripts/gds/cli-lib.js +6 -6
  59. package/scripts/gds/cutover-codemod.js +1 -1
  60. package/scripts/gds/dev-lib.js +2 -2
  61. package/scripts/gds/diagnose-achievements.js +2 -2
  62. package/scripts/gds/fitness-checks-identity.js +6 -4
  63. package/scripts/gds/gate-review.js +14 -6
  64. package/scripts/gds/gen-diagrams.js +1 -1
  65. package/scripts/gds/leak-scan-allowlist.js +293 -0
  66. package/scripts/gds/learning-capture.js +1 -1
  67. package/scripts/gds/linkify-refs.js +2 -2
  68. package/scripts/gds/local-preview-lib.js +1 -1
  69. package/scripts/gds/mirror-lineage-check.js +1 -1
  70. package/scripts/gds/mirror-redact.js +17 -2
  71. package/scripts/gds/newcomer-floor.templates.json +1 -1
  72. package/scripts/gds/package-core.js +18 -1
  73. package/scripts/gds/paste-token.js +1 -1
  74. package/scripts/gds/provision-units.js +2 -2
  75. package/scripts/gds/publish-manifest.js +15 -0
  76. package/scripts/gds/repo-metrics.js +1 -1
  77. package/scripts/gds/sandbox-stage.js +1 -1
  78. package/scripts/gds/seed-bongos-coreB-tranche1-tasks.js +1 -1
  79. package/scripts/gds/seed-bongos-coreB-tranche2-tasks.js +1 -1
  80. package/scripts/gds/seed-bongos-goal-hierarchy-tasks.js +1 -1
  81. package/scripts/gds/seed-bongos-tasks.js +2 -2
  82. package/scripts/gds/seed-gds-v4-tasks.js +1 -1
  83. package/scripts/gds/seed-medusa-tasks.js +10 -10
  84. package/scripts/gds/seed-module-upstreaming.js +1 -1
  85. package/scripts/gds/seed-productization-tasks.js +6 -6
  86. package/scripts/gds/seed-provisioning-tasks.js +2 -2
  87. package/scripts/gds/seed-staging.sh +19 -6
  88. package/scripts/gds/ship-deploy-target.js +1 -1
  89. package/scripts/gds/ship-flow.js +1 -1
  90. package/scripts/gds/smoke-gds.js +1 -1
  91. package/scripts/status-probe.mjs +2 -2
  92. package/src/bongos/auth-discord.js +2 -2
  93. package/src/bongos/live-channel.js +1 -1
  94. package/src/bongos/permission-path-check.js +80 -3
  95. package/src/bongos/pool.js +1 -1
  96. package/src/bongos/routes/auth.js +11 -11
  97. package/src/bongos/routes/security.js +1 -1
  98. package/src/bongos/routes.js +1 -1
  99. package/src/branding.js +1 -1
  100. package/src/devbox-downloads.js +1 -1
  101. package/src/instance-config.js +2 -2
  102. package/src/module-api.js +1 -1
  103. package/src/module-loader/provenance.js +2 -2
  104. package/tests/art_stage.mjs +2 -2
  105. package/tests/auth_anthropic_identity.mjs +1 -1
  106. package/tests/auth_duplicate_cookie.mjs +1 -1
  107. package/tests/auth_error_brand.mjs +1 -1
  108. package/tests/auth_inactive_builder.mjs +1 -1
  109. package/tests/backup_cli.mjs +4 -4
  110. package/tests/bongos_box_connect.mjs +2 -2
  111. package/tests/bongos_downloads.mjs +2 -2
  112. package/tests/bongos_login.mjs +5 -5
  113. package/tests/box_connect_e2e.mjs +2 -2
  114. package/tests/box_dns_repoint.mjs +10 -10
  115. package/tests/box_host_keys.mjs +1 -1
  116. package/tests/box_onboard.mjs +9 -9
  117. package/tests/box_scope_session.mjs +1 -1
  118. package/tests/box_terminal_ssrf.mjs +7 -7
  119. package/tests/boxes.mjs +9 -9
  120. package/tests/branding.mjs +2 -2
  121. package/tests/bump_version.mjs +2 -2
  122. package/tests/cf_tunnel.mjs +40 -40
  123. package/tests/cli_token_reissue.mjs +7 -7
  124. package/tests/cloud_bongos_pack.mjs +1 -1
  125. package/tests/currency_label.mjs +8 -8
  126. package/tests/dev_lib.mjs +4 -4
  127. package/tests/devbox_dmg_backdrop.mjs +1 -1
  128. package/tests/devbox_downloads.mjs +2 -2
  129. package/tests/devbox_icons.mjs +1 -1
  130. package/tests/discord_approvals.mjs +1 -1
  131. package/tests/discord_oauth.mjs +2 -2
  132. package/tests/executed_closure.mjs +6 -1
  133. package/tests/first_admin_bootstrap.mjs +1 -1
  134. package/tests/gate_approvals.mjs +5 -5
  135. package/tests/government_protected_surfaces.mjs +137 -0
  136. package/tests/grade_aggregates_unavailable.mjs +1 -1
  137. package/tests/hook_user_agents.mjs +2 -2
  138. package/tests/init.mjs +6 -6
  139. package/tests/instance_config.mjs +1 -1
  140. package/tests/instance_manifest.mjs +5 -5
  141. package/tests/instance_mismatch_guard.mjs +13 -13
  142. package/tests/leak_scan_allowlist.mjs +94 -0
  143. package/tests/migrate_root_resolution.mjs +4 -4
  144. package/tests/mirror_redact.mjs +28 -4
  145. package/tests/module_catalog.mjs +5 -5
  146. package/tests/module_submissions.mjs +3 -3
  147. package/tests/package_core.mjs +1 -1
  148. package/tests/parallel-load.mjs +6 -6
  149. package/tests/projects_hub.mjs +2 -2
  150. package/tests/provision.mjs +50 -50
  151. package/tests/provisioning_github_app_routes.mjs +1 -1
  152. package/tests/provisioning_onboard_mode.mjs +2 -2
  153. package/tests/publish_reconciler.mjs +4 -4
  154. package/tests/sandbox_stage.mjs +1 -1
  155. package/tests/scrubber_corpus.mjs +3 -3
  156. package/tests/search_isolation.mjs +1 -1
  157. package/tests/session_records.mjs +1 -1
  158. package/tests/ship_card.mjs +6 -6
  159. package/tests/ship_ci_deploy.mjs +1 -1
  160. package/tests/single_host_hall.mjs +9 -9
  161. package/tests/smoke_preflight.mjs +1 -1
  162. package/tests/smoke_target.mjs +1 -1
  163. package/tests/status-probe.mjs +1 -1
  164. package/tests/status_mirror_sync.mjs +1 -1
  165. package/docs/adr/0004-amazonprimea-name-and-trademark-acceptance.md +0 -45
  166. package/docs/design/mocks/_schemes.css +0 -604
  167. package/docs/design/mocks/component-sheet.html +0 -428
  168. package/docs/design/mocks/hall/_tokens.css +0 -370
  169. package/docs/design/mocks/hall/board/v1.html +0 -846
  170. package/docs/design/mocks/hall/board/v2.html +0 -858
  171. package/docs/design/mocks/hall/board/v3.html +0 -884
  172. package/docs/design/mocks/hall/goals/v1.html +0 -1212
  173. package/docs/design/mocks/hall/goals/v2.html +0 -1192
  174. package/docs/design/mocks/hall/goals/v3.html +0 -1205
  175. package/docs/design/mocks/hall/hall-sheet.html +0 -244
  176. package/docs/design/mocks/hall/reading/v1.html +0 -1125
  177. package/docs/design/mocks/hall/reading/v2.html +0 -1177
  178. package/docs/design/mocks/hall/reading/v3.html +0 -1150
  179. package/docs/design/mocks/hall/settings/v1.html +0 -1238
  180. package/docs/design/mocks/hall/settings/v2.html +0 -1235
  181. package/docs/design/mocks/hall/settings/v3.html +0 -1299
  182. package/docs/design/mocks/hall/task/v1.html +0 -633
  183. package/docs/design/mocks/hall/task/v2.html +0 -646
  184. package/docs/design/mocks/hall/task/v3.html +0 -647
  185. package/docs/design/mocks/hub/assets/r3-space-deepfield-half.jpg +0 -0
  186. package/docs/design/mocks/hub/assets/r3-space-deepfield-half.jpg.json +0 -13
  187. package/docs/design/mocks/hub/assets/r3-space-dust-half.jpg +0 -0
  188. package/docs/design/mocks/hub/assets/r3-space-dust-half.jpg.json +0 -13
  189. package/docs/design/mocks/hub/assets/r3-space-nebula-half.jpg +0 -0
  190. package/docs/design/mocks/hub/assets/r3-space-nebula-half.jpg.json +0 -13
  191. package/docs/design/mocks/hub/assets/r3-space-starfield-half.jpg +0 -0
  192. package/docs/design/mocks/hub/assets/r3-space-starfield-half.jpg.json +0 -13
  193. package/docs/design/mocks/hub/hub-sheet.html +0 -192
  194. package/docs/design/mocks/hub/mine.html +0 -465
  195. package/docs/design/mocks/hub/picker.html +0 -421
  196. package/docs/design/mocks/hub/planets.html +0 -356
  197. package/docs/design/mocks/hub/v1.html +0 -1209
  198. package/docs/design/mocks/hub/v2.html +0 -1257
  199. package/docs/design/mocks/hub/v3.html +0 -1496
  200. package/docs/design/mocks/hub/wizard.html +0 -421
  201. package/docs/design/mocks/landing/landing-sheet.html +0 -131
  202. package/docs/design/mocks/landing/v1.html +0 -956
  203. package/docs/design/mocks/landing/v2.html +0 -708
  204. package/docs/design/mocks/landing/v3.html +0 -911
  205. package/docs/design/mocks/landing-page-mock.html +0 -687
  206. package/docs/design/mocks/landing-palettes.html +0 -103
  207. package/docs/design/mocks/projects-hub-mock.html +0 -991
  208. package/docs/design/mocks/projects-hub-variants.html +0 -146
  209. package/docs/design/mocks/projects-page-mock.html +0 -167
  210. package/docs/design/mocks/world/blue-hud/_tokens.css +0 -246
  211. package/docs/design/mocks/world/blue-hud/hall.html +0 -595
  212. package/docs/design/mocks/world/blue-hud/landing.html +0 -424
  213. package/docs/design/mocks/world/blue-hud/status.html +0 -410
  214. package/docs/design/mocks/world/chrome-blobs/_tokens.css +0 -479
  215. package/docs/design/mocks/world/chrome-blobs/hall.html +0 -553
  216. package/docs/design/mocks/world/chrome-blobs/landing-b.html +0 -581
  217. package/docs/design/mocks/world/chrome-blobs/landing.html +0 -328
  218. package/docs/design/mocks/world/chrome-blobs/status.html +0 -416
  219. package/docs/design/mocks/world/natgeo-editorial/_tokens.css +0 -377
  220. package/docs/design/mocks/world/natgeo-editorial/hall.html +0 -649
  221. package/docs/design/mocks/world/natgeo-editorial/landing-b.html +0 -743
  222. package/docs/design/mocks/world/natgeo-editorial/landing.html +0 -475
  223. package/docs/design/mocks/world/natgeo-editorial/status.html +0 -437
  224. package/docs/design/mocks/world/organic-cards/_tokens.css +0 -462
  225. package/docs/design/mocks/world/organic-cards/hall.html +0 -625
  226. package/docs/design/mocks/world/organic-cards/landing-b.html +0 -736
  227. package/docs/design/mocks/world/organic-cards/landing.html +0 -420
  228. package/docs/design/mocks/world/organic-cards/status.html +0 -458
  229. package/docs/design/mocks/world/world-sheet.html +0 -156
@@ -1,1257 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <script>
6
- (function(){try{var q=new URLSearchParams(location.search || location.hash.slice(1) || (window.__Q || '')).get('mode');var m=(q==='light'||q==='dark')?q:localStorage.getItem('cb-color-mode');if(m==='light'||m==='dark')document.documentElement.setAttribute('data-mode',m);}catch(e){}})();
7
- </script>
8
- <meta name="viewport" content="width=device-width,initial-scale=1">
9
- <title>The project map, Cloud Bongos</title>
10
- <link rel="icon" href="data:,">
11
-
12
- <link rel="preconnect" href="https://fonts.googleapis.com">
13
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
14
- <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400&family=Manrope:wght@300;400;500;600&display=swap">
15
- <link rel="stylesheet" href="../../../../modules/public-landing/public/assets/cosmos.css">
16
- <style>
17
- body{
18
- /* v2 "the hairline" — the world's derived tier for this surface, by the recipes in DESIGN.md's
19
- frontmatter and the same derived names the landing's own sheet uses. cosmos.css already carries the fifteen contract
20
- tokens in both modes and owns --line (a hairline COLOUR here) and --ok/--warn/--err (the hub's
21
- fixed status vocabulary); none of those four is restated. --accent-on, --accent-soft, --card and
22
- --ctl-line are cosmos's too and are taken, not redeclared. */
23
- --glass:color-mix(in srgb,var(--ink) 5%,transparent);
24
- --glass-strong:color-mix(in srgb,var(--ink) 8%,transparent);
25
- --glass-edge:color-mix(in srgb,var(--ink) 12%,transparent);
26
- --accent-lift:color-mix(in srgb,var(--accent) 58%,#ffffff);
27
- --void-pill:rgba(0,0,0,.55);
28
- --void-ink:#ffffff;
29
- --focus:var(--accent-deep);
30
- --type-halo:none;
31
- --label-face:var(--font-mono);
32
- --label-size:11px;
33
- --label-space:.12em;
34
- --radius:16px;
35
- --radius-sm:10px;
36
- --radius-pill:999px;
37
- --top-h:64px;
38
- --gutter:clamp(16px,4vw,34px);
39
- --leader-len:68px;
40
- }
41
- :root[data-mode="dark"] body{
42
- --glass:rgba(255,255,255,.11);
43
- --glass-strong:rgba(255,255,255,.17);
44
- --glass-edge:rgba(255,255,255,.13);
45
- --focus:var(--accent);
46
- --type-halo:0 2px 18px rgba(0,0,0,.94),0 0 54px rgba(0,0,0,.72);
47
- --label-face:var(--font-body);
48
- --label-size:11.5px;
49
- --label-space:.1em;
50
- }
51
- @media (prefers-color-scheme:dark){
52
- :root:not([data-mode="light"]) body{
53
- --glass:rgba(255,255,255,.11);
54
- --glass-strong:rgba(255,255,255,.17);
55
- --glass-edge:rgba(255,255,255,.13);
56
- --focus:var(--accent);
57
- --type-halo:0 2px 18px rgba(0,0,0,.94),0 0 54px rgba(0,0,0,.72);
58
- --label-face:var(--font-body);
59
- --label-size:11.5px;
60
- --label-space:.1em;
61
- }
62
- :root:not([data-mode="light"]) .sun{display:block}
63
- :root:not([data-mode="light"]) .moon{display:none}
64
- }
65
- :root[data-mode="dark"] .sun{display:block}
66
- :root[data-mode="dark"] .moon{display:none}
67
- .sun{display:none}
68
-
69
- /* the two-tone ring, on every control, menu item and the search field. .is-focus paints the same
70
- declaration so a query string can show a ring :focus-visible cannot be forced into. */
71
- :focus-visible,.trig.is-focus,.bandT.is-focus,.qWrap.is-focus .qField,.qField:focus-within{outline:2px solid var(--focus);outline-offset:3px;box-shadow:0 0 0 8px var(--bg)}
72
- .qInput:focus-visible{outline:none;box-shadow:none}
73
- .skip{position:absolute;left:-9999px;top:0;z-index:100;min-height:24px;display:inline-flex;align-items:center;
74
- padding:11px 18px;border-radius:var(--radius-pill);background:var(--bg-card);color:var(--ink);
75
- border:1px solid var(--rule);font-size:15px;text-decoration:none}
76
- .skip:focus{left:14px;top:14px}
77
- main:focus{outline:none;box-shadow:none}
78
- .ic{fill:none;stroke:currentColor;stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round}
79
-
80
- /* ============ THE BAR: no pill. the page ground, closed by one hairline. ============ */
81
- .bar{position:relative;z-index:40;display:flex;align-items:center;gap:22px;height:var(--top-h);
82
- padding:0 var(--gutter);background:var(--bg);border-bottom:1px solid var(--rule)}
83
- .barLeft{display:flex;align-items:center;gap:26px;flex:1 1 auto;min-width:0}
84
- .barRight{display:flex;align-items:center;gap:20px;flex:none}
85
- .barMark{display:inline-flex;align-items:center;gap:10px;min-height:44px;flex:none;
86
- font-family:var(--font-display);font-size:17px;font-weight:500;letter-spacing:-.015em;color:var(--ink);
87
- text-decoration:none;text-shadow:var(--type-halo)}
88
- .barMark svg{width:23px;height:18px;display:block;flex:none}
89
- .barMark:hover{color:var(--ink)}
90
-
91
- /* the search: no field chrome, one bottom hairline that takes the ink on focus */
92
- .qWrap{display:flex;align-items:baseline;gap:12px;flex:0 1 340px;min-width:0;
93
- border-radius:var(--radius-sm)}
94
- .qField{flex:1 1 auto;min-width:0;display:flex;align-items:center;min-height:44px;
95
- border-bottom:1px solid var(--rule);transition:border-color 200ms cubic-bezier(.2,.7,.3,1)}
96
- .qWrap:focus-within .qField,.qWrap.is-focus .qField{border-bottom-color:var(--ink)}
97
- .qInput{width:100%;min-width:0;min-height:32px;padding:0 0 4px;background:none;border:0;outline:none;
98
- color:var(--ink);font-family:var(--label-face);font-size:13px;letter-spacing:.01em}
99
- .qInput::placeholder{color:var(--ink-faint);font-family:var(--label-face)}
100
- .qIcon{display:none;flex:none;width:17px;height:17px;margin-right:8px;color:var(--ink-faint)}
101
- .qCount{flex:none;font-family:var(--font-mono);font-size:11.5px;color:var(--ink-faint);
102
- font-variant-numeric:tabular-nums;white-space:nowrap}
103
- /* under 480 the field folds into this magnifier (see the 480 query); above it, never shown */
104
- .qToggle{display:none;align-items:center;justify-content:center;width:44px;height:44px;flex:none;
105
- cursor:pointer;background:none;border:0;border-radius:var(--radius-pill);color:var(--ink-soft);
106
- transition:color 200ms cubic-bezier(.2,.7,.3,1)}
107
- .qToggle svg{width:19px;height:19px}
108
- .qToggle:hover,.qToggle[aria-expanded="true"]{color:var(--ink)}
109
-
110
- /* the right group is lowercase text; the OPEN trigger is underlined in the accent, because an
111
- open menu is the live thing on the bar */
112
- .trig{display:inline-flex;align-items:center;gap:7px;min-height:44px;padding:0;cursor:pointer;
113
- background:none;border:0;border-radius:var(--radius-sm);
114
- font-family:var(--font-body);font-size:15px;font-weight:400;text-transform:lowercase;
115
- color:var(--ink-soft);transition:color 200ms cubic-bezier(.2,.7,.3,1)}
116
- .trig:hover,.trig.is-hover{color:var(--ink)}
117
- .trig[aria-expanded="true"]{color:var(--ink);text-decoration:underline;text-decoration-color:var(--accent);
118
- text-decoration-thickness:2px;text-underline-offset:7px}
119
- .trig .cv{width:13px;height:13px;flex:none;transition:transform 200ms cubic-bezier(.2,.7,.3,1)}
120
- .trig[aria-expanded="true"] .cv{transform:rotate(180deg)}
121
- .whoTrig{gap:9px}
122
- .whoTrig img{width:26px;height:26px;border-radius:var(--radius-pill);object-fit:cover;flex:none;
123
- border:1px solid var(--rule)}
124
- .whoName{max-width:14ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
125
- .modeBtn{display:inline-flex;align-items:center;justify-content:center;width:44px;height:44px;
126
- cursor:pointer;background:none;border:0;border-radius:var(--radius-pill);color:var(--ink-soft);
127
- transition:color 200ms cubic-bezier(.2,.7,.3,1)}
128
- .modeBtn:hover{color:var(--ink)}
129
- .modeBtn svg{width:20px;height:20px}
130
- /* the one filled shape on a bar with no pills */
131
- .signinDoor{display:inline-flex;align-items:center;justify-content:center;min-height:44px;padding:0 26px;
132
- border-radius:var(--radius-pill);background:color-mix(in srgb,var(--ink) 92%,var(--bg));color:var(--bg);
133
- font-size:15px;font-weight:500;text-transform:lowercase;text-decoration:none;
134
- transition:background 200ms cubic-bezier(.2,.7,.3,1)}
135
- .signinDoor:hover{background:var(--ink)}
136
-
137
- /* ============ THE BAND: the menu is not a floating card. it unfolds under the bar's edge,
138
- full width, and pushes the page down. ============ */
139
- .band{position:relative;z-index:39;background:var(--bg-card);border-bottom:1px solid var(--rule);
140
- animation:bandIn 220ms cubic-bezier(.16,1,.3,1) both}
141
- @keyframes bandIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
142
- .bandIn{padding:20px var(--gutter) 28px}
143
- .bandRow{display:grid;grid-template-columns:repeat(3,1fr);gap:0 34px;list-style:none;margin:0;padding:0}
144
- .bandCell{position:relative;border-top:1px solid var(--rule-soft);padding:15px 0 0}
145
- .bandCell.is-now::before{content:"";position:absolute;left:0;right:0;top:-1px;height:2px;background:var(--accent)}
146
- .bandT{display:inline-block;padding:2px 0;font-family:var(--font-display);font-size:24px;
147
- font-weight:300;line-height:1.14;letter-spacing:-.03em;text-transform:lowercase;
148
- color:var(--ink-soft);text-decoration:none;transition:color 200ms cubic-bezier(.2,.7,.3,1)}
149
- .bandT::after{content:"";position:absolute;inset:0}
150
- .bandCell.is-now .bandT,.bandCell:hover .bandT,.bandCell.is-hover .bandT{color:var(--ink)}
151
- .bandCell:hover,.bandCell.is-hover{border-top-color:var(--ink-faint)}
152
- .bandC{display:block;margin-top:5px;font-size:13.5px;line-height:1.45;color:var(--ink-soft)}
153
- .bandProfile{display:flex;align-items:flex-end;justify-content:space-between;gap:34px;flex-wrap:wrap}
154
- .bandWho{display:flex;align-items:center;gap:10px;padding-top:15px;border-top:1px solid var(--rule-soft);
155
- flex:0 1 auto;font-family:var(--label-face);font-size:var(--label-size);
156
- letter-spacing:var(--label-space);text-transform:uppercase;color:var(--ink-faint)}
157
- .bandWho img{width:30px;height:30px;border-radius:var(--radius-pill);object-fit:cover;
158
- border:1px solid var(--rule)}
159
- .bandProfile .bandRow{display:flex;justify-content:flex-end;gap:30px;flex:0 1 auto}
160
- .bandProfile .bandCell,.bandProfile .bandSlot{flex:0 0 168px;text-align:right}
161
- /* the deferred Settings item: a place it would go, rendering nothing at all - not even the
162
- hairline that declares a list item in this band. never a disabled item. */
163
- .bandSlot{border:0}
164
-
165
- /* ============ the carried surfaces: only the hit-area floors the world asks for ============ */
166
- .toolbar .search{display:none}
167
- .tbl th button{min-height:24px;display:inline-flex;align-items:center}
168
- .search input{min-height:24px}
169
- .foot a,.finePrint a,.lede a{display:inline-block;padding:3px 0;min-height:24px}
170
- #sub-table-body{overflow-x:auto}
171
- .hallGlyph{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;
172
- border-radius:var(--radius-sm);color:var(--ink-faint);transition:color 200ms cubic-bezier(.2,.7,.3,1)}
173
- .hallGlyph:hover{color:var(--accent-deep)}
174
- .hallGlyph svg{width:18px;height:18px}
175
- .tbl .colHall{width:1%;padding-left:0;padding-right:6px}
176
- .viewTitle{font-family:var(--font-display);font-weight:700;font-size:clamp(24px,3.4vw,32px);
177
- letter-spacing:-.5px;line-height:1.15;margin-bottom:8px}
178
- .hero .viewTitle{font-size:clamp(26px,3.8vw,36px);color:var(--chrome-ink);text-shadow:var(--chrome-glow)}
179
- .bongos{display:block;width:100%;height:auto;overflow:visible}
180
- .panel{display:none}
181
- .panel.on{display:block}
182
- .wizField{display:block;margin-bottom:18px}
183
- .wizField>span{display:block;font-size:13.5px;font-weight:700;color:var(--ink-soft);margin-bottom:7px}
184
- #view-new input[type=text]{width:100%;font-family:var(--font-body);font-size:17px;font-weight:600;
185
- color:var(--ink);background:var(--bg-card);border:2px solid var(--ctl-line);border-radius:14px;
186
- padding:13px 16px;outline:none}
187
- #view-new input[type=text]::placeholder{color:var(--ink-faint);font-weight:500}
188
- .hint{font-size:13px;font-weight:600;color:var(--ink-faint);margin-top:7px;min-height:18px}
189
- .nav{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:8px}
190
- .createSplit{display:grid;grid-template-columns:1fr 300px;gap:var(--pad-md);align-items:center}
191
- .steps{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:22px}
192
- .viewWrap{padding-bottom:40px}
193
- #view-mine .hero,#view-new .hero{max-width:none}
194
- .gh{width:16px;height:16px;fill:currentColor}
195
- .finePrint{font-size:12.5px;color:var(--ink-faint);line-height:1.5;margin-top:14px}
196
- .mineEmpty{font-size:13.5px;color:var(--ink-faint);margin:10px 0}
197
- .loading{font-size:14px;color:var(--ink-faint);margin-bottom:14px}
198
- .empty{text-align:center;padding:26px 0}
199
- .empty p{font-size:15px;color:var(--ink-soft);margin-bottom:16px;max-width:52ch;margin-left:auto;margin-right:auto}
200
-
201
- /* ============ THE CARD: "the specimen" ============ */
202
- .projCard{position:relative;container-type:inline-size;display:flex;flex-direction:column;
203
- width:min(560px,100%);text-align:left;background:var(--bg-card);border:1px solid var(--rule);
204
- border-radius:var(--radius);box-shadow:none;padding:0 0 26px;overflow:hidden}
205
- .projCard:not(.has-art){padding-top:26px}
206
- .cardScrim:not([hidden]) .projCard{animation:pcIn 260ms cubic-bezier(.16,1,.3,1) both}
207
- @keyframes pcIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
208
- .projCard .cardArt{order:0;display:block;width:100%;aspect-ratio:5/2;object-fit:cover;
209
- object-position:50% 100%;transform:scale(2);transform-origin:20% 96%}
210
- .projCard .cardX{position:absolute;top:12px;right:12px;z-index:4;display:inline-flex;
211
- align-items:center;justify-content:center;width:30px;height:30px;border:0;
212
- border-radius:var(--radius-pill);background:var(--glass-strong);color:var(--ink);cursor:pointer}
213
- .projCard.has-art .cardX{background:var(--void-pill);color:var(--void-ink)}
214
- .projCard .cardX svg{width:15px;height:15px}
215
- .projCard .cardDot{order:1;align-self:flex-start;display:block;width:54px;height:54px;
216
- border-radius:50%;margin:0 0 16px 26px;background:var(--acc,var(--accent));box-shadow:none}
217
- .projCard.has-art .cardDot{position:absolute;z-index:3;left:24px;top:calc(100cqw / 2.5 - 27px);
218
- margin:0;border:4px solid var(--bg-card)}
219
- .projCard h3{order:3;margin:0;padding:0 26px;font-family:var(--font-display);font-size:32px;
220
- font-weight:300;line-height:1.08;letter-spacing:-.03em;color:var(--ink)}
221
- .projCard.has-art h3{margin-top:44px}
222
- .projCard .cardTag{order:4;margin:9px 0 0;padding:0 26px;font-size:14.5px;line-height:1.5;
223
- color:var(--ink-soft);max-width:46ch}
224
- /* the relationship is a mono label, not a pill: it is a fact about you, not a control */
225
- .projCard .cardRel{order:5;display:block;margin:14px 0 0;padding:0 26px;background:none;
226
- border-radius:0;font-family:var(--label-face);font-size:var(--label-size);
227
- letter-spacing:var(--label-space);text-transform:uppercase;color:var(--ink-faint)}
228
- .projCard .cardFaces{order:6;display:flex;align-items:center;margin:18px 0 0;padding:0 26px}
229
- .projCard .cardFaces img{width:28px;height:28px;border-radius:50%;object-fit:cover;
230
- border:2px solid var(--bg-card);margin-right:-8px}
231
- .projCard .facesMore{margin-left:16px;font-family:var(--font-mono);font-size:11.5px;
232
- color:var(--ink-faint);font-variant-numeric:tabular-nums}
233
-
234
- /* the two figures. with art they are leader-line callouts ON the banner; with none they are the
235
- two-up band, which is also what every card becomes under 560. */
236
- .projCard .cardFigs{order:7;display:flex;gap:38px;margin:20px 0 0;padding:0 26px}
237
- .cardCall{display:block}
238
- .lead{display:none;width:var(--leader-len);height:26px;margin-bottom:10px;overflow:visible;
239
- color:color-mix(in srgb,var(--ink) 78%,transparent)}
240
- .fig{display:block;position:relative;width:fit-content;font-family:var(--font-display);
241
- font-size:clamp(21px,5cqw,28px);font-weight:300;letter-spacing:-.025em;line-height:1;
242
- font-variant-numeric:tabular-nums;color:var(--ink);text-shadow:var(--type-halo)}
243
- .cap{display:block;position:relative;width:fit-content;margin-top:7px;font-size:13px;
244
- color:var(--ink-soft);text-shadow:var(--type-halo)}
245
- @media (min-width:561px){
246
- .projCard.has-art .cardFigs{position:absolute;z-index:2;left:0;right:0;top:0;
247
- height:calc(100cqw / 2.5);margin:0;padding:0;display:block;pointer-events:none}
248
- .projCard.has-art .cardCall{position:absolute;width:max-content;max-width:42%}
249
- .projCard.has-art .cardCall.a{left:26px;top:14px}
250
- .projCard.has-art .cardCall.b{right:26px;top:calc(100cqw / 2.5 - 112px);text-align:right}
251
- .projCard.has-art .cardCall.b .fig,.projCard.has-art .cardCall.b .cap{margin-left:auto}
252
- .projCard.has-art .cardCall .lead{display:block}
253
- .projCard.has-art .cardCall.b .lead{transform:scaleX(-1);margin-left:auto}
254
- /* each callout carries ONE feathered patch of the CARD ground under its figure and caption
255
- together: SOLID to 10px past every edge of the glyph boxes (no column of the text ever sits
256
- on the fade), then a 34px feather to nothing, so the patch reads as haze in the art and never
257
- as a slab of paint on it. The feather is a mask, not a shadow (the world has no shadow
258
- vocabulary). It starts under the figure, never behind the leader. */
259
- .projCard.has-art .cardCall::before{content:"";position:absolute;
260
- left:-44px;right:-44px;top:18px;bottom:-24px;z-index:-1;pointer-events:none;
261
- background:var(--bg-card);border-radius:44px/30px;filter:blur(3px);
262
- -webkit-mask-image:linear-gradient(90deg,transparent,var(--ink) 34px,var(--ink) calc(100% - 34px),transparent),
263
- linear-gradient(180deg,transparent,var(--ink) 10px,var(--ink) calc(100% - 12px),transparent);
264
- -webkit-mask-composite:source-in;
265
- mask-image:linear-gradient(90deg,transparent,var(--ink) 34px,var(--ink) calc(100% - 34px),transparent),
266
- linear-gradient(180deg,transparent,var(--ink) 10px,var(--ink) calc(100% - 12px),transparent);
267
- mask-composite:intersect}
268
- }
269
- .projCard .cardMeta{order:8;display:grid;grid-template-columns:auto 1fr;justify-content:normal;
270
- gap:0;margin:22px 0 0;padding:0 26px;text-align:left}
271
- .cardMeta dt{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);
272
- text-transform:uppercase;color:var(--ink-faint);padding:11px 20px 11px 0;
273
- border-top:1px solid var(--rule-soft);line-height:19px}
274
- .cardMeta dd{margin:0;padding:11px 0;text-align:right;font-size:14px;color:var(--ink);
275
- border-top:1px solid var(--rule-soft);font-variant-numeric:tabular-nums}
276
- .cardMeta .ownerAv{display:inline-block;width:16px;height:16px;border-radius:50%;object-fit:cover;
277
- vertical-align:-3px;margin-right:6px}
278
- .projCard .cardActions{order:9;display:flex;align-items:center;justify-content:flex-start;
279
- gap:22px;flex-wrap:wrap;margin:24px 0 0;padding:0 26px}
280
- .pcDoor{display:inline-flex;align-items:center;justify-content:center;min-height:48px;padding:0 30px;
281
- border-radius:var(--radius-pill);background:var(--accent);color:var(--accent-on);font-size:16px;
282
- font-weight:500;text-transform:lowercase;text-decoration:none;
283
- transition:background 200ms cubic-bezier(.2,.7,.3,1)}
284
- .pcDoor:hover{background:var(--accent-lift);color:var(--accent-on)}
285
- .pcHall{display:inline-flex;align-items:center;gap:8px;min-height:24px;color:var(--accent-deep);
286
- font-size:15px;text-transform:lowercase;text-decoration:underline;text-underline-offset:4px}
287
- .pcHall svg{width:17px;height:17px}
288
-
289
- /* ============ the boards ============ */
290
- #board{padding:26px var(--gutter) 60px}
291
- .boardTitle{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);
292
- text-transform:uppercase;color:var(--ink-faint);margin-bottom:22px}
293
- .boardRow{border-top:1px solid var(--rule);padding-top:0;margin-bottom:38px}
294
- .boardLbl{display:block;font-family:var(--font-mono);font-size:11.5px;color:var(--ink-faint);
295
- padding:9px 0 12px}
296
- .boardGrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(400px,1fr));gap:34px;
297
- align-items:start}
298
- .boardCell .projCard{width:100%}
299
- .boardStack{display:flex;flex-direction:column;gap:6px}
300
- .boardSpec{border-top:1px solid var(--rule);margin-bottom:30px}
301
- .boardSpec .qWrap{width:340px;padding-top:16px}
302
-
303
- @media (max-width:900px){
304
- body{--top-h:60px}
305
- .bar{gap:10px;padding:0 12px}
306
- .barLeft{gap:12px}
307
- .barRight{gap:12px}
308
- .barMark{min-width:44px;justify-content:center}
309
- .barMark span{display:none}
310
- .qWrap{flex:1 1 90px;max-width:340px;gap:8px}
311
- .trig{font-size:13.5px}
312
- .whoName{display:none}
313
- .modeBtn{width:40px;height:40px}
314
- .modeBtn svg{width:18px;height:18px}
315
- .signinDoor{padding:0 16px;font-size:13.5px}
316
- .bandIn{padding:0 12px 16px}
317
- .bandRow{grid-template-columns:1fr;gap:0}
318
- .bandT{font-size:20px}
319
- .bandProfile{display:block}
320
- .bandProfile .bandRow{display:block;text-align:left}
321
- .bandProfile .bandCell,.bandProfile .bandSlot{flex:none;text-align:left}
322
- .bandWho{padding-bottom:14px}
323
- .createSplit{grid-template-columns:1fr}
324
- .bongoCell{display:none}
325
- .boardGrid{grid-template-columns:1fr}
326
- }
327
- @media (max-width:640px){
328
- .barRight{gap:8px}
329
- .bar{gap:8px;padding:0 10px}
330
- .projCard h3{font-size:26px;padding:0 20px}
331
- .projCard .cardMeta{padding:0 20px}
332
- .cardTag,.cardRel,.cardFaces,.cardFigs,.cardMeta,.cardActions{padding-left:20px;padding-right:20px}
333
- .cardDot{margin-left:20px}
334
- .projCard.has-art .cardDot{left:18px}
335
- .cardActions{gap:16px}
336
- }
337
- @media (max-width:480px){
338
- /* no field this narrow can hold a query beside four controls (45px at 320 with the word gone),
339
- so the search folds into a magnifier in the field's place; pressed, it drops ONE hairline row
340
- of the page ground under the bar carrying the full-width field and its count, and the sky's
341
- spotlight reports what the query matched. Escape, or an opening menu, folds it back. */
342
- .qToggle{display:inline-flex}
343
- .qWrap{display:none}
344
- .qWrap.is-open:not([hidden]){display:flex;position:absolute;left:0;right:0;top:100%;z-index:41;
345
- align-items:center;gap:10px;max-width:none;height:52px;padding:0 10px;border-radius:0;
346
- background:var(--bg);border-bottom:1px solid var(--rule)}
347
- .qWrap.is-open .qField{min-height:40px}
348
- .qIcon{display:block}
349
- .bar{gap:6px}
350
- .barLeft{gap:8px}
351
- .barRight{gap:6px}
352
- .signinDoor{padding:0 13px}
353
- }
354
- </style>
355
- </head>
356
- <body>
357
- <!--
358
- v2 "THE HAIRLINE" — the /projects hub recomposed inside DESIGN.md's chrome world (task 1003299,
359
- goal 1000080). A MOCK: it writes no build file. Sky, orbs, hover card, toolbar, table, mine view,
360
- new view and footer are carried verbatim from public-landing/projects.html and render from the
361
- shipping cosmos.css; the bar, the two menus, the search and the poster card are this version.
362
-
363
- THESIS. Hairlines, not containers. The hub is an Operate surface and the world's structure rule is
364
- already the answer to a bar: 1px rules separate, nothing is boxed. So there is no pill on this bar
365
- at all. Its ground IS the page ground, closed by one hairline, and the only filled shape it can
366
- ever hold is the sign-in door. The menus refuse to be floating cards: each is a full-width BAND
367
- that unfolds under the bar's edge and PUSHES the page down, so a destination is a place the page
368
- opens into rather than a list that hovers over it. The card follows the same logic one step
369
- further: it is a specimen sheet, its two figures annotated ON the art with the landing's own
370
- leader-line callouts, its facts a mono ledger of hairline rows.
371
-
372
- BAR. Ground var(--bg) (the same value the sky paints, so the difference is read by the 1px
373
- var(--rule) bottom edge, not by a tone), 64px, not sticky, gutter clamp(16px,4vw,34px) so it lines
374
- up with the toolbar under it. LEFT: the bongo-pair glyph + "Cloud Bongos" as plain type (Manrope
375
- 500, 17px, -0.015em) linking home, then the search - a bare field with only a bottom hairline that
376
- takes var(--ink) on focus, a var(--label-face) placeholder, 340px flexible, the count to its right
377
- in var(--ink-faint), tabular. No magnifier above 480: the field is bare by thesis and the label
378
- names it; under 480 the field folds into a 44px magnifier that drops one hairline row under the
379
- bar (see 390).
380
- RIGHT: "Projects" with a caret, a 20px glyph mode toggle in a 44px hit area, and the identity as
381
- avatar + login + caret. Every right-group control is lowercase text with no chrome; the open
382
- trigger takes a 2px accent underline, because an open menu is the live thing on the bar. Signed
383
- out the identity slot is the landing's sign-in door verbatim (inverted ink, lowercase) - one
384
- filled pill on a bar that has no others.
385
-
386
- MENU. Not a panel: a BAND. var(--bg-card) ground, a 1px var(--rule) bottom edge, full bleed, three
387
- cells in a hairline-separated row, each a lowercase display title (Manrope 300, 24px) over one
388
- clause in var(--ink-soft); the whole cell is the target (the anchor's ::after stretches over it).
389
- The current view carries a 2px accent hairline on its TOP edge - the rail-step-lit grammar from
390
- DESIGN.md, moved onto a menu - plus var(--ink) ink and aria-current="page". The profile band is
391
- the same band: the identity in its left cell, "My projects" / a deferred Settings slot that renders
392
- nothing / "Sign out" right-aligned. Opening pushes the page down (the sky moves, the bar does not
393
- float); one authored move, a 6px rise + fade over 220ms on the world's exponential ease-out, dead
394
- under the kill switch. Escape closes and returns focus to the trigger; Tab walks the items; Arrow
395
- keys move between them; a click outside closes.
396
-
397
- CARD. min(560px,100%), var(--bg-card) on a 1px var(--rule) edge, 16px, no shadow. Six regions,
398
- flat children of the card, in the brief's order. (1) the banner, a bare 5:2 <img> cropped into the
399
- chrome of the platform's own social card. (2) the two figures as LEADER-LINE CALLOUTS on it, a 1px
400
- diagonal leader, a 300-weight tabular figure and a 13px caption, each carrying its own feathered
401
- patch of the card ground behind the glyphs only. (3) the accent disc, 54px, half over the banner's
402
- lower edge at the left, ringed 4px in the card ground so it sits ON the card. (4) name at the
403
- headline scale (Manrope 300, 32px), tagline, and the relationship as a mono label rather than a
404
- pill - it is a fact about you, not a control. (5) faces, five 28px avatars at -8px overlap ringed
405
- in the card ground, +N past five. (6) the facts as a two-column mono ledger, label left in
406
- var(--label-face), value right and tabular, ONE hairline per row (both cells stretch to the row, so
407
- the rule never steps at the column boundary); then ONE accent door
408
- ("Open site") with the hall entry beside it as an underlined var(--accent-deep) text link. With no
409
- art the callouts become the two-up band and the disc returns to the top of the card body; the
410
- ladder holds down to the floor (disc, name, tagline, relationship, one door).
411
-
412
- LIGHT. Paper with hairlines. Nothing inverts: the bar is var(--bg) closed by var(--rule), the band
413
- is var(--bg-card) on var(--bg) and the difference is read by its edge, the card is var(--bg-card)
414
- on paper. The only mode-swapped materials are the label face (mono on paper, the body face on
415
- black), the focus colour (var(--accent-deep) light, var(--accent) dark) and the black type halo,
416
- which is dark-only. No black panel ever lands on the light page.
417
-
418
- 390. The bar keeps ONE row at 60px down to 320: the wordmark folds away and the glyph stays, and
419
- the search keeps a real (narrower) field rather than collapsing to an icon - a bare field has no
420
- chrome to protect, and hiding the map's only filter to save 90px is the wrong trade down to 480. Under 480
421
- the row cannot carry a field that holds a query beside four controls (the field measured 45px at
422
- 320 with the word gone), so the search folds into a 44px magnifier in the field's place; pressed,
423
- it drops ONE hairline row of the page ground under the bar, full width, carrying the field and its
424
- count, and Escape or an opening menu folds it back. The filter is never hidden, one press away,
425
- and the row is this version's own grammar. From 481 to 640 the count stays in the bar, where it
426
- fits. (An earlier draft of this note dropped the count at 640 "because
427
- the toolbar already carries it" - it does not: this version moved the search, and #mapCount with
428
- it, out of the toolbar.) The search is capped at its desktop width under 900, so it shrinks with
429
- the viewport instead of swelling into the space the folded wordmark frees. Both bands stack to one
430
- cell per row, the profile band included. Under 560 the card's callouts become the two-up band -
431
- the same fallback the no-art rung uses, so the mobile card and the artless card are one
432
- composition rather than two.
433
-
434
- BEYOND THE SKIN
435
- - [given] The nav row leaves the bar; Projects becomes a right-group band. COLLISION:
436
- projects_hub_pre_uat.mjs:203 pins cosmos's `.shell .navrow{position:absolute;left:50%;...}` rule -
437
- deleting .navrow from cosmos.css breaks it; retarget or keep a .navrow rule.
438
- projects_hub.mjs:42-43 stays SAFE only while each band item keeps `href="?view=<v>" data-nav="<v>"`
439
- as adjacent attributes, which this mock does.
440
- - [given] Home is removed; the logo is home. COLLISION: projects_hub.mjs:40 (the Home-link regex)
441
- breaks hard; :46 and :48 (`a.dataset.nav === 'home'`) go dead. Retarget :40 to the mark.
442
- - [given] The hall pill leaves the bar and becomes a per-project affordance. COLLISION:
443
- landing_page.mjs:214/217/220 and projects_hub.mjs:105 all pin `{{buildersOrigin}}` in the hub and
444
- the pill is the only match. ARCHITECTURE: `clientBranding()` withholds domains and a {{token}} is
445
- one value per instance, so "only where the hall address differs" needs a NEW per-project field on
446
- /projects/featured (or /my-projects); it cannot be a token.
447
- - [given] The map search moves into the bar and hides on mine/new. SAFE: cosmos keeps
448
- `.search:focus-within` and `.mineBar .search .count{display:none;}`; pageInert's pinned selector
449
- string already covers a bar-hosted search through `header.shell`.
450
- - [given] A profile band on the chip. WATCH: projects.html:5307 uses a SINGULAR
451
- `querySelector('[data-nav="mine"]')`; the band's "Your projects" is that one node here, but a
452
- second `data-nav="mine"` anywhere (e.g. a profile item pointing at ?view=mine) becomes a latent
453
- signed-out bug. Use querySelectorAll.
454
- - [consequence] Nothing is removed from the sky. The brief's "retire the top wash" applies to the
455
- landing (--sky-topscrim); the hub's `.sky` is already a flat var(--sky-bg) with no scrim, so a
456
- grounded bar costs it nothing.
457
- - [consequence] The sky gets its height back. `.field{height:calc(100vh - 232px)}` was measured
458
- against a 69px bar plus a three-control toolbar; a 64px bar and a two-control toolbar leave
459
- ~30px unclaimed at 1440, and at 390 the old three-row stack (161px of bar + three toolbar rows)
460
- becomes one 60px row plus one toolbar row. Retarget the 232.
461
- - [consequence] `#mapCount` leaves the toolbar with the search and its string changes from
462
- "N projects" to "1 of 4" while a query is live. SAFE: projects_hub.mjs:389/393 pin the
463
- computation (`var shown = list.length + (selfShown ? 1 : 0)`), not the wording; the copy registry
464
- must be regenerated.
465
- - [consequence] The document ships ONE first-level heading, and it sits directly under the main
466
- element rather than inside #view-map, so the mine and new views are not left with no top-level
467
- heading at all; setView rewrites its text ("The project map" / "Your projects" / "Start a
468
- project"). Today projects.html carries ten (one per view and per wizard panel), only ever one
469
- visible; the rest are demoted a level. COLLISION: projects_hub_pre_uat.mjs:451 pins the literal
470
- adjacency of the view-map section and the visually-hidden "The project map" heading; that
471
- assertion moves with the heading.
472
- - [given] builders leaves the facts for the figure band and becomes a callout on the art.
473
- COLLISION: projects_hub.mjs:496-498 and :586-602 pin id="cardBuilders" inside the card slice and
474
- its cardFact('cardBuilders', ...) wiring (HUB-TESTS.md:94 and :97); both must be retargeted to
475
- the figure, or the ledger keeps a fifth row this composition does not have.
476
- - [consequence] A hall glyph adds a 7th cell to the table. COLLISION: projects_hub.mjs:531-535
477
- requires the <th> count, #selfRow's <td> count and the generated-row template to match - all
478
- three must gain the cell together.
479
- - [proposal] The toolbar becomes a second hairline row under the bar (Sky | Table and Only mine as
480
- lowercase text controls on one rule), so the map reads as two rules and then sky. Not built here:
481
- the toolbar is out of this round's scope. COLLISION: none in the static tests; cosmos's `.seg` and
482
- `.switch` are only pinned by the contrast PAIRS table.
483
- - [proposal] The mine view's empty state becomes a band of the same grammar (one title, one clause,
484
- one door) instead of a centred card, so "you own nothing yet" reads like a destination rather
485
- than an error. COLLISION: projects_hub_pre_uat.mjs pins the empty-state copy, not its markup.
486
- - [proposal] `About` (goal 1000063) becomes a FOURTH cell of the Projects band. The grid already
487
- takes it; the order Map / Your projects / New project / About keeps every pinned regex intact.
488
- - [proposal] The card grows from cosmos's `width:min(420px,100%)` to min(560px,100%). The poster
489
- needs the width; no test pins it.
490
- - [proposal] The table's "unknown" mark is an em dash, which the world's taste ban forbids and the
491
- card already refuses ("absent facts HIDE, never dash"). Propose a middot or an empty cell.
492
- COLLISION: projects_hub.mjs:564-580 pins `p.builders == null ? '—'` and tagsHtml's `return '—'`.
493
- In this mock every carried em dash is written as the entity `&#8212;` so the render is byte-for-
494
- byte today's while the mock's own copy check stays honest about dashes THIS version authored.
495
- - [proposal] Three carried controls sit under the world's 24px floor and this mock floors them
496
- page-locally: the footer links (no vertical padding), the table's sort buttons (`all:unset`) and
497
- the `.search` inputs (13px, no padding). COLLISION: projects_hub_pre_uat.mjs:430-433 pins
498
- `.tbl th button{all:unset;cursor:pointer;}` verbatim in cosmos - adding a min-height there needs
499
- the literal retargeted.
500
- - [proposal] "Runs" is the label of the TAGS field, not of an address. The brief's Data pins
501
- cloudbongos.com as the platform's Runs, and this card renders the ADDRESS on every rung (Off the
502
- Boats: amazonprimea.com) so the ladder reads Runs one way; the table keeps the module list in
503
- its Runs column. Propose splitting them: an address fact ("Runs") and a separate tags fact.
504
- COLLISION: projects_hub.mjs:586-602 wires cardFact('cardRuns', tagsHtml(p.tags)) and
505
- HUB-TESTS.md:120 pins tagsHtml([]) returning "core"; both move with the split.
506
- - [proposal] Faces and shipped-in-30-days do not exist on any feed. /projects/featured carries
507
- art_url (unread) and owner_avatar only; there is no avatar list and no shipped_30d. Both need new
508
- enrichment fields in modules/platform-identity/project-stats.js + routes/projects.js.
509
-
510
- DATA. Real, fetched 2026-08-27. Builder jaxri (avatar from the live GitHub CDN). Catalog: Off the
511
- Boats / Demo / Emersonian Circles from /projects/featured, plus the apex self row. Orbs carry the
512
- inline styles the live page computes: the list is name-sorted, so Demo takes ORB_SLOTS[0]
513
- (22%,30%,74px), Emersonian Circles [1] (78%,66%,60px), Off the Boats [2] (33%,74%,56px), and
514
- assignAccents() sorts the catalog by origin, giving Off the Boats var(--sea), Demo
515
- var(--terracotta), Emersonian Circles var(--gold-soft); the self orb is var(--accent) at 122px,
516
- 50%/44%. The `card` rung is the platform: builders 9 (the apex leaderboard's rows with credits
517
- above zero), shipped in 30 days 30+ (the feed caps at 30 rows and all 30 fall inside the window),
518
- faces jaxri / lars589 / IAMMASTERQUA / scootermack / LarsCode +4, Type hidden (null in the feed),
519
- Runs "cloudbongos.com" (the brief's Data pins the address; the field the card reads today is tags,
520
- which renders "core" for an instance with none - see the proposal above), Owner lars589, Listed
521
- Jul 14, 2026 (the Demo row's date, as the brief allows, since the apex has no listing row of its
522
- own). The `card-member` rung is Off the Boats: builders 13 from ITS leaderboard (the featured
523
- feed's 11 is a different count from a different source and is what the table row shows), shipped in
524
- 30 days a real 0 (its newest ship is 2026-06-30), faces 5 + 8, and NO hall entry because its hall
525
- is amazonprimea.com/builders, the same address as its site. ART: the platform has no art_url, so
526
- the banner is public-landing/assets/world/og-card.jpg - a real 1200x630 capture of the built
527
- landing, the platform's art in the only sense the platform has one - cropped 5:2 and scaled into
528
- its chrome so the landing's own headline stays out of frame. No other image is invented.
529
- MEMBERSHIP is illustrative: jaxri is rendered as a member (you build here) of both the platform and
530
- Off the Boats, per the brief; the live /my-projects fixture returns nothing.
531
-
532
- TOKENS. Declared on body: --glass, --glass-strong, --glass-edge, --accent-lift, --void-pill,
533
- --void-ink, --focus, --type-halo, --label-face, --label-size, --label-space, --radius, --radius-sm,
534
- --radius-pill, --top-h, plus this page's --gutter and --leader-len. The eight that actually flip
535
- between modes (--glass, --glass-strong, --glass-edge, --focus, --type-halo, --label-face,
536
- --label-size, --label-space) are the ones restated in the dark block and its byte-identical
537
- prefers-color-scheme twin; the rest are declared once, which is also what lets the 900px block
538
- take --top-h to 60px without a (0,2,1) dark rule outranking it. Taken from
539
- cosmos.css unchanged: the fifteen contract tokens, --accent-on, --accent-soft, --card, --ctl-line,
540
- --font-mono, --pad-*, and the four names that keep cosmos's meaning here and are never redeclared -
541
- --line (a hairline COLOUR, not a width) and --ok / --warn / --err (the hub's fixed status
542
- vocabulary its chips depend on). Two recipes are adapted because a mock may carry no new literal:
543
- --focus resolves to var(--accent-deep) in light rather than world.css's mix toward #3a1400, and
544
- --accent-on is cosmos's color-mix rather than DESIGN.md's #2a1305.
545
- -->
546
- <div class="sky" aria-hidden="true">
547
- <span class="star" style="top:2%;left:12%;width:2px;height:2px;"></span>
548
- <span class="star" style="top:5%;left:28%;width:1.5px;height:1.5px;animation-delay:.7s;"></span>
549
- <span class="star" style="top:3%;left:47%;width:2px;height:2px;animation-delay:1.4s;"></span>
550
- <span class="star" style="top:8%;left:63%;width:1.5px;height:1.5px;animation-delay:.3s;"></span>
551
- <span class="star" style="top:4%;left:78%;width:2.5px;height:2.5px;animation-delay:2s;"></span>
552
- <span class="star" style="top:10%;left:88%;width:1.5px;height:1.5px;animation-delay:1s;"></span>
553
- <span class="star" style="top:14%;left:20%;width:1.5px;height:1.5px;animation-delay:1.8s;"></span>
554
- <span class="star" style="top:22%;left:55%;width:2px;height:2px;animation-delay:.5s;"></span>
555
- <span class="star" style="top:34%;left:36%;width:1.5px;height:1.5px;animation-delay:2.4s;"></span>
556
- <span class="star" style="top:30%;left:78%;width:1.5px;height:1.5px;animation-delay:1.2s;"></span>
557
- <span class="star" style="top:48%;left:8%;width:2px;height:2px;animation-delay:.9s;"></span>
558
- <span class="star" style="top:55%;left:90%;width:1.5px;height:1.5px;animation-delay:1.6s;"></span>
559
- <span class="star" style="top:68%;left:24%;width:1.5px;height:1.5px;animation-delay:2.1s;"></span>
560
- <span class="star" style="top:76%;left:64%;width:2px;height:2px;animation-delay:.4s;"></span>
561
- <span class="star" style="top:88%;left:42%;width:1.5px;height:1.5px;animation-delay:1.9s;"></span>
562
- </div>
563
-
564
- <svg width="0" height="0" style="position:absolute" aria-hidden="true" focusable="false">
565
- <symbol id="i-caret" viewBox="0 0 20 20"><path class="ic" d="m5.6 8 4.4 4.4L14.4 8"/></symbol>
566
- <symbol id="i-hall" viewBox="0 0 20 20"><path class="ic" d="M2.8 7.4 10 3.5l7.2 3.9"/><path class="ic" d="M5.2 8.8v5.6M10 8.8v5.6M14.8 8.8v5.6"/><path class="ic" d="M3.4 16.3h13.2"/></symbol>
567
- <symbol id="i-close" viewBox="0 0 20 20"><path class="ic" d="M5.6 5.6l8.8 8.8M14.4 5.6l-8.8 8.8"/></symbol>
568
- </svg>
569
-
570
- <a class="skip" href="#hubMain">Skip to content</a>
571
-
572
- <header class="bar">
573
- <div class="barLeft">
574
- <a class="barMark" href="/" aria-label="Cloud Bongos, home">
575
- <svg viewBox="0 0 26 20" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.4"
576
- stroke-linecap="round" stroke-linejoin="round">
577
- <ellipse cx="8.4" cy="5.6" rx="6.6" ry="2.9"/>
578
- <path d="M1.8 5.6v6.6a6.6 2.9 0 0 0 13.2 0V5.6"/>
579
- <ellipse cx="19.6" cy="8.4" rx="4.7" ry="2.1"/>
580
- <path d="M14.9 8.4v4.6a4.7 2.1 0 0 0 9.4 0V8.4"/>
581
- </svg>
582
- <span>Cloud Bongos</span>
583
- </a>
584
- <button class="qToggle" type="button" aria-expanded="false" aria-controls="qWrap" aria-label="Search projects">
585
- <svg viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor"
586
- stroke-width="1.6" stroke-linecap="round"><circle cx="11" cy="11" r="7"/><path d="M20 20l-3.5-3.5"/></svg>
587
- </button>
588
- <div class="qWrap" id="qWrap">
589
- <div class="qField">
590
- <svg class="qIcon" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor"
591
- stroke-width="1.6" stroke-linecap="round"><circle cx="11" cy="11" r="7"/><path d="M20 20l-3.5-3.5"/></svg>
592
- <label class="vh" for="mapQ">Search projects</label>
593
- <input class="qInput" type="text" id="mapQ" placeholder="Find a project" aria-label="Search projects">
594
- </div>
595
- <span class="qCount" id="mapCount">4 projects</span>
596
- </div>
597
- </div>
598
- <div class="barRight">
599
- <button class="trig projTrig" type="button" data-menu="projects" aria-expanded="false" aria-controls="menuProjects">Projects<svg class="cv" aria-hidden="true"><use href="#i-caret"></use></svg></button>
600
- <button class="modeBtn" type="button" aria-label="Toggle light or dark">
601
- <svg class="moon" viewBox="0 0 20 20" aria-hidden="true" fill="none" stroke="currentColor"
602
- stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">
603
- <path d="M16.2 12.4A6.8 6.8 0 0 1 7.6 3.8a6.8 6.8 0 1 0 8.6 8.6z"/>
604
- </svg>
605
- <svg class="sun" viewBox="0 0 20 20" aria-hidden="true" fill="none" stroke="currentColor"
606
- stroke-width="1.4" stroke-linecap="round">
607
- <circle cx="10" cy="10" r="3.9"/>
608
- <path d="M10 1.6v2.1M10 16.3v2.1M1.6 10h2.1M16.3 10h2.1M4.1 4.1l1.5 1.5M14.4 14.4l1.5 1.5M15.9 4.1l-1.5 1.5M5.6 14.4l-1.5 1.5"/>
609
- </svg>
610
- </button>
611
- <button class="trig whoTrig" type="button" data-menu="profile" aria-expanded="false" aria-controls="menuProfile" aria-label="Account, jaxri"><img src="https://avatars.githubusercontent.com/u/64553469?v=4" alt=""><span class="whoName">jaxri</span><svg class="cv" aria-hidden="true"><use href="#i-caret"></use></svg></button>
612
- <a class="signinDoor" href="/api/bongos/auth/web/start?return=/projects" hidden>Sign in</a>
613
- </div>
614
- </header>
615
-
616
- <div class="band" id="menuProjects" data-menu="projects" hidden>
617
- <div class="bandIn">
618
- <ul class="bandRow">
619
- <li class="bandCell is-now" data-view="map"><a class="bandT" href="?view=map" data-nav="map" aria-current="page">Map</a><span class="bandC">the sky of every project</span></li>
620
- <li class="bandCell" data-view="mine"><a class="bandT" href="?view=mine" data-nav="mine">Your projects</a><span class="bandC">the ones you own or build on</span></li>
621
- <li class="bandCell" data-view="new"><a class="bandT" href="?view=new" data-nav="new">New project</a><span class="bandC">five questions and a home</span></li>
622
- </ul>
623
- </div>
624
- </div>
625
-
626
- <div class="band" id="menuProfile" data-menu="profile" hidden>
627
- <div class="bandIn bandProfile">
628
- <p class="bandWho"><img src="https://avatars.githubusercontent.com/u/64553469?v=4" alt="">jaxri</p>
629
- <ul class="bandRow">
630
- <li class="bandCell"><a class="bandT" href="https://builders.cloudbongos.com/my-projects">My projects</a></li>
631
- <li class="bandSlot" aria-hidden="true"></li>
632
- <li class="bandCell"><a class="bandT" href="/api/bongos/auth/logout">Sign out</a></li>
633
- </ul>
634
- </div>
635
- </div>
636
-
637
- <main id="hubMain">
638
- <h1 class="vh" id="pageH1">The project map</h1>
639
- <section id="view-map">
640
- <div class="toolbar">
641
- <div class="seg" role="group" aria-label="Map display">
642
- <button type="button" id="sub-sky" aria-pressed="true">Sky</button>
643
- <button type="button" id="sub-table" aria-pressed="false">Table</button>
644
- </div>
645
-
646
- <label class="switch" title="Show only projects you own or build on">
647
- <input type="checkbox" id="onlyMine"><span class="track"></span> Only mine
648
- </label>
649
- </div>
650
-
651
- <div class="fieldWrap" id="sub-sky-body">
652
- <div class="field" id="orbField">
653
- <div class="ground"></div>
654
- <div class="stars" id="mapStars" aria-hidden="true"></div>
655
- <div class="skyCam" id="skyCam">
656
- <!--apex-only:start-->
657
- <a class="worldOrb" id="selfOrb" href="/" data-rel="builder" style="--acc:var(--accent);--d:122px;left:50%;top:44%">
658
- <span class="badge">you build here</span>
659
- <span class="disc"></span>
660
- <span class="nm" data-brand-product-inline>Cloud Bongos</span>
661
- <span class="sub">this platform</span>
662
- </a>
663
- <!--apex-only:end-->
664
- <a class="worldOrb" data-generated data-rel="none" data-origin="https://demo.cloudbongos.com" href="https://demo.cloudbongos.com" target="_blank" rel="noopener" title="A live demo instance on Cloud Bongos." style="--acc:var(--terracotta);--d:74px;left:22%;top:30%">
665
- <span class="disc"></span>
666
- <span class="nm">Demo</span>
667
- </a>
668
- <a class="worldOrb" data-generated data-rel="none" data-origin="https://emersonian-circles.cloudbongos.com" href="https://emersonian-circles.cloudbongos.com" target="_blank" rel="noopener" title="An independent project built on Cloud Bongos." style="--acc:var(--gold-soft);--d:60px;left:78%;top:66%">
669
- <span class="disc"></span>
670
- <span class="nm">Emersonian Circles</span>
671
- </a>
672
- <a class="worldOrb" data-generated data-rel="builder" data-origin="https://amazonprimea.com" href="https://amazonprimea.com" target="_blank" rel="noopener" title="A singular shop, and a broader videogame in a persistent ancient Greek world that collides with modern day commerce &amp; products." style="--acc:var(--sea);--d:56px;left:33%;top:74%">
673
- <span class="badge">you build here</span>
674
- <span class="disc"></span>
675
- <span class="nm">Off the Boats</span>
676
- </a>
677
- </div>
678
- <div class="fieldEmpty">
679
- <p id="emptyMsg">You don&rsquo;t own or build on anything here yet.</p>
680
- <span class="sub">Turn the filter off to see what everyone else is building &#8212; or start your own.</span>
681
- <a class="btn small" href="?view=new" data-nav-to="new">Start a project</a>
682
- </div>
683
- <div class="hintbar"><span><kbd>drag</kbd> pan</span><span><kbd>scroll</kbd> zoom</span><span><kbd>click a project</kbd> open it</span></div>
684
- <div class="hoverCard" id="hoverCard" hidden aria-hidden="true"></div>
685
- </div>
686
- </div>
687
-
688
- <div class="wrap viewWrap" id="sub-table-body" hidden>
689
- <table class="tbl">
690
- <thead>
691
- <tr>
692
- <th class="colName" data-sort="name" aria-sort="ascending"><button type="button" class="thSort">Project</button></th>
693
- <th class="colType">Type</th>
694
- <th class="right" data-sort="builders"><button type="button" class="thSort">Builders</button></th>
695
- <th class="colTags">Runs</th>
696
- <th class="right colStatus">Status</th>
697
- <th class="colHall"></th>
698
- <th class="colGo"></th>
699
- </tr>
700
- </thead>
701
- <tbody id="mapRows">
702
- <!--apex-only:start-->
703
- <tr id="selfRow">
704
- <td class="colName"><span class="pname"><span class="pdot" style="--acc:var(--accent)"></span>
705
- <span><span class="ptitle" data-brand-product-inline>Cloud Bongos</span><span id="selfPill"> <span class="pill">you build here</span></span><br>
706
- <span class="ptag">this platform</span></span></span></td>
707
- <td class="colType">&#8212;</td>
708
- <td class="right num" id="selfBuilders">9</td>
709
- <td class="colTags" id="selfTags">&#8212;</td>
710
- <td class="right colStatus"><span class="chip calm"><span class="cdot"></span>&#8212;</span></td>
711
- <td class="colHall"><a class="hallGlyph" href="https://builders.cloudbongos.com" aria-label="Builders' hall"><svg aria-hidden="true"><use href="#i-hall"></use></svg></a></td>
712
- <td class="colGo"><a class="btn small ghost" href="/">Open &#8594;</a></td>
713
- </tr>
714
- <!--apex-only:end-->
715
- <tr data-generated>
716
- <td class="colName"><span class="pname"><span class="pdot" style="--acc:var(--terracotta)"></span>
717
- <span><span class="ptitle">Demo</span><br>
718
- <span class="ptag">A live demo instance on Cloud Bongos.</span></span></span></td>
719
- <td class="colType">&#8212;</td>
720
- <td class="right num">1</td>
721
- <td class="colTags"><span class="tags">core</span></td>
722
- <td class="right colStatus"><span class="chip calm"><span class="cdot"></span>&#8212;</span></td>
723
- <td class="colHall"></td>
724
- <td class="colGo"><a class="btn small ghost" href="https://demo.cloudbongos.com" target="_blank" rel="noopener">Open &#8594;</a></td>
725
- </tr>
726
- <tr data-generated>
727
- <td class="colName"><span class="pname"><span class="pdot" style="--acc:var(--gold-soft)"></span>
728
- <span><span class="ptitle">Emersonian Circles</span><br>
729
- <span class="ptag">An independent project built on Cloud Bongos.</span></span></span></td>
730
- <td class="colType">&#8212;</td>
731
- <td class="right num">1</td>
732
- <td class="colTags"><span class="tags">core</span></td>
733
- <td class="right colStatus"><span class="chip calm"><span class="cdot"></span>&#8212;</span></td>
734
- <td class="colHall"></td>
735
- <td class="colGo"><a class="btn small ghost" href="https://emersonian-circles.cloudbongos.com" target="_blank" rel="noopener">Open &#8594;</a></td>
736
- </tr>
737
- <tr data-generated>
738
- <td class="colName"><span class="pname"><span class="pdot" style="--acc:var(--sea)"></span>
739
- <span><span class="ptitle">Off the Boats</span> <span class="pill">you build here</span><br>
740
- <span class="ptag">A singular shop, and a broader videogame in a persistent ancient Greek world that collides with modern day commerce &amp; products.</span></span></span></td>
741
- <td class="colType"><span class="typeTag">Game</span></td>
742
- <td class="right num">11</td>
743
- <td class="colTags"><span class="tags"><span>art-pipeline</span> <span>character-anim</span> <span>demo</span> <span>+1</span></span></td>
744
- <td class="right colStatus"><span class="chip calm"><span class="cdot"></span>&#8212;</span></td>
745
- <td class="colHall"></td>
746
- <td class="colGo"><a class="btn small ghost" href="https://amazonprimea.com" target="_blank" rel="noopener">Open &#8594;</a></td>
747
- </tr>
748
- </tbody>
749
- </table>
750
- <p class="finePrint" style="text-align:center">Sort by Project or Builders.</p>
751
- </div>
752
-
753
- <div class="cardScrim" id="cardScrim" hidden>
754
- <div class="projCard has-art" id="projCard" role="dialog" aria-modal="true" aria-labelledby="cardName">
755
- <button type="button" class="cardX" id="cardClose" aria-label="Close"><svg aria-hidden="true"><use href="#i-close"></use></svg></button>
756
- <img class="cardArt" id="cardArt" src="../../../../modules/public-landing/public/assets/world/og-card.jpg" alt="">
757
- <p class="cardFigs" id="cardFigs">
758
- <span class="cardCall a" id="figA"><svg class="lead" viewBox="0 0 68 26" preserveAspectRatio="none" aria-hidden="true"><line x1="0" y1="25" x2="67" y2="1" stroke="currentColor" stroke-width="1" vector-effect="non-scaling-stroke"/></svg><span class="fig" id="figABig">9</span><span class="cap">builders</span></span>
759
- <span class="cardCall b" id="figB"><svg class="lead" viewBox="0 0 68 26" preserveAspectRatio="none" aria-hidden="true"><line x1="0" y1="25" x2="67" y2="1" stroke="currentColor" stroke-width="1" vector-effect="non-scaling-stroke"/></svg><span class="fig" id="figBBig">30+</span><span class="cap">shipped in 30 days</span></span>
760
- </p>
761
- <span class="cardDot" id="cardDot" style="--acc:var(--accent)"></span>
762
- <h3 id="cardName">Cloud Bongos</h3>
763
- <p class="cardTag" id="cardTag">Building Digitally, as simple as Bongos.</p>
764
- <p class="cardRel" id="cardRel">you build here</p>
765
- <p class="cardFaces" id="cardFaces"><img src="https://avatars.githubusercontent.com/u/64553469?v=4" alt=""><img src="https://avatars.githubusercontent.com/u/283020391?v=4" alt=""><img src="https://avatars.githubusercontent.com/u/290184380?v=4" alt=""><img src="https://avatars.githubusercontent.com/u/295990394?v=4" alt=""><img src="https://avatars.githubusercontent.com/u/35337514?v=4" alt=""><span class="facesMore" id="facesMore">+4</span></p>
766
- <dl class="cardMeta" id="cardMeta">
767
- <dt id="cardTypeL" data-fact="Type" hidden>Type</dt><dd id="cardType" data-fact="Type" hidden></dd>
768
- <dt id="cardRunsL" data-fact="Runs">Runs</dt><dd id="cardRuns" data-fact="Runs">cloudbongos.com</dd>
769
- <dt id="cardOwnerL" data-fact="Owner">Owner</dt><dd id="cardOwner" data-fact="Owner"><img class="ownerAv" src="https://avatars.githubusercontent.com/u/283020391?v=4" alt=""><span class="cardOwnerName" id="cardOwnerName">lars589</span></dd>
770
- <dt id="cardListedL" data-fact="Listed">Listed</dt><dd id="cardListed" data-fact="Listed">Jul 14, 2026</dd>
771
- </dl>
772
- <div class="cardActions" id="cardActions"><a class="pcDoor cardOpen" id="cardOpen" href="https://cloudbongos.com">Open site</a><a class="pcHall cardHall" id="cardHall" href="https://builders.cloudbongos.com"><svg aria-hidden="true"><use href="#i-hall"></use></svg>Builders' hall</a></div>
773
- </div>
774
- </div>
775
- </section>
776
-
777
- <section id="view-mine" hidden>
778
- <div class="wrap viewWrap">
779
- <div class="hero" style="padding-left:0;padding-right:0">
780
- <h2 class="viewTitle">Your projects</h2>
781
- <p class="lede">Every project you own &#8212; what it is, whether it is answering, and the controls that matter.
782
- Each card says what the platform <b>last verified</b>, and when.</p>
783
- </div>
784
-
785
- <div class="card">
786
- <section id="gate" hidden>
787
- <p class="lede">Every project you own, in one place &#8212; its live status, its address, and the controls that matter. Sign in with GitHub to see yours.</p>
788
- <a class="btn" id="signin" href="/api/bongos/auth/web/start?return=/projects%3Fview%3Dmine">
789
- <svg class="gh" viewBox="0 0 16 16" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>
790
- Sign in with GitHub
791
- </a>
792
- </section>
793
-
794
- <section id="mine">
795
- <div class="mineBar" id="mineBar">
796
- <div class="search">
797
- <svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="M20 20l-3.5-3.5"/></svg>
798
- <input type="text" id="mineQ" placeholder="Search your projects by name, repo or address&hellip;" aria-label="Search your projects">
799
- <span class="count" id="mineCount"></span>
800
- </div>
801
- <div class="filters" role="group" aria-label="Filter by state">
802
- <button type="button" data-f="all" aria-controls="projectsList" aria-pressed="true">All <span class="n">0</span></button>
803
- <button type="button" data-f="live" aria-controls="projectsList" aria-pressed="false">Live <span class="n">0</span></button>
804
- <button type="button" data-f="attention" aria-controls="projectsList" aria-pressed="false">Needs you <span class="n">0</span></button>
805
- <button type="button" data-f="working" aria-controls="projectsList" aria-pressed="false">Working <span class="n">0</span></button>
806
- <button type="button" data-f="gone" aria-controls="projectsList" aria-pressed="false">Torn down <span class="n">0</span></button>
807
- </div>
808
- </div>
809
-
810
- <section class="empty" id="empty">
811
- <p>You don't own any projects yet. Name one, pick a home for its code, and it gets an address of its own in this sky.</p>
812
- <a class="btn" href="?view=new" data-nav-to="new">Create your first project</a>
813
- </section>
814
- <ul class="projects" id="projectsList"></ul>
815
- <p class="finePrint">Buttons here ask the platform to act; each card reports only what was verified, and when. A standup keeps going even with this tab closed.</p>
816
- </section>
817
- </div>
818
- </div>
819
- </section>
820
-
821
- <section id="view-new" hidden>
822
- <div class="wrap viewWrap">
823
- <div class="card">
824
- <ol class="steps" aria-label="Creation steps">
825
- <li class="now" data-step="1" aria-current="step"><span class="snum">1</span><span class="slbl">Name</span></li>
826
- <li data-step="2"><span class="snum">2</span><span class="slbl">Type</span></li>
827
- <li data-step="3"><span class="snum">3</span><span class="slbl">Home</span></li>
828
- <li data-step="4"><span class="snum">4</span><span class="slbl">Address</span></li>
829
- <li data-step="5"><span class="snum">5</span><span class="slbl">Create</span></li>
830
- </ol>
831
-
832
- <div class="createSplit">
833
- <div>
834
- <section class="panel" id="panel-gate" aria-labelledby="gate-h">
835
- <h2 class="viewTitle" id="gate-h">Create a project</h2>
836
- <p class="lede">Your own <strong data-brand-product-inline>Cloud Bongos</strong> project: its own repository, its own home on the web, and a hall of AI builders who do the building. You set the direction; agents lay down the code. Sign in with GitHub so the project belongs to <em>you</em> from the very first beat.</p>
837
- <a class="btn big" id="wizSignin" href="/api/bongos/auth/web/start?return=/projects%3Fview%3Dnew&amp;repos=1">
838
- <svg class="gh" viewBox="0 0 16 16" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>
839
- Sign in with GitHub
840
- </a>
841
- <p class="finePrint" id="wizScopeNote">Signing in asks GitHub for repository access. GitHub words this as access to <em>all</em> your repositories &#8212; that is the only permission it offers that can write to a private repo. We use it to create (or read) the one repo your project lives in and push its starting files. Nothing else is touched.</p>
842
- <p class="finePrint">Free while we find our feet. Your project&rsquo;s repo and server are yours &#8212;
843
- <span data-brand-product-inline>Cloud Bongos</span> is <a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">open source (AGPL-3.0)</a>
844
- and capture-resistant by design.</p>
845
- </section>
846
-
847
- <section class="panel on" id="panel-name" aria-labelledby="name-h">
848
- <h2 class="viewTitle" id="name-h">Start a project</h2>
849
- <p class="lede">Your own project: its own repository, its own home on the web, and a hall of AI builders
850
- who do the building. You set the direction; agents lay down the code.</p>
851
- <form id="newForm" data-next="2">
852
- <label class="wizField"><span>Project name</span>
853
- <input type="text" id="newName" placeholder="What should it be called?" autocomplete="off" maxlength="80" aria-label="Project name">
854
- <div class="hint" id="h-name">&nbsp;</div>
855
- </label>
856
- <div class="nav">
857
- <span></span>
858
- <button type="submit" class="btn">Continue &#8594;</button>
859
- </div>
860
- </form>
861
- <p class="finePrint">Free while we find our feet. Your project&rsquo;s repo and server are yours &#8212;
862
- <span data-brand-product-inline>Cloud Bongos</span> is <a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">open source (AGPL-3.0)</a>
863
- and capture-resistant by design.</p>
864
- </section>
865
- </div>
866
-
867
- <div class="bongoCell">
868
- <svg class="bongos" viewBox="0 0 440 320" role="img" aria-label="A pair of bongo drums" style="--skin-hi:#fffdf7;--skin-mid:#f4e6cd;--skin-lo:#cdb896;--drum-shadow:#000000">
869
- <defs>
870
- <linearGradient id="shellBig" x1="0" y1="0" x2="1" y2="0.35">
871
- <stop offset="0%" stop-color="var(--accent-deep)"/><stop offset="16%" stop-color="var(--accent)"/>
872
- <stop offset="48%" stop-color="var(--accent)"/><stop offset="86%" stop-color="var(--accent-deep)"/>
873
- <stop offset="100%" stop-color="var(--accent-deep)"/>
874
- </linearGradient>
875
- <linearGradient id="shellSml" x1="0" y1="0" x2="1" y2="0.35">
876
- <stop offset="0%" stop-color="var(--accent-deep)"/><stop offset="18%" stop-color="var(--terracotta)"/>
877
- <stop offset="55%" stop-color="var(--terracotta)"/><stop offset="100%" stop-color="var(--accent-deep)"/>
878
- </linearGradient>
879
- <radialGradient id="skin" cx="0.36" cy="0.26" r="0.9">
880
- <stop offset="0%" stop-color="var(--skin-hi)"/><stop offset="58%" stop-color="var(--skin-mid)"/>
881
- <stop offset="100%" stop-color="var(--skin-lo)"/>
882
- </radialGradient>
883
- <radialGradient id="halo" cx="0.5" cy="0.5" r="0.5">
884
- <stop offset="0%" stop-color="var(--accent)" stop-opacity="0.30"/>
885
- <stop offset="48%" stop-color="var(--accent)" stop-opacity="0.13"/>
886
- <stop offset="100%" stop-color="var(--accent)" stop-opacity="0"/>
887
- </radialGradient>
888
- <filter id="soft" x="-60%" y="-160%" width="220%" height="420%"><feGaussianBlur stdDeviation="9"/></filter>
889
- </defs>
890
- <ellipse class="glow" cx="222" cy="170" rx="215" ry="140" fill="url(#halo)"/>
891
- <ellipse class="bshadow" cx="220" cy="286" rx="142" ry="14" fill="var(--drum-shadow)" opacity=".22" filter="url(#soft)"/>
892
- <path d="M 214,196 C 232,188 250,188 264,196 L 258,232 C 244,225 230,225 219,232 Z" fill="var(--accent-deep)"/>
893
- <path d="M 214,196 C 232,188 250,188 264,196 L 262,205 C 247,197 231,197 216,205 Z" fill="var(--ink)" opacity=".22"/>
894
- <g class="drumBig">
895
- <path d="M 74,118 C 76,166 90,228 98,266 A 52,17 0 0 0 202,266 C 210,228 224,166 226,118 Z" fill="url(#shellBig)"/>
896
- <path d="M 172,124 C 174,176 186,232 192,268 A 52,17 0 0 0 202,266 C 210,228 224,166 226,118 Z" fill="var(--ink)" opacity=".17"/>
897
- <path d="M 92,126 C 94,174 104,230 110,266 A 52,17 0 0 1 100,267 C 92,230 78,172 76,124 Z" fill="#fff" opacity=".20"/>
898
- <ellipse cx="150" cy="121" rx="80" ry="29" fill="var(--accent-deep)"/>
899
- <ellipse cx="150" cy="117" rx="80" ry="29" fill="var(--accent)"/>
900
- <ellipse cx="150" cy="117" rx="80" ry="29" fill="#fff" opacity=".13"/>
901
- <ellipse cx="150" cy="118" rx="70" ry="24" fill="url(#skin)"/>
902
- <ellipse cx="150" cy="118" rx="70" ry="24" fill="none" stroke="var(--ink)" stroke-opacity=".09" stroke-width="1.4"/>
903
- <ellipse cx="150" cy="118" rx="55" ry="18" fill="none" stroke="var(--ink)" stroke-opacity=".045" stroke-width="1"/>
904
- <g fill="var(--accent-deep)">
905
- <path d="M 78,138 l 9,0 -1.5,20 -6,0 z"/><path d="M 120,148 l 9,0 -1,21 -7,0 z"/>
906
- <path d="M 172,148 l 9,0 -1,21 -7,0 z"/><path d="M 214,138 l 9,0 -1.5,20 -6,0 z"/>
907
- </g>
908
- </g>
909
- <g class="drumSml">
910
- <path d="M 240,134 C 242,178 252,230 258,266 A 40,13 0 0 0 338,266 C 344,230 354,178 356,134 Z" fill="url(#shellSml)"/>
911
- <path d="M 314,140 C 316,186 324,234 328,268 A 40,13 0 0 0 338,266 C 344,230 354,178 356,134 Z" fill="var(--ink)" opacity=".19"/>
912
- <path d="M 254,140 C 256,182 264,232 268,266 A 40,13 0 0 1 260,267 C 254,232 244,182 242,138 Z" fill="#fff" opacity=".17"/>
913
- <ellipse cx="296" cy="137" rx="60" ry="22" fill="var(--accent-deep)"/>
914
- <ellipse cx="296" cy="133" rx="60" ry="22" fill="var(--terracotta)"/>
915
- <ellipse cx="296" cy="133" rx="60" ry="22" fill="#fff" opacity=".13"/>
916
- <ellipse cx="296" cy="134" rx="51" ry="18" fill="url(#skin)"/>
917
- <ellipse cx="296" cy="134" rx="51" ry="18" fill="none" stroke="var(--ink)" stroke-opacity=".09" stroke-width="1.4"/>
918
- <ellipse cx="296" cy="134" rx="39" ry="13" fill="none" stroke="var(--ink)" stroke-opacity=".045" stroke-width="1"/>
919
- <g fill="var(--accent-deep)">
920
- <path d="M 243,152 l 8,0 -1,17 -6,0 z"/><path d="M 276,160 l 8,0 -1,18 -6,0 z"/>
921
- <path d="M 312,160 l 8,0 -1,18 -6,0 z"/><path d="M 345,152 l 8,0 -1,17 -6,0 z"/>
922
- </g>
923
- </g>
924
- </svg>
925
- </div>
926
- </div>
927
- </div>
928
- </div>
929
- </section>
930
- </main>
931
-
932
- <footer class="foot"><!--apex-only:start--><span>A non-profit, capture-resistant commons. </span><!--apex-only:end-->Built by agents, on <span data-brand-product-inline>Cloud&nbsp;Bongos</span>. <a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">Open source (AGPL-3.0)</a>.<nav class="footNav" aria-label="Site"><a href="/privacy">Privacy</a><a href="/terms">Terms</a><a href="/contact">Contact</a></nav></footer>
933
-
934
- <div id="board" hidden></div>
935
-
936
- <script>
937
- /* The mock's state contract. It reads ?state= once on load and applies ONE state; the default is
938
- in-map. ?mode= is handled by the pre-paint script in <head>. No JS motion, and no markup built from strings: every
939
- string is markup or textContent, every board specimen is a cloneNode of the real component. */
940
- (function () {
941
- var $ = function (id) { return document.getElementById(id); };
942
- var AV = 'https://avatars.githubusercontent.com/u/';
943
-
944
- var STATES = {
945
- 'out-map': { on: 0, view: 'map', sub: 'sky' },
946
- 'in-map': { on: 1, view: 'map', sub: 'sky' },
947
- 'in-map-projects': { on: 1, view: 'map', sub: 'sky', menu: 'projects' },
948
- 'in-map-profile': { on: 1, view: 'map', sub: 'sky', menu: 'profile' },
949
- 'in-map-focus': { on: 1, view: 'map', sub: 'sky', menu: 'projects', ring: 1 },
950
- 'in-map-search': { on: 1, view: 'map', sub: 'sky', q: 'emer' },
951
- 'in-mine': { on: 1, view: 'mine' },
952
- 'in-new': { on: 1, view: 'new' },
953
- 'out-new': { on: 0, view: 'new' },
954
- 'in-table': { on: 1, view: 'map', sub: 'table' },
955
- 'card': { on: 1, view: 'map', sub: 'sky', card: 'self' },
956
- 'card-member': { on: 1, view: 'map', sub: 'sky', card: 'otb' },
957
- 'card-noart': { on: 1, view: 'map', sub: 'sky', card: 'self', noart: 1 },
958
- 'card-nofaces': { on: 1, view: 'map', sub: 'sky', card: 'self', nofaces: 1 },
959
- 'card-onefig': { on: 1, view: 'map', sub: 'sky', card: 'self', onefig: 1 },
960
- 'card-nofig': { on: 1, view: 'map', sub: 'sky', card: 'self', nofig: 1 },
961
- 'card-floor': { on: 1, view: 'map', sub: 'sky', card: 'self', floor: 1 }
962
- };
963
-
964
- var CARDS = {
965
- self: {
966
- acc: 'var(--accent)', name: 'Cloud Bongos', tag: 'Building Digitally, as simple as Bongos.',
967
- rel: 'you build here', art: 1,
968
- faces: [AV + '64553469?v=4', AV + '283020391?v=4', AV + '290184380?v=4', AV + '295990394?v=4', AV + '35337514?v=4'],
969
- more: '+4', builders: '9', shipped: '30+',
970
- type: '', runs: 'cloudbongos.com', owner: 'lars589', ownerAv: AV + '283020391?v=4', listed: 'Jul 14, 2026',
971
- site: 'https://cloudbongos.com', hall: 'https://builders.cloudbongos.com'
972
- },
973
- otb: {
974
- acc: 'var(--sea)', name: 'Off the Boats',
975
- tag: 'A singular shop, and a broader videogame in a persistent ancient Greek world that collides with modern day commerce & products.',
976
- rel: 'you build here', art: 0,
977
- faces: [AV + '283020391?v=4', AV + '293641659?v=4', AV + '290184380?v=4', AV + '295990394?v=4', AV + '35337514?v=4'],
978
- more: '+8', builders: '13', shipped: '0',
979
- type: 'Game', runs: 'amazonprimea.com', owner: 'lars589',
980
- ownerAv: AV + '283020391?v=4', listed: 'Jul 15, 2026',
981
- site: 'https://amazonprimea.com', hall: ''
982
- }
983
- };
984
-
985
- /* the field's starfield: the same deterministic walk the live page runs (projects.html:2272) */
986
- (function starfield() {
987
- var frag = document.createDocumentFragment();
988
- for (var i = 0; i < 90; i++) {
989
- var s = document.createElement('i');
990
- s.style.left = ((i * 37 + 11) % 100) + '%';
991
- s.style.top = ((i * 61 + 7) % 100) + '%';
992
- s.style.opacity = (0.25 + ((i * 13) % 50) / 100).toFixed(2);
993
- frag.appendChild(s);
994
- }
995
- $('mapStars').appendChild(frag);
996
- })();
997
-
998
- function each(root, sel, fn) { Array.prototype.forEach.call(root.querySelectorAll(sel), fn); }
999
-
1000
- /* one function paints the bar, its two bands and the toolbar, scoped to any root: the page for a
1001
- single state, a cloned row for a board. */
1002
- function applyBar(root, cfg) {
1003
- var onView = cfg.view || 'map';
1004
- each(root, '.signinDoor', function (n) { n.hidden = !!cfg.on; });
1005
- each(root, '.whoTrig', function (n) { n.hidden = !cfg.on; });
1006
- each(root, '.qWrap', function (n) { n.hidden = onView !== 'map'; });
1007
- each(root, '.qToggle', function (n) { n.hidden = onView !== 'map'; });
1008
- each(root, '.band', function (b) { b.hidden = b.getAttribute('data-menu') !== cfg.menu; });
1009
- each(root, '.trig', function (t) {
1010
- t.setAttribute('aria-expanded', t.getAttribute('data-menu') === cfg.menu ? 'true' : 'false');
1011
- });
1012
- each(root, '.bandCell[data-view]', function (c) {
1013
- var now = c.getAttribute('data-view') === onView;
1014
- c.classList.toggle('is-now', now);
1015
- var a = c.querySelector('a');
1016
- if (!a) return;
1017
- if (now) a.setAttribute('aria-current', 'page'); else a.removeAttribute('aria-current');
1018
- });
1019
- each(root, '.qInput', function (i) { i.value = cfg.q || ''; });
1020
- each(root, '.qCount', function (c) { c.textContent = cfg.q ? '1 of 4' : '4 projects'; });
1021
- each(root, '.qWrap', function (w) { w.classList.toggle('is-focus', !!cfg.q); });
1022
- each(root, '.qWrap', function (w) { w.classList.toggle('is-open', !!cfg.q); });
1023
- each(root, '.qToggle', function (b) { b.setAttribute('aria-expanded', cfg.q ? 'true' : 'false'); });
1024
- each(root, '.switch', function (s) { s.hidden = !cfg.on; });
1025
- if (cfg.ring) {
1026
- each(root, '#menuProjects .bandCell:nth-child(2) .bandT, .band[data-menu="projects"] .bandCell:nth-child(2) .bandT',
1027
- function (a) { a.classList.add('is-focus'); });
1028
- each(root, '.whoTrig', function (t) { t.classList.add('is-focus'); });
1029
- }
1030
- }
1031
-
1032
- function applyCard(card, d, opt) {
1033
- var art = !!d.art && !opt.noart && !opt.floor;
1034
- card.classList.toggle('has-art', art);
1035
- each(card, '.cardArt', function (n) { n.hidden = !art; });
1036
- each(card, '.cardDot', function (n) { n.style.setProperty('--acc', d.acc); });
1037
- card.querySelector('h3').textContent = d.name;
1038
- each(card, '.cardTag', function (n) { n.textContent = d.tag; });
1039
- each(card, '.cardRel', function (n) { n.textContent = d.rel; n.hidden = !d.rel; });
1040
-
1041
- each(card, '.cardFaces', function (n) { n.hidden = !!opt.nofaces || !!opt.floor; });
1042
- each(card, '.cardFaces img', function (img, i) { img.src = d.faces[i]; });
1043
- each(card, '.facesMore', function (n) { n.textContent = d.more; });
1044
-
1045
- var noA = !!opt.floor || !!opt.nofig;
1046
- var noB = noA || !!opt.onefig;
1047
- each(card, '.cardFigs', function (n) { n.hidden = noA; });
1048
- each(card, '.cardCall.b', function (n) { n.hidden = noB; });
1049
- each(card, '.cardCall.a .fig', function (n) { n.textContent = d.builders; });
1050
- each(card, '.cardCall.b .fig', function (n) { n.textContent = d.shipped; });
1051
-
1052
- each(card, '.cardMeta', function (n) { n.hidden = !!opt.floor; });
1053
- var pair = function (label, value) {
1054
- var dd = card.querySelector('dd[data-fact="' + label + '"]');
1055
- var dt = card.querySelector('dt[data-fact="' + label + '"]');
1056
- if (!dd || !dt) return;
1057
- dd.hidden = dt.hidden = !value;
1058
- if (value && label !== 'Owner') dd.textContent = value;
1059
- };
1060
- pair('Type', d.type);
1061
- pair('Runs', d.runs);
1062
- pair('Owner', d.owner);
1063
- pair('Listed', d.listed);
1064
- each(card, '.cardOwnerName', function (n) { n.textContent = d.owner; });
1065
- each(card, '.ownerAv', function (n) { n.src = d.ownerAv; });
1066
-
1067
- each(card, '.cardOpen', function (n) { n.href = d.site; });
1068
- each(card, '.cardHall', function (n) { n.hidden = !d.hall || !!opt.floor; if (d.hall) n.href = d.hall; });
1069
- }
1070
-
1071
- /* board specimens are clones of the real components; ids are stripped so the page keeps one of
1072
- each and a scoped query inside a row can never reach out of it. */
1073
- function clone(node) {
1074
- var c = node.cloneNode(true);
1075
- c.removeAttribute('id');
1076
- c.removeAttribute('hidden');
1077
- each(c, '[id]', function (n) { n.removeAttribute('id'); });
1078
- return c;
1079
- }
1080
- function labelled(parent, text, cls) {
1081
- var wrap = document.createElement('div');
1082
- wrap.className = cls;
1083
- var lbl = document.createElement('span');
1084
- lbl.className = 'boardLbl';
1085
- lbl.textContent = text;
1086
- wrap.appendChild(lbl);
1087
- parent.appendChild(wrap);
1088
- return wrap;
1089
- }
1090
-
1091
- var state = new URLSearchParams(location.search || location.hash.slice(1) || (window.__Q || '')).get('state') || 'in-map';
1092
- var board = $('board');
1093
- var header = document.querySelector('header.bar');
1094
- var bandP = $('menuProjects');
1095
- var bandU = $('menuProfile');
1096
- var toolbar = document.querySelector('.toolbar');
1097
- var scrim = $('cardScrim');
1098
- var card = $('projCard');
1099
-
1100
- if (state.indexOf('board-') === 0) {
1101
- document.querySelector('.sky').hidden = true;
1102
- document.querySelector('.skip').hidden = true;
1103
- header.hidden = true;
1104
- bandP.hidden = true;
1105
- bandU.hidden = true;
1106
- $('hubMain').hidden = true;
1107
- document.querySelector('.foot').hidden = true;
1108
- board.hidden = false;
1109
-
1110
- var title = document.createElement('p');
1111
- title.className = 'boardTitle';
1112
- board.appendChild(title);
1113
-
1114
- if (state === 'board-bar') {
1115
- title.textContent = 'v2 the hairline / the bar, every state';
1116
- ['out-map', 'in-map', 'in-map-projects', 'in-map-profile', 'in-map-focus', 'in-map-search', 'in-mine', 'in-new']
1117
- .forEach(function (s) {
1118
- var row = labelled(board, s, 'boardRow');
1119
- var h = clone(header); var b1 = clone(bandP); var b2 = clone(bandU); var tb = clone(toolbar);
1120
- row.appendChild(h); row.appendChild(b1); row.appendChild(b2); row.appendChild(tb);
1121
- var cfg = STATES[s];
1122
- applyBar(row, cfg);
1123
- tb.hidden = cfg.view !== 'map';
1124
- });
1125
- } else if (state === 'board-card') {
1126
- title.textContent = 'v2 the hairline / the poster card, the whole ladder';
1127
- var grid = document.createElement('div');
1128
- grid.className = 'boardGrid';
1129
- board.appendChild(grid);
1130
- ['card', 'card-member', 'card-noart', 'card-nofaces', 'card-onefig', 'card-nofig', 'card-floor']
1131
- .forEach(function (s) {
1132
- var cell = labelled(grid, s, 'boardCell');
1133
- var c = clone(card);
1134
- cell.appendChild(c);
1135
- applyCard(c, CARDS[STATES[s].card], STATES[s]);
1136
- });
1137
- } else if (state === 'board-menu') {
1138
- title.textContent = 'v2 the hairline / the band and the search, component states';
1139
- var specs = [
1140
- ['the trigger, closed', 'projects', null, null],
1141
- ['the band, open', 'projects', 'projects', null],
1142
- ['keyboard focus on an item', 'projects', 'projects', 'focus'],
1143
- ['hover on an item', 'projects', 'projects', 'hover'],
1144
- ['the profile band, open', 'profile', 'profile', null]
1145
- ];
1146
- specs.forEach(function (sp) {
1147
- var row = labelled(board, sp[0], 'boardSpec');
1148
- var h = clone(header); var b1 = clone(bandP); var b2 = clone(bandU);
1149
- row.appendChild(h); row.appendChild(b1); row.appendChild(b2);
1150
- applyBar(row, { on: 1, view: 'map', menu: sp[2] });
1151
- if (sp[3] === 'focus') each(row, '.band:not([hidden]) .bandCell:nth-child(2) .bandT', function (a) { a.classList.add('is-focus'); });
1152
- if (sp[3] === 'hover') each(row, '.band:not([hidden]) .bandCell:nth-child(2)', function (c) { c.classList.add('is-hover'); });
1153
- });
1154
- [['the search, at rest', '', 0], ['the search, focused', '', 1], ['the search, with a value', 'emer', 0]]
1155
- .forEach(function (sp) {
1156
- var row = labelled(board, sp[0], 'boardSpec');
1157
- var q = clone(document.querySelector('.qWrap'));
1158
- row.appendChild(q);
1159
- each(q, '.qInput', function (i) { i.value = sp[1]; });
1160
- each(q, '.qCount', function (c) { c.textContent = sp[1] ? '1 of 4' : '4 projects'; });
1161
- if (sp[2]) q.classList.add('is-focus');
1162
- });
1163
- }
1164
- return;
1165
- }
1166
-
1167
- var cfg = STATES[state] || STATES['in-map'];
1168
- applyBar(document, cfg);
1169
- ['map', 'mine', 'new'].forEach(function (v) { $('view-' + v).hidden = (v !== cfg.view); });
1170
- $('pageH1').textContent = cfg.view === 'mine' ? 'Your projects'
1171
- : cfg.view === 'new' ? (cfg.on ? 'Start a project' : 'Create a project')
1172
- : 'The project map';
1173
- $('sub-sky-body').hidden = cfg.sub === 'table';
1174
- $('sub-table-body').hidden = cfg.sub !== 'table';
1175
- $('sub-sky').setAttribute('aria-pressed', cfg.sub === 'table' ? 'false' : 'true');
1176
- $('sub-table').setAttribute('aria-pressed', cfg.sub === 'table' ? 'true' : 'false');
1177
- $('gate').hidden = !!cfg.on;
1178
- $('mine').hidden = !cfg.on;
1179
- $('panel-gate').classList.toggle('on', !cfg.on);
1180
- $('panel-name').classList.toggle('on', !!cfg.on);
1181
-
1182
- /* the spotlight is the live page's, not this version's: a query dims what does not match and
1183
- leaves it as backdrop, out of the tab order. */
1184
- if (cfg.q) {
1185
- each(document, '.worldOrb', function (o) {
1186
- var hit = /emersonian/i.test(o.textContent);
1187
- o.classList.add(hit ? 'match' : 'dim');
1188
- if (!hit) { o.setAttribute('aria-hidden', 'true'); o.tabIndex = -1; }
1189
- });
1190
- $('mapQ').value = 'emer';
1191
- document.querySelector('.qWrap').classList.add('is-focus');
1192
- }
1193
-
1194
- if (cfg.card) {
1195
- scrim.hidden = false;
1196
- applyCard(card, CARDS[cfg.card], cfg);
1197
- }
1198
-
1199
- /* the disclosure: Escape closes and hands focus back, a click outside closes, the arrows walk
1200
- the items. The open move itself is CSS, so the kill switch owns it. */
1201
- /* the folded search (under 480): the magnifier opens one row under the bar and hands focus to
1202
- the field; Escape folds it and returns focus; an opening menu folds it. Above 480 the class
1203
- is inert: the field is always in the bar. */
1204
- var qWrap = document.querySelector('.qWrap');
1205
- var qToggle = document.querySelector('.qToggle');
1206
- function setSearch(open) {
1207
- qWrap.classList.toggle('is-open', open);
1208
- qToggle.setAttribute('aria-expanded', open ? 'true' : 'false');
1209
- }
1210
- qToggle.addEventListener('click', function () {
1211
- var open = qToggle.getAttribute('aria-expanded') !== 'true';
1212
- setSearch(open);
1213
- if (open) $('mapQ').focus();
1214
- });
1215
-
1216
- each(document, '.trig', function (t) {
1217
- t.addEventListener('click', function () {
1218
- setSearch(false);
1219
- var name = t.getAttribute('data-menu');
1220
- var open = t.getAttribute('aria-expanded') !== 'true';
1221
- each(document, '.trig', function (o) { o.setAttribute('aria-expanded', 'false'); });
1222
- each(document, '.band', function (b) { b.hidden = true; });
1223
- if (!open) return;
1224
- t.setAttribute('aria-expanded', 'true');
1225
- var band = document.querySelector('.band[data-menu="' + name + '"]');
1226
- band.hidden = false;
1227
- });
1228
- });
1229
- document.addEventListener('keydown', function (ev) {
1230
- var open = document.querySelector('.band:not([hidden])');
1231
- if (!open && ev.key === 'Escape' && qWrap.classList.contains('is-open') && getComputedStyle(qToggle).display !== 'none') {
1232
- setSearch(false);
1233
- qToggle.focus();
1234
- return;
1235
- }
1236
- if (!open) return;
1237
- if (ev.key === 'Escape') {
1238
- var trig = document.querySelector('.trig[aria-expanded="true"]');
1239
- open.hidden = true;
1240
- if (trig) { trig.setAttribute('aria-expanded', 'false'); trig.focus(); }
1241
- return;
1242
- }
1243
- if (ev.key !== 'ArrowDown' && ev.key !== 'ArrowUp') return;
1244
- var items = Array.prototype.slice.call(open.querySelectorAll('a'));
1245
- var at = items.indexOf(document.activeElement);
1246
- ev.preventDefault();
1247
- items[(at + (ev.key === 'ArrowDown' ? 1 : items.length - 1) + items.length) % items.length].focus();
1248
- });
1249
- document.addEventListener('click', function (ev) {
1250
- if (ev.target.closest && (ev.target.closest('.band') || ev.target.closest('.trig'))) return;
1251
- each(document, '.trig', function (o) { o.setAttribute('aria-expanded', 'false'); });
1252
- each(document, '.band', function (b) { b.hidden = true; });
1253
- });
1254
- })();
1255
- </script>
1256
- </body>
1257
- </html>