@bongos/core 1.21.113 → 1.21.115

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 (265) hide show
  1. package/.bongos-core.json +608 -188
  2. package/.claude/skills/ask-for-help/SKILL.md +1 -1
  3. package/.claude/skills/bongos-feedback/SKILL.md +1 -1
  4. package/.claude/skills/builder-sequence/SKILL.md +1 -1
  5. package/.claude/skills/builder-ship/SKILL.md +1 -0
  6. package/GOVERNMENT.md +128 -11
  7. package/clients/bongos-client/README.md +1 -1
  8. package/clients/bongos-client/bongos-client.global.js +28 -0
  9. package/clients/bongos-client/index.cjs +28 -0
  10. package/clients/bongos-client/index.d.ts +45 -2
  11. package/clients/bongos-client/index.mjs +28 -0
  12. package/docs/adr/0372-an-ask-is-stored-per-docket-matter-and-the-server-refuses-slop.md +33 -0
  13. package/docs/adr/0373-an-ai-session-files-a-blocker-with-an-ask.md +31 -0
  14. package/docs/adr/0374-a-building-lead-is-a-held-position-appointed-by-a-people-matter.md +30 -0
  15. package/docs/adr/0375-who-may-build-without-a-vote-comes-from-the-form-of-government.md +38 -0
  16. package/docs/adr/0376-every-ask-is-routed-to-a-real-seat-on-the-server.md +30 -0
  17. package/docs/adr/0377-governance-is-a-held-position-read-on-me.md +24 -0
  18. package/docs/adr/0378-a-leads-build-outs-are-rows-built-at-once-or-by-a-passed-vote.md +33 -0
  19. package/docs/adr/0379-the-treasury-is-pots-per-goal-a-reserve-and-borrowing-over-real-spend.md +32 -0
  20. package/docs/adr/0380-a-strictness-preset-is-a-bundle-of-laws-switched-by-one-laws-vote.md +26 -0
  21. package/docs/adr/README.md +10 -1
  22. package/docs/api/openapi.json +989 -87
  23. package/docs/api-reference.md +18 -4
  24. package/docs/copy-inventory.md +709 -626
  25. package/docs/copy-registry.json +1673 -858
  26. package/docs/file-map.md +2 -1
  27. package/docs/module-api-changelog.md +4 -0
  28. package/docs/packs/engineer.md +2 -1
  29. package/docs/page-inventory.json +91 -6
  30. package/docs/page-readings.json +4875 -4748
  31. package/modules/builder-settings/law-locks.js +202 -0
  32. package/modules/builder-settings/routes/settings.js +66 -6
  33. package/modules/builder-settings/settings.js +7 -0
  34. package/modules/discord/ship-broadcast.js +22 -13
  35. package/modules/government/asks-db.js +54 -0
  36. package/modules/government/asks.js +269 -0
  37. package/modules/government/board-idea-reads.js +68 -0
  38. package/modules/government/board.js +21 -57
  39. package/modules/government/build-rights.js +148 -0
  40. package/modules/government/buildouts.js +160 -0
  41. package/modules/government/counts.js +81 -0
  42. package/modules/government/db.js +242 -0
  43. package/modules/government/decision-board.js +234 -8
  44. package/modules/government/decision-rules.js +2 -0
  45. package/modules/government/docket.js +114 -6
  46. package/modules/government/law-board.js +131 -0
  47. package/modules/government/laws.js +241 -0
  48. package/modules/government/leads.js +170 -0
  49. package/modules/government/migrations/government_027_laws.sql +143 -0
  50. package/modules/government/migrations/government_028_asks.sql +57 -0
  51. package/modules/government/migrations/government_029_leads.sql +135 -0
  52. package/modules/government/migrations/government_030_buildouts.sql +111 -0
  53. package/modules/government/migrations/government_031_treasury.sql +107 -0
  54. package/modules/government/module.json +2 -1
  55. package/modules/government/pollers/treasury-borrow.js +62 -0
  56. package/modules/government/positions.js +107 -0
  57. package/modules/government/presets.js +149 -0
  58. package/modules/government/routes/government.js +425 -2
  59. package/modules/government/routing.js +154 -0
  60. package/modules/government/treasury-db.js +166 -0
  61. package/modules/government/treasury.js +241 -0
  62. package/modules/hall-ui/public/atlas.html +2 -2
  63. package/modules/hall-ui/public/blockers.html +2 -2
  64. package/modules/hall-ui/public/board-room.html +2 -2
  65. package/modules/hall-ui/public/board-room.js +2 -0
  66. package/modules/hall-ui/public/city-draw.js +604 -11
  67. package/modules/hall-ui/public/city.css +157 -1
  68. package/modules/hall-ui/public/city.html +37 -7
  69. package/modules/hall-ui/public/city.js +441 -21
  70. package/modules/hall-ui/public/city.states.json +110 -5
  71. package/modules/hall-ui/public/collab.html +2 -2
  72. package/modules/hall-ui/public/commands.html +2 -2
  73. package/modules/hall-ui/public/copy-desk.html +2 -2
  74. package/modules/hall-ui/public/craft-mode.js +112 -29
  75. package/modules/hall-ui/public/deploy.html +2 -2
  76. package/modules/hall-ui/public/diagram-viewer.css +12 -0
  77. package/modules/hall-ui/public/diagram-viewer.js +4 -0
  78. package/modules/hall-ui/public/diagrams.html +2 -2
  79. package/modules/hall-ui/public/drachmae.html +2 -2
  80. package/modules/hall-ui/public/fleet.html +2 -2
  81. package/modules/hall-ui/public/gate.html +2 -2
  82. package/modules/hall-ui/public/goal-map.html +2 -2
  83. package/modules/hall-ui/public/goals.html +2 -2
  84. package/modules/hall-ui/public/gov-feeds.js +141 -0
  85. package/modules/hall-ui/public/gov-living.js +1002 -0
  86. package/modules/hall-ui/public/gov-positions.js +97 -0
  87. package/modules/hall-ui/public/government.html +2 -2
  88. package/modules/hall-ui/public/hall-render.js +3 -0
  89. package/modules/hall-ui/public/idea.html +2 -2
  90. package/modules/hall-ui/public/ideas.html +2 -2
  91. package/modules/hall-ui/public/index.html +3 -3
  92. package/modules/hall-ui/public/modules.html +2 -2
  93. package/modules/hall-ui/public/my-agents.css +334 -0
  94. package/modules/hall-ui/public/my-agents.html +72 -0
  95. package/modules/hall-ui/public/my-agents.js +603 -0
  96. package/modules/hall-ui/public/my-agents.states.json +37 -0
  97. package/modules/hall-ui/public/primer.html +2 -2
  98. package/modules/hall-ui/public/profile-connections.js +40 -28
  99. package/modules/hall-ui/public/profile.html +2 -2
  100. package/modules/hall-ui/public/project-settings.html +2 -2
  101. package/modules/hall-ui/public/ranks.html +2 -2
  102. package/modules/hall-ui/public/roadmap.html +2 -2
  103. package/modules/hall-ui/public/room.css +8 -0
  104. package/modules/hall-ui/public/roster.html +2 -2
  105. package/modules/hall-ui/public/sessions.html +2 -2
  106. package/modules/hall-ui/public/settings.html +2 -2
  107. package/modules/hall-ui/public/shell.js +116 -16
  108. package/modules/hall-ui/public/street-card.js +18 -1
  109. package/modules/hall-ui/public/street-deck.css +299 -0
  110. package/modules/hall-ui/public/street-deck.js +1145 -0
  111. package/modules/hall-ui/public/street-depth.js +712 -0
  112. package/modules/hall-ui/public/street-desk.js +5 -2
  113. package/modules/hall-ui/public/street-draw.js +109 -16
  114. package/modules/hall-ui/public/street-inside.js +137 -22
  115. package/modules/hall-ui/public/street-office.js +705 -0
  116. package/modules/hall-ui/public/street.css +161 -11
  117. package/modules/hall-ui/public/street.html +41 -11
  118. package/modules/hall-ui/public/street.js +78 -66
  119. package/modules/hall-ui/public/street.states.json +129 -21
  120. package/modules/hall-ui/public/studio.html +2 -2
  121. package/modules/hall-ui/public/style.css +5 -1
  122. package/modules/hall-ui/public/task.html +2 -2
  123. package/modules/hall-ui/public/thinking.html +2 -2
  124. package/modules/hall-ui/public/tweak-editor.html +2 -2
  125. package/modules/hall-ui/public/watch.html +2 -2
  126. package/modules/hall-ui/public/watch.js +14 -1
  127. package/modules/hall-ui/public/work.html +2 -2
  128. package/modules/hall-ui/records/living-city.md +57 -0
  129. package/modules/hall-ui/records/my-agents.md +21 -0
  130. package/modules/hall-ui/records/oversight-family.md +1 -0
  131. package/modules/hall-ui/records/profile-connections.md +10 -0
  132. package/modules/hall-ui/records/reading-rooms.md +2 -0
  133. package/modules/ideas/routes/blockers.js +92 -3
  134. package/modules/lifecycle/module.json +2 -1
  135. package/modules/lifecycle/moving.js +17 -1
  136. package/modules/lifecycle/routes/moving.js +6 -0
  137. package/modules/lifecycle/routes/project-settings.js +23 -0
  138. package/modules/onboarding/approval-broadcast.js +8 -0
  139. package/modules/onboarding/routes/access-requests.js +16 -6
  140. package/modules/platform-identity/connections.js +13 -2
  141. package/modules/platform-identity/routes/connections.js +7 -1
  142. package/modules/public-landing/public/account-private.states.json +2 -2
  143. package/modules/public-landing/public/account.html +65 -5
  144. package/modules/public-landing/public/account.states.json +3 -3
  145. package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.jpg.json +1 -1
  146. package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.webp +0 -0
  147. package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.webp.json +13 -0
  148. package/modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg.json +1 -1
  149. package/modules/public-landing/public/assets/world/r2-chrome-blob-one.webp +0 -0
  150. package/modules/public-landing/public/assets/world/r2-chrome-blob-one.webp.json +13 -0
  151. package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.jpg.json +1 -1
  152. package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.webp +0 -0
  153. package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.webp.json +13 -0
  154. package/modules/public-landing/public/assets/world.css +4 -4
  155. package/modules/public-landing/public/connections-empty.states.json +14 -0
  156. package/modules/public-landing/public/connections-inactive.states.json +14 -0
  157. package/modules/public-landing/public/connections.html +450 -0
  158. package/modules/public-landing/public/connections.probes.json +414 -0
  159. package/modules/public-landing/public/connections.states.json +17 -0
  160. package/modules/public-landing/public/contact.html +2 -2
  161. package/modules/public-landing/public/guild.html +2 -2
  162. package/modules/public-landing/public/index.html +2 -2
  163. package/modules/public-landing/public/invitations.html +2 -2
  164. package/modules/public-landing/public/privacy.html +2 -2
  165. package/modules/public-landing/public/projects-hall-live.states.json +0 -5
  166. package/modules/public-landing/public/projects.html +169 -6
  167. package/modules/public-landing/public/projects.probes.json +10 -6
  168. package/modules/public-landing/public/projects.states.json +13 -13
  169. package/modules/public-landing/public/terms.html +2 -2
  170. package/modules/ui-design/kit/fixtures/connections-empty.json +12 -0
  171. package/modules/ui-design/kit/fixtures/connections-inactive.json +7 -0
  172. package/modules/ui-design/kit/fixtures/connections.json +22 -0
  173. package/modules/ui-design/kit/serve.js +95 -0
  174. package/package-lock.json +2 -2
  175. package/package.json +1 -1
  176. package/release-notes.json +128 -0
  177. package/scripts/discord/README.md +8 -5
  178. package/scripts/discord/post.js +12 -1
  179. package/scripts/gds/artifact-audit.js +20 -3
  180. package/scripts/gds/ask.js +164 -0
  181. package/scripts/gds/autobongos-verify.js +21 -2
  182. package/scripts/gds/government-walk.js +638 -4
  183. package/scripts/gds/pulse.js +9 -6
  184. package/scripts/gds/ship-escalation.js +22 -3
  185. package/scripts/gds/ship-finish.js +5 -4
  186. package/scripts/gds/ship-merge-gate.js +18 -6
  187. package/scripts/hall-preview/README.md +41 -1
  188. package/scripts/hall-preview/pref-writes.js +140 -0
  189. package/scripts/hall-preview/refresh-fixtures.js +101 -7
  190. package/scripts/hall-preview/scrub-identity.js +366 -7
  191. package/scripts/hall-preview/server.js +27 -1
  192. package/scripts/render-diagrams.sh +4 -2
  193. package/src/bongos/auth-idp.js +6 -1
  194. package/src/bongos/project-settings.js +41 -0
  195. package/src/bongos/route-rank-check.js +27 -0
  196. package/src/bongos/routes/me.js +68 -7
  197. package/src/bongos/serve-internal.js +5 -1
  198. package/src/module-api.js +7 -1
  199. package/src/platform-server.js +10 -0
  200. package/tests/application_lifecycle.mjs +47 -5
  201. package/tests/conductor_main_e2e.mjs +21 -0
  202. package/tests/connections_api.mjs +18 -0
  203. package/tests/craft_mode_nav.mjs +5 -3
  204. package/tests/craft_mode_tallies.mjs +52 -16
  205. package/tests/discord_approvals.mjs +11 -0
  206. package/tests/discord_channels.mjs +20 -0
  207. package/tests/fixtures/gov-seated.mjs +64 -0
  208. package/tests/fixtures/government-ask-cards.json +1284 -0
  209. package/tests/government_act_alone_buildings.mjs +1 -0
  210. package/tests/government_agent_asks.mjs +322 -0
  211. package/tests/government_asks.mjs +408 -0
  212. package/tests/government_build_rights.mjs +253 -0
  213. package/tests/government_buildouts.mjs +382 -0
  214. package/tests/government_counts.mjs +143 -0
  215. package/tests/government_decision_rules.mjs +5 -1
  216. package/tests/government_laws.mjs +488 -0
  217. package/tests/government_laws_session.mjs +321 -0
  218. package/tests/government_leads.mjs +375 -0
  219. package/tests/government_positions.mjs +127 -0
  220. package/tests/government_presets.mjs +215 -0
  221. package/tests/government_routes.mjs +8 -1
  222. package/tests/government_routing.mjs +118 -0
  223. package/tests/government_seed.mjs +5 -0
  224. package/tests/government_treasury.mjs +394 -0
  225. package/tests/governor_docket.mjs +26 -2
  226. package/tests/hall_buildouts.mjs +208 -0
  227. package/tests/hall_city.mjs +14 -6
  228. package/tests/hall_city_living.mjs +617 -0
  229. package/tests/hall_gov_counts.mjs +336 -0
  230. package/tests/hall_gov_living.mjs +636 -0
  231. package/tests/hall_gov_real_site.mjs +655 -0
  232. package/tests/hall_matter_card.mjs +6 -1
  233. package/tests/hall_my_agents.mjs +481 -0
  234. package/tests/hall_nav.mjs +107 -4
  235. package/tests/hall_page_gate_map.mjs +4 -0
  236. package/tests/hall_preview_governor_fixtures.mjs +30 -3
  237. package/tests/hall_preview_scrub.mjs +59 -1
  238. package/tests/hall_preview_security.mjs +169 -0
  239. package/tests/hall_reading_world.mjs +44 -0
  240. package/tests/hall_roster_world.mjs +6 -2
  241. package/tests/hall_street.mjs +18 -2
  242. package/tests/hall_street_amend.mjs +1 -1
  243. package/tests/hall_street_deck.mjs +609 -0
  244. package/tests/hall_street_depth.mjs +559 -0
  245. package/tests/hall_street_desk.mjs +1 -1
  246. package/tests/hall_street_inside.mjs +29 -5
  247. package/tests/hall_tweak_editor.mjs +4 -1
  248. package/tests/hub_account_page.mjs +94 -3
  249. package/tests/hub_connections_page.mjs +507 -0
  250. package/tests/hub_invite_badge.mjs +1 -1
  251. package/tests/idp_device_exchange.mjs +18 -0
  252. package/tests/landing_legal_pages.mjs +8 -5
  253. package/tests/landing_page.mjs +23 -4
  254. package/tests/nav_permission_atoms.mjs +4 -1
  255. package/tests/profile_connections_ui.mjs +88 -25
  256. package/tests/projects_hub.mjs +15 -0
  257. package/tests/projects_hub_pre_uat.mjs +4 -2
  258. package/tests/render_check.mjs +2 -2
  259. package/tests/ship_broadcast_routing.mjs +57 -16
  260. package/tests/ship_broadcast_server.mjs +40 -7
  261. package/tests/ship_merge_gate.mjs +19 -0
  262. package/tests/ui_design_kit.mjs +2 -2
  263. package/tests/watch_applications_queue.mjs +23 -0
  264. package/tests/wizard_physics.mjs +1 -1
  265. package/tests/wizard_physics_more.mjs +133 -0
