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
+54 -3
View File
@@ -292,16 +292,67 @@ export function createOfficeScene(office: Office, options: OfficeSceneOptions):
const floorY = plan.level(viewpoint.levelId)?.floorY ?? 0;
const focus = viewpoint.focus;
const TARGET_Y = 1.2;
/**
* The correction scales the whole offset from the target, not the ground
* distance alone, and the difference is the entire fix.
*
* Pushing the camera back while leaving `height` where it was does not step
* away from the shot, it *flattens* it: the reference pack's establishing
* view is 32 m out and 14 m up, a comfortable look down onto the floor, and
* multiplying only the 32 leaves the camera ninety-odd metres away and still
* fourteen up — a near-horizontal squint at the edge of a floor plate,
* stranded near the horizon with the bottom half of the frame empty. Tried
* it; it was worse than the bug. Scaling both preserves the elevation angle
* exactly, so the shot is the one the author framed, from further away.
*/
const k = widthCorrection();
const above = Math.max(focus.height, TARGET_Y + 0.3) - TARGET_Y;
return {
target: new THREE.Vector3(focus.at.x, floorY + TARGET_Y, focus.at.z),
position: new THREE.Vector3(
focus.at.x + Math.sin(focus.rotation) * focus.distance,
floorY + Math.max(focus.height, TARGET_Y + 0.3),
focus.at.z + Math.cos(focus.rotation) * focus.distance,
focus.at.x + Math.sin(focus.rotation) * focus.distance * k,
floorY + TARGET_Y + above * k,
focus.at.z + Math.cos(focus.rotation) * focus.distance * k,
),
};
}
/**
* The aspect ratio a pack's `distance` was written against.
*
* Every viewpoint in `lumbridge-hq.ts` was framed by eye in a landscape
* browser, which makes 16:9 the honest reading of what those numbers mean —
* and this is the only place that reading is written down, so a pack author
* who wants to know what `distance: 32` promises can find out.
*/
const AUTHORED_ASPECT = 16 / 9;
/**
* How much further back a narrow viewport has to stand.
*
* `camera.fov` is *vertical*, so the width you can see is
* `distance * tan(fov / 2) * aspect` — and a phone held upright has an aspect
* near 0.5 against the 1.78 the pack was written for. At the same distance
* that is a third of the width, which is exactly what the arrival shot looked
* like: a thirty-four-metre floor plate shoved off the corner of the screen
* with the top of the frame full of empty sky. The pack was not wrong and the
* renderer was not wrong; the number simply meant something else on that
* screen.
*
* So the correction preserves the *width* the author framed, which is the
* thing they were actually choosing — "everything in this building is
* somewhere in this frame" is a statement about width, and the extra height a
* tall screen throws in for free costs nothing. It only ever pushes back,
* never pulls in: a viewport wider than 16:9 already shows more than was asked
* for, and creeping closer to trim it would crop an establishing shot on a
* desktop to make a rule tidy.
*/
function widthCorrection(): number {
const aspect = kit.camera.aspect;
if (!(aspect > 0) || !Number.isFinite(aspect)) return 1;
return Math.max(1, AUTHORED_ASPECT / aspect);
}
/**
* Where you arrive when the pack declares no viewpoints at all.
*