1
0

The phone can reach the plan, read the disclosure, and see the room

Three things, all on the door people actually open. office.lumbridgecorp.com is
a link you send somebody, and a link somebody is sent is opened on a phone.

**The plan had no way in.** The stylesheet builds it a whole bottom-sheet layout
below 600px — full width above the rail, 38dvh tall, hover-only text dropped —
and the only thing that could open it was the `M` key, which on a phone is not a
control that is hard to find but a control that does not exist. The comment
above that layout says "it is still behind `M`, so it costs nothing until it is
asked for", and on a phone it could not be asked for. So there is a button, in
the one cluster a thumb reaches, at every width rather than only the narrow ones
— a control only a keyboard can reach is a control some people do not have, and
that is as true at 1920 as at 390. It is glyph-only, because the `M` hint beside
it already carries the word and two controls labelled "plan" in one bar is one
too many, and it carries `aria-pressed` for the same reason the chapter buttons
do: a toggle that does not look like its state is a button that appears to do
nothing on every second press.

**The disclosure was behind a hamburger.** "Sample occupancy — these people are
invented" lived on `#office-badge` inside `#panel`, and on a phone `#panel` is a
sheet that starts closed. The one sentence standing between twenty-five invented
people at real desks and a screenshot presented as a staff list was hidden, on
the device most likely to take the screenshot. It moves to `#source`, which is
where this page already says what on screen is and is not real, is fixed and
always drawn, and which the phone stylesheet itself calls "the one caption that
is never allowed to be dropped for space". It replaces the city's liveness line
while you are inside rather than joining it, because the markers, the sky and
the traffic are facts about a board behind you — and "live data" printed over
invented colleagues is the exact species of lie that wording was rewritten to
stop telling. It also needed a rule of its own: `.source` on a phone is one
nowrap ellipsised line, which rendered this as "sample occupancy · these people
…", a truncation that removes the only word that mattered. A disclosure cut off
mid-clause is worse than none, because it looks like a caption somebody bothered
to write and nobody goes looking for the rest.

**The room did not fit.** `camera.fov` is vertical, so visible width is
`distance * tan(fov/2) * aspect`, and a phone held upright has an aspect near 0.5
against the 16:9 those viewpoints were framed by eye against. At the same
distance that is a third of the width: the arrival shot put a thirty-four-metre
floor plate off the corner of the screen under a frame full of empty sky. The
pack was not wrong and the renderer was not wrong — the number meant something
else on that screen. `poseFor` now preserves the width the author framed, which
is the thing they were choosing; "everything in this building is somewhere in
this frame" is a statement about width, and the extra height a tall screen throws
in costs nothing. It only ever pushes back, never pulls in, so a window wider
than 16:9 is untouched.

The correction scales the whole offset from the target and not the ground
distance alone, and that is the entire fix rather than a refinement. Multiplying
only the distance does not step back from a shot, it flattens it: 32 m out and
14 m up became ninety-odd metres out and still fourteen up, a near-horizontal
squint at the edge of a floor plate stranded near the horizon. I built that
first and it was worse than the bug.

Checked on an iPhone 13 viewport and at 1920x1080 and 1440x900. Phone: the whole
floor centred at the authored angle with people at the benches, the disclosure
on two lines and complete, the button tapped open and the plan sheet up with
eight rooms named. Desktop 16:9 is pixel-identical — the correction is exactly 1
there — and the console is clean on all three.
This commit is contained in:
2026-08-06 02:32:19 -07:00
parent dac12cecec
commit 270cddda31
3 changed files with 108 additions and 26 deletions
+37 -23
View File
@@ -840,6 +840,7 @@ function watchOccupancy() {
scene.setPresence(people);
officePlan?.setPresence(people);
renderOfficeBadge();
renderSource();
});
}
@@ -973,6 +974,35 @@ function renderLegend() {
*/
function renderSource() {
if (!source) return;
/**
* The office's provenance line, which outranks the city's.
*
* It used to live on `#office-badge` inside `#panel`, and on a phone `#panel`
* is a bottom sheet that starts closed — so the one sentence standing between
* twenty-five invented people at real desks and a screenshot presented as a
* staff list was behind a hamburger, on the device most likely to take the
* screenshot. This slot is the right home for it anyway and not merely a
* visible one: `#source` is where this page already says what on screen is and
* is not real, it is fixed and always drawn, and the phone stylesheet calls it
* "the one caption that is never allowed to be dropped for space".
*
* It replaces the city's label rather than joining it, because while you are
* standing in the office the markers, the sky and the traffic are facts about
* a board behind you — and "live data" printed over invented colleagues is the
* exact species of lie the liveness wording was rewritten to stop telling.
*/
const sample = inside && presenceIsSample;
// A class rather than an inline style, so the stylesheet keeps the decision
// about how a phone lays this out and this keeps the decision about what it
// says. The rule it turns on is the one that stops the sentence being
// ellipsised down to the half that reads as reassuring.
document.body.classList.toggle("sample-occupancy", sample);
if (sample) {
const caption = "sample occupancy · these people are invented";
if (source.textContent !== caption) source.textContent = caption;
source.hidden = false;
return;
}
const label = describeLiveness({
markers: liveData,
// An override is a sky somebody invented, so it retires the claim for as
@@ -1041,29 +1071,11 @@ function renderCredits() {
function renderOfficeBadge() {
if (!officeBadge) return;
const publicOffice = inside && office !== null && office.depth === "public";
/**
* The other thing worth saying in this spot, and the more urgent of the two.
*
* The public badge above explains an absence. This one explains a *presence*,
* which is the direction that can actually mislead somebody: twenty-five
* invented names sitting at real desks look exactly like a staff list, and the
* only thing standing between that and a screenshot presented as one is this
* sentence. `sample.ts` says the same thing to a reader of the source; this
* says it to the person looking at the room.
*
* It is not suppressed on a real deployment whose API happened to be down,
* because that is precisely the case where it is needed: a `/presence` that
* did not answer falls back to the fabricated roster, and a member who is not
* told that will read invented people as their colleagues.
*/
const sampleOffice = inside && office !== null && office.depth === "full" && presenceIsSample;
officeBadge.hidden = !publicOffice && !sampleOffice;
if (sampleOffice) {
officeBadge.replaceChildren(
document.createTextNode("Sample occupancy — these people are invented."),
);
return;
}
// The fabricated-occupancy caption used to be here too and is now on
// `#source` — see `renderSource`. This badge keeps the message that is a call
// to action rather than a disclosure, because that one belongs beside the
// office controls and survives being missed; the other one does not.
officeBadge.hidden = !publicOffice;
if (!publicOffice) return;
officeBadge.replaceChildren(
document.createTextNode("Public view — the building, not the people. "),
@@ -1269,6 +1281,8 @@ function togglePlan() {
applyPlan();
}
planToggle?.addEventListener("click", () => togglePlan());
panelToggle?.addEventListener("click", () => {
panelOpen = !panelOpen;
applyPanel();