@@ -31,6 +31,28 @@ body[data-page="street"] {
31
31
  --street-wash: color-mix(in srgb, var(--ink) 4.5%, transparent);
32
32
  --street-crate: color-mix(in srgb, var(--ink) 55%, transparent);
33
33
  --street-crate-fill: color-mix(in srgb, var(--ink) 4%, transparent);
34
+ /* the living street (task 1004685): the skyline's faces before the haze,
35
+ the skylanes, the pods, a sitting's shaft (an ink wash: paper cannot
36
+ glow) and the edge that keeps a salmon window legible on paper */
37
+ --street-depth-face: var(--bg-card);
38
+ --street-depth-side: color-mix(in srgb, var(--ink) 10%, var(--bg-card));
39
+ --street-depth-edge: color-mix(in srgb, var(--ink) 22%, transparent);
40
+ --street-depth-lane: color-mix(in srgb, var(--ink) 22%, transparent);
41
+ --street-depth-pod: var(--ink);
42
+ --street-depth-shaft: color-mix(in srgb, var(--ink) 9%, transparent);
43
+ --street-depth-needs-edge: var(--accent-deep);
44
+ /* the planes (integration pass): distance is atmosphere. On paper the far row
45
+ fades into the paper through a cool sea haze, the near row a step nearer,
46
+ a fog band dissolves their feet into the ground line, and a lit road is an
47
+ ink wash (paper cannot glow). Task 1004718 deepened the paper haze: the
48
+ rows read as distance (cooler, bluer) rather than as pale paper */
49
+ --street-depth-haze-near: color-mix(in srgb, var(--sea) 12%, color-mix(in srgb, var(--ink) 10%, var(--bg)));
50
+ --street-depth-haze-near-side: color-mix(in srgb, var(--sea) 13%, color-mix(in srgb, var(--ink) 16%, var(--bg)));
51
+ --street-depth-haze-edge: color-mix(in srgb, var(--sea) 14%, color-mix(in srgb, var(--ink) 10%, transparent));
52
+ --street-depth-haze-far: color-mix(in srgb, var(--sea) 17%, color-mix(in srgb, var(--ink) 5%, var(--bg)));
53
+ --street-depth-fog: color-mix(in srgb, var(--sea) 11%, color-mix(in srgb, var(--ink) 3%, var(--bg)));
54
+ --street-depth-pool: color-mix(in srgb, var(--ink) 13%, transparent);
55
+ --street-depth-lane-w: 1.3px;
34
56
  }
