@bongos/core 1.19.927 → 1.19.929

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 (53) hide show
  1. package/.bongos-core.json +83 -53
  2. package/docs/copy-inventory.md +36 -15
  3. package/docs/copy-registry.json +239 -28
  4. package/docs/file-map.md +1 -1
  5. package/docs/module-api-changelog.md +4 -0
  6. package/modules/hall-ui/public/atlas.html +1 -1
  7. package/modules/hall-ui/public/blockers.html +1 -1
  8. package/modules/hall-ui/public/board-room.html +1 -1
  9. package/modules/hall-ui/public/collab.html +1 -1
  10. package/modules/hall-ui/public/copy-desk.html +1 -1
  11. package/modules/hall-ui/public/deploy.html +1 -1
  12. package/modules/hall-ui/public/diagrams.html +1 -1
  13. package/modules/hall-ui/public/drachmae.html +1 -1
  14. package/modules/hall-ui/public/gate.html +1 -1
  15. package/modules/hall-ui/public/goals.html +1 -1
  16. package/modules/hall-ui/public/government.html +1 -1
  17. package/modules/hall-ui/public/idea-objects.js +109 -0
  18. package/modules/hall-ui/public/idea.html +1 -1
  19. package/modules/hall-ui/public/ideas.html +1 -1
  20. package/modules/hall-ui/public/index.html +1 -1
  21. package/modules/hall-ui/public/modules.html +1 -1
  22. package/modules/hall-ui/public/people.html +1 -1
  23. package/modules/hall-ui/public/primer.html +1 -1
  24. package/modules/hall-ui/public/profile.html +1 -1
  25. package/modules/hall-ui/public/project-settings.html +1 -1
  26. package/modules/hall-ui/public/ranks.html +1 -1
  27. package/modules/hall-ui/public/roadmap.html +1 -1
  28. package/modules/hall-ui/public/roster.html +1 -1
  29. package/modules/hall-ui/public/sessions.html +1 -1
  30. package/modules/hall-ui/public/settings.html +1 -1
  31. package/modules/hall-ui/public/shell.js +11 -0
  32. package/modules/hall-ui/public/studio.html +1 -1
  33. package/modules/hall-ui/public/task.html +1 -1
  34. package/modules/hall-ui/public/thinking.css +286 -0
  35. package/modules/hall-ui/public/thinking.html +74 -0
  36. package/modules/hall-ui/public/thinking.js +418 -0
  37. package/modules/hall-ui/public/thinking.states.json +20 -0
  38. package/modules/hall-ui/public/watch.html +1 -1
  39. package/modules/hall-ui/public/work.html +1 -1
  40. package/package-lock.json +2 -2
  41. package/package.json +1 -1
  42. package/scripts/gds/provision-config.js +11 -0
  43. package/scripts/gds/provision-core-upgrade.js +90 -10
  44. package/scripts/gds/provision.js +3 -3
  45. package/src/bongos/serve-internal.js +7 -1
  46. package/src/module-api.js +1 -1
  47. package/tests/core_upgrade_runner.mjs +153 -0
  48. package/tests/hall_nav.mjs +1 -1
  49. package/tests/hall_page_gate_map.mjs +6 -0
  50. package/tests/hall_thinking_room.mjs +366 -0
  51. package/tests/helpers.mjs +124 -0
  52. package/tests/nav_permission_atoms.mjs +1 -1
  53. package/tests/provision.mjs +41 -0
