@bongos/core 1.24.85 → 1.24.87
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.
- package/.bongos-core.json +83 -73
- package/docs/adr/0402-helm-is-the-halls-look-and-the-hall-is-dark-only.md +15 -0
- package/docs/copy-inventory.md +22 -21
- package/docs/copy-registry.json +32 -23
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +2 -0
- package/docs/page-readings.json +195 -195
- package/modules/hall-ui/public/ai-studio.html +1 -1
- package/modules/hall-ui/public/atlas.html +1 -1
- package/modules/hall-ui/public/blockers.html +1 -1
- package/modules/hall-ui/public/board-room.html +1 -1
- package/modules/hall-ui/public/city-draw.js +127 -69
- package/modules/hall-ui/public/city.css +37 -3
- package/modules/hall-ui/public/city.html +16 -5
- package/modules/hall-ui/public/city.js +26 -1
- package/modules/hall-ui/public/collab.html +1 -1
- package/modules/hall-ui/public/commands.html +1 -1
- package/modules/hall-ui/public/copy-desk.html +1 -1
- package/modules/hall-ui/public/craft-mode.js +4 -1
- package/modules/hall-ui/public/deploy.html +1 -1
- package/modules/hall-ui/public/goal-map.html +2 -2
- package/modules/hall-ui/public/goals.html +1 -1
- package/modules/hall-ui/public/government.html +1 -1
- package/modules/hall-ui/public/home-city-draw.js +88 -54
- package/modules/hall-ui/public/home-city.css +25 -3
- package/modules/hall-ui/public/idea.html +1 -1
- package/modules/hall-ui/public/ideas.html +1 -1
- package/modules/hall-ui/public/index.html +6 -4
- package/modules/hall-ui/public/market-watch-item.html +1 -1
- package/modules/hall-ui/public/market-watch-rules.html +1 -1
- package/modules/hall-ui/public/market-watch.html +1 -1
- package/modules/hall-ui/public/modules.html +1 -1
- package/modules/hall-ui/public/money.html +1 -1
- package/modules/hall-ui/public/my-agents.html +1 -1
- package/modules/hall-ui/public/options-menu.js +4 -0
- package/modules/hall-ui/public/primer.html +1 -1
- package/modules/hall-ui/public/profile.html +1 -1
- package/modules/hall-ui/public/project-settings.html +1 -1
- package/modules/hall-ui/public/roster.html +1 -1
- package/modules/hall-ui/public/settings.css +15 -0
- package/modules/hall-ui/public/settings.html +1 -1
- package/modules/hall-ui/public/shell-game.js +82 -2
- package/modules/hall-ui/public/sky-time.js +206 -0
- package/modules/hall-ui/public/street.html +2 -2
- package/modules/hall-ui/public/studio.html +1 -1
- package/modules/hall-ui/public/style.css +118 -1
- package/modules/hall-ui/public/task.html +1 -1
- package/modules/hall-ui/public/thinking.html +2 -2
- package/modules/hall-ui/public/tweak-editor.html +1 -1
- package/modules/hall-ui/public/visor.css +13 -14
- package/modules/hall-ui/public/watch.html +1 -1
- package/modules/hall-ui/public/work.html +1 -1
- package/modules/hall-ui/records/home-city.md +13 -0
- package/modules/hall-ui/records/living-city.md +12 -0
- package/modules/hall-ui/records/options-menu.md +7 -0
- package/modules/hall-ui/records/visor.md +1 -1
- package/modules/ui-design/public/brand.html +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/src/module-api.js +1 -1
- package/tests/hall_city.mjs +72 -22
- package/tests/hall_city_living.mjs +7 -2
- package/tests/hall_options_menu.mjs +63 -4
- package/tests/hall_sky_time.mjs +361 -0
- package/tests/hall_street.mjs +2 -1
- package/tests/hall_visor.mjs +18 -8
- package/tests/home_city.mjs +54 -5
- package/tests/page_notes_panel.mjs +9 -1
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
// modules/hall-ui/public/sky-time.js — the hour of the city's sky (task 1005196; ADR 0402,
|
|
2
|
+
// "The city's sky keeps the hour").
|
|
3
|
+
//
|
|
4
|
+
// WHAT THIS IS. The city in the clouds is drawn in one of four key palettes — night, dawn,
|
|
5
|
+
// midday, dusk — or a blend of two neighbours, so the game can show time passing. The four
|
|
6
|
+
// palettes are tokens (style.css, the hall tier: --sky-<key>-<role>); this file reads them
|
|
7
|
+
// off the page once, blends them for the hour, and writes the result back as the
|
|
8
|
+
// --sky-now-<role> tokens on <html>. The painters (city-draw.js, home-city-draw.js) and the
|
|
9
|
+
// sheets (city.css, home-city.css) only ever spend --sky-now-*, so they carry no colour of
|
|
10
|
+
// their own and repaint with no redraw when the hour moves.
|
|
11
|
+
//
|
|
12
|
+
// ART ONLY. These colours paint the scene (sky, cloud, building faces, windows). They are
|
|
13
|
+
// never a control's or a sentence's colour: the hall's chrome, panels and text stay Helm
|
|
14
|
+
// night at every hour (DESIGN.md, "Dawn only in art").
|
|
15
|
+
//
|
|
16
|
+
// THE HOUR SOURCE is one function, hourNow(), and it is an OPEN OWNER DECISION what it
|
|
17
|
+
// should read (the viewer's clock, the project's, a game clock). Until that is settled it
|
|
18
|
+
// is the viewer's local clock. For tests and screenshots it can be pinned, first match wins:
|
|
19
|
+
// <html data-sky-hour-at="13"> an attribute on the root
|
|
20
|
+
// ?hour=13 in the page's query
|
|
21
|
+
// #hour=13 (or #decide&hour=13) in the page's hash
|
|
22
|
+
// A value outside 0–24 wraps round the clock; anything that is not a number is ignored.
|
|
23
|
+
//
|
|
24
|
+
// NO MOTION. The palette is set on load and at most once a minute after; nothing animates
|
|
25
|
+
// between two palettes, so there is nothing for prefers-reduced-motion to stop.
|
|
26
|
+
//
|
|
27
|
+
// PURE HALF. blend(), paletteAt(), hourFrom() and wrap() touch no DOM and no clock, so
|
|
28
|
+
// tests/hall_sky_time.mjs runs them in node; read(), apply() and start() are the page half.
|
|
29
|
+
(function (root) {
|
|
30
|
+
'use strict';
|
|
31
|
+
|
|
32
|
+
const KEYS = Object.freeze(['night', 'dawn', 'midday', 'dusk']);
|
|
33
|
+
// a colour per role, in the order DESIGN.md lists them
|
|
34
|
+
const ROLES = Object.freeze(['top', 'mid', 'cloud', 'cloud-shade', 'roof', 'lit', 'shade', 'base', 'lamp', 'line']);
|
|
35
|
+
// a 0–1 strength per number
|
|
36
|
+
const NUMBERS = Object.freeze(['windows', 'stars']);
|
|
37
|
+
// Round the clock: a key holds between two stops that name it, and blends to its
|
|
38
|
+
// neighbour between two that differ. Night holds to 4, dawn is 6, midday holds 12–14,
|
|
39
|
+
// dusk is 18, and the evening runs down to night at 24.
|
|
40
|
+
const STOPS = Object.freeze([[0, 'night'], [4, 'night'], [6, 'dawn'], [12, 'midday'], [14, 'midday'], [18, 'dusk'], [24, 'night']]);
|
|
41
|
+
// the hour each key is shown at, whole (the midpoint of a hold)
|
|
42
|
+
const KEY_HOURS = Object.freeze({ night: 0, dawn: 6, midday: 13, dusk: 18 });
|
|
43
|
+
const TICK_MS = 60 * 1000;
|
|
44
|
+
|
|
45
|
+
const wrap = (h) => { const n = Number(h); return Number.isFinite(n) ? ((n % 24) + 24) % 24 : 0; };
|
|
46
|
+
|
|
47
|
+
// '#rrggbb', 'rgb(r, g, b)' or the browser's 'color(srgb r g b)' → [r, g, b]; null when it is none of them
|
|
48
|
+
function rgbOf(c) {
|
|
49
|
+
if (Array.isArray(c)) return c.length >= 3 && c.every((n) => Number.isFinite(Number(n))) ? [Number(c[0]), Number(c[1]), Number(c[2])] : null;
|
|
50
|
+
const s = String(c == null ? '' : c).trim();
|
|
51
|
+
let m = /^#([0-9a-f]{6})$/i.exec(s);
|
|
52
|
+
if (m) { const n = parseInt(m[1], 16); return [(n >> 16) & 255, (n >> 8) & 255, n & 255]; }
|
|
53
|
+
m = /^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)/i.exec(s);
|
|
54
|
+
if (m) return [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
55
|
+
m = /^color\(srgb\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)/i.exec(s);
|
|
56
|
+
if (m) return [Number(m[1]), Number(m[2]), Number(m[3])].map((n) => n * 255);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const hex2 = (n) => ('0' + Math.max(0, Math.min(255, Math.round(n))).toString(16)).slice(-2);
|
|
60
|
+
const hexOf = (v) => '#' + hex2(v[0]) + hex2(v[1]) + hex2(v[2]);
|
|
61
|
+
|
|
62
|
+
// Which two keys the hour stands between, and how far along (0 at `a`, 1 at `b`).
|
|
63
|
+
function spanAt(hour) {
|
|
64
|
+
const h = wrap(hour);
|
|
65
|
+
for (let i = 1; i < STOPS.length; i++) {
|
|
66
|
+
if (h <= STOPS[i][0]) {
|
|
67
|
+
const a = STOPS[i - 1], b = STOPS[i];
|
|
68
|
+
return { a: a[1], b: b[1], t: b[0] === a[0] ? 0 : (h - a[0]) / (b[0] - a[0]) };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return { a: 'night', b: 'night', t: 0 };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// A LINE is drawn against the cloud (a scaffold, a lane, a plot's edge), so it is light at
|
|
75
|
+
// night and dark by day, and half-way between the two it would be the cloud's own grey.
|
|
76
|
+
// So in a span that touches night the line keeps its day colour until the cloud has gone
|
|
77
|
+
// most of the way dark (LINE_TURN: the share of night at which it starts and ends its
|
|
78
|
+
// turn), and every other role blends evenly.
|
|
79
|
+
const LINE_TURN = Object.freeze([0.6, 0.9]);
|
|
80
|
+
function lineShare(s) {
|
|
81
|
+
if (s.a === s.b || (s.a !== 'night' && s.b !== 'night')) return s.t;
|
|
82
|
+
const night = s.b === 'night' ? s.t : 1 - s.t;
|
|
83
|
+
const turned = Math.max(0, Math.min(1, (night - LINE_TURN[0]) / (LINE_TURN[1] - LINE_TURN[0])));
|
|
84
|
+
return s.b === 'night' ? turned : 1 - turned;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// The palette for an hour from the four key palettes: { top: '#rrggbb', …, windows: 0–1,
|
|
88
|
+
// stars: 0–1 }. `keys` is { night: { top, mid, …, windows, stars }, dawn: …, … } with each
|
|
89
|
+
// colour a hex, an rgb() string or an [r, g, b]. A role a key does not carry is left out.
|
|
90
|
+
function blend(hour, keys) {
|
|
91
|
+
const s = spanAt(hour), A = (keys && keys[s.a]) || {}, B = (keys && keys[s.b]) || {};
|
|
92
|
+
const out = {};
|
|
93
|
+
ROLES.forEach((role) => {
|
|
94
|
+
const a = rgbOf(A[role]), b = rgbOf(B[role]);
|
|
95
|
+
if (!a || !b) return;
|
|
96
|
+
const t = role === 'line' ? lineShare(s) : s.t;
|
|
97
|
+
out[role] = hexOf(a.map((v, i) => v + (b[i] - v) * t));
|
|
98
|
+
});
|
|
99
|
+
NUMBERS.forEach((n) => {
|
|
100
|
+
const a = Number(A[n]), b = Number(B[n]);
|
|
101
|
+
if (!Number.isFinite(a) || !Number.isFinite(b)) return;
|
|
102
|
+
out[n] = Math.round((a + (b - a) * s.t) * 1000) / 1000;
|
|
103
|
+
});
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// The pinned hour in a root attribute, a query string or a hash; null when none is a number.
|
|
108
|
+
function hourFrom(attr, search, hash) {
|
|
109
|
+
const pick = (v) => (v != null && String(v).trim() !== '' && Number.isFinite(Number(v)) ? wrap(Number(v)) : null);
|
|
110
|
+
const inQuery = (s) => { const m = /(?:^|[?#&])hour=([^&#]*)/.exec(String(s || '')); return m ? pick(decodeURIComponent(m[1])) : null; };
|
|
111
|
+
const a = pick(attr);
|
|
112
|
+
if (a != null) return a;
|
|
113
|
+
const q = inQuery(search);
|
|
114
|
+
return q != null ? q : inQuery(hash);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ======================= THE HOUR SOURCE (an open owner decision) =======================
|
|
118
|
+
// The one place the city learns what time it is: 0–24, fractional. Today: a pinned hour
|
|
119
|
+
// if the page carries one (see the header), otherwise the viewer's local clock.
|
|
120
|
+
function hourNow(win) {
|
|
121
|
+
const w = win || (typeof window !== 'undefined' ? window : null);
|
|
122
|
+
const doc = w && w.document;
|
|
123
|
+
const loc = w && w.location;
|
|
124
|
+
const pinned = hourFrom(doc && doc.documentElement && doc.documentElement.getAttribute ? doc.documentElement.getAttribute('data-sky-hour-at') : null, loc && loc.search, loc && loc.hash);
|
|
125
|
+
if (pinned != null) return pinned;
|
|
126
|
+
const d = new Date();
|
|
127
|
+
return d.getHours() + d.getMinutes() / 60;
|
|
128
|
+
}
|
|
129
|
+
// =========================================================================================
|
|
130
|
+
|
|
131
|
+
// ---------- the page half ----------
|
|
132
|
+
// The four key palettes as the page defines them, read once: a colour by letting the
|
|
133
|
+
// browser compute the token on a hidden probe (sky-draw.js's way), a number off the root.
|
|
134
|
+
let cached = null;
|
|
135
|
+
function read(doc) {
|
|
136
|
+
if (cached) return cached;
|
|
137
|
+
const d = doc || (typeof document !== 'undefined' ? document : null);
|
|
138
|
+
if (!d || !d.documentElement || typeof getComputedStyle !== 'function') return null;
|
|
139
|
+
const host = d.body || d.documentElement;
|
|
140
|
+
const probe = d.createElement('i');
|
|
141
|
+
probe.hidden = true;
|
|
142
|
+
host.appendChild(probe);
|
|
143
|
+
const rootStyle = getComputedStyle(d.documentElement);
|
|
144
|
+
const keys = {};
|
|
145
|
+
let found = 0;
|
|
146
|
+
KEYS.forEach((key) => {
|
|
147
|
+
const k = {};
|
|
148
|
+
ROLES.forEach((role) => {
|
|
149
|
+
const name = '--sky-' + key + '-' + role;
|
|
150
|
+
// an undefined token would compute to the inherited colour, so ask the root first
|
|
151
|
+
if (!String(rootStyle.getPropertyValue(name) || '').trim()) return;
|
|
152
|
+
probe.style.color = '';
|
|
153
|
+
probe.style.color = 'var(' + name + ')';
|
|
154
|
+
const v = rgbOf(getComputedStyle(probe).color);
|
|
155
|
+
if (v) { k[role] = v; found += 1; }
|
|
156
|
+
});
|
|
157
|
+
NUMBERS.forEach((n) => {
|
|
158
|
+
const v = Number(String(rootStyle.getPropertyValue('--sky-' + key + '-' + n) || '').trim());
|
|
159
|
+
if (Number.isFinite(v)) k[n] = v;
|
|
160
|
+
});
|
|
161
|
+
keys[key] = k;
|
|
162
|
+
});
|
|
163
|
+
probe.remove();
|
|
164
|
+
// a page without the hall's token tier has nothing to blend: leave the sheet's own defaults
|
|
165
|
+
if (!found) return null;
|
|
166
|
+
cached = keys;
|
|
167
|
+
return cached;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function paletteAt(hour, doc) { const keys = read(doc); return keys ? blend(hour, keys) : null; }
|
|
171
|
+
|
|
172
|
+
// Write the hour's palette onto <html> as --sky-now-<role>, and say which hour it is.
|
|
173
|
+
let lastHour = null;
|
|
174
|
+
function apply(doc, hour) {
|
|
175
|
+
const d = doc || (typeof document !== 'undefined' ? document : null);
|
|
176
|
+
if (!d || !d.documentElement) return null;
|
|
177
|
+
const h = hour == null ? hourNow(d.defaultView || null) : wrap(hour);
|
|
178
|
+
const p = paletteAt(h, d);
|
|
179
|
+
if (!p) return null;
|
|
180
|
+
const el = d.documentElement;
|
|
181
|
+
Object.keys(p).forEach((name) => el.style.setProperty('--sky-now-' + name, String(p[name])));
|
|
182
|
+
el.setAttribute('data-sky-hour', String(Math.round(h * 10) / 10));
|
|
183
|
+
lastHour = h;
|
|
184
|
+
return p;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// On load, then at most once a minute (a hidden tab catches up when it is looked at again).
|
|
188
|
+
let timer = null;
|
|
189
|
+
function start(doc) {
|
|
190
|
+
const d = doc || (typeof document !== 'undefined' ? document : null);
|
|
191
|
+
if (!d || timer) return;
|
|
192
|
+
apply(d);
|
|
193
|
+
timer = setInterval(() => { if (!d.hidden) apply(d); }, TICK_MS);
|
|
194
|
+
if (typeof d.addEventListener === 'function') d.addEventListener('visibilitychange', () => { if (!d.hidden) apply(d); });
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const api = { KEYS, ROLES, NUMBERS, STOPS, KEY_HOURS, wrap, rgbOf, spanAt, blend, hourFrom, hourNow, read, paletteAt, apply, start, hour: () => (lastHour == null ? hourNow() : lastHour) };
|
|
198
|
+
if (typeof module === 'object' && module.exports) module.exports = api;
|
|
199
|
+
if (root) {
|
|
200
|
+
root.OTBSkyTime = api;
|
|
201
|
+
if (root.document) {
|
|
202
|
+
if (root.document.readyState === 'loading') root.document.addEventListener('DOMContentLoaded', () => start(root.document));
|
|
203
|
+
else start(root.document);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
})(typeof window !== 'undefined' ? window : null);
|
|
@@ -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-
|
|
13
|
+
<link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm4d">
|
|
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">
|
|
@@ -233,7 +233,7 @@
|
|
|
233
233
|
<script src="/builders/palette.js" defer></script>
|
|
234
234
|
<script src="/builders/gov-living.js?v=2026-10-09-positions1"></script>
|
|
235
235
|
<script src="/builders/gov-feeds.js?v=2026-10-09-living3"></script>
|
|
236
|
-
<script src="/builders/city-draw.js?v=2026-
|
|
236
|
+
<script src="/builders/city-draw.js?v=2026-11-11-helm4b"></script>
|
|
237
237
|
<script src="/builders/street-draw.js?v=2026-10-09-living3"></script>
|
|
238
238
|
<script src="/builders/street-card.js?v=2026-10-04-record1"></script>
|
|
239
239
|
<script src="/builders/street-inside.js?v=2026-10-09-living3"></script>
|
|
@@ -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-
|
|
15
|
+
<link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm4d">
|
|
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">
|
|
@@ -310,11 +310,94 @@
|
|
|
310
310
|
edge: nothing on a phone, growing to its full step on a desk, with no
|
|
311
311
|
breakpoint. */
|
|
312
312
|
--night-glass: color-mix(in srgb, var(--bg) 72%, transparent);
|
|
313
|
+
/* THE PLATE ON THE SKY (task 1005196): words and controls that sit on the hour's sky stand
|
|
314
|
+
on a small plate of the night, sized to what it holds, never on a darkened sky. */
|
|
315
|
+
--night-plate: color-mix(in srgb, var(--bg) 88%, transparent);
|
|
313
316
|
--line-strong: 1.5px;
|
|
314
317
|
--helm-panel-w: 400px;
|
|
315
318
|
--helm-row: 60px;
|
|
316
319
|
--helm-mark: 60px;
|
|
317
320
|
--helm-inset: clamp(0px, calc((100vw - var(--helm-panel-w)) * .1), 96px);
|
|
321
|
+
|
|
322
|
+
/* THE SKY KEEPS THE HOUR (task 1005196). The city in the clouds is painted in
|
|
323
|
+
one of four key palettes, or a blend of two neighbours, so the game can
|
|
324
|
+
show time passing. ART ONLY: these paint the scene (sky, cloud, building
|
|
325
|
+
faces, windows) and are never a control's or a sentence's colour; the
|
|
326
|
+
chrome, the panels and the text stay Helm night at every hour. The
|
|
327
|
+
literals for the four palettes live HERE and nowhere else: a painter or a
|
|
328
|
+
sheet spends --sky-now-*, which sky-time.js sets to the hour's blend (and
|
|
329
|
+
which rest at night below, for a page that does not load it).
|
|
330
|
+
Ten roles: top (the far sky), mid (the sky), cloud (the lit cloud and the
|
|
331
|
+
light from below), cloud-shade (a cloud's far side), roof / lit / shade
|
|
332
|
+
(a building's top, its lit face, its shade face), base (what a face fades
|
|
333
|
+
to where it stands in the cloud), lamp (a lit window, a beacon, a star), line (a drawn
|
|
334
|
+
line against the cloud: scaffold, crane, lane). Two strengths, 0 to 1:
|
|
335
|
+
windows (how strongly windows glow) and stars. */
|
|
336
|
+
--sky-night-top: #02060c;
|
|
337
|
+
--sky-night-mid: #07121f;
|
|
338
|
+
--sky-night-cloud: #456780;
|
|
339
|
+
--sky-night-cloud-shade: #1a3348;
|
|
340
|
+
--sky-night-roof: #2a475e;
|
|
341
|
+
--sky-night-lit: #10263a;
|
|
342
|
+
--sky-night-shade: #081522;
|
|
343
|
+
--sky-night-base: #3c5d75;
|
|
344
|
+
--sky-night-lamp: #ffe9c9;
|
|
345
|
+
--sky-night-line: #8fb3c8;
|
|
346
|
+
--sky-night-windows: .95;
|
|
347
|
+
--sky-night-stars: 1;
|
|
348
|
+
--sky-dawn-top: #050d16;
|
|
349
|
+
--sky-dawn-mid: #17334a;
|
|
350
|
+
--sky-dawn-cloud: #fbe3c8;
|
|
351
|
+
--sky-dawn-cloud-shade: #b08e8c;
|
|
352
|
+
--sky-dawn-roof: #a9c0cc;
|
|
353
|
+
--sky-dawn-lit: #3f5b70;
|
|
354
|
+
--sky-dawn-shade: #1f3a50;
|
|
355
|
+
--sky-dawn-base: #f0c9ab;
|
|
356
|
+
--sky-dawn-lamp: #ffe2c4;
|
|
357
|
+
--sky-dawn-line: #2f4b61;
|
|
358
|
+
--sky-dawn-windows: .6;
|
|
359
|
+
--sky-dawn-stars: .3;
|
|
360
|
+
--sky-midday-top: #1d5687;
|
|
361
|
+
--sky-midday-mid: #4a90c2;
|
|
362
|
+
--sky-midday-cloud: #fff6dc;
|
|
363
|
+
--sky-midday-cloud-shade: #e8d2a8;
|
|
364
|
+
--sky-midday-roof: #fff3d4;
|
|
365
|
+
--sky-midday-lit: #dcc79c;
|
|
366
|
+
--sky-midday-shade: #7f93a8;
|
|
367
|
+
--sky-midday-base: #ffe6b3;
|
|
368
|
+
--sky-midday-lamp: #fff6dc;
|
|
369
|
+
--sky-midday-line: #2f679a;
|
|
370
|
+
--sky-midday-windows: .45;
|
|
371
|
+
--sky-midday-stars: 0;
|
|
372
|
+
--sky-dusk-top: #0a0f24;
|
|
373
|
+
--sky-dusk-mid: #2a2552;
|
|
374
|
+
--sky-dusk-cloud: #ffc79a;
|
|
375
|
+
--sky-dusk-cloud-shade: #a8748a;
|
|
376
|
+
--sky-dusk-roof: #c7a3b0;
|
|
377
|
+
--sky-dusk-lit: #4a4470;
|
|
378
|
+
--sky-dusk-shade: #231f44;
|
|
379
|
+
--sky-dusk-base: #f6b596;
|
|
380
|
+
--sky-dusk-lamp: #ffe2c4;
|
|
381
|
+
--sky-dusk-line: #302b55;
|
|
382
|
+
--sky-dusk-windows: .8;
|
|
383
|
+
--sky-dusk-stars: .6;
|
|
384
|
+
/* the hour's palette: night until sky-time.js says otherwise */
|
|
385
|
+
--sky-now-top: var(--sky-night-top);
|
|
386
|
+
--sky-now-mid: var(--sky-night-mid);
|
|
387
|
+
--sky-now-cloud: var(--sky-night-cloud);
|
|
388
|
+
--sky-now-cloud-shade: var(--sky-night-cloud-shade);
|
|
389
|
+
--sky-now-roof: var(--sky-night-roof);
|
|
390
|
+
--sky-now-lit: var(--sky-night-lit);
|
|
391
|
+
--sky-now-shade: var(--sky-night-shade);
|
|
392
|
+
--sky-now-base: var(--sky-night-base);
|
|
393
|
+
--sky-now-lamp: var(--sky-night-lamp);
|
|
394
|
+
--sky-now-line: var(--sky-night-line);
|
|
395
|
+
--sky-now-windows: var(--sky-night-windows);
|
|
396
|
+
--sky-now-stars: var(--sky-night-stars);
|
|
397
|
+
/* two mixes every scene needs: the haze between sky and cloud that distance
|
|
398
|
+
fades into, and a window's glow at the hour's strength */
|
|
399
|
+
--sky-now-haze: color-mix(in srgb, var(--sky-now-mid) 55%, var(--sky-now-cloud));
|
|
400
|
+
--sky-now-glow: color-mix(in srgb, var(--sky-now-lamp) calc(var(--sky-now-windows) * 100%), transparent);
|
|
318
401
|
}
|
|
319
402
|
|
|
320
403
|
/* ---------- dark theme: the Helm neutrals ----------
|
|
@@ -809,7 +892,7 @@ html[data-theme="dark"] .brand-mark__mark { --mark-ink: #e2e1e4; }
|
|
|
809
892
|
html[data-game="on"] .visor__hint--sheet .visor__dots { display: block; }
|
|
810
893
|
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
894
|
html[data-game="on"] .visor__main .visor__key { display: none; }
|
|
812
|
-
html[data-game="on"] .visor__sheet { bottom: calc(var(--visor-inset-bottom) +
|
|
895
|
+
html[data-game="on"] .visor__sheet { bottom: calc(var(--visor-inset-bottom) + 112px); max-height: 60vh; }
|
|
813
896
|
}
|
|
814
897
|
/* task 1004424 (BV2.PS10): THE FOUNDING MARK. A pill in .rank-badge's grammar (same
|
|
815
898
|
height, type and radius) so it sits beside a rank without competing with it; the
|
|
@@ -1160,6 +1243,8 @@ html[data-theme="dark"] .gplate__light { display: none; }
|
|
|
1160
1243
|
cursor: pointer;
|
|
1161
1244
|
}
|
|
1162
1245
|
.upd-pill:hover { border-color: var(--ink-faint); }
|
|
1246
|
+
/* in game mode the pill floats over the scene, which by day is a bright sky: it stands on the night */
|
|
1247
|
+
html[data-game="on"] .upd-pill:not([data-face="done"]) { background: var(--bg-card); }
|
|
1163
1248
|
.upd-pill:focus-visible { outline: 0; border-radius: var(--press-radius); }
|
|
1164
1249
|
.upd-pill__dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); flex-shrink: 0; }
|
|
1165
1250
|
.upd-pill[data-face="held"] .upd-pill__dot { background: var(--ink-faint); }
|
|
@@ -1398,6 +1483,38 @@ html[data-embed="on"] .app-foot,
|
|
|
1398
1483
|
html[data-embed="on"] .app-topbar { display: none; }
|
|
1399
1484
|
html[data-embed="on"] .app-body { margin-left: 0; }
|
|
1400
1485
|
html[data-embed="on"] .app-content { padding-top: 0; }
|
|
1486
|
+
/* An embedded screen is a sub-window, so it reads at window size, not page size
|
|
1487
|
+
(the owner, 2026-11-11: "WoW sizing"): everything in it is drawn smaller, and it
|
|
1488
|
+
has no ground of its own, so the options window's glass and the place behind
|
|
1489
|
+
show through. */
|
|
1490
|
+
html[data-embed="on"] { zoom: 0.82; }
|
|
1491
|
+
html[data-embed="on"], html[data-embed="on"] body { background: transparent; }
|
|
1492
|
+
/* THE OPTIONS WINDOW (shell-game.js openOverlay): in game mode the menu opens over
|
|
1493
|
+
the place you are in. The scrim only darkens a little, so the place stays legible;
|
|
1494
|
+
the window is night glass, so its colours carry through. */
|
|
1495
|
+
html.opt-window-open { overflow: hidden; }
|
|
1496
|
+
.opt-window { position: fixed; inset: 0; z-index: var(--z-modal); display: grid; place-items: center; }
|
|
1497
|
+
.opt-window[hidden] { display: none; }
|
|
1498
|
+
/* the scrim is the overlay's own ground, so a click on it lands on the dialog root (OTBDialog closes) */
|
|
1499
|
+
.opt-window::before { content: ''; position: absolute; inset: 0; background: var(--scrim); opacity: .33; pointer-events: none; }
|
|
1500
|
+
.opt-window__box {
|
|
1501
|
+
position: relative; box-sizing: border-box;
|
|
1502
|
+
width: min(1040px, calc(100vw - 32px)); height: min(720px, calc(100vh - 48px));
|
|
1503
|
+
background: color-mix(in srgb, var(--bg) 48%, transparent);
|
|
1504
|
+
-webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
|
|
1505
|
+
border: var(--line-strong) solid var(--rule);
|
|
1506
|
+
border-radius: var(--press-radius);
|
|
1507
|
+
box-shadow: var(--ds-shadow-pop);
|
|
1508
|
+
}
|
|
1509
|
+
.opt-window__frame { display: block; width: 100%; height: 100%; border: 0; background: transparent; color-scheme: dark; }
|
|
1510
|
+
.opt-window__x {
|
|
1511
|
+
position: absolute; top: var(--ds-space-xs); right: var(--ds-space-xs); z-index: 1;
|
|
1512
|
+
width: 28px; height: 28px; padding: 0; display: grid; place-items: center;
|
|
1513
|
+
font-size: var(--ds-text-2xl); line-height: 1; color: var(--ink-soft);
|
|
1514
|
+
background: transparent; border: var(--line) solid var(--rule-soft); border-radius: var(--press-radius); cursor: pointer;
|
|
1515
|
+
}
|
|
1516
|
+
.opt-window__x:hover { color: var(--ink); background: var(--glass); }
|
|
1517
|
+
.opt-window__x:focus-visible { outline: 0; box-shadow: var(--press-focus); }
|
|
1401
1518
|
/* Said once on entering, since the choice is remembered and a page can open in it. */
|
|
1402
1519
|
.fullscreen-hint {
|
|
1403
1520
|
position: fixed;
|
|
@@ -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-
|
|
11
|
+
<link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm4d">
|
|
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-
|
|
16
|
-
<link rel="stylesheet" href="/builders/visor.css?v=2026-11-11-
|
|
15
|
+
<link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm4d">
|
|
16
|
+
<link rel="stylesheet" href="/builders/visor.css?v=2026-11-11-helm4c">
|
|
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
|
|
@@ -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-
|
|
13
|
+
<link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm4d">
|
|
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
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
Hairlines, not cards: nothing on the glass is a box but the button you press and the key in
|
|
5
5
|
a hint. It shows in game mode only, and it is an overlay, never layout, so the stage under it
|
|
6
6
|
never changes size (ADR 0399 §2.1).
|
|
7
|
-
IT READS OVER ANY SKY, with no per-hour style
|
|
8
|
-
|
|
9
|
-
wider stroke of the night
|
|
7
|
+
IT READS OVER ANY SKY, with no per-hour style, AND IT DARKENS NONE OF IT (task 1005196: the
|
|
8
|
+
hour's sky runs to every edge of the view, so the glass has no bands and no vignette). The
|
|
9
|
+
marks carry themselves: every line is drawn over a wider stroke of the night, a key sits on a chip of
|
|
10
|
+
the night, and the place, each hint and your credits stand on a small night plate. Colours, sizes and corners
|
|
10
11
|
are the hall tier's tokens; nothing here is typed in. Record: modules/hall-ui/records/visor.md. */
|
|
11
12
|
.visor { display: none; }
|
|
12
13
|
html[data-game="on"] .visor:not([hidden]) {
|
|
@@ -14,7 +15,6 @@ html[data-game="on"] .visor:not([hidden]) {
|
|
|
14
15
|
/* where the glass sits, so a scene's pop-up can keep clear of it (ADR 0399 §2.3) */
|
|
15
16
|
--visor-inset-top: 64px; --visor-inset-bottom: 76px;
|
|
16
17
|
--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));
|
|
18
18
|
color: var(--ink); font-family: var(--font-body);
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -26,17 +26,15 @@ html[data-game="on"] .visor:not([hidden]) {
|
|
|
26
26
|
.visor__frame--foot { bottom: calc(var(--visor-inset-bottom) - 44px); height: 18px; }
|
|
27
27
|
.visor__frame path { vector-effect: non-scaling-stroke; }
|
|
28
28
|
.visor__line { fill: none; stroke: var(--ink-soft); stroke-opacity: .6; stroke-width: var(--line-strong); }
|
|
29
|
-
.visor__line--under { stroke: var(--
|
|
30
|
-
.visor__dot { fill: var(--ink-soft); stroke: var(--
|
|
29
|
+
.visor__line--under { stroke: var(--bg); stroke-opacity: 1; stroke-width: 5px; }
|
|
30
|
+
.visor__dot { fill: var(--ink-soft); stroke: var(--bg); stroke-width: var(--line-strong); }
|
|
31
31
|
.visor__notch, .visor__sight { position: absolute; left: 50%; transform: translate(-50%, -50%); overflow: visible; }
|
|
32
32
|
.visor__notch { top: calc(var(--visor-inset-top) - 36px); width: 12px; height: 9px; }
|
|
33
33
|
.visor__sight { top: 50%; width: 24px; height: 24px; }
|
|
34
34
|
|
|
35
|
-
/* type over the scene
|
|
36
|
-
|
|
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
|
-
}
|
|
35
|
+
/* type over the scene stands on a small plate of the night, cut like everything pressable and
|
|
36
|
+
sized to its words: the place, each key hint, and your name and credits. No band, no halo. */
|
|
37
|
+
.visor__place, .visor__who, .visor__hint { background: var(--night-plate); clip-path: var(--cut-path-sm); }
|
|
40
38
|
/* the name voice, for every name on the glass */
|
|
41
39
|
.visor__place, .visor__name, .visor__unit, .visor__main, .visor__hint, .visor__key, .visor__sheet-title, .visor__cap {
|
|
42
40
|
font-family: var(--name-face); font-weight: var(--name-weight); text-transform: var(--name-case);
|
|
@@ -46,7 +44,7 @@ html[data-game="on"] .visor:not([hidden]) {
|
|
|
46
44
|
.visor__place {
|
|
47
45
|
position: absolute; left: 50%; top: calc(var(--visor-inset-top) - 26px); transform: translateX(-50%); margin: 0;
|
|
48
46
|
font-size: var(--ds-text-2xl); letter-spacing: var(--name-space-wide); line-height: 1.2;
|
|
49
|
-
color: var(--ink-soft); white-space: nowrap;
|
|
47
|
+
color: var(--ink-soft); white-space: nowrap; padding: 0 var(--ds-space-xs);
|
|
50
48
|
}
|
|
51
49
|
|
|
52
50
|
/* the foot row: you on the left, the one thing to press in the middle, the key hints on the
|
|
@@ -63,7 +61,8 @@ html[data-game="on"] .visor:not([hidden]) {
|
|
|
63
61
|
flex: none; width: 56px; height: 56px; object-fit: cover; object-position: 50% 12%;
|
|
64
62
|
background: var(--glass-strong); clip-path: var(--cut-path-sm);
|
|
65
63
|
}
|
|
66
|
-
|
|
64
|
+
/* your name and credits stand in a box of the night, cut like the sprite beside it */
|
|
65
|
+
.visor__who { display: flex; flex-direction: column; gap: 2px; padding: var(--ds-space-hair) var(--ds-space-xs); }
|
|
67
66
|
.visor__name { font-size: var(--ds-text-xs); letter-spacing: var(--name-space); line-height: 1; color: var(--ink-soft); }
|
|
68
67
|
.visor__credits { display: flex; align-items: baseline; gap: var(--ds-space-xs); white-space: nowrap; }
|
|
69
68
|
.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; }
|
|
@@ -88,7 +87,7 @@ html[data-game="on"] .visor:not([hidden]) {
|
|
|
88
87
|
}
|
|
89
88
|
.visor__hint {
|
|
90
89
|
pointer-events: auto; box-sizing: border-box; display: inline-flex; align-items: center; gap: var(--ds-space-hair);
|
|
91
|
-
min-height: 44px; padding: 0
|
|
90
|
+
min-height: 44px; padding: 0 var(--ds-space-xs) 0 var(--ds-space-hair); border: 0; border-radius: var(--press-radius);
|
|
92
91
|
color: var(--ink-soft); font-size: var(--ds-text-lg); letter-spacing: var(--name-space-tight); white-space: nowrap;
|
|
93
92
|
text-decoration: none; cursor: pointer;
|
|
94
93
|
transition: color var(--ds-dur-fast) var(--ds-ease);
|
|
@@ -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-
|
|
14
|
+
<link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm4d">
|
|
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-
|
|
11
|
+
<link rel="stylesheet" href="/builders/style.css?v=2026-11-11-helm4d">
|
|
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. -->
|
|
@@ -208,6 +208,19 @@ So choosing Governing on Home, or opening Home with it chosen, goes to `GovCity.
|
|
|
208
208
|
(`home-city.js` `streetHop`). In Governing the street's "← Home" link therefore lands back on the street;
|
|
209
209
|
switching craft from the top bar leaves it. Pinned in `tests/home_city_widget.mjs`.
|
|
210
210
|
|
|
211
|
+
## The city in the clouds, and the hour (task 1005196)
|
|
212
|
+
|
|
213
|
+
The same city, repainted (owner direction, 2026-11-11; ADR 0402's amendment and `DESIGN.md` "The sky keeps the hour"). Nothing about the layout, the data, the doors or the tips changed.
|
|
214
|
+
|
|
215
|
+
- **No ground.** The ground plane is gone. A box that stands on the ground (a plinth, the museum's wings, a civic base) fades into the cloud at its foot through the two gradients `city-draw.js` `skyDefs` describes, which this scene puts in its `<defs>`; a box above the ground keeps plain faces. Banks of cloud lie behind the city, a puff at the foot of each tower, and a bank in front (`.hc-cloud--*`, softened by two SVG filters in the scene's own `<defs>`).
|
|
216
|
+
- **No colour is typed in the painter.** `theme()` and `skyTheme()` name the hour's tokens (`--sky-now-*`) and mixes of them; a mode argument is ignored (the hall is dark only). The artists' glass is the pack's `--sea`, a face pin is `--bg-card` and `--ink`, and what waits on you is still the only `--accent`.
|
|
217
|
+
- **The sky runs edge to edge and nothing darkens it** (owner, 2026-11-11). With the bars up, the sky fills the city's panel (`.hc-stage`). In game mode it is the ground of the whole Home view, behind the visor: Home sets the hall's own ground seam (`--plate`, `style.css` `body::before`) to the hour's sky and turns the veil off, and the panel is clear. The light from below is the scene's own (`.hc-svg`, a glow that ends inside the scene), so it sits under the city at any width. What is read on the sky stands on a plate of the night (`--night-plate`): the ladder (two strong hairlines, sized to its rungs) and, in game mode, the greeting; the owner chip and the slider keep their own night surfaces. Tried and dropped: an oval of sky floating in the dark (a spotlight), then a sky that faded to a night stage toward the ladder and the slider (dark edges).
|
|
218
|
+
- **The cloud's shape is `city-draw.js`'s** (`Gov.cloudAt`, `Gov.puffAt`), asked for and never copied; a scene drawn without the Governor City has plain faces and no cloud.
|
|
219
|
+
- **The idea stars follow the hour.** They sit in one group (`.hc-stars`) shown at `--sky-now-stars`: out at night, gone by day. The door under them is there at every hour.
|
|
220
|
+
- **Quiet still reads.** The faded core and museum (`.hc-quiet`) stand at about 60% and part-drained, not 30% and grey, so they read against bright cloud.
|
|
221
|
+
- **The hour** is `sky-time.js` (loaded before the painter): the viewer's clock, or `/?hour=13` to pin it.
|
|
222
|
+
- Pinned by `tests/home_city.mjs` (tokens only, the cloud's place in the draw order) and `tests/hall_sky_time.mjs`.
|
|
223
|
+
|
|
211
224
|
## Private areas
|
|
212
225
|
|
|
213
226
|
A private goal the viewer is neither a member of nor an Archon over still stands as a tower (its counts are structure), but the server sends no title for it or for the ideas tied to it; the drawing labels them "A private area" and "A private idea" (ADR 0112, as the goal map and the sky apply it). Pinned in `tests/home_city.mjs`.
|
|
@@ -57,3 +57,15 @@ The city's plaque and door, the street's header and readings, the deck and the s
|
|
|
57
57
|
## Verifying
|
|
58
58
|
|
|
59
59
|
`node scripts/hall-preview/server.js --port 4620 --fixture-me --rank archon`, then `node modules/ui-design/kit/render.js --page city|street|my-agents` at 1440, 390 and 320 in both modes (restart the harness after editing a page's HTML). The states' expectations are the harness fixtures' (task 1004718 refreshed the ones that went stale when the harness became the real site). Tests: `tests/hall_gov_living.mjs`, `tests/hall_gov_counts.mjs`, `tests/hall_city_living.mjs`, `tests/hall_street_depth.mjs`, `tests/hall_street_deck.mjs`, `tests/hall_buildouts.mjs`, `tests/craft_mode_tallies.mjs`, `tests/hall_gov_real_site.mjs`.
|
|
60
|
+
|
|
61
|
+
## The city in the clouds, and the hour (task 1005196)
|
|
62
|
+
|
|
63
|
+
The owner's direction of 2026-11-11: the same single city, made a city in the clouds by art and colour, with the time of day shown as a full round (ADR 0402's amendment; `DESIGN.md` "The sky keeps the hour"). The layout, the buildings, the data, the pins, the plates, the hit targets and the motion are unchanged; the geometry is still the prototype's, path for path (`tests/hall_city.mjs`).
|
|
64
|
+
|
|
65
|
+
- **No ground.** `cityModel` draws no ground plane and no road fill. The ring road keeps its dashed lane marks (the parcels and the spend conduit run along them), the plaza is a patch of light from below, and a plot keeps its faint outline and grid.
|
|
66
|
+
- **Bases fade into cloud.** `isoBox` gives a box that stands on the ground the fading face (`tone.lg` / `tone.rg`: a gradient, then the plain colour as the fallback for a drawing with no `<defs>`, which is how the street's office drawing still paints). `skyDefs` lists the gradients, one pair per haze share in use; `city.js` puts them in `#cityDefs`.
|
|
67
|
+
- **Cloud and stars are data.** `model.clouds` is two banks behind the city, a row of puffs per depth row (one at each standing building's foot; `city.js` lays each row after its buildings) and three banks in front; `model.stars` is one path. The sheet gives each its sky token; two SVG filters in `city.html` soften the banks. Nothing that moves is blurred.
|
|
68
|
+
- **Haze** now mixes toward `--sky-now-haze` (sky into cloud) instead of the void, at the same shares.
|
|
69
|
+
- **Every colour is the hour's.** `isoTheme()` names `--sky-now-*` tokens and mixes; a mode argument is ignored. Lit windows (`--city-win-lit`) and the dashed window rows are the lamp at the hour's window strength, so they are strongest at night; salmon windows and pins are still the only `--accent`.
|
|
70
|
+
- **What you read stays Helm night, and the sky is never darkened for it.** The frame is the hour's sky to all four edges (no vignette, no bands; the lowest bank of cloud is a shaded cloud, not a dark one). The wordmark, the plaque, the line under the door and the archive note each stand on a small cut plate of the night (`--night-plate`), and in game mode the Update pill is solid night; a moving or settled pin and a pod take a night edge; lanes, cranes and the tally take the hour's `line` colour, which is light at night and dark by day.
|
|
71
|
+
- **The hour** is `sky-time.js`, loaded before `city-draw.js`: the viewer's local clock (an open owner decision), or pinned with `/city?hour=13`, `#hour=13` or `<html data-sky-hour-at="13">`. Set on load and at most once a minute; nothing animates.
|
|
@@ -24,3 +24,10 @@ The menu is a Helm screen, written from the hall tier's tokens alone (`settings.
|
|
|
24
24
|
- **Widths:** the panel is `min(--helm-panel-w, 32%)` of the content column; below 860px it stacks above the screen as before. A long label wraps inside its row.
|
|
25
25
|
- **Not built** (the mock shows them on a Controls screen the hall does not have): the key-binding rows, "Reset all keys", and a visible "Back to the city" button. Escape still closes the menu in game mode.
|
|
26
26
|
- **Tests:** `tests/hall_helm.mjs` ("the options menu is a Helm screen"); the kit states in `settings.states.json` render clean at 1440, 390 and 320.
|
|
27
|
+
|
|
28
|
+
## The options window (owner, 2026-11-11: "WoW sizing, an overlay")
|
|
29
|
+
|
|
30
|
+
- **In game mode the menu opens over the place, not as a page.** Escape away from the menu, or a click on any link to `/settings` (the visor's Options row, the profile chip), draws one window (`shell-game.js` `openOverlay`, `window.OTBOptionsOverlay`), built on the shared dialog (`OTBDialog.bind`: Escape, scrim, close button, focus trap and return), with the page behind made `inert` while it is up, holding the menu framed with `?embed=1`; the place stays drawn behind it. Escape, the scrim or the × shuts it; `options-menu.js` `close()` shuts the window when it is in one. A page that cannot draw it falls back to the menu page. A plain visit to `/settings` is the page, as before.
|
|
31
|
+
- **Window sized, see-through.** The window is `min(1040px, …) × min(720px, …)` of glass at 48% of the ground with a light blur, over the shared `--scrim` at a third. Every embedded screen is drawn at `zoom: 0.82` with no ground of its own (`style.css`), and the menu inside it takes smaller words (`settings.css`, `html[data-embed="on"]`); a window-high length divides by the zoom (`--opt-vh`).
|
|
32
|
+
- **One review palette.** A framed screen docks no artist palette of its own (`craft-mode.js` `applyDocked`); the page around it has one.
|
|
33
|
+
- **Tests:** `tests/hall_options_menu.mjs` ("away from the menu, Escape opens the options as a window…"), `tests/page_notes_panel.mjs` (the frame docks no panel).
|
|
@@ -9,7 +9,7 @@ The glass was redrawn to the owner's mock-up "Cloud city: in the visor, minimal"
|
|
|
9
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
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
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
|
|
12
|
+
- **It reads over any sky, with no per-hour style, and it darkens none of it** (task 1005196, owner 2026-11-11: "I don't like how dark the edges of the view are"). The glass paints nothing of its own: the bands of `--night-glass` at its top and bottom edge are gone, and so is the dark halo round its type. The marks carry themselves. The place, each key hint and your name and credits stand on a small cut plate of the night (`--night-plate`, the ground at 88%), sized to the words; every line is drawn twice (a wider solid stroke of the night under the ice hairline, `.visor__line--under`), so an arc is ice on night and a dark line on cream cloud; a key sits on a chip of the night. One new token, `--night-plate`. Tried first and dropped: a 4px night stroke round each letter with no plate, which filled the condensed capitals in.
|
|
13
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
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
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.
|