35
57
  html[data-theme="dark"] body[data-page="street"] {
36
58
  --street-rule: color-mix(in srgb, var(--ink) 12%, transparent);
@@ -38,6 +60,18 @@ html[data-theme="dark"] body[data-page="street"] {
38
60
  --street-accent-text: color-mix(in srgb, var(--accent) 70%, #ffffff);
39
61
  --street-glow: 0 0 20px color-mix(in srgb, var(--accent) 50%, transparent);
40
62
  --street-crate: color-mix(in srgb, var(--ink) 60%, transparent);
63
+ --street-depth-face: color-mix(in srgb, var(--ink) 14%, var(--bg));
64
+ --street-depth-side: color-mix(in srgb, var(--ink) 8%, var(--bg));
65
+ --street-depth-edge: color-mix(in srgb, var(--ink) 13%, transparent);
66
+ --street-depth-shaft: color-mix(in srgb, var(--ink) 14%, transparent);
67
+ --street-depth-needs-edge: transparent;
68
+ --street-depth-haze-near: color-mix(in srgb, var(--sea) 7%, color-mix(in srgb, var(--ink) 9%, var(--bg)));
69
+ --street-depth-haze-near-side: color-mix(in srgb, var(--sea) 6%, color-mix(in srgb, var(--ink) 5%, var(--bg)));
70
+ --street-depth-haze-edge: color-mix(in srgb, var(--ink) 8%, transparent);
71
+ --street-depth-haze-far: color-mix(in srgb, var(--sea) 12%, color-mix(in srgb, var(--ink) 4%, var(--bg)));
72
+ --street-depth-fog: color-mix(in srgb, var(--sea) 10%, color-mix(in srgb, var(--ink) 5%, var(--bg)));
73
+ --street-depth-pool: color-mix(in srgb, var(--ink) 30%, transparent);
74
+ --street-depth-lane: color-mix(in srgb, var(--ink) 24%, transparent);
41
75
  }
42
76
  @media (prefers-color-scheme: dark) {
43
77
  html:not([data-theme]) body[data-page="street"] {
@@ -46,6 +80,18 @@ html[data-theme="dark"] body[data-page="street"] {
46
80
  --street-accent-text: color-mix(in srgb, var(--accent) 70%, #ffffff);
47
81
  --street-glow: 0 0 20px color-mix(in srgb, var(--accent) 50%, transparent);
48
82
  --street-crate: color-mix(in srgb, var(--ink) 60%, transparent);
83
+ --street-depth-face: color-mix(in srgb, var(--ink) 14%, var(--bg));
84
+ --street-depth-side: color-mix(in srgb, var(--ink) 8%, var(--bg));
85
+ --street-depth-edge: color-mix(in srgb, var(--ink) 13%, transparent);
86
+ --street-depth-shaft: color-mix(in srgb, var(--ink) 14%, transparent);
87
+ --street-depth-needs-edge: transparent;
88
+ --street-depth-haze-near: color-mix(in srgb, var(--sea) 7%, color-mix(in srgb, var(--ink) 9%, var(--bg)));
89
+ --street-depth-haze-near-side: color-mix(in srgb, var(--sea) 6%, color-mix(in srgb, var(--ink) 5%, var(--bg)));
90
+ --street-depth-haze-edge: color-mix(in srgb, var(--ink) 8%, transparent);
91
+ --street-depth-haze-far: color-mix(in srgb, var(--sea) 12%, color-mix(in srgb, var(--ink) 4%, var(--bg)));
92
+ --street-depth-fog: color-mix(in srgb, var(--sea) 10%, color-mix(in srgb, var(--ink) 5%, var(--bg)));
93
+ --street-depth-pool: color-mix(in srgb, var(--ink) 30%, transparent);
94
+ --street-depth-lane: color-mix(in srgb, var(--ink) 24%, transparent);
49
95
  }
50
96
  }
51
97
 
@@ -86,14 +132,23 @@ html[data-fullscreen="on"] .street-top { padding-right: 184px; }
86
132
  .street-mode svg { display: block; stroke-width: 1.4; }
87
133
 
88
134
  /* the street band: the prototype's 1440-wide artboard, scaled by street.js */
89
- .street-window { flex: 1 1 auto; min-height: 0; width: 100%; max-width: none; margin: 0; padding: 0; box-sizing: border-box; display: flex; overflow-x: auto; overflow-y: hidden; position: relative; }
90
- .street-sizer { position: relative; margin: auto; flex: none; overflow: hidden; }
91
- .street-board { position: absolute; left: 0; top: 0; width: 1440px; height: 900px; transform-origin: 0 0; transform: scale(var(--street-scale, 1)) translateY(-64px); }
135
+ /* The living street's parallax (task 1004685) is Keith Clark's, sideways and
136
+ CSS only: the window is the perspective, the sizer and the board keep the
137
+ 3D (an overflow other than visible would flatten it, so the sizer clips
138
+ nothing; the window still does), and each skyline row sits deeper and is
139
+ scaled back to its own size, so it slides at a half and a third of the
140
+ scroll. Its transform-origin is the band's middle (y 446 = 196 + 500 / 2,
141
+ the band less its empty sky and bare foot, task 1004718),
142
+ which is the window's middle, so a row only ever moves sideways; with no
143
+ sideways scroll it does not move at all. */
144
+ .street-window { flex: 1 1 auto; min-height: 0; width: 100%; max-width: none; margin: 0; padding: 0; box-sizing: border-box; display: flex; overflow-x: auto; overflow-y: hidden; position: relative; perspective: 1px; perspective-origin: 50% 50%; }
145
+ .street-sizer { position: relative; margin: auto; flex: none; overflow: visible; transform-style: preserve-3d; }
146
+ .street-board { position: absolute; left: 0; top: 0; width: 1440px; height: 900px; transform-origin: 0 0; transform: scale(var(--street-scale, 1)) translateY(-196px); transform-style: preserve-3d; }
92
147
  .street-svg { position: absolute; left: 0; top: 0; pointer-events: none; overflow: visible; }
93
148
  .street-layer { position: absolute; left: 0; top: 0; width: 100%; height: 900px; pointer-events: none; }
94
149
  .street-layer > * { pointer-events: auto; }
95
150
 
96
- .street-evening { position: absolute; left: 0; right: 0; top: 104px; display: flex; flex-direction: column; align-items: center; gap: 10px; pointer-events: none; z-index: 2; }
151
+ .street-evening { position: absolute; left: 0; right: 0; top: 216px; display: flex; flex-direction: column; align-items: center; gap: 10px; pointer-events: none; z-index: 2; }
97
152
  .street-evening[hidden] { display: none; }
98
153
  .street-evening__h { margin: 0; font-size: 46px; font-weight: 300; letter-spacing: -0.042em; line-height: 1; }
99
154
  .street-evening__line { margin: 0; font-size: 16px; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
@@ -163,6 +218,83 @@ html[data-fullscreen="on"] .street-top { padding-right: 184px; }
163
218
  .street-sign__figure { font-size: 26px; font-weight: 300; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; }
164
219
  .street-sign__line { font-size: 12.5px; line-height: 1.35; color: var(--ink-soft); }
165
220
 
221
+ /* ── THE LIVING STREET (task 1004685, the living-city prototype) ──
222
+ Three planes, front-on, in the street's own drawing (street-draw.js
223
+ depthOf). Every colour here is a token; the civic row's own faces are
224
+ drawing data, as before. Motion only says "this is live": the pods, the
225
+ jib, the hook, the scaffold's platform and a sitting's ring, all at 20 s
226
+ or slower and all dead under the kill switch below. */
227
+ .street-depth-layer { position: absolute; left: 0; top: 0; width: 100%; height: 900px; pointer-events: none; }
228
+ /* a skyline row is a 1 px box (its svg paints the row as overflow, street.js
229
+ depthSvg), so its depth never widens the sideways scroll; its origin is the
230
+ band's middle, set from the street's width */
231
+ .street-depth-back { position: absolute; left: 0; top: 0; width: 1px; height: 1px; pointer-events: none; transform-origin: var(--street-depth-ox, 720px) 446px; }
232
+ .street-depth-back--near { transform: translateZ(-1px) scale(2); }
233
+ .street-depth-back--far { transform: translateZ(-2px) scale(3); }
234
+ .street-depth-svg { position: absolute; left: 0; top: 0; overflow: visible; pointer-events: none; }
235
+ /* BACK: the goals' skyline, hazed toward the void. Atmosphere is the depth cue:
236
+ the near row is a toned silhouette with a hairline, the far row a flat cool
237
+ haze with no edge at all, and the fog band below dissolves both into the
238
+ ground line, so the civic row (no haze, full detail) is the crisp subject */
239
+ .street-depth-sil__face, .street-depth-sil__side { stroke-width: 1; stroke-linejoin: round; }
240
+ .street-depth-back--near .street-depth-sil__face { fill: var(--street-depth-haze-near); stroke: var(--street-depth-haze-edge); }
241
+ .street-depth-back--near .street-depth-sil__side { fill: var(--street-depth-haze-near-side); stroke: var(--street-depth-haze-edge); }
242
+ .street-depth-back--far .street-depth-sil__face, .street-depth-back--far .street-depth-sil__side { fill: var(--street-depth-haze-far); stroke: none; }
243
+ /* the fog band: the skyline's feet fade into the ground line behind the civic row */
244
+ .street-depth-fog { position: absolute; left: 0; top: 404px; width: calc(var(--street-depth-ox, 720px) * 2); height: 156px; pointer-events: none; background: linear-gradient(to bottom, transparent, color-mix(in srgb, var(--street-depth-fog) 70%, transparent) 62%, var(--street-depth-fog) 96%); }
245
+ /* a lit road: a pool under a building or district with agents at work, as strong as it is busy */
246
+ .street-depth-pool { position: absolute; height: 40px; margin-top: -4px; pointer-events: none; border-radius: 50%; background: radial-gradient(closest-side, var(--street-depth-pool), transparent); opacity: calc(0.35 + 0.65 * var(--street-depth-pool-a, 0.5)); }
247
+ /* SKY: the skylanes and their pods, one per active claim, a fixed gap apart */
248
+ .street-depth-lane { fill: none; stroke: var(--street-depth-lane); stroke-width: var(--street-depth-lane-w, 1.3px); stroke-linecap: round; }
249
+ .street-depth-pod { position: absolute; left: 0; top: 0; width: 11px; height: 6px; box-sizing: border-box; border-radius: 999px; background: var(--street-depth-pod); box-shadow: inset 0 -1.5px 0 color-mix(in srgb, var(--bg) 35%, transparent); offset-rotate: auto; animation: street-depth-travel 28s linear infinite; }
250
+ .street-depth-train { position: absolute; left: 0; top: 0; height: 6px; margin-top: -3px; offset-rotate: 0deg; offset-distance: 50%; animation: street-depth-shuttle 24s ease-in-out -12s infinite alternate; }
251
+ .street-depth-pod--road { top: 0; offset-path: none; animation: none; }
252
+ /* an open sitting: a shaft of light from the roof (ink, so paper washes rather than glows) */
253
+ .street-depth-shaft { position: absolute; background: linear-gradient(to top, var(--street-depth-shaft), transparent 85%); }
254
+ .street-depth-shaft__track { position: absolute; inset: 0; overflow: hidden; -webkit-mask-image: linear-gradient(to top, transparent, #000 18%, #000 55%, transparent); mask-image: linear-gradient(to top, transparent, #000 18%, #000 55%, transparent); }
255
+ .street-depth-shaft__ring { position: absolute; left: 1px; right: 1px; bottom: 30%; height: 6px; box-sizing: border-box; border-radius: 50%; border: 1px solid color-mix(in srgb, var(--ink) 35%, transparent); animation: street-depth-rise 24s linear infinite; }
256
+ /* FRONT: what needs you lights its floor's windows in salmon, the only salmon
257
+ the street spends besides its pins; the ring beside them fills with age */
258
+ .street-depth-needs { fill: var(--accent); stroke: var(--street-depth-needs-edge); stroke-width: 0.6; }
259
+ .street-depth-age { fill: none; stroke: var(--street-rule-strong); stroke-width: 1.2; }
260
+ .street-depth-age__arc { fill: none; stroke: var(--accent); stroke-width: 1.6; stroke-linecap: round; }
261
+ /* construction: a live site's crane (the jib swings, the hook reels, the
262
+ scaffold's platform climbs), and a proposal's parked crane and tally arc */
263
+ .street-depth-crane { fill: none; stroke: color-mix(in srgb, var(--ink) 66%, transparent); stroke-width: 1.15; stroke-linecap: round; stroke-linejoin: round; }
264
+ .street-depth-slab { fill: color-mix(in srgb, var(--ink) 40%, transparent); }
265
+ .street-depth-weight { fill: color-mix(in srgb, var(--ink) 55%, transparent); }
266
+ .street-depth-platform { fill: color-mix(in srgb, var(--ink) 55%, transparent); transform-box: view-box; animation: street-depth-climb 22s ease-in-out infinite alternate; }
267
+ .street-depth-jib { transform-box: view-box; animation: street-depth-swing 26s ease-in-out infinite; }
268
+ .street-depth-line { transform-box: view-box; animation: street-depth-reel 26s ease-in-out infinite; }
269
+ .street-depth-slab { transform-box: view-box; animation: street-depth-hoist 26s ease-in-out infinite; }
270
+ .street-depth-tally { fill: none; stroke: var(--street-rule-strong); stroke-width: 1; stroke-dasharray: 2 3; }
271
+ .street-depth-seat { stroke-width: 1.2; }
272
+ .street-depth-seat--open { fill: var(--bg); stroke: color-mix(in srgb, var(--ink) 55%, transparent); }
273
+ .street-depth-seat--for { fill: var(--ink); stroke: none; }
274
+ .street-depth-seat--against { fill: color-mix(in srgb, var(--ink) 30%, var(--bg)); stroke: none; }
275
+ /* the Lead's name plate at street level; "owner" where nobody holds the building */
276
+ .street-depth-plate { position: absolute; display: flex; justify-content: center; pointer-events: none; }
277
+ .street-depth-plate__t { box-sizing: border-box; max-width: 100%; height: 16px; padding: 0 5px; border: 1px solid var(--street-rule-strong); border-radius: 3px; background: var(--bg); color: var(--ink-soft); font-size: 11px; line-height: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
278
+ .street-depth-plate--owner .street-depth-plate__t { border-style: dashed; color: var(--ink-faint); }
279
+ .street-depth-plate--you .street-depth-plate__t { color: var(--ink); border-color: color-mix(in srgb, var(--ink) 60%, transparent); }
280
+ /* the readings as tiles: one number and two words, hidden at zero */
281
+ .street-sign[hidden] { display: none; }
282
+ .street-depth-tile__fig { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
283
+ .street-depth-tile__n { flex: none; }
284
+ .street-depth-tile__words { font-size: 14px; font-weight: 400; letter-spacing: 0; color: var(--ink-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
285
+ /* "N need you" is the door to the decision deck */
286
+ .street-depth-deck { color: inherit; text-decoration: none; border-radius: 999px; padding: 4px 10px; margin: -4px -10px; min-height: 24px; }
287
+ .street-depth-deck::after { content: '→'; font-size: 14px; color: var(--street-accent-text); }
288
+ .street-depth-deck:hover { background: color-mix(in srgb, var(--accent) 9%, transparent); }
289
+ .street-depth-deck:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
290
+ @keyframes street-depth-travel { from { offset-distance: 0%; } to { offset-distance: 100%; } }
291
+ @keyframes street-depth-shuttle { from { offset-distance: 0%; } to { offset-distance: 100%; } }
292
+ @keyframes street-depth-swing { 0%, 100% { transform: scaleX(1); } 50% { transform: scaleX(-1); } }
293
+ @keyframes street-depth-reel { 0%, 100% { transform: scaleY(1); } 50% { transform: scaleY(0.5); } }
294
+ @keyframes street-depth-hoist { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(var(--street-depth-lift, 0px)); } }
295
+ @keyframes street-depth-climb { from { transform: translateY(var(--street-depth-rise, 0px)); } to { transform: translateY(0); } }
296
+ @keyframes street-depth-rise { from { transform: translateY(calc(var(--street-depth-climb, 200px) * 0.3)) scaleX(0.6); } to { transform: translateY(calc(var(--street-depth-climb, 200px) * -0.7)) scaleX(1.5); } }
297
+
166
298
  /* the matter card, over the street (short form until task 1004528) */
167
299
  .street-card { position: absolute; left: var(--street-card-x, 32px); bottom: 16px; width: 390px; max-height: 300px; overflow-y: auto; box-sizing: border-box; background: var(--bg-card); border: 1px solid var(--street-rule-strong); border-radius: 16px; z-index: 8; padding: 14px 20px 18px; display: flex; flex-direction: column; gap: 11px; }
168
300
  .street-card[hidden] { display: none; }
@@ -196,6 +328,13 @@ html[data-fullscreen="on"] .street-top { padding-right: 184px; }
196
328
  .street-hud .street-label { font-size: 11px; }
197
329
  .street-hud__n { font-size: 16px; }
198
330
  .street-signs { margin: 0 16px; grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: 180px; overflow-x: auto; overflow-y: hidden; }
331
+ /* the strip opens on what needs you, so a phone sees it before it scrolls */
332
+ .street-hud .street-depth-deck { order: -1; }
333
+ }
334
+ @media (max-width: 420px) {
335
+ /* the top bar already names the street; the strip keeps the wordmark and the switch.
336
+ The kicker is the page's one h1, so it leaves the eye, not the outline (task 1004718) */
337
+ .street-kicker { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }
199
338
  }
200
339
  @media (max-width: 560px) {
201
340
  .street-card { left: 16px; right: 16px; width: auto; }
@@ -226,12 +365,13 @@ html[data-fullscreen="on"] .street-top { padding-right: 184px; }
226
365
  .inside-layer { position: absolute; left: 0; top: 0; width: 100%; height: 708px; pointer-events: none; }
227
366
  .inside-layer > * { pointer-events: auto; }
228
367
  .inside-floor { position: absolute; background: transparent; border: 0; padding: 0; margin: 0; cursor: pointer; border-radius: 0; }
229
- .inside-floor:hover { outline: 1px solid color-mix(in srgb, var(--accent) 55%, transparent); outline-offset: -3px; }
368
+ .inside-floor:hover { outline: 1px solid color-mix(in srgb, var(--ink) 40%, transparent); outline-offset: -3px; }
230
369
 
231
370
  /* the lift: it slides to the chosen floor */
232
- .inside-lift { position: absolute; width: 18px; border-radius: 3px; background: var(--accent); display: flex; align-items: center; justify-content: center; pointer-events: none; transition: transform 650ms cubic-bezier(.2,.7,.3,1); }
371
+ /* the lift and the chosen floor are ink: salmon inside a building is only what needs you (task 1004718) */
372
+ .inside-lift { position: absolute; width: 18px; border-radius: 3px; background: var(--ink); display: flex; align-items: center; justify-content: center; pointer-events: none; transition: transform 650ms cubic-bezier(.2,.7,.3,1); }
233
373
  .inside-lift[hidden] { display: none; }
234
- .inside-lift__bar { width: 8px; height: 2px; border-radius: 1px; background: var(--accent-on); }
374
+ .inside-lift__bar { width: 8px; height: 2px; border-radius: 1px; background: var(--bg); }
235
375
  .inside-annex { position: absolute; box-sizing: border-box; border: 1px dashed var(--street-rule-strong); display: flex; align-items: flex-end; justify-content: center; padding: 12px; text-align: center; font-size: 12.5px; line-height: 1.4; color: var(--ink-faint); }
236
376
 
237
377
  /* a district: its name, its work labelled on the road, the two-line legend */
@@ -287,12 +427,16 @@ html[data-fullscreen="on"] .street-top { padding-right: 184px; }
287
427
  /* the chosen floor, its sign large; the block re-enters on every pick */
288
428
  .inside-open { display: flex; flex-direction: column; gap: 10px; }
289
429
  .inside-open__head { display: flex; align-items: center; gap: 16px; }
290
- .inside-plate { width: 64px; height: 64px; flex-shrink: 0; box-sizing: border-box; border-radius: 12px; border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent); display: flex; align-items: center; justify-content: center; }
430
+ .inside-plate { width: 64px; height: 64px; flex-shrink: 0; box-sizing: border-box; border-radius: 12px; border: 1px solid var(--street-rule-strong); display: flex; align-items: center; justify-content: center; }
291
431
  .inside-plate svg { display: block; }
292
- .inside-plate path { fill: none; stroke: var(--accent); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
293
- .inside-plate path + path { fill: var(--accent); stroke: none; }
432
+ .inside-plate path { fill: none; stroke: var(--ink); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
433
+ .inside-plate path + path { fill: var(--ink); stroke: none; }
434
+ .inside-plate--hot { border-color: color-mix(in srgb, var(--accent) 55%, transparent); }
435
+ .inside-plate--hot path { stroke: var(--accent); }
436
+ .inside-plate--hot path + path { fill: var(--accent); }
294
437
  .inside-open__words { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
295
- .inside-open__kicker { font-size: 11.5px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--street-accent-text); }
438
+ .inside-open__kicker { font-size: 11.5px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-faint); }
439
+ .inside-open__kicker--hot { color: var(--street-accent-text); }
296
440
  .inside-open__title { margin: 0; font-size: 21px; font-weight: 300; letter-spacing: -0.02em; }
297
441
  .inside-line { display: flex; justify-content: space-between; gap: 14px; padding: 9px 0; border-top: 1px solid var(--street-rule); font-size: 14.5px; line-height: 1.4; }
298
442
  .inside-line__meta { color: var(--ink-faint); white-space: nowrap; font-variant-numeric: tabular-nums; }
@@ -428,4 +572,10 @@ html[data-fullscreen="on"] .street-top { padding-right: 184px; }
428
572
  standing at the chosen floor without travelling there. */
429
573
  @media (prefers-reduced-motion: reduce) {
430
574
  body[data-page="street"] *, body[data-page="street"] *::before, body[data-page="street"] *::after { animation: none !important; transition: none !important; }
575
+ /* the living street (task 1004685) holds still too: no parallax on scroll.
576
+ Its rest frame is whole: the skyline in place, every pod parked on its
577
+ lane a fixed gap apart (the count is the agents), the crane parked, the
578
+ scaffold at its % complete, the ring in its shaft, the salmon windows lit. */
579
+ .street-window { perspective: none; }
580
+ .street-depth-back { transform: none; }
431
581
  }
@@ -11,8 +11,9 @@
11
11
  links, injected into this head by the server like every hall page's
12
12
  (tests/hall_tokens.mjs: no hall page hardcodes a web-font stylesheet). -->
13
13
  <link rel="stylesheet" href="/builders/style.css?v=2026-07-02-shell1">
14
- <link rel="stylesheet" href="/builders/street.css?v=2026-10-04-pins1">
14
+ <link rel="stylesheet" href="/builders/street.css?v=2026-10-09-living3">
15
15
  <link rel="stylesheet" href="/builders/street-card.css?v=2026-10-04-record1">
16
+ <link rel="stylesheet" href="/builders/street-deck.css?v=2026-10-13-glass1">
16
17
  </head>
17
18
  <!-- THE STREET (task 1004525, goal 1000125, R03). The street view of
18
19
  docs/design/mocks/governor-city/Street.dc.html (renderVals' street branch,
@@ -72,12 +73,16 @@
72
73
  <span class="street-today__pips" id="streetTodayPips" aria-hidden="true"></span>
73
74
  <span class="street-today__n" id="streetTodayN">0 decided</span>
74
75
  </div>
75
- <div class="street-hud__item">
76
+ <!-- "needs you N" opens the decision deck (task 1004685): every ask, yours first.
77
+ Both numbers are gov-living.js countsOf, the ones the city, the deck and
78
+ the shell's tally print: needs you (asks on your seats) and moving (tasks
79
+ being built or waiting to land). -->
80
+ <a class="street-hud__item street-depth-deck" id="hudDeck" href="/builders/street#deck">
76
81
  <span class="street-hud__dot" id="hudDot" aria-hidden="true"></span>
77
- <span class="street-label">needs you</span>
82
+ <span class="street-label">to do</span>
78
83
  <span class="street-hud__n" id="hudNeeds">—</span>
79
- </div>
80
- <div class="street-hud__item">
84
+ </a>
85
+ <div class="street-hud__item" title="tasks being built or waiting to land">
81
86
  <span class="street-hud__dot street-hud__dot--moving" aria-hidden="true"></span>
82
87
  <span class="street-label">moving</span>
83
88
  <span class="street-hud__n" id="hudMoving">—</span>
@@ -100,10 +105,20 @@
100
105
  <p class="street-evening__h">nothing waits on you.</p>
101
106
  <p class="street-evening__line" id="streetEveningLine"></p>
102
107
  </div>
108
+ <!-- THE LIVING STREET (task 1004685): behind the civic row, the goals'
109
+ skyline in two hazed rows (each its own depth, so it slides slower
110
+ when the street scrolls sideways) and the sky with its skylanes and
111
+ sittings; in front of it, what needs you, construction, the Leads'
112
+ plates and the road. street.js fills them from street-draw.js. -->
113
+ <div class="street-depth-back street-depth-back--far" id="streetDepthFar" aria-hidden="true"></div>
114
+ <div class="street-depth-back street-depth-back--near" id="streetDepthNear" aria-hidden="true"></div>
115
+ <div class="street-depth-fog" id="streetDepthFog" aria-hidden="true"></div>
116
+ <div class="street-depth-layer" id="streetDepthSky" aria-hidden="true"></div>
103
117
  <svg class="street-svg" id="streetSvg" width="1440" height="900" viewBox="0 0 1440 900" aria-hidden="true" focusable="false">
104
118
  <g id="streetFlat"></g>
105
119
  <g id="streetBlds" class="street-blds"></g>
106
120
  </svg>
121
+ <div class="street-depth-layer" id="streetDepthFront" aria-hidden="true"></div>
107
122
  <div class="street-layer" id="streetCrates" aria-hidden="true"></div>
108
123
  <div class="street-layer" id="streetCols"></div>
109
124
  <div class="street-layer" id="streetGroups" aria-hidden="true"></div>
@@ -166,6 +181,10 @@
166
181
  <span class="inside-meter__line" id="insideGrowth"></span>
167
182
  </div>
168
183
  </div>
184
+ <!-- the government layer (task 1004685): who leads it, its stack of
185
+ asks, and the Rules law book, the Treasury, the Charter's preset;
186
+ street-office.js fills it -->
187
+ <div class="govx" id="insideGovt" hidden></div>
169
188
  <div class="inside-open" id="insideOpenA"></div>
170
189
  <div class="inside-open" id="insideOpenB" hidden></div>
171
190
  <div class="inside-record">
@@ -197,6 +216,12 @@
197
216
  <div class="street-card__body" id="streetCardBody"></div>
198
217
  </div>
199
218
 
219
+ <!-- THE DECISION DECK (task 1004685): #deck and #deck=<building>, one
220
+ ask at a time in the ask format. The street stays in view (task 1004702):
221
+ a glass panel beside the building that asks, a slim bar at the foot.
222
+ street-deck.js draws it. -->
223
+ <section class="deck" id="streetDeck" aria-labelledby="deckTitle" hidden></section>
224
+
200
225
  <div class="street-toast" id="streetToast" role="status" aria-live="polite" hidden></div>
201
226
 
202
227
  </main>
@@ -204,15 +229,20 @@
204
229
  </div>
205
230
 
206
231
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
207
- <script src="/builders/craft-mode.js?v=2026-10-08-city-tab"></script>
208
- <script src="/builders/shell.js?v=2026-10-08-glyph-fix"></script>
232
+ <script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
233
+ <script src="/builders/shell.js?v=2026-10-10-hall-keys1"></script>
209
234
  <script src="/builders/palette.js" defer></script>
210
- <script src="/builders/city-draw.js?v=2026-10-04-pins1"></script>
211
- <script src="/builders/street-draw.js?v=2026-10-04-pins1"></script>
235
+ <script src="/builders/gov-living.js?v=2026-10-09-positions1"></script>
236
+ <script src="/builders/gov-feeds.js?v=2026-10-09-living3"></script>
237
+ <script src="/builders/city-draw.js?v=2026-10-09-living3"></script>
238
+ <script src="/builders/street-draw.js?v=2026-10-09-living3"></script>
212
239
  <script src="/builders/street-card.js?v=2026-10-04-record1"></script>
213
- <script src="/builders/street-inside.js?v=2026-10-03-desk1"></script>
240
+ <script src="/builders/street-inside.js?v=2026-10-09-living3"></script>
241
+ <script src="/builders/street-depth.js?v=2026-10-09-living3"></script>
214
242
  <script src="/builders/street-desk.js?v=2026-10-04-record1"></script>
215
243
  <script src="/builders/street-amend.js?v=2026-10-02-amend1"></script>
216
- <script src="/builders/street.js?v=2026-10-04-record1"></script>
244
+ <script src="/builders/street-deck.js?v=2026-10-13-glass1"></script>
245
+ <script src="/builders/street-office.js?v=2026-10-09-living3"></script>
246
+ <script src="/builders/street.js?v=2026-10-13-glass1"></script>
217
247
  </body>
218
248
  </html>
@@ -80,8 +80,11 @@
80
80
  var NARROW = 900;
81
81
  var I = window.GovInside;
82
82
  // The artboard's street band: the prototype's 900-high board less its 64 px
83
- // header and 128 px footer, which this page lays out as real rows.
84
- var BAND_TOP = 64, BAND_H = 708;
83
+ // header and 128 px footer, which this page lays out as real rows, and less
84
+ // the empty sky over the skylanes and the bare ground under the brackets
85
+ // (task 1004718): 132 px off the top, 76 off the foot. street.css translateY
86
+ // matches BAND_TOP; street-depth.js skyTop sits 40 under it.
87
+ var BAND_TOP = 196, BAND_H = 500;
85
88
  var CARD_W = 390;
86
89
 
87
90
  var $ = function (id) { return document.getElementById(id); };
@@ -159,9 +162,16 @@
159
162
  // 1440 x 708 at scale 1 is the prototype exactly. A wider or taller stage
160
163
  // grows it, a smaller one shrinks it; a phone keeps it near full size and the
161
164
  // street scrolls sideways inside its window, so a pin stays a pin.
165
+ // A stage taller than the street needs (a wide window scales the street to its
166
+ // width) gives the spare height to the foot of the page, under the readings, so
167
+ // the street sits on them with no empty sky above it (task 1004718). The window
168
+ // gives the height back whenever a building's inside is open.
162
169
  function fit() {
170
+ var win = $('streetWindow'), inside = $('streetInside');
171
+ var insideOn = !!(inside && !inside.hidden);
172
+ if (win && win.style) win.style.flex = '';
163
173
  fitInside();
164
- var win = $('streetWindow'), sizer = $('streetSizer'), board = $('streetBoard');
174
+ var sizer = $('streetSizer'), board = $('streetBoard');
165
175
  if (!win || !sizer || !board) return;
166
176
  var W = view.model ? view.model.width : S.ARTBOARD_W;
167
177
  var cw = win.clientWidth || window.innerWidth || 1440, ch = win.clientHeight || 708;
@@ -171,6 +181,7 @@
171
181
  view.scale = s;
172
182
  sizer.style.width = Math.round(W * s) + 'px';
173
183
  sizer.style.height = Math.round(BAND_H * s) + 'px';
184
+ if (!insideOn && win.style && Math.round(BAND_H * s) < ch - 1) win.style.flex = '0 0 ' + Math.round(BAND_H * s) + 'px';
174
185
  board.style.width = W + 'px';
175
186
  board.style.setProperty('--street-scale', String(s));
176
187
  var svg = $('streetSvg');
@@ -210,13 +221,26 @@
210
221
  .then(function (r) { return r && r.ok ? r.json() : null; })
211
222
  .catch(function () { return null; });
212
223
  }
224
+ // The street's three reads go through gov-feeds.js when it is on the page (one
225
+ // read per feed per page view, shared with the living model and the shell's
226
+ // tally). Every load after the first follows an act, so it reads afresh what an
227
+ // act can change, once: the living model and every count re-read with it.
228
+ function readStreet() {
229
+ var F = window.GovFeeds;
230
+ if (F) {
231
+ if (data.loaded) F.refresh();
232
+ return F.read(['docket', 'goals', 'rollups']).then(function (r) { return [r.docket, r.goals, r.rollups]; });
233
+ }
234
+ return Promise.all([getJson(FEEDS.docket), getJson(FEEDS.goals), getJson(FEEDS.rollups)]);
235
+ }
213
236
  function load() {
214
- return Promise.all([getJson(FEEDS.docket), getJson(FEEDS.goals), getJson(FEEDS.rollups)]).then(function (res) {
237
+ return readStreet().then(function (res) {
215
238
  data.docket = res[0] && Array.isArray(res[0].matters) ? res[0] : null;
216
239
  data.goals = res[1] && Array.isArray(res[1].goals) ? res[1].goals : [];
217
240
  data.rollups = res[2] && Array.isArray(res[2].rollups) ? res[2].rollups : [];
218
241
  data.loaded = true;
219
242
  draw();
243
+ if (window.GovDeck) window.GovDeck.page.draw();
220
244
  var viewer = data.docket && data.docket.viewer && data.docket.viewer.id;
221
245
  if (viewer == null) return null;
222
246
  return getJson(FEEDS.earnings(viewer)).then(function (e) {
@@ -263,61 +287,18 @@
263
287
  return el;
264
288
  }
265
289
 
266
- // One pin per building (task 1004569): its tier is its class, a label shows
267
- // only when the collision pass placed it (street-pin--shown; the rest show
268
- // on hover or focus), and a count badge says how many matters are open there.
269
- function drawPin(p) {
270
- var keys = p.matters && p.matters.length ? p.matters : [p.key];
271
- var el = node('button', 'street-pin street-pin--' + p.state + (p.shown ? ' street-pin--shown' : ''));
272
- el.setAttribute('type', 'button');
273
- el.setAttribute('aria-label', p.aria);
274
- el.setAttribute('aria-expanded', keys.indexOf(view.open) >= 0 ? 'true' : 'false');
275
- el.setAttribute('aria-controls', 'streetCard');
276
- el.setAttribute('data-matter', p.key);
277
- el.setAttribute('data-matters', keys.join(' '));
278
- el.setAttribute('data-building', p.building);
279
- at(el, p.x, p.y, S.PIN_W, p.h);
280
- var label = node('span', 'street-pin__label', p.label);
281
- if (p.label) label.setAttribute('title', p.label);
282
- el.appendChild(label);
283
- var head = node('span', 'street-pin__head');
284
- if (p.state === 'urgent') head.appendChild(node('span', 'street-pin__ring'));
285
- if (p.isCount) head.appendChild(node('span', 'street-pin__count', p.count));
286
- else if (p.state !== 'settled') {
287
- var svg = document.createElementNS(SVG_NS, 'svg');
288
- svg.setAttribute('width', '18');
289
- svg.setAttribute('height', '18');
290
- svg.setAttribute('viewBox', '0 0 18 18');
291
- svg.setAttribute('aria-hidden', 'true');
292
- var fill = document.createElementNS(SVG_NS, 'path');
293
- fill.setAttribute('d', p.gf || '');
294
- var line = document.createElementNS(SVG_NS, 'path');
295
- line.setAttribute('d', p.gl || '');
296
- svg.appendChild(fill);
297
- svg.appendChild(line);
298
- head.appendChild(svg);
299
- }
300
- if (!p.isCount && Number(p.count) > 1) head.appendChild(node('span', 'street-pin__badge', p.count));
301
- el.appendChild(head);
302
- el.appendChild(node('span', 'street-pin__stem'));
303
- el.addEventListener('click', function () { openCard(keys[0], el, keys); });
304
- return el;
305
- }
306
-
307
- function drawSign(sg) {
308
- var a = node('a', 'street-sign' + (sg.hot ? ' street-sign--hot' : '') + (sg.here ? ' street-sign--here' : ''));
309
- a.setAttribute('href', sg.href);
310
- a.setAttribute('aria-label', sg.aria);
311
- a.setAttribute('data-reading', sg.key);
312
- if (sg.here) a.setAttribute('aria-current', 'location');
313
- var top = node('span', 'street-sign__top');
314
- top.appendChild(node('span', 'street-sign__dot'));
315
- top.appendChild(node('span', 'street-label', sg.label));
316
- a.appendChild(top);
317
- a.appendChild(node('span', 'street-sign__figure', sg.figure));
318
- a.appendChild(node('span', 'street-sign__line', sg.line));
319
- return a;
320
- }
290
+ // ---------- the exterior's painters live in street-depth.js (task 1004685) ----------
291
+ // A pin, a reading, the depth layers and the living street's reads are
292
+ // GovStreetDepth.page's (street-depth.js); this file hands it the street's
293
+ // data, its card opener and a redraw, and keeps the street's own wiring.
294
+ var X = window.GovStreetDepth.page;
295
+ function drawPin(p) { return X.pin(p, { open: view.open, openCard: openCard }); }
296
+ function drawSign(sg, tile) { return X.sign(sg, tile); }
297
+ function drawDepth(dm, W) { return X.depth(dm, W); }
298
+ // the living model arrives after the street's own reads: the street and the deck draw it
299
+ function loadLiving() { return X.loadLiving(data, function () { draw(); if (window.GovDeck) window.GovDeck.page.draw(); }); }
300
+ function livingModel() { return X.livingModel(data); }
301
+ var living = X.living;
321
302
 
322
303
  function draw() {
323
304
  var mode = currentMode();
@@ -328,8 +309,16 @@
328
309
  var sizerEl = $('streetSizer');
329
310
  var showInside = !!(h && inside && I);
330
311
  if (sizerEl) sizerEl.hidden = showInside;
331
- var m = S.streetModel({ docket: data.docket, goals: data.goals, rollups: data.rollups, earnings: data.earnings, mode: mode, now: Date.now(), here: here() });
312
+ // the living street (task 1004685): three planes over the living model
313
+ var lm = livingModel();
314
+ data.living = lm;
315
+ // the header prints the counts every surface prints (gov-living.js countsOf, less
316
+ // what the deck answered here); "—" while the living model is still reading
317
+ var counts = lm && window.GovFeeds ? window.GovFeeds.countsFrom(lm) : (window.GovFeeds && window.GovLiving ? null : undefined);
318
+ data.counts = counts || null;
319
+ var m = S.streetModel({ docket: data.docket, goals: data.goals, rollups: data.rollups, earnings: data.earnings, mode: mode, now: Date.now(), here: here(), depth: true, living: lm, counts: counts, roster: living.feeds ? living.feeds.roster : null });
332
320
  view.model = m;
321
+ drawDepth(m.depth, m.width);
333
322
  var flat = $('streetFlat'), blds = $('streetBlds');
334
323
  clear(flat); clear(blds);
335
324
  m.flat.forEach(function (p) { flat.appendChild(pathEl(p)); });
@@ -375,7 +364,8 @@
375
364
  m.pins.forEach(function (p) { pins.appendChild(drawPin(p)); });
376
365
 
377
366
  var signs = $('streetSigns'); clear(signs);
378
- m.signs.forEach(function (sg) { signs.appendChild(drawSign(sg)); });
367
+ var tiles = m.depth && data.loaded ? m.depth.tiles : null;
368
+ m.signs.forEach(function (sg) { signs.appendChild(drawSign(sg, tiles ? tiles[sg.key] : undefined)); });
379
369
 
380
370
  var eve = $('streetEvening');
381
371
  if (eve) eve.hidden = !(m.evening && !view.open) || showInside;
@@ -383,6 +373,9 @@
383
373
  if (eveLine) eveLine.textContent = m.eveningLine;
384
374
 
385
375
  drawToday();
376
+ // "N need you" is the way into the decision deck (V3 builds #deck)
377
+ var deck = $('hudDeck');
378
+ if (deck && m.hud.deck) { deck.setAttribute('href', m.hud.deck.href); deck.setAttribute('aria-label', m.hud.deck.aria); }
386
379
  $('hudNeeds').textContent = m.hud.needs;
387
380
  $('hudMoving').textContent = m.hud.moving;
388
381
  $('hudShare').textContent = m.hud.share;
@@ -404,6 +397,7 @@
404
397
  markAnswer();
405
398
  fit();
406
399
  if (view.open) fillCard();
400
+ loadLiving();
407
401
  return m;
408
402
  }
409
403
 
@@ -438,7 +432,9 @@
438
432
  var startFloor = st.floor == null && D && data.docket ? D.firstNeeds(st.key, data.docket) : null;
439
433
  var cleared = [];
440
434
  Object.keys(desk.cleared).forEach(function (k) { var p = k.split(':'); if (p[0] === st.key) cleared.push(Number(p[1])); });
441
- var bv = I.buildingModel({ key: st.key, docket: data.docket, goals: data.goals, mode: mode, now: Date.now(), floor: st.floor == null ? startFloor : st.floor, drawer: st.drawer, picks: st.picks, cleared: cleared });
435
+ // the Lead's office on top, its build-outs and the floors they put up (street-office.js, task 1004711)
436
+ var OP = window.GovOffice && window.GovOffice.page, bi = OP ? OP.input(st.key) : {};
437
+ var bv = I.buildingModel({ key: st.key, docket: data.docket, goals: data.goals, mode: mode, now: Date.now(), floor: st.floor == null ? startFloor : st.floor, drawer: st.drawer, picks: st.picks, cleared: cleared.concat(bi.cleared || []), office: bi.office, works: bi.works, extraFloors: bi.floors });
442
438
  // a sitting's ballot is read once, so its row can vote in place
443
439
  var C = window.GovMatterCard;
444
440
  if (!desk.roomAsked && !data.room && C && data.docket && data.docket.matters.some(function (x) { return x && x.building === st.key && C.needsRoom(x); })) {
@@ -520,7 +516,7 @@
520
516
  // the chosen floor: two blocks that take turns, so the block re-enters on every pick
521
517
  var on = $(bv.flipA ? 'insideOpenA' : 'insideOpenB'), off = $(bv.flipA ? 'insideOpenB' : 'insideOpenA');
522
518
  if (off) { off.hidden = true; clear(off); }
523
- if (on) { on.hidden = false; fillDesk(on, bv); }
519
+ if (on) { on.hidden = false; if (!(OP && OP.inside(bv, on, $('insideGovt')))) fillDesk(on, bv); }
524
520
  setText('insideOnFile', bv.onFile);
525
521
  var drawers = $('insideDrawers'); clear(drawers);
526
522
  bv.drawers.forEach(function (dw) {
@@ -571,11 +567,12 @@
571
567
  el.setAttribute('data-desk', dm.key);
572
568
  // the header: the floor's sign, its name, its status
573
569
  var head = node('div', 'inside-open__head');
574
- var plate = node('span', 'inside-plate');
570
+ // the sign and the kicker are salmon only while the floor needs you (task 1004718)
571
+ var plate = node('span', 'inside-plate' + (dm.hot ? ' inside-plate--hot' : ''));
575
572
  plate.appendChild(icon(bv.sym.d, bv.sym.f, 34));
576
573
  head.appendChild(plate);
577
574
  var words = node('div', 'inside-open__words');
578
- words.appendChild(node('span', 'inside-open__kicker', 'floor ' + (bv.selFloor + 1)));
575
+ words.appendChild(node('span', 'inside-open__kicker' + (dm.hot ? ' inside-open__kicker--hot' : ''), 'floor ' + (bv.selFloor + 1)));
579
576
  words.appendChild(node('h3', 'inside-open__title', dm.name));
580
577
  words.appendChild(node('span', 'desk-status' + (dm.hot ? ' desk-status--hot' : ''), dm.status));
581
578
  head.appendChild(words);
@@ -1248,6 +1245,11 @@
1248
1245
  var ctx = { room: data.room, constitution: data.constitution, now: Date.now(), goals: (data.goals || []).filter(function (g) { return g && g.status === 'open'; }), pick: card.pick };
1249
1246
  var cv = D && D.cardFor ? D.cardFor(view.open, data.docket, ctx) : C ? C.model({ docket: data.docket, key: view.open, room: data.room, constitution: data.constitution, now: Date.now() }) : null;
1250
1247
  if (!cv) { closeCard(false); return null; }
1248
+ // a stored ask opens as its building's glass card, answered in place (task 1004702)
1249
+ var G = window.GovDeck && window.GovDeck.page, frame = $('streetCard');
1250
+ var glass = !!(G && G.streetCard && G.streetCard($('streetCardBody'), view.open, card.page, { close: function () { closeCard(true); }, turn: function (k) { turnPage(k, 'next'); }, redraw: fillCard, done: function () { if (view.open) fillCard(); } }));
1251
+ if (frame) frame.setAttribute('data-glass', glass ? 'ask' : 'matter');
1252
+ if (glass) { card.cv = cv; placeCard(); return cv; }
1251
1253
  var recordable = !!(cv.row && cv.row.choices && cv.row.choices.record && cv.row.choices.record.file);
1252
1254
  if (recordable) { var cid = criterionOf(cv.row.card); C.withChecks(cv, rec.checks[cid] || null, rec.checks[cid] === false); }
1253
1255
  card.cv = cv;
@@ -1256,7 +1258,7 @@
1256
1258
  $('streetCardTitle').textContent = cv.title;
1257
1259
  var pager = C.pager ? C.pager(card.page, view.open) : null;
1258
1260
  C.render(document, $('streetCardBody'), cv, { act: actOn, openForm: openForm, ask: askFor, more: toggleMore, pick: pickQueued, page: turnPage, record: recordable ? cardRecord(cv) : null }, { form: card.form, ask: card.ask, more: card.more, error: card.error, busy: card.busy, fresh: card.fresh, pager: pager });
1259
- var frame = $('streetCard'); if (frame) frame.setAttribute('data-more', card.more ? 'open' : 'closed');
1261
+ if (frame) frame.setAttribute('data-more', card.more ? 'open' : 'closed');
1260
1262
  placeCard();
1261
1263
  return cv;
1262
1264
  }
@@ -1312,6 +1314,7 @@
1312
1314
  var cx = pin ? originX + (pin.x + 55) * view.scale : stageW / 2;
1313
1315
  var x = Math.max(16, Math.min(stageW - CARD_W - 16, Math.round(cx - CARD_W / 2)));
1314
1316
  card.style.setProperty('--street-card-x', x + 'px');
1317
+ if (window.GovDeck) window.GovDeck.page.placeStreetCard(card, view.open);
1315
1318
  }
1316
1319
  function openCard(key, opener, keys) {
1317
1320
  view.open = key;
@@ -1322,6 +1325,7 @@
1322
1325
  if (!fillCard()) return;
1323
1326
  el.hidden = false;
1324
1327
  draw();
1328
+ placeCard();
1325
1329
  var close = $('streetCardClose');
1326
1330
  if (close && close.focus) close.focus();
1327
1331
  if (card.cv && card.cv.loading) return loadRoom();
@@ -1333,6 +1337,7 @@
1333
1337
  card.cv = null; card.form = null; card.ask = null; card.more = false; card.pick = null; card.error = ''; card.busy = false; card.fresh = null; card.page = null;
1334
1338
  var el = $('streetCard');
1335
1339
  if (el) el.hidden = true;
1340
+ if (window.GovDeck) window.GovDeck.page.unspot();
1336
1341
  draw();
1337
1342
  // The pins were redrawn, so focus goes back to the new button for the same matter.
1338
1343
  if (restore !== false && was && was.getAttribute) {
@@ -1461,6 +1466,13 @@
1461
1466
  if (media && media.addEventListener) media.addEventListener('change', function () { if (!storedTheme()) { applyTheme(); draw(); } });
1462
1467
  painted = applyTheme();
1463
1468
  today = readToday();
1469
+ // the decision deck (#deck) and the government inside each building, over the
1470
+ // street's own senders (street-deck.js, street-office.js; task 1004685)
1471
+ var env = { data: function () { return data; }, getJson: getJson, send: send, say: say, draw: draw, load: load, mode: currentMode, today: function () { return today.n; }, decided: function () { bumpToday(); drawToday(); }, done: function (id) { desk.done[id] = true; }, pickFloor: pickFloor, inside: function () { return view.inside; } };
1472
+ if (window.GovDeck) window.GovDeck.page.mount(env);
1473
+ if (window.GovOffice) window.GovOffice.page.mount(env);
1474
+ // an answer in the deck moves the header's counts as it moves the deck's
1475
+ if (window.GovFeeds) window.GovFeeds.onChange(function () { if (data.loaded) draw(); });
1464
1476
  draw();
1465
1477
  watchShellTheme();
1466
1478
  return load();