@@ -0,0 +1,109 @@
1
+ // modules/hall-ui/public/idea-objects.js — THE FIVE OBJECTS, as a reader's
2
+ // words (task 1004098 / BV2.R03 "the room", goal 1000095).
3
+ //
4
+ // The server derives which object a thought IS — once, in
5
+ // modules/ideas/idea-state.js, from the ledger (ADR 0322). This file is the
6
+ // other half of that decision and deliberately does NOT re-derive anything: it
7
+ // maps a state the server already answered onto the word, the sentence and the
8
+ // mark a surface draws for it. `resolveIdeaState` is the authority on WHICH of
9
+ // the five; this is the authority on how one LOOKS.
10
+ //
11
+ // IT IS SHARED BECAUSE TWO SURFACES DRAW THESE AND A THIRD WILL. The room
12
+ // (this task) is the first, the sky (BV2.R26) is the next, and the idea record
13
+ // is the obvious third. A vocabulary copied into each of them is how the sky
14
+ // ends up calling a thought "burned out" while the room beside it says
15
+ // "declined" — the same failure the resolver exists to prevent, one layer up.
16
+ // The descent on /idea/:id is deliberately NOT a consumer: a descent row is a
17
+ // TASK, and marking one with an idea-level object would be exactly the
18
+ // re-derivation this pair of files refuses (see idea.js, "what a row is marked
19
+ // by").
20
+ //
21
+ // THE WORDS ARE THE DESIGN'S, taken from docs/design/ideator-surface-direction.md
22
+ // §"The five objects" rather than invented here, so the surface says what the
23
+ // owner said. Where that table is a fragment ("it burned, then the project
24
+ // removed it"), it is spelled into a sentence and nothing more.
25
+ //
26
+ // NO DOM, no fetch, no state. One frozen table and three lookups, so a page
27
+ // under test can read it without booting a page.
28
+ (function () {
29
+ 'use strict';
30
+
31
+ // Canonical order. It is the order the design lists them in and the order a
32
+ // filter row draws them in — brightest and most actionable first, folded and
33
+ // declined last — so two surfaces cannot disagree about where a pill sits.
34
+ var ORDER = ['star', 'protostar', 'satellite', 'remnant', 'cold-body'];
35
+
36
+ var OBJECTS = {
37
+ star: {
38
+ label: 'burning',
39
+ noun: 'star',
40
+ blurb: 'caused work that is still alive. it dims as pieces of that work are removed',
41
+ },
42
+ protostar: {
43
+ label: 'waiting',
44
+ noun: 'protostar',
45
+ blurb: 'filed, not yet ignited',
46
+ },
47
+ satellite: {
48
+ label: 'folded in',
49
+ noun: 'satellite',
50
+ blurb: 'folded into another thought. it orbits that one now, and its value counts there',
51
+ },
52
+ remnant: {
53
+ label: 'burned out',
54
+ noun: 'remnant',
55
+ blurb: 'it burned, then the project removed it. it keeps every bit of its size',
56
+ },
57
+ 'cold-body': {
58
+ label: 'declined',
59
+ noun: 'cold body',
60
+ blurb: 'declined at triage. it never ignited, so it has no size at all',
61
+ },
62
+ };
63
+
64
+ // An unknown state is passed through rather than swallowed, the way idea.js
65
+ // passes through an unknown descent relation: a sixth object would be a real
66
+ // answer the server gave, and drawing it unstyled beats drawing nothing or,
67
+ // worse, silently drawing it as a protostar.
68
+ function objectFor(state) {
69
+ var key = String(state || '');
70
+ return OBJECTS[key] || { label: key || 'unknown', noun: key || 'unknown', blurb: '' };
71
+ }
72
+
73
+ // POSITION is the second axis and it is only load-bearing for a protostar —
74
+ // the design is explicit that a star has a position too, and that the
75
+ // distinction only MEANS something for a thought nothing has claimed yet.
76
+ // So this returns a sentence for exactly that case and null otherwise,
77
+ // rather than letting every row carry a word that tells a reader nothing.
78
+ function positionWord(state) {
79
+ if (!state || state.state !== 'protostar') return null;
80
+ if (state.position !== 'well') return 'nothing has claimed it';
81
+ return state.awaitingNod ? 'waiting for a nod' : 'has a home';
82
+ }
83
+
84
+ // Brightness as a reader's phrase. THE THREE ANSWERS ARE THREE, never two:
85
+ // a number → this share of the work it caused is still alive
86
+ // null → the removal facts could not be read at all
87
+ // no work → nothing to be alive or dead yet
88
+ // Collapsing "we could not look" into "none of it survived" is the exact
89
+ // mistake modules/ideas/idea-state.js step 3 was written to prevent, and it
90
+ // would render a healthy instance as a field of hollow rings the moment the
91
+ // lifecycle module failed to mount.
92
+ function brightnessWord(state) {
93
+ if (!state || state.hasWork !== true) return null;
94
+ if (state.attributable !== true || state.brightness === null || state.brightness === undefined) {
95
+ return 'still alive — unknown';
96
+ }
97
+ var pct = Math.round(Number(state.brightness) * 100);
98
+ if (pct >= 100) return 'all of it still alive';
99
+ if (pct <= 0) return 'none of it still alive';
100
+ return pct + '% still alive';
101
+ }
102
+
103
+ window.OTBIdeaObjects = {
104
+ ORDER: ORDER,
105
+ objectFor: objectFor,
106
+ positionWord: positionWord,
107
+ brightnessWord: brightnessWord,
108
+ };
109
+ })();
@@ -52,7 +52,7 @@
52
52
 
