@bongos/core 1.24.51 → 1.24.53

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 (95) hide show
  1. package/.bongos-core.json +125 -95
  2. package/docs/copy-inventory.md +225 -242
  3. package/docs/copy-registry.json +278 -452
  4. package/docs/file-map.md +1 -1
  5. package/docs/module-api-changelog.md +4 -0
  6. package/docs/page-inventory.json +33 -0
  7. package/docs/page-readings.json +515 -531
  8. package/modules/discord/webhook.js +28 -6
  9. package/modules/hall-ui/public/ai-studio.html +1 -0
  10. package/modules/hall-ui/public/approval-queue.css +3 -11
  11. package/modules/hall-ui/public/approval-queue.js +13 -20
  12. package/modules/hall-ui/public/atlas.html +1 -0
  13. package/modules/hall-ui/public/blockers.html +1 -0
  14. package/modules/hall-ui/public/board-room.html +1 -0
  15. package/modules/hall-ui/public/bulk-toolbar.js +5 -1
  16. package/modules/hall-ui/public/city.html +1 -0
  17. package/modules/hall-ui/public/collab.html +1 -0
  18. package/modules/hall-ui/public/commands.html +1 -0
  19. package/modules/hall-ui/public/controls.css +100 -0
  20. package/modules/hall-ui/public/copy-desk.css +7 -11
  21. package/modules/hall-ui/public/copy-desk.html +17 -16
  22. package/modules/hall-ui/public/copy-desk.js +13 -9
  23. package/modules/hall-ui/public/deploy.html +1 -0
  24. package/modules/hall-ui/public/goal-inbox.js +32 -17
  25. package/modules/hall-ui/public/goal-map.html +1 -0
  26. package/modules/hall-ui/public/goals-page.js +7 -1
  27. package/modules/hall-ui/public/goals.html +1 -0
  28. package/modules/hall-ui/public/government.html +1 -0
  29. package/modules/hall-ui/public/government.js +10 -2
  30. package/modules/hall-ui/public/hall-dialog.js +255 -0
  31. package/modules/hall-ui/public/hall-kit.js +9 -2
  32. package/modules/hall-ui/public/idea.html +1 -0
  33. package/modules/hall-ui/public/ideas.html +1 -0
  34. package/modules/hall-ui/public/index.html +1 -0
  35. package/modules/hall-ui/public/market-watch-item.html +2 -1
  36. package/modules/hall-ui/public/market-watch-lib.js +16 -9
  37. package/modules/hall-ui/public/market-watch-rules.html +1 -0
  38. package/modules/hall-ui/public/market-watch.css +2 -3
  39. package/modules/hall-ui/public/market-watch.html +2 -1
  40. package/modules/hall-ui/public/modules.html +1 -0
  41. package/modules/hall-ui/public/money.html +1 -0
  42. package/modules/hall-ui/public/my-agents.html +1 -0
  43. package/modules/hall-ui/public/oversight.css +3 -33
  44. package/modules/hall-ui/public/primer.html +1 -0
  45. package/modules/hall-ui/public/profile.html +1 -0
  46. package/modules/hall-ui/public/project-settings.html +1 -0
  47. package/modules/hall-ui/public/roster.html +1 -0
  48. package/modules/hall-ui/public/settings-ai-keys.js +5 -1
  49. package/modules/hall-ui/public/settings-passkeys.js +5 -1
  50. package/modules/hall-ui/public/settings-sessions.js +10 -2
  51. package/modules/hall-ui/public/settings.html +1 -0
  52. package/modules/hall-ui/public/settings.js +5 -1
  53. package/modules/hall-ui/public/street.html +1 -0
  54. package/modules/hall-ui/public/studio.css +6 -32
  55. package/modules/hall-ui/public/studio.html +4 -3
  56. package/modules/hall-ui/public/studio.js +40 -35
  57. package/modules/hall-ui/public/style.css +2 -8
  58. package/modules/hall-ui/public/task.html +1 -0
  59. package/modules/hall-ui/public/task.js +26 -12
  60. package/modules/hall-ui/public/thinking.html +1 -0
  61. package/modules/hall-ui/public/tweak-editor.html +1 -0
  62. package/modules/hall-ui/public/watch.html +1 -0
  63. package/modules/hall-ui/public/work.html +1 -0
  64. package/modules/hall-ui/public/work.js +7 -1
  65. package/modules/public-landing/public/index.html +11 -8
  66. package/modules/ui-design/public/brand/og-card.png +0 -0
  67. package/package-lock.json +2 -2
  68. package/package.json +1 -1
  69. package/public-docs/index.html +107 -12
  70. package/release-notes.json +12 -0
  71. package/scripts/bongos/gen-brand.js +40 -6
  72. package/scripts/discord/lib.js +17 -3
  73. package/scripts/status-mirror-sync.js +11 -7
  74. package/src/bongos/serve-internal.js +3 -2
  75. package/src/error-page.js +120 -0
  76. package/src/link-preview.js +71 -0
  77. package/src/module-api.js +1 -1
  78. package/src/platform-server.js +22 -8
  79. package/tests/brand_surfaces.mjs +213 -0
  80. package/tests/bulk_toolbar.mjs +1 -1
  81. package/tests/discord_board_broadcast.mjs +23 -10
  82. package/tests/docs_openapi_route.mjs +2 -1
  83. package/tests/hall_approval_queue.mjs +8 -2
  84. package/tests/hall_audit.mjs +1 -1
  85. package/tests/hall_city.mjs +1 -1
  86. package/tests/hall_collab_world.mjs +9 -6
  87. package/tests/hall_dialog.mjs +401 -0
  88. package/tests/hall_error_envelope.mjs +1 -1
  89. package/tests/hall_oversight_world.mjs +9 -7
  90. package/tests/hall_record_world.mjs +1 -1
  91. package/tests/hall_street.mjs +1 -1
  92. package/tests/hall_studio_home.mjs +1 -1
  93. package/tests/home_attention_cards.mjs +2 -0
  94. package/tests/kind_axis_unset.mjs +1 -1
  95. package/tests/market_watch_pages.mjs +3 -1
