@bongos/core 1.24.83 → 1.24.84

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 (49) hide show
  1. package/.bongos-core.json +53 -53
  2. package/docs/copy-inventory.md +2 -2
  3. package/docs/copy-registry.json +2 -2
  4. package/docs/module-api-changelog.md +2 -0
  5. package/docs/page-readings.json +33 -33
  6. package/modules/hall-ui/public/ai-studio.html +1 -1
  7. package/modules/hall-ui/public/atlas.html +1 -1
  8. package/modules/hall-ui/public/blockers.html +1 -1
  9. package/modules/hall-ui/public/board-room.html +1 -1
  10. package/modules/hall-ui/public/city.css +3 -2
  11. package/modules/hall-ui/public/city.html +4 -4
  12. package/modules/hall-ui/public/collab.html +1 -1
  13. package/modules/hall-ui/public/commands.html +1 -1
  14. package/modules/hall-ui/public/copy-desk.html +1 -1
  15. package/modules/hall-ui/public/deploy.html +1 -1
  16. package/modules/hall-ui/public/goal-map.html +3 -3
  17. package/modules/hall-ui/public/goals.html +1 -1
  18. package/modules/hall-ui/public/government.html +1 -1
  19. package/modules/hall-ui/public/home-city.css +0 -4
  20. package/modules/hall-ui/public/idea.html +1 -1
  21. package/modules/hall-ui/public/ideas.html +1 -1
  22. package/modules/hall-ui/public/index.html +4 -4
  23. package/modules/hall-ui/public/market-watch-item.html +1 -1
  24. package/modules/hall-ui/public/market-watch-rules.html +1 -1
  25. package/modules/hall-ui/public/market-watch.html +1 -1
  26. package/modules/hall-ui/public/modules.html +1 -1
  27. package/modules/hall-ui/public/money.html +1 -1
  28. package/modules/hall-ui/public/my-agents.html +1 -1
  29. package/modules/hall-ui/public/primer.html +1 -1
  30. package/modules/hall-ui/public/profile.html +1 -1
  31. package/modules/hall-ui/public/project-settings.html +1 -1
  32. package/modules/hall-ui/public/roster.html +1 -1
  33. package/modules/hall-ui/public/settings.html +1 -1
  34. package/modules/hall-ui/public/street.html +1 -1
  35. package/modules/hall-ui/public/studio.html +1 -1
  36. package/modules/hall-ui/public/style.css +21 -11
  37. package/modules/hall-ui/public/task.html +1 -1
  38. package/modules/hall-ui/public/thinking.html +3 -3
  39. package/modules/hall-ui/public/tweak-editor.html +1 -1
  40. package/modules/hall-ui/public/visor.css +108 -47
  41. package/modules/hall-ui/public/visor.js +50 -33
  42. package/modules/hall-ui/public/watch.html +1 -1
  43. package/modules/hall-ui/public/work.html +1 -1
  44. package/modules/hall-ui/records/visor.md +14 -2
  45. package/package-lock.json +2 -2
  46. package/package.json +1 -1
  47. package/release-notes.json +6 -0
  48. package/src/module-api.js +1 -1
  49. package/tests/hall_visor.mjs +128 -10
@@ -8,7 +8,7 @@
8
8
  <link rel="icon" href="/modules/ui-design/public/brand/favicon.svg" type="image/svg+xml">
9
9
  <link rel="preconnect" href="https://fonts.googleapis.com">
10
10
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
- <link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm2">
11
+ <link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm3">
12
12
  <!-- task 1003441: the identity cell (avatar, name, login). It was shared with
13
13
  the cross-project /people page until task 1003444 deleted it. -->
14
14
  <link rel="stylesheet" href="/builders/roster.css">
@@ -11,7 +11,7 @@
11
11
  <link rel="icon" href="/modules/ui-design/public/brand/favicon.svg" type="image/svg+xml">
12
12
  <link rel="preconnect" href="https://fonts.googleapis.com">
13
13
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
14
- <link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm2">
14
+ <link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm3">
15
15
  <!-- The PANEL archetype (task 1003308): the row, the switch, the field, the
16
16
  chip control and the family's voices, shared with profile and pair.
17
17
  settings.css then holds only what this page
@@ -10,7 +10,7 @@
10
10
  <!-- The faces (Manrope, JetBrains Mono) come from the branding pack's font
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
- <link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm2">
13
+ <link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm3">
14
14
  <link rel="stylesheet" href="/builders/street.css?v=2026-11-11-helm">
15
15
  <link rel="stylesheet" href="/builders/street-card.css?v=2026-10-04-record1">
16
16
  <link rel="stylesheet" href="/builders/street-deck.css?v=2026-10-17-tidy">
@@ -12,7 +12,7 @@
12
12
  <link rel="icon" href="/modules/ui-design/public/brand/favicon.svg" type="image/svg+xml">
13
13
  <link rel="preconnect" href="https://fonts.googleapis.com">
14
14
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
15
- <link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm2">
15
+ <link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm3">
16
16
  <link rel="stylesheet" href="/builders/studio.css?v=2026-10-09-day">
17
17
  <link rel="stylesheet" href="/builders/approval-queue.css?v=2026-09-29-aqfix">
18
18
  <link rel="stylesheet" href="/builders/home-gallery.css?v=2026-11-04-studio">
@@ -186,7 +186,7 @@
186
186
  seventy rules across the page sheets spell it, but in the hall it is a
187
187
  square corner: a field, a tag, a chip, a toggle and a toast are square
188
188
  inside one hairline, and only what you press is cut (--cut-path). A thing
189
- that must be round (a portrait in the visor) says --ds-radius-pill. */
189
+ that must be round says --ds-radius-pill. */
190
190
  --radius-pill: 0px;
191
191
 