53
53
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
54
54
 
55
- <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
55
+ <script src="/builders/shell.js?v=2026-09-20-room1"></script>
56
56
  <script src="/builders/palette.js" defer></script>
57
57
  <script src="/builders/dom-utils.js?v=2026-06-23-contrib" defer></script>
58
58
  <!-- task-detail.js still owns the #/idea/N and #/blocker/N overlay, so a ref
@@ -192,7 +192,7 @@
192
192
 
193
193
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
194
194
 
195
- <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
195
+ <script src="/builders/shell.js?v=2026-09-20-room1"></script>
196
196
  <script src="/builders/palette.js" defer></script>
197
197
  <script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
198
198
  <!-- task 1003069 (R08) — the quick-filing form's routed result links a task
@@ -102,7 +102,7 @@
102
102
  <script src="/builders/brand-apply.js?v=2026-06-27-r75" defer></script>
103
103
  <!-- The app shell (sidebar + top bar + view router). Dependency-free; loaded
104
104
  BEFORE builders.js so the chrome ids exist when events are wired. -->
105
- <script src="/builders/shell.js?v=2026-09-20-coreupdate1" defer></script>
105
+ <script src="/builders/shell.js?v=2026-09-20-room1" defer></script>
106
106
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
107
107
  <script src="/builders/palette.js" defer></script>
108
108
  <!-- task 2166: the shared component kit (window.OTBKit) — pager/ledger/rail.
@@ -80,7 +80,7 @@
80
80
 
81
81
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
82
82
 
83
- <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
83
+ <script src="/builders/shell.js?v=2026-09-20-room1"></script>
84
84
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
85
85
  <script src="/builders/palette.js" defer></script>
86
86
  <script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
@@ -55,7 +55,7 @@
55
55
 
56
56
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
57
57
  chrome exists before the page script runs. -->
58
- <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
58
+ <script src="/builders/shell.js?v=2026-09-20-room1"></script>
59
59
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
60
60
  <script src="/builders/palette.js" defer></script>
61
61
  <!-- shared frontend toolkit (window.OTB — rank labels, number formats) + the
@@ -115,7 +115,7 @@
115
115
 
116
116
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
117
117
 
118
- <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
118
+ <script src="/builders/shell.js?v=2026-09-20-room1"></script>
119
119
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
120
120
  <script src="/builders/palette.js" defer></script>
121
121
  <!-- the shared markdown reader (window.OTBMdReader) — primer.js renders through
@@ -187,7 +187,7 @@
187
187
 
188
188
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
189
189
  chrome exists before the page script runs. -->
190
- <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
190
+ <script src="/builders/shell.js?v=2026-09-20-room1"></script>
191
191
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
192
192
  <script src="/builders/palette.js" defer></script>
193
193
  <!-- shared frontend toolkit (window.OTB) — loaded before profile.js, which pulls from it. -->
@@ -58,7 +58,7 @@
58
58
 
59
59
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
60
60
 
61
- <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
61
+ <script src="/builders/shell.js?v=2026-09-20-room1"></script>
62
62
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
63
63
  <script src="/builders/palette.js" defer></script>
64
64
  <script src="/builders/dom-utils.js?v=2026-06-16-gate"></script>
@@ -68,7 +68,7 @@
68
68
 
69
69
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
70
70
  chrome exists before the page script runs. -->
71
- <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
71
+ <script src="/builders/shell.js?v=2026-09-20-room1"></script>
72
72
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
73
73
  <script src="/builders/palette.js" defer></script>
74
74
  <!-- shared frontend toolkit (window.OTB) — loaded before ranks.js, which pulls from it. -->
@@ -41,7 +41,7 @@
41
41
 
42
42
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
43
43
 
44
- <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
44
+ <script src="/builders/shell.js?v=2026-09-20-room1"></script>
45
45
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
46
46
  <script src="/builders/palette.js" defer></script>
47
47
  <script src="/builders/dom-utils.js?v=2026-06-14-domutils"></script>
@@ -50,7 +50,7 @@
50
50
 
51
51
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
52
52
  chrome exists before the page script runs. -->
53
- <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
53
+ <script src="/builders/shell.js?v=2026-09-20-room1"></script>
54
54
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
55
55
  <script src="/builders/palette.js" defer></script>
56
56
  <!-- shared frontend toolkit (window.OTB — rank labels, number formats) + the