@@ -1,8 +1,9 @@
1
1
  /* oversight.css — the oversight family's archetype (task 1003469, goal
2
2
  1000081): what an oversight ROW is in the chrome world, and the pieces the
3
3
  pages (government, watch, gate) share around it — the figure
4
- band inside a panel, the group head, the inline form, the grant toggle, the
5
- confirm modal. The third family sheet after record.css and room.css: a page
4
+ band inside a panel, the group head, the inline form, the grant toggle (the
5
+ confirm modal it once kept is the hall's one dialog now, controls.css
6
+ .hall-dialog, task 1005085). The third family sheet after record.css and room.css: a page
6
7
  sheet holds only what one page has and the others do not. The collaboration
7
8
  pages (collab, copy desk — task 1003471) ride the same row rather than a
8
9
  fourth: a page whose repeated thing is a titled row with facts and actions
@@ -318,34 +319,3 @@ a.fact-chip:hover { color: var(--accent-ink); border-color: var(--accent-ink); t
318
319
  the 24px floor and the equal negative margin hands the space back. */
319
320
  .ov-foot a { display: inline-block; color: var(--accent-ink); min-height: 24px; min-width: 24px; padding: 3px 4px; margin: -3px -4px; line-height: 18px; }
320
321
 
321
- /* ---- the confirm modal ----------------------------------------------------------
322
- Gate's confirm dialog. A --bg-card panel closed by one rule
323
- hairline and no shadow (the hub's project card is the reference), on the
324
- shell's --z-modal layer, over a scrim mixed from the ink. */
325
- .ov-modal-bg {
326
- position: fixed;
327
- inset: 0;
328
- z-index: var(--z-modal);
329
- display: flex;
330
- align-items: center;
331
- justify-content: center;
332
- padding: 20px;
333
- background: var(--scrim);
334
- }
335
- .ov-modal {
336
- width: 100%;
337
- max-width: 480px;
338
- padding: 22px 24px 24px;
339
- background: var(--bg-card);
340
- border: var(--line) solid var(--rule);
341
- border-radius: var(--radius);
342
- }
343
- .ov-modal .scroll__h { margin-bottom: 10px; }
344
- .ov-modal p { margin: 0 0 12px; font-size: 13.5px; line-height: 1.5; color: var(--ink-soft); }
345
- .ov-modal p strong, .ov-modal p b { color: var(--ink); font-weight: 500; }
346
- .ov-modal p:last-of-type { margin-bottom: 0; }
347
- .ov-modal label:not(.ov-modal__check) { display: block; margin: 14px 0 6px; font-family: var(--label-face); font-size: 10.5px; letter-spacing: var(--label-space); text-transform: var(--label-case); color: var(--ink-faint); }
348
- .ov-modal input[type="text"], .ov-modal textarea { width: 100%; box-sizing: border-box; }
349
- .ov-modal__check { display: flex; align-items: flex-start; gap: 8px; margin: 14px 0 0; font-size: 13px; color: var(--ink); }
350
- .ov-modal__check input { margin-top: 3px; width: 16px; height: 16px; }
351
- .ov-modal__actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px; }
@@ -114,6 +114,7 @@
114
114
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
115
115
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
116
116
  <script src="/builders/shell.js?v=2026-11-07-brand-mark"></script>
117
+ <script src="/builders/hall-dialog.js"></script>
117
118
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
118
119
  <script src="/builders/palette.js" defer></script>
119
120
  <!-- the shared markdown reader (window.OTBMdReader) — primer.js renders through
@@ -250,6 +250,7 @@
250
250
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
251
251
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
252
252
  <script src="/builders/shell.js?v=2026-11-07-brand-mark"></script>
253
+ <script src="/builders/hall-dialog.js"></script>
253
254
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
254
255
  <script src="/builders/palette.js" defer></script>