192
192
  /* ---- the shell's metrics. --topbar-h stays under the kit's 80px top-bar
@@ -790,16 +790,26 @@ html[data-theme="dark"] .brand-mark__mark { --mark-ink: #e2e1e4; }
790
790
  .founding-band { flex-wrap: wrap; row-gap: 2px; padding: 8px 16px; }
791
791
  .founding-band__desc { display: none; }
792
792
  .founding-band__stage { margin-left: 0; white-space: normal; }
793
- /* task 1005148, the visor (visor.css): the top of a phone belongs to the scene and the shell’s
794
- corner buttons, so the compass and the place line step aside and all three pills share the
795
- bottom row: the main action, Actions (its dots and key only), the sprite with the credits. */
796
- html[data-game="on"] .visor:not([hidden]) { --visor-inset-right: var(--ds-space-gutter-narrow); --visor-inset-left: var(--ds-space-gutter-narrow); --visor-inset-bottom: 40px; }
797
- html[data-game="on"] .visor__place, html[data-game="on"] .visor__cell, html[data-game="on"] .visor__mark { display: none; }
798
- html[data-game="on"] .visor__pill--actions { top: auto; right: auto; left: 50%; bottom: var(--visor-inset-bottom); transform: translateX(-50%); padding: 0 var(--ds-space-xs); }
799
- html[data-game="on"] .visor__pill--actions > span:not(.visor__dots) { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
800
- html[data-game="on"] .visor__sheet { top: auto; bottom: calc(var(--visor-inset-bottom) + 56px); right: var(--visor-inset-right); max-height: 70vh; }
801
- html[data-game="on"] .visor__me .visor__name { display: none; }
802
- html[data-game="on"] .visor__me { padding: 4px var(--ds-space-xs) 4px 4px; }
793
+ /* tasks 1005148 and 1005195, the visor (visor.css): the top of a phone belongs to the scene
794
+ and the shell’s corner buttons, so the top arc, its notch and the place all step aside. The foot row keeps its three parts in one line and nothing leans: the sprite
795
+ with the count over its word, the one accent button (its name may take two lines; its key is in the sheet), and of
796
+ the key hints only Actions, as its dots (every other hint is a row in its sheet). */
797
+ html[data-game="on"] #visor { --visor-inset-bottom: 52px; }
798
+ html[data-game="on"] .visor__place, html[data-game="on"] .visor__notch, html[data-game="on"] .visor__frame--top, html[data-game="on"] .visor__name { display: none; }
799
+ html[data-game="on"] .visor__foot { grid-template-columns: auto minmax(0, 1fr) auto; column-gap: var(--ds-space-hair); }
800
+ html[data-game="on"] .visor__me, html[data-game="on"] .visor__hints { transform: none; }
801
+ html[data-game="on"] .visor__me { gap: var(--ds-space-hair); }
802
+ html[data-game="on"] .visor__sprite { width: 36px; height: 36px; }
803
+ html[data-game="on"] .visor__num { font-size: var(--ds-text-4xl); }
804
+ html[data-game="on"] .visor__credits { flex-direction: column; align-items: flex-start; gap: 2px; }
805
+ html[data-game="on"] .visor__unit { font-size: var(--ds-text-2xs); line-height: 1; }
806
+ html[data-game="on"] .visor__main { min-width: 0; overflow-wrap: anywhere; min-height: 48px; padding: var(--ds-space-hair) var(--ds-space-xs); gap: var(--ds-space-hair); font-size: var(--ds-text-xl); clip-path: var(--cut-path-sm); }
807
+ html[data-game="on"] .visor__hint:not([data-visor-sheet]) { display: none; }
808
+ html[data-game="on"] .visor__hint--sheet { min-width: 44px; justify-content: center; }
809
+ html[data-game="on"] .visor__hint--sheet .visor__dots { display: block; }
810
+ html[data-game="on"] .visor__hint--sheet > :not(.visor__dots) { position: absolute; width: 1px; height: 1px; padding: 0; border: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
811
+ html[data-game="on"] .visor__main .visor__key { display: none; }
812
+ html[data-game="on"] .visor__sheet { bottom: calc(var(--visor-inset-bottom) + 80px); max-height: 70vh; }
803
813
  }
804
814
  /* task 1004424 (BV2.PS10): THE FOUNDING MARK. A pill in .rank-badge's grammar (same
805
815
  height, type and radius) so it sits beside a rank without competing with it; the
@@ -8,7 +8,7 @@
8
8
  <link rel="icon" href="/modules/ui-design/public/brand/favicon.svg" type="image/svg+xml">
9
9
  <link rel="preconnect" href="https://fonts.googleapis.com">
10
10
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
- <link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm2">
11
+ <link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm3">
12
12
  <!-- The record archetype (shared with idea.html) and this page's own two
13
13
  shapes. Promoted out of an inline <style> by task 1003305 — the block was
14
14
  the hall's last substantive one and sat at its hall_audit ratchet. -->
@@ -12,8 +12,8 @@
12
12
  links, injected into this head by the server like every hall page's
13
13
  (tests/hall_tokens.mjs: no hall page hardcodes a web-font stylesheet).
14
14
  The template carried its own Google Fonts link for the same two faces. -->
15
- <link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm2">
16
- <link rel="stylesheet" href="/builders/visor.css?v=2026-11-11-helm2">
15
+ <link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm3">
16
+ <link rel="stylesheet" href="/builders/visor.css?v=2026-11-11-helm3">
17
17
  <link rel="stylesheet" href="/builders/thinking.css?v=2026-11-10-visor">
18
18
  </head>
19
19
  <!-- THE SKY (task 1004231 / sky part 3 of 5, goal 1000095; ADR 0331, ADR 0227
@@ -146,7 +146,7 @@
146
146
  <script src="/builders/shell.js?v=2026-11-11-view2"></script>
147
147
  <script src="/builders/hall-dialog.js"></script>
148
148
  <!-- task 1005148: the visor (glass over the scene, game mode only; ADR 0399), after the shell and hall-dialog.js -->
149
- <script src="/builders/visor.js?v=2026-11-11-view2"></script>
149
+ <script src="/builders/visor.js?v=2026-11-11-helm3"></script>
150
150
  <script src="/builders/palette.js" defer></script>
151
151
 
152
152
  <script src="/builders/idea-objects.js?v=2026-09-27-split"></script>
@@ -10,7 +10,7 @@
10
10
  <link rel="icon" href="/modules/ui-design/public/brand/favicon.svg" type="image/svg+xml">
11
11
  <link rel="preconnect" href="https://fonts.googleapis.com">
12
12
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
13
- <link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm2">
13
+ <link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm3">
14
14
  <link rel="stylesheet" href="/builders/tweak-editor.css?v=2026-09-29-tw27-aqfix">
15
15
  </head>
16
16
  <!-- THE TWEAK EDITOR (task 1004321 / BV2.TW10, goal 1000095; ADR 0341 D4 and
@@ -1,80 +1,141 @@
1
- /* visor.css — the first-person visor (task 1005148, visor.js; ADR 0399). Glass over the scene:
2
- the frame lines, the place, three pills and the actions sheet. It shows in game mode only, and
3
- it is an overlay, never layout, so the stage under it never changes size (ADR 0399 §2.1).
4
- Colours are the hall's tokens; the frame is hairline ink, never a glow (DESIGN.md: no
5
- near-black-plus-neon HUD). Record: modules/hall-ui/records/visor.md. */
1
+ /* visor.css — the first-person visor (task 1005148, visor.js; ADR 0399), in the Helm look: the
2
+ minimal visor (task 1005195, ADR 0402). Glass over the scene: two thin arcs, the place in the
3
+ name voice, and one foot row (your credits, the one accent button, a quiet row of key hints).
4
+ Hairlines, not cards: nothing on the glass is a box but the button you press and the key in
5
+ a hint. It shows in game mode only, and it is an overlay, never layout, so the stage under it
6
+ never changes size (ADR 0399 §2.1).
7
+ IT READS OVER ANY SKY, with no per-hour style: the glass darkens toward its top and bottom
8
+ edge (--night-glass), every word wears the type halo (--type-halo), every line is drawn over a
9
+ wider stroke of the night and so is every letter, and a key sits on a chip of the night. Colours, sizes and corners
10
+ are the hall tier's tokens; nothing here is typed in. Record: modules/hall-ui/records/visor.md. */
6
11
  .visor { display: none; }
7
12
  html[data-game="on"] .visor:not([hidden]) {
8
13
  display: block; position: fixed; inset: 0; z-index: 4; pointer-events: none;
9
14
  /* where the glass sits, so a scene's pop-up can keep clear of it (ADR 0399 §2.3) */
10
- --visor-inset-top: 64px; --visor-inset-right: 40px; --visor-inset-bottom: 76px; --visor-inset-left: 40px;
15
+ --visor-inset-top: 64px; --visor-inset-bottom: 76px;
16
+ --visor-inset-right: max(var(--ds-space-gutter-narrow), var(--helm-inset)); --visor-inset-left: var(--visor-inset-right);
17
+ background: linear-gradient(to bottom, var(--night-glass), transparent 128px, transparent calc(100% - 260px), var(--night-glass));
11
18
  color: var(--ink); font-family: var(--font-body);
12
19
  }
13
20
 
14
- /* the frame: stretched over the stage, every stroke one hairline at any size */
15
- .visor__frame { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
21
+ /* the frame: each arc is stretched across the stage, every stroke one hairline at any size. The
22
+ top arc, its notch and the place all sit inside the top inset, above where a scene starts its
23
+ own top row; the foot arc runs under the foot row. */
24
+ .visor__frame { position: absolute; left: 0; width: 100%; overflow: visible; }
25
+ .visor__frame--top { top: calc(var(--visor-inset-top) - 50px); height: 42px; }
26
+ .visor__frame--foot { bottom: calc(var(--visor-inset-bottom) - 44px); height: 18px; }
16
27
  .visor__frame path { vector-effect: non-scaling-stroke; }
17
- .visor__line { fill: none; stroke: var(--rule); stroke-width: var(--ds-line); }
18
- .visor__line--side { stroke: var(--rule-soft); }
19
- .visor__cell { fill: color-mix(in srgb, var(--ink) 12%, transparent); stroke: var(--rule); stroke-width: var(--ds-line); }
20
- .visor__mark { fill: var(--accent); stroke: none; }
28
+ .visor__line { fill: none; stroke: var(--ink-soft); stroke-opacity: .6; stroke-width: var(--line-strong); }
29
+ .visor__line--under { stroke: var(--night-glass); stroke-opacity: 1; stroke-width: 5px; }
30
+ .visor__dot { fill: var(--ink-soft); stroke: var(--night-glass); stroke-width: var(--line-strong); }
31
+ .visor__notch, .visor__sight { position: absolute; left: 50%; transform: translate(-50%, -50%); overflow: visible; }
32
+ .visor__notch { top: calc(var(--visor-inset-top) - 36px); width: 12px; height: 9px; }
33
+ .visor__sight { top: 50%; width: 24px; height: 24px; }
21
34
 
22
- /* the place, under the compass */
35
+ /* type over the scene: the halo, and a stroke of the night painted under each letter, the same
36
+ move as the lines' */
37
+ .visor__place, .visor__who, .visor__hint {
38
+ text-shadow: var(--type-halo); -webkit-text-stroke: 4px var(--night-glass); paint-order: stroke fill;
39
+ }
40
+ /* the name voice, for every name on the glass */
41
+ .visor__place, .visor__name, .visor__unit, .visor__main, .visor__hint, .visor__key, .visor__sheet-title, .visor__cap {
42
+ font-family: var(--name-face); font-weight: var(--name-weight); text-transform: var(--name-case);
43
+ }
44
+
45
+ /* the place, under the top arc */
23
46
  .visor__place {
24
- position: absolute; left: 50%; top: max(9.5%, 84px); transform: translateX(-50%); margin: 0;
25
- font-family: var(--font-mono); font-size: var(--ds-text-2xs); letter-spacing: .14em; text-transform: uppercase;
47
+ position: absolute; left: 50%; top: calc(var(--visor-inset-top) - 26px); transform: translateX(-50%); margin: 0;
48
+ font-size: var(--ds-text-2xl); letter-spacing: var(--name-space-wide); line-height: 1.2;
26
49
  color: var(--ink-soft); white-space: nowrap;
27
50
  }
28
51
 
29
- /* the pills: the only parts of the glass that take a pointer */
30
- .visor__pill, .visor__me {
31
- position: absolute; pointer-events: auto; box-sizing: border-box;
32
- display: inline-flex; align-items: center; gap: var(--ds-space-xs);
33
- min-height: 46px; padding: 0 var(--ds-space-sm);
34
- background: var(--bg-card); color: var(--ink); border: var(--ds-line) solid var(--rule);
35
- border-radius: var(--ds-radius-pill); font: inherit; font-size: var(--ds-text-sm); text-decoration: none;
52
+ /* the foot row: you on the left, the one thing to press in the middle, the key hints on the
53
+ right. The two side groups lean in a little, as the glass curves. */
54
+ .visor__foot {
55
+ position: absolute; left: var(--visor-inset-left); right: var(--visor-inset-right); bottom: var(--visor-inset-bottom);
56
+ display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; column-gap: var(--ds-space-sm);
57
+ }
58
+ .visor__me {
59
+ justify-self: start; display: flex; align-items: center; gap: var(--ds-space-sm);
60
+ transform: perspective(900px) rotateY(16deg); transform-origin: left center;
61
+ }
62
+ .visor__sprite {
63
+ flex: none; width: 56px; height: 56px; object-fit: cover; object-position: 50% 12%;
64
+ background: var(--glass-strong); clip-path: var(--cut-path-sm);
65
+ }
66
+ .visor__who { display: flex; flex-direction: column; gap: 2px; }
67
+ .visor__name { font-size: var(--ds-text-xs); letter-spacing: var(--name-space); line-height: 1; color: var(--ink-soft); }
68
+ .visor__credits { display: flex; align-items: baseline; gap: var(--ds-space-xs); white-space: nowrap; }
69
+ .visor__num { font-family: var(--name-face); font-size: var(--ds-text-headline); font-weight: 400; line-height: 1; color: var(--ink); font-variant-numeric: tabular-nums; }
70
+ .visor__unit { font-size: var(--ds-text-md); letter-spacing: var(--name-space); color: var(--ink-soft); }
71
+
72
+ /* the one accent button: the cut corner, the dark ink, the key that takes it */
73
+ .visor__main {
74
+ pointer-events: auto; box-sizing: border-box; justify-self: center;
75
+ display: inline-flex; align-items: center; justify-content: space-between; gap: var(--ds-space-sm);
76
+ min-height: 56px; padding: var(--ds-space-hair) var(--ds-space-sm) var(--ds-space-hair) var(--ds-space-md);
77
+ background: var(--accent); color: var(--accent-on); border: 0; border-radius: var(--press-radius); clip-path: var(--cut-path);
78
+ font-size: var(--ds-text-3xl); font-weight: var(--name-weight-lit); letter-spacing: var(--name-space-tight); line-height: 1.1;
79
+ text-decoration: none; cursor: pointer; transition: background-color var(--ds-dur-fast) var(--ds-ease);
80
+ }
81
+ .visor__main:hover { background: var(--accent-lift); color: var(--accent-on); text-decoration: none; }
82
+
83
+ /* the key hints: a key and a name, no box round the pair. The only other parts of the glass
84
+ that take a pointer. */
85
+ .visor__hints {
86
+ justify-self: end; display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: 0 var(--ds-space-md);
87
+ transform: perspective(900px) rotateY(-16deg); transform-origin: right center;
88
+ }
89
+ .visor__hint {
90
+ pointer-events: auto; box-sizing: border-box; display: inline-flex; align-items: center; gap: var(--ds-space-hair);
91
+ min-height: 44px; padding: 0; background: none; border: 0; border-radius: var(--press-radius);
92
+ color: var(--ink-soft); font-size: var(--ds-text-lg); letter-spacing: var(--name-space-tight); white-space: nowrap;
93
+ text-decoration: none; cursor: pointer;
94
+ transition: color var(--ds-dur-fast) var(--ds-ease);
95
+ }
96
+ .visor__hint:hover { color: var(--ink); text-decoration: none; }
97
+ .visor__hint[hidden] { display: none; }
98
+ /* Actions on a phone: three dots on the same chip of the night a key sits on (style.css shows it) */
99
+ .visor__dots {
100
+ display: none; box-sizing: content-box; width: 14px; height: 4px; padding: 10px 5px; border: var(--line-strong) solid currentColor;
101
+ background: radial-gradient(circle, currentColor 1.5px, transparent 2px) 0 0 / 5px 4px repeat-x content-box, var(--bg);
36
102
  }
37
- .visor__pill { cursor: pointer; }
38
- .visor__pill:hover { border-color: var(--ink-faint); }
39
- .visor__pill:focus-visible, .visor__row:focus-visible, .visor__close:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
40
- .visor__pill--main { left: var(--visor-inset-left); bottom: var(--visor-inset-bottom); background: var(--accent); color: var(--accent-on); border-color: var(--accent); font-weight: 500; }
41
- .visor__pill--main:hover { border-color: var(--accent-lift); }
42
- .visor__pill--actions { right: var(--visor-inset-right); top: var(--visor-inset-top); }
43
- .visor__pill[hidden] { display: none; }
44
- .visor__dots { width: 14px; height: 4px; background: radial-gradient(circle, currentColor 1.5px, transparent 2px) 0 0 / 5px 4px repeat-x; }
45
- .visor__me { right: var(--visor-inset-right); bottom: var(--visor-inset-bottom); padding: 4px var(--ds-space-sm) 4px 4px; pointer-events: none; }
46
- .visor__sprite { width: 38px; height: 38px; border-radius: var(--ds-radius-pill); object-fit: cover; object-position: 50% 12%; background: var(--bg-deep); }
47
- .visor__who { display: flex; flex-direction: column; line-height: 1.2; }
48
- .visor__name { font-weight: 500; }
49
- .visor__credits { font-size: var(--ds-text-2xs); color: var(--ink-soft); font-variant-numeric: tabular-nums; }
50
103
  .visor__key {
51
- font-family: var(--font-mono); font-size: var(--ds-text-2xs); line-height: 1; padding: 3px 5px;
52
- border: var(--ds-line) solid currentColor; border-radius: var(--ds-radius-pill); opacity: .75;
104
+ box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; flex: none;
105
+ min-width: 24px; height: 24px; padding: 0 5px;
106
+ border: var(--line-strong) solid currentColor; background: var(--bg);
107
+ font-size: var(--ds-text-sm); letter-spacing: 0; line-height: 1; text-shadow: none; -webkit-text-stroke: 0;
53
108
  }
109
+ .visor__main .visor__key { background: none; }
110
+ /* a cut corner clips an outline, so every control here takes the inset two-tone ring */
111
+ .visor__main:focus-visible, .visor__hint:focus-visible, .visor__row:focus-visible, .visor__close:focus-visible { outline: 0; border-radius: var(--press-radius); box-shadow: var(--press-focus); }
54
112
 
55
- /* the sheet: above the scene's pop-ups, under the modals (ADR 0399 §2.3 layer 4) */
113
+ /* the sheet: above the scene's pop-ups, under the modals (ADR 0399 §2.3 layer 4). The night
114
+ panel between two strong hairlines, standing over the hints that opened it, clear of the foot
115
+ row even when the hints have wrapped onto a second line. */
56
116
  .visor__sheet {
57
- position: absolute; right: var(--visor-inset-right); top: var(--visor-inset-top); z-index: 1;
117
+ position: absolute; right: var(--visor-inset-right); bottom: calc(var(--visor-inset-bottom) + 100px); z-index: 1;
58
118
  pointer-events: auto; box-sizing: border-box; width: min(18rem, calc(100vw - 2 * var(--ds-space-gutter-narrow)));
59
- max-height: calc(100% - var(--visor-inset-top) - var(--visor-inset-bottom)); overflow-y: auto;
60
- padding: var(--ds-space-sm); background: var(--bg-card); border: var(--ds-line) solid var(--rule);
61
- border-radius: var(--ds-radius-panel); box-shadow: var(--ds-shadow-pop);
119
+ max-height: calc(100% - var(--visor-inset-top) - var(--visor-inset-bottom) - 100px); overflow-y: auto;
120
+ padding: var(--ds-space-sm); background: var(--bg-card);
121
+ border: 0 solid var(--rule); border-left-width: var(--line-strong); border-right-width: var(--line-strong);
62
122
  }
63
123
  .visor__sheet-head { display: flex; align-items: center; justify-content: space-between; }
64
- .visor__sheet-title { margin: 0; font-size: var(--ds-text-md); font-weight: 500; }
65
- .visor__close { min-width: 32px; min-height: 32px; background: none; border: 0; color: var(--ink-soft); cursor: pointer; }
66
- .visor__cap { margin: var(--ds-space-xs) 0 2px; font-family: var(--font-mono); font-size: var(--ds-text-2xs); letter-spacing: .12em; text-transform: uppercase; color: var(--ink-soft); }
124
+ .visor__sheet-title { margin: 0; font-size: var(--ds-text-xl); letter-spacing: var(--name-space); color: var(--ink); }
125
+ .visor__close { min-width: 32px; min-height: 32px; padding: 0; background: none; border: 0; color: var(--ink-soft); cursor: pointer; }
126
+ .visor__cap { margin: var(--ds-space-xs) 0 2px; font-size: var(--ds-text-xs); letter-spacing: var(--name-space); color: var(--ink-soft); }
67
127
  .visor__rows { list-style: none; margin: 0; padding: 0; }
68
128
  .visor__rows + .visor__rows { margin-top: var(--ds-space-xs); }
69
129
  .visor__row {
70
130
  display: flex; align-items: center; justify-content: space-between; gap: var(--ds-space-xs);
71
- box-sizing: border-box; width: 100%; min-height: 36px; padding: 0 2px;
131
+ box-sizing: border-box; width: 100%; min-height: 40px; padding: 0 2px;
72
132
  background: none; border: 0; border-top: var(--ds-line) solid var(--rule-soft);
73
- color: var(--ink); font: inherit; font-size: var(--ds-text-sm); text-align: left; text-decoration: none; cursor: pointer;
133
+ color: var(--ink); font: inherit; font-size: var(--ds-text-md); text-align: left; text-decoration: none; cursor: pointer;
74
134
  }
75
135
  .visor__row:hover { color: var(--accent-ink); }
76
136
  .visor__row--hint { cursor: default; color: var(--ink-soft); }
77
137
  .visor__row--hint:hover { color: var(--ink-soft); }
138
+ .visor__sheet .visor__key { color: var(--ink-soft); background: none; }
78
139
 
79
140
  /* a phone (640px and narrower): the rules live in style.css’s shared phone block, so the
80
141
  visor adds no breakpoint of its own (the design-count ratchet). */
@@ -1,19 +1,21 @@
1
1
  // visor.js — the first-person visor: glass over the scene (task 1005148, BONGOS-V3 R08,
2
2
  // goal 1000142, criterion `you-are-a-character-in-first-person`). Built to ADR 0399.
3
3
  //
4
- // What is on the glass (the design the owner agreed on 2026-11-10, task 1005148):
5
- // - the frame: thin curved visor lines at the top, the bottom and both sides, a small
6
- // centre cross, and a compass of nine slanted cells along the top line with the one
7
- // salmon marker under its centre. Drawn once as SVG; it is ornament, never a control.
8
- // - the place, in a mono line under the compass ("THE SKY", "GOVERNOR CITY", …).
9
- // - bottom-left: your character's main action, the one accent pill, with its key.
10
- // - bottom-right: your sprite (third person) with your character's name and your credits
4
+ // What is on the glass (the minimal visor, Helm look 3, task 1005195; first built in task
5
+ // 1005148):
6
+ // - the frame: two thin arcs, one along the top of the glass and one under the foot row,
7
+ // a small notch under the top arc and a ring-and-dot sight at the centre. Drawn as SVG;
8
+ // it is ornament, never a control.
9
+ // - the place, in the name voice under the top arc ("THE SKY", "GOVERNOR CITY", …).
10
+ // - the foot row. Left: your sprite (third person), your character's name and your credits
11
11
  // (builders.total_credits — credits only, never a payout; the spec's "does not exist").
12
- // - top-right: "Actions" (key A), which opens the sheet: your character's actions, then
13
- // the keys of the place you are in, then Options on Escape.
12
+ // Centre: your character's main action, the one accent button, with its key. Right: the
13
+ // quiet key hints, no boxes: your other actions, "Actions" (key A), which opens the
14
+ // sheet (your character's actions, then the keys of the place you are in, then Options
15
+ // on Escape), and Options itself.
14
16
  //
15
17
  // The rules it keeps (ADR 0399):
16
- // - one overlay, #visor, with pointer-events: none; only its pills take a pointer, so a
18
+ // - one overlay, #visor, with pointer-events: none; only its controls take a pointer, so a
17
19
  // drag, a wheel or a click on the world reaches the scene unchanged (§2.1).
18
20
  // - no camera: a scene registers itself once with OTBVisor.scene({ name, place, actions,
19
21
  // keys }) and a movement action calls the scene's own function (§2.2). A page with no
@@ -34,7 +36,7 @@
34
36
 
35
37
  // ---------- each character's actions ----------
36
38
  // `href` opens a page the hall already has; `tool` opens one of craft-mode's tools on
37
- // this page. The first is the character's main action (the accent pill).
39
+ // this page. The first is the character's main action (the accent button).
38
40
  var ACTIONS = {
39
41
  artisan: [
40
42
  { id: 'claim', label: 'Claim a task', key: 'c', href: '/work' },
@@ -112,23 +114,33 @@
112
114
  // ---------- drawing ----------
113
115
  var root = null, sheet = null, returnFocus = null;
114
116
  var NS = 'http://www.w3.org/2000/svg';
115
- // The frame is drawn in a 1000×1000 box stretched over the stage (preserveAspectRatio
116
- // none), with non-scaling strokes so every line stays one hairline at any size.
117
+ // Every mark is drawn twice: a wider stroke of the night under the ice hairline, so the
118
+ // line reads over a midday sky as well as at night (visor.css .visor__line--under).
119
+ function twice(shape) { return shape.replace('class="', 'class="visor__line--under ') + shape; }
120
+ function svgOpen(cls, box, stretch) {
121
+ return '<svg class="' + cls + '" viewBox="' + box + '"' + (stretch ? ' preserveAspectRatio="none"' : '') + ' aria-hidden="true" focusable="false" xmlns="' + NS + '">';
122
+ }
123
+ // The two arcs are each a 1000×100 box stretched across the stage (preserveAspectRatio
124
+ // none), with non-scaling strokes so each stays one hairline at any size. The notch and the
125
+ // sight are drawn at their own size, so neither is ever stretched.
117
126
  function frameSvg() {
118
- var cells = '';
119
- for (var i = -4; i <= 4; i++) {
120
- var x = 500 + i * 38, y = 30 + Math.pow(i / 4, 2) * 14, s = i * 2;
121
- cells += '<path class="visor__cell" d="M' + (x - 15) + ' ' + (y + 4 - s / 2) + 'l30 ' + s + 'l-6 15l-30 ' + (-s) + 'z"/>';
122
- }
123
- return '<svg class="visor__frame" viewBox="0 0 1000 1000" preserveAspectRatio="none" aria-hidden="true" focusable="false" xmlns="' + NS + '">' +
124
- '<path class="visor__line" d="M0 120Q500 -10 1000 120"/>' +
125
- '<path class="visor__line" d="M0 880Q500 1010 1000 880"/>' +
126
- '<path class="visor__line visor__line--side" d="M18 120Q4 500 18 880M982 120Q996 500 982 880"/>' +
127
- '<path class="visor__line" d="M490 500h-12M510 500h12M500 490v-14M500 510v14"/>' +
128
- cells + '<path class="visor__mark" d="M494 76l6 9l6 -9z"/>' +
129
- '</svg>';
127
+ return svgOpen('visor__frame visor__frame--top', '0 0 1000 100', true) + twice('<path class="visor__line" d="M76 100Q500 -100 924 100"/>') + '</svg>' +
128
+ svgOpen('visor__frame visor__frame--foot', '0 0 1000 100', true) + twice('<path class="visor__line" d="M76 0Q500 200 924 0"/>') + '</svg>' +
129
+ svgOpen('visor__notch', '0 0 12 9') + twice('<path class="visor__line" d="M0 0l6 9l6 -9"/>') + '</svg>' +
130
+ svgOpen('visor__sight', '0 0 24 24') + twice('<circle class="visor__line" cx="12" cy="12" r="11"/>') + '<circle class="visor__dot" cx="12" cy="12" r="2"/></svg>';
130
131
  }
131
132
  function keyHtml(k) { k = String(k); return '<kbd class="visor__key">' + esc(k.length === 1 ? k.toUpperCase() : k) + '</kbd>'; }
133
+ // The count and its unit are two spans, so the number can be white and the word ice; the
134
+ // text between them is still creditsText's, word for word.
135
+ function creditsHtml(n) {
136
+ var t = creditsText(n), at = t.lastIndexOf(' ');
137
+ if (at < 0) return esc(t);
138
+ return '<span class="visor__num">' + esc(t.slice(0, at)) + '</span> <span class="visor__unit">' + esc(t.slice(at + 1)) + '</span>';
139
+ }
140
+ // A quiet key hint: the key in its small box, then the name. No box round the pair.
141
+ function hintHtml(a) {
142
+ return '<a class="visor__hint" data-visor-act="' + esc(a.id) + '" ' + actionAttrs(a) + '>' + keyHtml(a.key) + '<span>' + esc(a.label) + '</span></a>';
143
+ }
132
144
  function actionAttrs(a) {
133
145
  return a.href ? 'href="' + esc(hallHref(a.href)) + '"' : 'href="#" role="button"';
134
146
  }
@@ -138,14 +150,19 @@
138
150
  var n = credits();
139
151
  return frameSvg() +
140
152
  (scene.place ? '<p class="visor__place">' + esc(scene.place) + '</p>' : '') +
141
- '<a class="visor__pill visor__pill--main" data-visor-act="' + esc(main.id) + '" ' + actionAttrs(main) + '>' +
142
- '<span>' + esc(main.label) + '</span>' + keyHtml(main.key) + '</a>' +
143
- '<button class="visor__pill visor__pill--actions" type="button" data-visor-sheet aria-expanded="false" aria-controls="visor-sheet">' +
144
- '<span class="visor__dots" aria-hidden="true"></span><span>Actions</span>' + keyHtml(SHEET_KEY) + '</button>' +
145
- '<div class="visor__me">' +
146
- (sprite ? '<img class="visor__sprite" src="' + esc(sprite) + '" alt="" width="250" height="290" decoding="async">' : '') +
147
- '<span class="visor__who"><span class="visor__name">' + esc(NAMES[id]) + '</span>' +
148
- '<span class="visor__credits" data-visor-credits>' + esc(creditsText(n)) + '</span></span>' +
153
+ '<div class="visor__foot">' +
154
+ '<div class="visor__me">' +
155
+ (sprite ? '<img class="visor__sprite" src="' + esc(sprite) + '" alt="" width="250" height="290" decoding="async">' : '') +
156
+ '<span class="visor__who"><span class="visor__name">' + esc(NAMES[id]) + '</span>' +
157
+ '<span class="visor__credits" data-visor-credits>' + creditsHtml(n) + '</span></span>' +
158
+ '</div>' +
159
+ '<a class="visor__main" data-visor-act="' + esc(main.id) + '" ' + actionAttrs(main) + '>' +
160
+ '<span>' + esc(main.label) + '</span>' + keyHtml(main.key) + '</a>' +
161
+ '<div class="visor__hints">' + acts.slice(1).map(hintHtml).join('') +
162
+ '<button class="visor__hint visor__hint--sheet" type="button" data-visor-sheet aria-expanded="false" aria-controls="visor-sheet">' +
163
+ '<span class="visor__dots" aria-hidden="true"></span>' + keyHtml(SHEET_KEY) + '<span>Actions</span></button>' +
164
+ '<a class="visor__hint visor__hint--options" href="' + esc(hallHref('/settings')) + '">' + keyHtml('Esc') + '<span>Options</span></a>' +
165
+ '</div>' +
149
166
  '</div>';
150
167
  }
151
168
  function sheetHtml(id) {
@@ -11,7 +11,7 @@
11
11
  <link rel="icon" href="/modules/ui-design/public/brand/favicon.svg" type="image/svg+xml">
12
12
  <link rel="preconnect" href="https://fonts.googleapis.com">
13
13
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
14
- <link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm2">
14
+ <link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm3">
15
15
  <!-- The oversight family's sheet (task 1003469): the figure band, the group
16
16
  head, the row, the form. Then the page's own: severities, the report
17
17
  reader, the grade trend. The attention tiles + tabs come from style.css's
@@ -8,7 +8,7 @@
8
8
  <link rel="icon" href="/modules/ui-design/public/brand/favicon.svg" type="image/svg+xml">
9
9
  <link rel="preconnect" href="https://fonts.googleapis.com">
10
10
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
- <link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm2">
11
+ <link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm3">
12
12
  <!-- Page-local styles (task 2170) — extracted from the old inline <style>
13
13
  block per the hall placement rules; shared pager/count-pill styles now
14
14
  live in style.css's hall-kit section. -->
@@ -2,9 +2,21 @@
2
2
 
3
3
  Covers: `visor.js` + `visor.css`, loaded on Home, `/city`, `/goal-map` and `/thinking`, and the three scene registrations (`sky-interaction.js`, `city.js`, `home-city.js`). Spec: `docs/specs/bongos-v3-game-conversion.md` C2 `you-are-a-character-in-first-person`. Game mode itself is in `game-mode.md`.
4
4
 
5
- ## What it settled
5
+ ## The minimal visor (Helm look 3, task 1005195)
6
6
 
7
- - **The look the owner agreed (2026-11-10, five mock-up rounds).** A quiet layout with "hairline instruments" from the helmet round: thin curved visor lines across the top, bottom and both sides, a small centre cross, and a compass of nine slanted cells along the top line with the one salmon marker under its centre. No tracker or radar (tried and dropped), no clipped corners: the pills stay round (DESIGN.md "pill or nothing"). No glow (DESIGN.md's rejected near-black-plus-neon HUD). The frame is one SVG in a 1000×1000 box stretched over the stage with non-scaling strokes, so it is a hairline at every size.
7
+ The glass was redrawn to the owner's mock-up "Cloud city: in the visor, minimal". Look and layout only: every action, key, id, aria attribute, copy string and data source in `visor.js` is as task 1005148 left it. Where this section and the older bullets below disagree about the look or where a part sits, this section is current.
8
+
9
+ - **What is on the glass.** Two thin arcs (one along the top, one under the foot row), a notch under the top arc, a ring-and-dot sight at the centre, the place in the name voice under the top arc, and one foot row: the sprite in a cut box with the character's name and the credits (white count, ice word); the one accent button, cut corner, with its key; the quiet key hints (a key in a small box and a name, no box round the pair). The compass cells, the side lines, the centre cross and the round pills are gone.
10
+ - **Where each control went.** The main action is the accent button, centred. The character's other actions (after the game-places filter) are key hints on the right; they were rows in the sheet only. **Actions (A)** is demoted from a top-right pill to a key hint and still opens the same sheet, which now stands over the hints. **Options (Esc)** is the last hint, the same link the sheet's last row is. Nothing was removed.
11
+ - **In the mock-up, not built, because the visor has no data for it:** the four progress segments, the marker and name on the building in view (the visor owns no camera, ADR 0399 §2.2), and the party bars with a speaker's name and a Voice key. Each is a scene or a later task handing the visor real data; none is drawn empty.
12
+ - **It reads over any sky, with no per-hour style.** The glass darkens toward its top and bottom edge (`--night-glass`), type wears `--type-halo` and a stroke of the night painted under each letter, every line is drawn twice (a wider night stroke under the ice hairline, `.visor__line--under`), and a key sits on a chip of the night. No new token.
13
+ - **A phone.** The shared phone block in `style.css` (no breakpoint of the visor's own): the top arc, its notch and the place step aside (the top of a phone is the scene's and the shell's); the foot row stays one line with nothing leaning (the sprite, the count over its word, the accent button whose name may wrap and whose key is left to the sheet, and of the hints only Actions, as three dots on a night chip). Between a phone and a wide desk the hints wrap onto a second line, right-aligned, rather than run into the button.
14
+ - **Scenes.** The top arc, the notch and the place sit inside the top 64px (`--visor-inset-top`), the band the sky already leaves free, so no scene's top row is struck through. Home no longer shifts its greeting row left (there is no pill top-right to clear). The city's door rises above the foot row at every width, because the accent button is centred where the door stood. The sky's `visor-on` offsets are unchanged.
15
+ - **Looking at it.** The stand-in scene in `tests/hall_visor.mjs` over the hall's real sheets; put a bright gradient behind it to check midday.
16
+
17
+ ## What it settled (task 1005148)
18
+
19
+ - **The first look (2026-11-10, five mock-up rounds; replaced by the minimal visor above).** A quiet layout with "hairline instruments" from the helmet round: thin curved visor lines across the top, bottom and both sides, a small centre cross, and a compass of nine slanted cells along the top line with the one salmon marker under its centre. No tracker or radar (tried and dropped), no clipped corners: the pills stay round (DESIGN.md "pill or nothing"). No glow (DESIGN.md's rejected near-black-plus-neon HUD). The frame is one SVG in a 1000×1000 box stretched over the stage with non-scaling strokes, so it is a hairline at every size.
8
20
  - **What sits where.** Bottom-left: the character's main action, the one accent pill, with its key. Bottom-right: the sprite (task 1005147's art, `OTBCraftMode.spriteSrc`) with the character's name and the credit count (`/me` `builder.total_credits` through `OTBCraftMode.me()`; credits only, never a payout). Top-right: Actions (key A), which opens the sheet: the character's actions, then "This place" (the scene's own actions and keys), then Options on Escape. A place line under the compass names the scene; the sky leaves it empty because its own heading names it.
9
21
  - **Each character's actions** (`ACTIONS`): Artisan Claim a task C (`/work`), Blockers B, Goal sky G, Your thinking T. Artist Review this page R, Edit its words E, Write an idea I (the three craft-mode tools, through `OTBCraftMode.openTool`), Studio S. Patron Board room O, Governor city V, Blockers B, Watch W. Every one is a page or tool the hall already has; the visor gates nothing, and the server's rank checks answer exactly as before (ADR 0399 §2.5). No key a scene binds is reused (the sky's L 0 / ?, the gallery's F, the palette's K, the city card's digits, arrows and Tab).
10
22
  - **Glass, not layout (ADR 0399 §2.1).** `#visor` is `position: fixed` with `pointer-events: none`; only the pills and the sheet take a pointer. A drag across a frame line pans the sky (test-pinned).
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.24.83",
3
+ "version": "1.24.84",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.24.83",
9
+ "version": "1.24.84",
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.83",
3
+ "version": "1.24.84",
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",
@@ -11601,5 +11601,11 @@
11601
11601
  "id": "1005194",
11602
11602
  "text": "The options menu and the choose-your-character screen now wear the Helm look: a night panel with wide capital rows, and three tall cut-corner doors with the real characters. Fields and tags across the hall are squared to match"
11603
11603
  }
11604
+ ],
11605
+ "1.24.84": [
11606
+ {
11607
+ "id": "1005195",
11608
+ "text": "The in-game visor is now minimal: two thin arcs, the place name, your credits, one salmon button for the current action and a quiet row of key hints. It stays readable over a bright daytime sky as well as at night."
11609
+ }
11604
11610
  ]
11605
11611
  }
package/src/module-api.js CHANGED
@@ -81,7 +81,7 @@ const { sleep } = require('./sleep');
81
81
  // MAJOR (see allowBoxScope below): passes the request through untouched.
82
82
  function deprecatedNoopMiddleware(_req, _res, next) { next(); }
83
83
 
84
- const CORE_VERSION = '1.24.83'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
84
+ const CORE_VERSION = '1.24.84'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
85
85
 
86
86
  // A namespaced logger so a module's log lines are attributable + consistent.
87
87
  // Usage: const log = api.logger('discord'); log.info('mounted');