@@ -99,7 +99,7 @@
99
99
 
100
100
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
101
101
  chrome exists before the page script runs. -->
102
- <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
102
+ <script src="/builders/shell.js?v=2026-09-20-room1"></script>
103
103
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
104
104
  <script src="/builders/palette.js" defer></script>
105
105
  <!-- shared frontend toolkit (window.OTB) — loaded BEFORE sessions.js so it can pull from it. -->
@@ -416,7 +416,7 @@
416
416
 
417
417
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
418
418
  chrome exists before the page scripts run. -->
419
- <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
419
+ <script src="/builders/shell.js?v=2026-09-20-room1"></script>
420
420
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
421
421
  <script src="/builders/palette.js" defer></script>
422
422
  <!-- shared frontend toolkit (window.OTB) — loaded before consuming IIFEs. -->
@@ -216,6 +216,17 @@
216
216
  // behind the ideator craft would hide the ideator's own surface from
217
217
  // everyone else who has a thought, which is most of the value.
218
218
  { id: 'ideas', label: 'Ideas', icon: 'goals', href: '/ideas', gate: 'signed' },
219
+ // task 1004098 (BV2.R03 of goal 1000095): THE ROOM — what you filed and
220
+ // what became of it. Directly under Ideas because the two are the daily
221
+ // loop: you file on one and you find out on the other, and the whole
222
+ // criterion (`ideator-nothing-is-lost`) is that the second half must be
223
+ // reachable without asking anyone. NOT in the 'You' group beside Standing
224
+ // and Profile, even though it is your own view: that group is your
225
+ // numbers, and this page's subject is the project's work, seen from the
226
+ // thought that caused it. gate 'signed' for the same reason Ideas is —
227
+ // there is no permission gate on having a thought, and the route reads the
228
+ // VIEWER's own trail, so there is nothing here a rank could protect.
229
+ { id: 'thinking', label: 'Your thinking', icon: 'goals', href: '/thinking', gate: 'signed' },
219
230
  { id: 'copy-desk', label: 'Copy desk', icon: 'docs', href: '/copy-desk', gate: 'signed', module: 'copy-desk' },
220
231
  // task 1003117 (R06 of goal 1000074): the Studio — artist mode, "a place an
221
232
  // artist wants to be, not a form". Beside the copy desk (the artist's other
@@ -66,7 +66,7 @@
66
66
 
67
67
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
68
68
 
69
- <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
69
+ <script src="/builders/shell.js?v=2026-09-20-room1"></script>
70
70
  <script src="/builders/palette.js" defer></script>
71
71
  <script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
72
72
  <!-- The kit, for its ONE renderer: the full-size ship-time visual figure
@@ -57,7 +57,7 @@
57
57
 
58
58
  <!-- The app shell (sidebar + top bar + footer). Dependency-free; runs at parse
59
59
  time so the chrome exists before the deferred page scripts execute. -->
60
- <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
60
+ <script src="/builders/shell.js?v=2026-09-20-room1"></script>
61
61
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
62
62
  <script src="/builders/palette.js" defer></script>
63
63
  <!-- shared frontend toolkit (window.OTB) — loaded FIRST so task.js can pull from it. -->