255
256
  <!-- task 1004727: the ONE karma vote control every task/idea list renders -->
@@ -78,6 +78,7 @@
78
78
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
79
79
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
80
80
  <script src="/builders/shell.js?v=2026-11-07-brand-mark"></script>
81
+ <script src="/builders/hall-dialog.js"></script>
81
82
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
82
83
  <script src="/builders/palette.js" defer></script>
83
84
  <script src="/builders/hall-kit.js"></script>
@@ -60,6 +60,7 @@
60
60
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
61
61
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
62
62
  <script src="/builders/shell.js?v=2026-11-07-brand-mark"></script>
63
+ <script src="/builders/hall-dialog.js"></script>
63
64
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
64
65
  <script src="/builders/palette.js" defer></script>
65
66
  <!-- the branding pack's currency label, loaded BEFORE roster.js. -->
@@ -91,7 +91,11 @@
91
91
  }
92
92
 
93
93
  async function removeKey(provider, label) {
94
- if (!window.confirm(`Remove your ${label} key? Generations will run on ours and be charged to your balance.`)) return;
94
+ if (!(await window.OTBDialog.confirm({
95
+ title: `Remove your ${label} key?`,
96
+ body: 'Generations will run on ours and be charged to your balance.',
97
+ confirmLabel: 'Remove key', danger: true,
98
+ }))) return;
95
99
  const res = await api.request('DELETE', `${API}/ai-studio/keys/${encodeURIComponent(provider)}`);
96
100
  if (res.status === 401) { toAuth(); return; }
97
101
  if (!res.ok) { toast('Could not remove your key. Try again.'); return; }
@@ -52,7 +52,11 @@
52
52
  }
53
53
 
54
54
  async function remove(id) {
55
- if (!window.confirm('Remove this card? You can register it again later.')) return;
55
+ if (!(await window.OTBDialog.confirm({
56
+ title: 'Remove this card?',
57
+ body: 'You will no longer sign in with it. You can register it again later.',
58
+ confirmLabel: 'Remove card', danger: true,
59
+ }))) return;
56
60
  try {
57
61
  const res = await api.request('POST', `${API}/me/passkeys/remove`, { body: { credential_id: id } });
58
62
  if (!res.ok) throw fail(res);
@@ -71,7 +71,11 @@
71
71
  const row = btn.closest('.sessions-row');
72
72
  const id = row.getAttribute('data-id');
73
73
  const isCurrent = !!row.querySelector('.sessions-row__badge');
74
- if (isCurrent && !window.confirm('That is the session you are using right now. Ending it signs you out of this page.')) return;
74
+ if (isCurrent && !(await window.OTBDialog.confirm({
75
+ title: 'End this session?',
76
+ body: 'That is the session you are using right now. Ending it signs you out of this page.',
77
+ confirmLabel: 'End session', danger: true, trigger: btn,
78
+ }))) return;
75
79
  btn.disabled = true;
76
80
  try {
77
81
  const res = await api.request('POST', `${API}/me/sessions/revoke`, { body: { session_id: id } });
@@ -91,7 +95,11 @@
91
95
  // then sends the builder back to sign-in rather than leaving a dead page up.
92
96
  async function endAll() {
93
97
  const btn = $('#sessions-revoke-all');
94
- if (!window.confirm('End every session, including this one? You will need to sign in again, and any CLI token will need re-issuing.')) return;
98
+ if (!(await window.OTBDialog.confirm({
99
+ title: 'End every session, including this one?',
100
+ body: 'You will need to sign in again, and any CLI token will need re-issuing.',
101
+ confirmLabel: 'Sign out everywhere', danger: true, trigger: btn,
102
+ }))) return;
95
103
  btn.disabled = true;
96
104
  try {
97
105
  const res = await api.request('POST', `${API}/me/sessions/revoke-all`, { body: {} });
@@ -395,6 +395,7 @@
395
395
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
396
396
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
397
397
  <script src="/builders/shell.js?v=2026-11-07-brand-mark"></script>
398
+ <script src="/builders/hall-dialog.js"></script>
398
399
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
399
400
  <script src="/builders/palette.js" defer></script>
400
401
  <!-- shared component kit (window.OTBKit) — section rail (task 2166). -->
@@ -814,7 +814,11 @@
814
814
 
815
815
  async function removeArtKey() {
816
816
  const status = $('#artkey-status');
817
- if (!window.confirm('Remove your image-generation key? Art generation will stop until you add one again.')) return;
817
+ if (!(await window.OTBDialog.confirm({
818
+ title: 'Remove your image-generation key?',
819
+ body: 'Art generation will stop until you add one again.',
820
+ confirmLabel: 'Remove key', danger: true,
821
+ }))) return;
818
822
  if (status) status.textContent = 'Removing…';
819
823
  try {
820
824
  const res = await api.request('DELETE', `${API}/me/art-key/own`);
@@ -232,6 +232,7 @@
232
232
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
233
233
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
234
234
  <script src="/builders/shell.js?v=2026-11-07-brand-mark"></script>
235
+ <script src="/builders/hall-dialog.js"></script>
235
236
  <script src="/builders/palette.js" defer></script>
236
237
  <script src="/builders/gov-living.js?v=2026-10-09-positions1"></script>
237
238
  <script src="/builders/gov-feeds.js?v=2026-10-09-living3"></script>
@@ -73,7 +73,6 @@ body[data-page="studio"] {
73
73
  --st-panel: rgba(255, 255, 255, .075);
74
74
  --st-panel-edge: rgba(255, 255, 255, .18);
75
75
  --st-panel-rim: rgba(255, 255, 255, .22);
76
- --st-panel-drop: rgba(0, 0, 0, .55);
77
76
  --st-close: rgba(255, 255, 255, .08);
78
77
  --st-chip-edge: rgba(255, 255, 255, .16);
79
78
  --st-track: rgba(255, 255, 255, .1);
@@ -126,7 +125,6 @@ body[data-page="studio"][data-sky="day"] {
126
125
  --st-panel: rgba(255, 255, 255, .75);
127
126
  --st-panel-edge: rgba(60, 40, 20, .16);
128
127
  --st-panel-rim: rgba(255, 255, 255, .9);
129
- --st-panel-drop: rgba(60, 40, 20, .18);
130
128
  --st-close: rgba(60, 40, 20, .08);
131
129
  --st-chip-edge: rgba(60, 40, 20, .2);
132
130
  --st-track: rgba(60, 40, 20, .12);
@@ -150,7 +148,6 @@ body[data-page="studio"][data-sky="day"] .aq-overlay {
150
148
  --aq-glass: rgba(255, 255, 255, .75);
151
149
  --aq-glass-edge: rgba(60, 40, 20, .16);
152
150
  --aq-glass-rim: rgba(255, 255, 255, .9);
153
- --aq-drop: rgba(60, 40, 20, .18);
154
151
  --aq-ink: #1b1a1d;
155
152
  --aq-ink-soft: #4d4c52;
156
153
  --aq-ink-faint: #5a595f;
@@ -597,20 +594,13 @@ html.st-reading .st-glance { display: none; }
597
594
  page), and .88 on a phone.
598
595
  --------------------------------------------------------------------------- */
599
596
  .st-overlay {
600
- position: fixed;
601
- inset: 0;
602
- z-index: var(--z-modal, 160);
603
- display: flex;
604
- align-items: center;
605
- justify-content: center;
606
- padding: 24px;
607
- box-sizing: border-box;
597
+ /* the layer, the placement and the gutter are the shared .hall-scrim's
598
+ (controls.css, task 1005085); only the sky's tint is its own */
608
599
  background: var(--st-scrim);
609
600
  color: var(--st-ink);
610
601
  font-family: var(--st-face);
611
602
  color-scheme: dark;
612
603
  }
613
- .st-overlay[hidden] { display: none; }
614
604
  html[data-theme="dark"] body[data-page="studio"] { --st-scrim: rgba(4, 5, 9, .42); }
615
605
  @media (prefers-color-scheme: dark) {
616
606
  html:not([data-theme]) body[data-page="studio"] { --st-scrim: rgba(4, 5, 9, .42); }
@@ -627,7 +617,7 @@ html.st-open { overflow: hidden; }
627
617
  border: 1px solid var(--st-panel-edge);
628
618
  backdrop-filter: blur(30px) saturate(160%);
629
619
  -webkit-backdrop-filter: blur(30px) saturate(160%);
630
- box-shadow: inset 0 1px 0 var(--st-panel-rim), 0 40px 90px var(--st-panel-drop);
620
+ box-shadow: inset 0 1px 0 var(--st-panel-rim), var(--ds-shadow-pop);
631
621
  display: flex;
632
622
  flex-direction: column;
633
623
  gap: 22px;
@@ -797,28 +787,12 @@ html.st-open { overflow: hidden; }
797
787
  tokens, in a dialog that opens from the quiet door above.
798
788
  =========================================================================== */
799
789
  .st-reviews {
800
- position: fixed;
801
- inset: 0;
802
- z-index: var(--z-modal, 160);
803
- display: flex;
804
- align-items: center;
805
- justify-content: center;
806
- padding: 24px;
807
- box-sizing: border-box;
808
- background: var(--scrim);
790
+ /* the shared .hall-scrim (controls.css, task 1005085) is the layer and the
791
+ scrim; the panel inside is the shared .hall-dialog */
809
792
  color: var(--ink);
810
793
  font-family: var(--font-body);
811
794
  }
812
- .st-reviews[hidden] { display: none; }
813
- .st-reviews__dialog {
814
- width: min(780px, 100%);
815
- max-height: 100%;
816
- overflow-y: auto;
817
- box-sizing: border-box;
818
- padding: clamp(18px, 3vw, 28px);
819
- border-radius: var(--radius);
820
- background: var(--bg);
821
- }
795
+ .st-reviews__dialog { width: min(780px, 100%); }
822
796
  .st-reviews__head { display: flex; align-items: center; gap: 12px; margin-bottom: 6px; }
823
797
  .st-reviews__head .btn-ghost { margin-left: auto; }
824
798
  .st-reviews__title {
@@ -177,7 +177,7 @@
177
177
  <!-- The two small glass panels: the Tweak/CopyWrite Flow and Artist
178
178
  Review Status. The Approval queue is its own component
179
179
  (approval-queue.js), mounted by studio.js. -->
180
- <div class="st-overlay" id="st-panel" hidden>
180
+ <div class="hall-scrim st-overlay" id="st-panel" hidden>
181
181
  <div class="st-dialog" role="dialog" aria-modal="true" aria-labelledby="st-panel-title">
182
182
  <div class="st-dialog__head">
183
183
  <h2 class="st-dialog__title" id="st-panel-title" tabindex="-1">Work through tweaks</h2>
@@ -189,8 +189,8 @@
189
189
 
190
190
  <!-- The reviews filed before Tweak Mode (R04, retired in TW14): the
191
191
  garden, one piece at a time, answered here until none remain. -->
192
- <div class="st-reviews" id="st-reviews" hidden>
193
- <div class="st-reviews__dialog" role="dialog" aria-modal="true" aria-labelledby="st-reviews-title">
192
+ <div class="hall-scrim st-reviews" id="st-reviews" hidden>
193
+ <div class="hall-dialog hall-dialog--wide st-reviews__dialog" role="dialog" aria-modal="true" aria-labelledby="st-reviews-title">
194
194
  <div class="st-reviews__head">
195
195
  <h2 class="st-reviews__title" id="st-reviews-title" tabindex="-1">Reviews from before Tweak Mode</h2>
196
196
  <button type="button" class="btn-ghost" data-close-reviews>close</button>
@@ -226,6 +226,7 @@
226
226
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
227
227
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
228
228
  <script src="/builders/shell.js?v=2026-11-07-brand-mark"></script>
229
+ <script src="/builders/hall-dialog.js"></script>
229
230
  <script src="/builders/palette.js" defer></script>
230
231
  <!-- The kit, for its ONE renderer: the full-size ship-time visual figure
231
232
  (task 1003829), which the older reviews still show. -->
@@ -821,27 +821,33 @@
821
821
  body.innerHTML = home.panel === 'flow' ? flowHtml() : home.panel === 'site' ? siteHtml() : statusHtml();
822
822
  }
823
823
 
824
- function openPanel(kind, opener) {
824
+ // The two panels run on the hall's one dialog behaviour (hall-dialog.js, task
825
+ // 1005085): Escape closes the top one only, Tab stays inside, a click on the
826
+ // scrim closes, the scroll locks, and focus goes back to the door that opened it.
827
+ function panelModal() {
825
828
  const overlay = byId('st-panel');
826
- if (!overlay || !TITLES[kind]) return;
829
+ if (!overlay) return null;
830
+ return window.OTBDialog.bind(overlay, {
831
+ panel: overlay.querySelector('[role="dialog"]'),
832
+ lockClass: 'st-open',
833
+ initialFocus: () => byId('st-panel-title'),
834
+ onClose: () => { home.panel = null; },
835
+ });
836
+ }
837
+
838
+ function openPanel(kind, opener) {
839
+ const m = panelModal();
840
+ if (!m || !TITLES[kind]) return;
827
841
  home.panel = kind;
828
- home.opener = opener || null;
829
842
  home.picking = false;
830
843
  setText('st-panel-title', TITLES[kind]);
831
844
  paintPanel();
832
- overlay.hidden = false;
833
- document.documentElement.classList.add('st-open');
834
- const title = byId('st-panel-title');
835
- if (title) title.focus();
845
+ m.open(opener || null);
836
846
  }
837
847
 
838
848
  function closePanel() {
839
- const overlay = byId('st-panel');
840
- if (!overlay || overlay.hidden) return;
841
- overlay.hidden = true;
842
- home.panel = null;
843
- document.documentElement.classList.remove('st-open');
844
- if (home.opener && home.opener.focus) home.opener.focus();
849
+ const m = panelModal();
850
+ if (m) m.close();
845
851
  }
846
852
 
847
853
  // Release a round the reads cannot name (task 1004837). One line of why is
@@ -875,24 +881,29 @@
875
881
 
876
882
  // ---- the older reviews' dialog --------------------------------------------
877
883
 
878
- function openReviews(opener) {
884
+ function reviewsModal() {
879
885
  const box = byId('st-reviews');
880
- if (!box) return;
881
- home.opener = opener || null;
882
- box.hidden = false;
883
- document.documentElement.classList.add('st-open');
884
- const title = byId('st-reviews-title');
885
- if (title) title.focus();
886
+ if (!box) return null;
887
+ return window.OTBDialog.bind(box, {
888
+ panel: box.querySelector('[role="dialog"]'),
889
+ lockClass: 'st-open',
890
+ initialFocus: () => byId('st-reviews-title'),
891
+ // the door hides once the last review is answered: fall back to the flow door
892
+ returnFocus: (opener) => {
893
+ const door = byId('st-older');
894
+ return opener && !opener.hidden ? opener : (door && !door.hidden ? door : byId('st-flow'));
895
+ },
896
+ });
897
+ }
898
+
899
+ function openReviews(opener) {
900
+ const m = reviewsModal();
901
+ if (m) m.open(opener || null);
886
902
  }
887
903
 
888
904
  function closeReviews() {
889
- const box = byId('st-reviews');
890
- if (!box || box.hidden) return;
891
- box.hidden = true;
892
- document.documentElement.classList.remove('st-open');
893
- const door = byId('st-older');
894
- const back = home.opener && !home.opener.hidden ? home.opener : (door && !door.hidden ? door : byId('st-flow'));
895
- if (back && back.focus) back.focus();
905
+ const m = reviewsModal();
906
+ if (m) m.close();
896
907
  }
897
908
 
898
909
  // ---- the studio's own words, typed over in place ---------------------------
@@ -1072,7 +1083,7 @@
1072
1083
  const overlay = byId('st-panel');
1073
1084
  if (overlay) {
1074
1085
  overlay.addEventListener('click', (ev) => {
1075
- if (ev.target === overlay || ev.target.closest('[data-close-panel]')) { closePanel(); return; }
1086
+ if (ev.target.closest('[data-close-panel]')) { closePanel(); return; }
1076
1087
  const review = ev.target.closest('[data-review-page]');
1077
1088
  if (review && approvals) { ev.preventDefault(); const id = review.getAttribute('data-review-page'); closePanel(); approvals.open(id); return; }
1078
1089
  const rel = ev.target.closest('[data-release-page]');
@@ -1087,15 +1098,9 @@
1087
1098
  const reviews = byId('st-reviews');
1088
1099
  if (reviews) {
1089
1100
  reviews.addEventListener('click', (ev) => {
1090
- if (ev.target === reviews || ev.target.closest('[data-close-reviews]')) closeReviews();
1101
+ if (ev.target.closest('[data-close-reviews]')) closeReviews();
1091
1102
  });
1092
1103
  }
1093
- document.addEventListener('keydown', (ev) => {
1094
- if (ev.key !== 'Escape') return;
1095
- if (home.panel) { closePanel(); return; }
1096
- const box = byId('st-reviews');
1097
- if (box && !box.hidden) closeReviews();
1098
- });
1099
1104
 
1100
1105
  const hint = byId('st-hint');
1101
1106
  if (hint) hint.addEventListener('click', () => beginWords(null));
@@ -4604,14 +4604,8 @@ body[data-page="home"] #home-stats .stat-row { border-bottom-left-radius: 0; bor
4604
4604
  .attn-card__n { font-family: var(--font-display); font-size: 30px; font-weight: 300; line-height: 1; font-variant-numeric: tabular-nums; }
4605
4605
  .attn-card__t { font-weight: 600; }
4606
4606
  .attn-card__s { margin-top: auto; font-size: 12px; color: var(--ink-soft); }
4607
- .attn-dialog {
4608
- width: min(640px, calc(100vw - 32px)); max-height: 80vh; padding: 22px 24px; overflow: auto;
4609
- background: var(--bg-card); color: var(--ink); border: 0; border-radius: var(--radius);
4610
- }
4611
- .attn-dialog::backdrop { background: var(--scrim); }
4612
- .attn-dialog__head { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }
4613
- .attn-dialog__h { flex: 1; margin: 0; font-family: var(--font-display); font-weight: 500; font-size: 20px; }
4614
- .attn-dialog__x { min-width: 32px; min-height: 32px; background: none; border: 0; color: var(--ink-soft); font-size: 22px; cursor: pointer; }
4607
+ /* The window a card opens is the hall's one dialog (controls.css .hall-dialog,
4608
+ task 1005085); the slice's own heading is the dialog's title, so it hides. */
4615
4609
  .attn-dialog__body .ginbox__sec-h { display: none; }
4616
4610
 
4617
4611
  .home-split { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; align-items: start; }
@@ -60,6 +60,7 @@
60
60
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
61
61
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
62
62
  <script src="/builders/shell.js?v=2026-11-07-brand-mark"></script>
63
+ <script src="/builders/hall-dialog.js"></script>
63
64
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
64
65
  <script src="/builders/palette.js" defer></script>
65
66
  <!-- task-detail.js still owns the #/idea/N and #/blocker/N deep-link overlay
@@ -173,6 +173,19 @@
173
173
  }
174
174
  }
175
175
 
176
+ // Confirm a task. A verify-kind task's confirm attests the verification
177
+ // actually passed (the server requires { verified: true }), so the Archon
178
+ // affirms it in the hall's own confirm first (task 1005085).
179
+ async function confirmTask(t, btn) {
180
+ if (t.kind === 'verify' && !(await window.OTBDialog.confirm({
181
+ title: `Confirm that verification for #${t.id} actually passed?`,
182
+ body: 'The task is recorded as confirmed, with your word that its verification passed.',
183
+ confirmLabel: 'It passed', trigger: btn,
184
+ }))) return;
185
+ const body = t.kind === 'verify' ? { verified: true } : {};
186
+ runLifecycle(btn, `${API}/tasks/${encodeURIComponent(t.id)}/confirm`, body, `#${t.id} confirmed.`);
187
+ }
188
+
176
189
  // The two lifecycle DECISIONS (ship, confirm) as the city's ask card
177
190
  // (hall-ask.js; task 1004822, criterion wa6-hall-reads-like-home). Only a
178
191
  // decision the page waits on gets the card — claim, copy and retire stay plain
@@ -898,13 +911,7 @@
898
911
  }
899
912
  const confirmBtn = $('#confirm-btn');
900
913
  if (confirmBtn) {
901
- confirmBtn.addEventListener('click', () => {
902
- // A verify-kind task's confirm attests the verification actually passed
903
- // (the server requires { verified: true }); make the Archon affirm it.
904
- if (t.kind === 'verify' && !window.confirm(`Confirm that verification for #${t.id} actually passed?`)) return;
905
- const body = t.kind === 'verify' ? { verified: true } : {};
906
- runLifecycle(confirmBtn, `${API}/tasks/${encodeURIComponent(t.id)}/confirm`, body, `#${t.id} confirmed.`);
907
- });
914
+ confirmBtn.addEventListener('click', () => confirmTask(t, confirmBtn));
908
915
  }
909
916
 
910
917
  // task 1003063 (R10): PATCH goal_id, then a full reload so the goal line, the
@@ -995,13 +1002,20 @@
995
1002
  // reason is the record noting that someone did this and declining to say
996
1003
  // why. Asking costs one line.
997
1004
  if (!reason) { toast('Say why it is being retired — the reason goes in the record.'); return; }
998
- if (!window.confirm(`Retire task ${t.id}? It leaves the claimable queue. Promote restores it to the backlog.`)) return;
999
- abandonOpen = false;
1000
- runWrite(abandonBtn,
1001
- () => postJSON(`${API}/tasks/${encodeURIComponent(t.id)}/abandon`, { reason }),
1002
- `Task ${t.id} retired. Promote restores it to the backlog.`);
1005
+ retireTask(t, abandonBtn, reason);
1003
1006
  });
1004
1007
  }
1008
+ async function retireTask(task, btn, reason) {
1009
+ if (!(await window.OTBDialog.confirm({
1010
+ title: `Retire task ${task.id}?`,
1011
+ body: 'It leaves the claimable queue. Promote restores it to the backlog.',
1012
+ confirmLabel: 'Retire task', danger: true, trigger: btn,
1013
+ }))) return;
1014
+ abandonOpen = false;
1015
+ runWrite(btn,
1016
+ () => postJSON(`${API}/tasks/${encodeURIComponent(task.id)}/abandon`, { reason }),
1017
+ `Task ${task.id} retired. Promote restores it to the backlog.`);
1018
+ }
1005
1019
 
1006
1020
  const moveGoalBtn = $('#move-goal-btn');
1007
1021
  if (moveGoalBtn) {
@@ -142,6 +142,7 @@
142
142
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
143
143
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
144
144
  <script src="/builders/shell.js?v=2026-11-07-brand-mark"></script>
145
+ <script src="/builders/hall-dialog.js"></script>
145
146
  <script src="/builders/palette.js" defer></script>
146
147
 
147
148
  <script src="/builders/idea-objects.js?v=2026-09-27-split"></script>
@@ -146,6 +146,7 @@
146
146
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
147
147
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
148
148
  <script src="/builders/shell.js?v=2026-11-07-brand-mark"></script>
149
+ <script src="/builders/hall-dialog.js"></script>
149
150
  <script src="/builders/palette.js" defer></script>
150
151
  <script src="/builders/brand-holes.js?v=2026-09-29-aqfix"></script>
151
152
  <script src="/builders/tweak-editor-lib.js?v=2026-09-29-tw27"></script>
@@ -272,6 +272,7 @@
272
272
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
273
273
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
274
274
  <script src="/builders/shell.js?v=2026-11-07-brand-mark"></script>
275
+ <script src="/builders/hall-dialog.js"></script>
275
276
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
276
277
  <script src="/builders/palette.js" defer></script>
277
278
  <!-- shared component kit (window.OTBKit) — tabs, ledger, pager (task 2166). -->
@@ -175,6 +175,7 @@
175
175
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
176
176
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
177
177
  <script src="/builders/shell.js?v=2026-11-07-brand-mark"></script>
178
+ <script src="/builders/hall-dialog.js"></script>
178
179
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
179
180
  <script src="/builders/palette.js" defer></script>
180
181
  <!-- task 1004727: the ONE karma vote control every task/idea list renders -->
@@ -759,8 +759,14 @@
759
759
  async function doRelease(claimId, taskId, btn) {
760
760
  const key = `release:${claimId}`;
761
761
  if (pending.has(key)) return;
762
- if (!window.confirm(`Release your claim on #${taskId}? It returns to the queue and no ${curLabel()} are earned.`)) return;
762
+ // Held while the confirm is open too, so a second click cannot stack a second dialog.
763
763
  pending.add(key);
764
+ const yes = await window.OTBDialog.confirm({
765
+ title: `Release your claim on #${taskId}?`,
766
+ body: `It returns to the queue and no ${curLabel()} are earned.`,
767
+ confirmLabel: 'Release', danger: true, trigger: btn,
768
+ });
769
+ if (!yes) { pending.delete(key); return; }
764
770
  if (btn) { btn.disabled = true; btn.textContent = 'Releasing…'; }
765
771
  try {
766
772
  const out = await claimAction.release(claimId, taskId);
@@ -320,10 +320,10 @@ h2{font-family:var(--font-display);font-weight:300;font-size:clamp(26px,3.1vw,42
320
320
  .figline:empty ~ .meter,.figline:empty ~ .sub{display:none}
321
321
  .rows:empty{display:none}
322
322
 
323
- /* verro labs: medallion, wordmark, one paragraph, no frame */
323
+ /* the maker's band (the pack's company): medallion, wordmark, one paragraph, no frame */
324
324
  .verro{display:grid;grid-template-columns:auto 1fr;gap:clamp(26px,4vw,58px);align-items:start}
325
325
  .verro img{width:clamp(92px,9vw,124px);height:auto}
326
- .wordmark{font-family:var(--font-mono);font-size:13px;letter-spacing:.3em;color:var(--ink)}
326
+ .wordmark{font-family:var(--font-mono);font-size:13px;letter-spacing:.3em;text-transform:uppercase;color:var(--ink)}
327
327
  .verro p.body{margin-top:18px;font-size:17.5px;line-height:1.55;color:var(--ink-soft);max-width:62ch}
328
328
 
329
329
  /* the basics: two columns, never three */
@@ -781,7 +781,7 @@ composition A; landing composition v3, picked by the owner 2026-08-27. No roll,
781
781
  <section class="band">
782
782
  <div class="wrap">
783
783
  <h2>Built on Bongos.</h2>
784
- <p class="sub">At present, all Bongos projects are held by Verro Labs or its pilot clients.</p>
784
+ <!--brand-if:companyName--><p class="sub">At present, all Bongos projects are held by {{companyName}} or its pilot clients.</p><!--/brand-if:companyName-->
785
785
  <ul class="orbs" data-orbs>
786
786
  <li class="orb-item o1">
787
787
  <span class="orb-bead" aria-hidden="true"></span>
@@ -885,16 +885,19 @@ composition A; landing composition v3, picked by the owner 2026-08-27. No roll,
885
885
  </section>
886
886
 
887
887
  <!--apex-only:start-->
888
- <section class="band">
888
+ <!-- The maker's band (task 1005091): the company is the pack's (identity.company), never
889
+ a name written into the portable landing, and the band drops whole when the pack
890
+ names none. The medallion is the hub's own asset. -->
891
+ <!--brand-if:companyName--><section class="band">
889
892
  <div class="wrap verro">
890
893
  <img src="/assets/verro-medallion.png" width="384" height="384" loading="lazy" decoding="async"
891
- alt="The Verro Labs mark: a galaxy and constellation above a river valley threaded with circuitry">
894
+ alt="The {{companyName}} mark">
892
895
  <div>
893
- <p class="wordmark">VERRO LABS</p>
894
- <p class="body">Built by Verro Labs, Cloud Bongos is our way of making AI co-building usable by entire project groups, whether that's a 5,000-person company, a 100-person department, a college research project, or a solo dev.</p>
896
+ <p class="wordmark">{{companyName}}</p>
897
+ <p class="body">Built by {{companyName}}, {{productName}} is our way of making AI co-building usable by entire project groups, whether that's a 5,000-person company, a 100-person department, a college research project, or a solo dev.</p>
895
898
  </div>
896
899
  </div>
897
- </section>
900
+ </section><!--/brand-if:companyName-->
898
901
  <!--apex-only:end-->
899
902
 
900
903
  <!--apex-only:start-->
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.24.51",
3
+ "version": "1.24.53",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.24.51",
9
+ "version": "1.24.53",
10
10
  "license": "AGPL-3.0-or-later",
11
11
  "dependencies": {
12
12
  "express": "^4.21.2",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.24.51",
3
+ "version": "1.24.53",
4
4
  "description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "main": "src/platform-server.js",