@@ -0,0 +1,286 @@
1
+ /* thinking.css — what THE ROOM has that no other hall page has
2
+ (task 1004098 / BV2.R03, goal 1000095).
3
+
4
+ The composition is the shell's: `.page-head--board`, `.scroll`, `.ledger` and
5
+ the kit's `.kit-tabs` strip all come from style.css and are not restated here.
6
+ What is left is the three shapes only this page has — the figure run over the
7
+ list, the size bar, and the mark for the five objects — plus the cells the
8
+ ledger has no opinion about.
9
+
10
+ NO COLOUR THAT IS NOT A TOKEN. Every value below is one of the world's
11
+ fifteen or a color-mix of them, so the page inherits dark and light from the
12
+ pack rather than declaring either: there is not one mode-specific rule in this
13
+ sheet, which is why it needs no dark twin. `--line` is a WIDTH; a hairline's
14
+ colour is `--rule` / `--rule-soft`.
15
+
16
+ NO MOTION. Nothing here transitions or animates, so the sheet sits entirely
17
+ inside the Kill Switch rather than needing to be excused by it. The one thing
18
+ that could have moved — a size bar growing as the background pages land —
19
+ deliberately does not: the bar is a measurement, and a measurement that
20
+ animates on every re-render turns a quiet page into a twitching one. */
21
+
22
+ /* ---- the figures over the list -------------------------------------------
23
+ The same object as the descent's `.ig-desc__figs` on /idea/:id, and that is
24
+ on purpose rather than by accident: the two pages state the same kind of fact
25
+ (a count over a body of work) and a reader moving between them should not
26
+ have to learn a second shape for it. It is not SHARED code, because the
27
+ descent's figures live in the idea record's own sheet and hoisting them into
28
+ style.css to serve two pages is an archetype decision, not a page's. If a
29
+ third surface wants them — the sky will — that is the moment. */
30
+ .tr-figs {
31
+ display: flex;
32
+ flex-wrap: wrap;
33
+ gap: var(--sp-md) var(--sp-lg);
34
+ margin: 0 0 var(--sp-md);
35
+ }
36
+ .tr-fig { display: grid; gap: 6px; }
37
+ .tr-fig__n {
38
+ font-size: 32px;
39
+ font-weight: 300;
40
+ line-height: 1;
41
+ letter-spacing: -0.03em;
42
+ font-variant-numeric: tabular-nums;
43
+ color: var(--ink);
44
+ }
45
+ /* Only ONE figure is warm, and it is the live one — "still burning" is the
46
+ number the page exists to make visible, and an accent spent on all four would
47
+ say nothing. "burned out" goes faint for the same reason it does in the
48
+ descent: the record of work that happened is not an error to shout. */
49
+ .tr-fig[data-tone="warm"] .tr-fig__n { color: var(--accent-deep); }
50
+ .tr-fig[data-tone="dim"] .tr-fig__n { color: var(--ink-faint); }
51
+ .tr-fig__label {
52
+ font-family: var(--label-face);
53
+ font-size: var(--label-size);
54
+ letter-spacing: var(--label-space);
55
+ text-transform: var(--label-case);
56
+ color: var(--ink-faint);
57
+ }
58
+
59
+ /* ---- the filter strip ---------------------------------------------------- */
60
+ /* The strip itself is `.kit-tabs`; all this adds is the air under it, because
61
+ the kit's own margin assumes a panel directly beneath and here a table
62
+ follows. */
63
+ .tr-filters { margin: 0 0 var(--sp-sm); }
64
+ /* `.kit-tabs` sets `overflow-x: auto`, which per spec makes the OTHER axis
65
+ `auto` too — and the strip's own `.setup__tab { margin-bottom: -1px }` leaves
66
+ it exactly 1px taller than its box, so a vertical scrollbar appears beside a
67
+ horizontal strip that cannot scroll vertically. It is the shared component's
68
+ defect rather than this page's, and it is not fixed in style.css from here:
69
+ that rule is on every page carrying a tab strip, and quietly changing the
70
+ hall's chrome inside a page's task is how a sheet stops being reviewable.
71
+ Scoped to this page, with the shared fix filed separately. */
72
+ .tr-filters .kit-tabs { overflow-y: hidden; }
73
+
74
+ /* ---- the rows ------------------------------------------------------------ */
75
+ /* The four short columns are pinned and the two prose ones share what is left,
76
+ so the row's SUBJECT — the thought — gets the width instead of losing it to
77
+ an outcome sentence that happens to be long. `width` on a th is a hint under
78
+ `table-layout: auto`, which is what is wanted here: a pinned column still
79
+ gives way rather than clipping when a cell genuinely needs more. */
80
+ .tr-table { table-layout: auto; }
81
+ .tr-table th:nth-child(1) { width: 34%; }
82
+ .tr-table th:nth-child(2) { width: 100px; }
83
+ .tr-table th:nth-child(3) { width: 30%; }
84
+ .tr-table th:nth-child(4) { width: 104px; }
85
+ .tr-table th:nth-child(5) { width: 142px; }
86
+ /* The thought is the row's subject, so it is the one cell that may take the
87
+ width and the one cell whose link reads at body size. */
88
+ .tr-thought a {
89
+ display: inline-block;
90
+ min-height: 24px;
91
+ padding: 3px 0;
92
+ line-height: 18px;
93
+ color: var(--ink);
94
+ border-bottom: var(--line) solid var(--rule);
95
+ }
96
+ .tr-thought a:hover { border-bottom-color: var(--ink); text-decoration: none; }
97
+ .tr-became { font-size: 13.5px; color: var(--ink-soft); }
98
+ .tr-became a {
99
+ display: inline-block;
100
+ min-height: 24px;
101
+ padding: 3px 0;
102
+ line-height: 18px;
103
+ color: var(--ink-soft);
104
+ border-bottom: var(--line) solid var(--rule-soft);
105
+ }
106
+ .tr-became a:hover { color: var(--ink); border-bottom-color: var(--ink); text-decoration: none; }
107
+ .tr-quiet { color: var(--ink-faint); }
108
+ /* The one actionable phrase on the page — a task sitting at backlog inside a
109
+ goal is one promote from claimable — so it is the only thing in a row that
110
+ takes the accent, and it says the word as well as wearing the colour. */
111
+ .tr-nod { color: var(--accent-ink); }
112
+
113
+ /* ---- size ---------------------------------------------------------------- */
114
+ /* Bar and number together, never the bar alone: size is long-tailed and its
115
+ compressed width is a comparison, not a quantity. The direction is explicit
116
+ that the true figure stays available wherever the drawing is. */
117
+ .tr-sizecell { white-space: nowrap; }
118
+ .tr-size { display: inline-flex; align-items: center; gap: 10px; }
119
+ .tr-size__bar {
120
+ display: block;
121
+ flex: none;
122
+ width: 56px;
123
+ height: 3px;
124
+ border-radius: 999px;
125
+ /* The track is the unfilled remainder; the fill is the accent at the server's
126
+ own compressed width. One element, so a row costs no extra box. */
127
+ background:
128
+ linear-gradient(to right, var(--fill, var(--accent)) 0 var(--w, 0%), transparent var(--w, 0%) 100%),
129
+ var(--rule-soft);
130
+ }
131
+ /* Every bit of its size, none of its colour — the remnant's whole picture. See
132
+ thinking.js sizeHtml() for why this is set from the state and not from the
133
+ number. `--ink-faint` and not `--rule`: at the hairline's weight a full-width
134
+ bar stops clearing the 3:1 non-text floor on the dark ground, and this bar is
135
+ the one that must still read as LARGE. */
136
+ .tr-size[data-lit="out"] { --fill: var(--ink-faint); }
137
+ .tr-size__n {
138
+ font-size: 13px;
139
+ font-variant-numeric: tabular-nums;
140
+ color: var(--ink-soft);
141
+ }
142
+ /* A size computed from a descent rather than from payouts that were made. It is
143
+ a footnote, not a warning — the number is real, it just means something
144
+ slightly different, and the title says which. */
145
+ .tr-hist {
146
+ display: block;
147
+ margin-top: 2px;
148
+ font-size: 11.5px;
149
+ color: var(--ink-faint);
150
+ text-decoration: none;
151
+ border-bottom: var(--line) dotted var(--rule);
152
+ cursor: help;
153
+ }
154
+
155
+ /* ---- the five objects ---------------------------------------------------- */
156
+ /* THE MARK RESTATES THE WORD IN SHAPE. Every object below differs from the
157
+ others by geometry — filled, haloed, ringed, outlined — and not only by tone,
158
+ because a mark distinguished by colour alone tells a colour-blind reader
159
+ nothing. That is the sheet's existing rule for the grade badge and the
160
+ descent's glyph; this is the same rule applied to a vocabulary of five.
161
+
162
+ `--lit` is the server's brightness, between 0 and 1, set per row by
163
+ thinking.js. It rides the star's opacity — and the star's ALONE, because the
164
+ other four objects have no live share to show and dimming them would invent
165
+ one. A star whose brightness could not be READ arrives with --lit: 1 and says
166
+ so in words (idea-objects.js brightnessWord); a full glyph over "still alive
167
+ — unknown" is the honest pair. The floor of 0.35 is a contrast floor: below
168
+ it the mark stops clearing 3:1 against the ground and a dim star becomes an
169
+ absent one, which is the wrong reading entirely — a star is never nothing. */
170
+ .tr-objcell { white-space: nowrap; }
171
+ .tr-obj { display: inline-flex; align-items: flex-start; gap: 10px; }
172
+ .tr-obj__glyph {
173
+ flex: none;
174
+ width: 9px;
175
+ height: 9px;
176
+ margin-top: 5px;
177
+ border-radius: 50%;
178
+ background: var(--ink-faint);
179
+ }
180
+ .tr-obj__body { display: grid; gap: 2px; min-width: 0; }
181
+ .tr-obj__label { font-size: 13.5px; color: var(--ink); }
182
+ .tr-obj__sub { font-size: 12px; color: var(--ink-faint); }
183
+
184
+ /* star — filled, accent, dimmed by its live share. */
185
+ .tr-obj[data-object="star"] .tr-obj__glyph {
186
+ background: var(--accent);
187
+ opacity: max(0.35, var(--lit, 1));
188
+ }
189
+ /* protostar — small and haloed: matter gathering, not yet burning. The halo is
190
+ a ring of the accent mixed back into the ground, so it reads as glow rather
191
+ than as a second border. */
192
+ .tr-obj[data-object="protostar"] .tr-obj__glyph {
193
+ width: 7px;
194
+ height: 7px;
195
+ margin-top: 6px;
196
+ background: var(--ink-soft);
197
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 18%, transparent);
198
+ }
199
+ /* satellite — a small body with an orbit around it, detached from the mark so
200
+ it reads as something going round rather than as a thick edge. */
201
+ .tr-obj[data-object="satellite"] .tr-obj__glyph {
202
+ width: 6px;
203
+ height: 6px;
204
+ margin-top: 6px;
205
+ background: var(--ink-soft);
206
+ outline: var(--line) solid var(--rule);
207
+ outline-offset: 4px;
208
+ }
209
+ /* remnant — the hollow ring, and the largest mark of the five. A thought that
210
+ caused a great deal of work the project then tore out renders LARGE and DEAD:
211
+ that is the whole reason size and brightness are two channels, so the object
212
+ that carries it must not be the quietest thing in the column. `--ink-faint`
213
+ and not `--rule`: at the hairline's weight this measured under the 3:1
214
+ non-text floor on the dark ground, the same finding the descent's "out" glyph
215
+ recorded. */
216
+ .tr-obj[data-object="remnant"] .tr-obj__glyph {
217
+ width: 12px;
218
+ height: 12px;
219
+ margin-top: 4px;
220
+ background: transparent;
221
+ box-shadow: inset 0 0 0 1.5px var(--ink-faint);
222
+ }
223
+ /* cold body — the smallest mark there is, a dark point with a hairline edge. It
224
+ never ignited, so it has no size at all, and the mark says that by being the
225
+ one object with nothing inside it and nothing around it. */
226
+ .tr-obj[data-object="cold-body"] .tr-obj__glyph {
227
+ width: 6px;
228
+ height: 6px;
229
+ margin-top: 6px;
230
+ background: var(--surface-2);
231
+ box-shadow: inset 0 0 0 var(--line) var(--ink-faint);
232
+ }
233
+
234
+ /* ---- notes + the empty state --------------------------------------------- */
235
+ .tr-note { font-size: 13.5px; color: var(--ink-faint); margin: 0; }
236
+ .tr-empty {
237
+ margin: 0;
238
+ padding: var(--sp-md) 0;
239
+ font-size: 14px;
240
+ color: var(--ink-soft);
241
+ max-width: var(--measure);
242
+ }
243
+ .tr-foot {
244
+ margin: var(--sp-md) 0 0;
245
+ font-size: 13px;
246
+ color: var(--ink-faint);
247
+ max-width: var(--measure);
248
+ }
249
+
250
+ /* ---- the phone ----------------------------------------------------------- */
251
+ /* Nothing below widens the document — the page never scrolls sideways, and the
252
+ restacking here is what makes that true without leaning on the shell's strip
253
+ at all. */
254
+ @media (max-width: 640px) {
255
+ .tr-fig__n { font-size: 26px; }
256
+ /* THE TABLE STOPS BEING A TABLE. The shell would give it a horizontal strip
257
+ (`:has(> .ledger)`) and the page would still measure clean — but five
258
+ columns inside a 390px strip is a table nobody can read: the thought column
259
+ collapses to one word per line while the object scrolls off the right, so
260
+ the row's two most important facts end up on opposite sides of a gesture.
261
+ A strip is the right answer for a ledger whose columns are a grid of
262
+ numbers; this one is a sentence plus three facts about it, and those stack.
263
+ The row becomes one flex line-box: the thought takes the width, and filed,
264
+ size and object run under it. */
265
+ .tr-table,
266
+ .tr-table tbody,
267
+ .tr-table td { display: block; width: auto; }
268
+ .tr-table thead { display: none; }
269
+ .tr-table tr {
270
+ display: flex;
271
+ flex-wrap: wrap;
272
+ align-items: baseline;
273
+ gap: 6px 16px;
274
+ padding: 14px 0;
275
+ border-bottom: 1px solid var(--rule-soft);
276
+ }
277
+ .tr-table tr:last-child { border-bottom: 0; }
278
+ .tr-table td { padding: 0; border: 0; }
279
+ .tr-table .tr-thought { flex-basis: 100%; font-size: 15px; }
280
+ /* Dropped on a phone: the outcome prose, the longest cell in the row and the
281
+ only one the idea's own record — one tap away on the title — states in
282
+ full. Size and object stay, because they are the two facts this page exists
283
+ for and the two the record does not put side by side. */
284
+ .tr-table .tr-became { display: none; }
285
+ .tr-obj__sub { white-space: normal; }
286
+ }
@@ -0,0 +1,74 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Your thinking — {{worldName}}</title>
7
+ <meta name="description" content="Everything you filed and what became of it: what it became, how much work it caused, and whether that work is still alive.">
8
+ <link rel="icon" href="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><text x='16' y='24' font-size='24' text-anchor='middle' font-family='sans-serif'>⌂</text></svg>">
9
+ <link rel="preconnect" href="https://fonts.googleapis.com">
10
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
+ <link rel="stylesheet" href="/builders/style.css?v=2026-07-02-shell1">
12
+ <link rel="stylesheet" href="/builders/thinking.css">
13
+ </head>
14
+ <!-- THE PAGE THE SPEC CALLS "THE ROOM" (task 1004098 / BV2.R03). The file is
15
+ `thinking` and not `room` because `room.css` is already taken, by the hall's
16
+ READING-ROOM archetype (the primer, diagrams, drachmae — task 1003306,
17
+ ADR 0228), and `board-room` is the government surface. Three unrelated
18
+ rooms in one directory is a collision waiting to be made by whoever next
19
+ links a sheet into a page; the page's own h1 is the design's phrase, so
20
+ nothing about the vocabulary is lost by naming the file after it. -->
21
+ <body data-page="thinking" data-page-title="Your thinking">
22
+
23
+ <div class="app">
24
+ <aside class="app-sidebar" id="app-sidebar" aria-label="Primary"></aside>
25
+ <div class="app-scrim" id="app-scrim" hidden></div>
26
+ <div class="app-body">
27
+ <header class="app-topbar" id="app-topbar"></header>
28
+ <div class="app-content">
29
+ <!-- The LABEL voice, like every other working surface: the page's own name
30
+ is the least interesting thing on it (modules/hall-ui/CLAUDE.md, the
31
+ two page-head voices). The room is a working surface, not a document,
32
+ so it takes the board's head and not the reading room's. -->
33
+ <div class="page-head page-head--board">
34
+ <h1 class="page-head__h">Your thinking</h1>
35
+ <p class="page-head__sub" id="tr-sub">Loading what you filed…</p>
36
+ </div>
37
+
38
+ <main id="hall-main">
39
+ <section class="scroll" id="tr-scroll" aria-labelledby="tr-h">
40
+ <h2 id="tr-h" class="scroll__h">What you filed, and what became of it</h2>
41
+ <p class="scroll__lede">
42
+ Your own view of your own thinking. Size is the work a thought caused and
43
+ never falls; brightness is how much of that work is still alive — that is
44
+ the part that can go out.
45
+ </p>
46
+
47
+ <!-- The figures, the filters and the rows are all written by
48
+ thinking.js from ONE read, so nothing here can be a second copy of
49
+ a number the server already computed. -->
50
+ <div id="tr-figs" class="tr-figs" role="status" aria-live="polite"></div>
51
+ <div id="tr-filters" class="tr-filters" role="group" aria-label="Show only"></div>
52
+ <div id="tr-body"><p class="tr-note">Loading…</p></div>
53
+ <p class="tr-foot" id="tr-foot" hidden></p>
54
+ </section>
55
+ </main>
56
+
57
+ <footer class="app-foot" id="app-foot"></footer>
58
+ </div>
59
+ </div>
60
+ </div>
61
+
62
+ <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
63
+
64
+ <script src="/builders/shell.js?v=2026-09-20-room1"></script>
65
+ <script src="/builders/palette.js" defer></script>
66
+ <script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
67
+ <!-- The filter strip is the kit's tab strip (makeTabs) rather than a fifth
68
+ hand-rolled pill row: it already carries the count pills, the hash sync and
69
+ the narrow-screen scroller this page would otherwise reinvent. -->
70
+ <script src="/builders/hall-kit.js"></script>
71
+ <script src="/builders/idea-objects.js?v=2026-09-20-r03"></script>
72
+ <script src="/builders/thinking.js?v=2026-09-20-r03"></script>
73
+ </body>
74
+ </html>