Text a phone can read
Status: built 2026-10-01; L3's phone pass (§7) 2026-10-02. Read with touch-controls.md (the layout that shows the frame small), m3-verification.md (decision 9 and "Text calibration": how the engine reports its text, and L3's 18 px floor), ADR-0002 (games are data) and ADR-0007 (Compatibility is the baseline).
Every gallery game plays on a phone with its touch controls drawn beside the game's frame. The frame keeps its 1920x1080 render size and is scaled into the 16:9 rect the controls leave: 390x219 CSS pixels on a 390x844 phone held upright, 503x283 sideways (528x297 for the platformers, which have fewer buttons). kit.ui sizes its text for the frame's rows, so a phone showed the game's own text at a fifth to a quarter of the size a 1080p monitor does. L3 measures text against the frame (18 px at 1080 rows, XAG 101's PC minimum), so it could not see this.
1. What a phone showed
The engine reports every text it draws (GwView.texts: its ink in the frame and its body height,
a capital's top to a descender's foot, as XAG 101 measures). Placed through where the frame sits
on the screen, that is CSS pixels. The smallest text each game showed in the phone visit
(tools/web.sh --touch, three device pixels a CSS pixel), before this change:
| Game | UI pitch at 1080 rows | Upright (frame 390x219) | Sideways (503x283 or 528x297) |
|---|---|---|---|
| Fog Signal | 3 (visual_novel) | 5.5 (every line) | 7.1 |
| Tideline | 3 (action_survival, 2 at 720) | 5.5 | 7.1 |
| Nightcoast | 3 (action_survival) | 5.5 | 7.1 |
| Lamplighter | no HUD: kit.grid's header in Godot's face, 39 and 30 px | 6.1 ("turn 0") | 7.9 |
| Burrow Run | 6 (platformer_2d) | 11.0 | 7.7 (kit.platformer's "paused", 28 px) |
| Acorn Rooftops | 6 (platformer_2d) | 11.0 | 7.7 ("paused") |
| Pier Pressure | 6 (puzzle_grid) | 11.0 | 14.1 |
With a keyboard on the phone (a key hides the controls and the frame fills the window, 693x390), the pitch-3 games showed 9.8.
2. The minimum: 11 pt, Apple's
Apple's Human Interface Guidelines (Typography, "Ensuring legibility") give iOS and iPadOS a default text size of 17 pt and a minimum of 11 pt, "for both custom and system fonts", and ask that game text be tested on each platform it runs on. On the web a CSS pixel is a point. Material Design's smallest style (Label small) is 11 sp too.
11 pt is a font's em. The engine reports body height, so the floor is put in those terms per face:
- The slate face (kit.ui,
GwUiFont): its em is ten pitches (size_at), its capitals seven (0.70 em; SF Pro's cap height is 0.705 em, so the comparison is like for like), its body nine. An 11 pt em is a body of 9.9 CSS pixels, a pitch of 1.1 points. - Godot's own face (Open Sans SemiBold, which kit.grid's header and kit.platformer's banner use): measured at 200 px, its ink runs from 0.765 em above the baseline to 0.24 below, a body of 1.005 em. An 11 pt em is an 11-pixel body there, over the check's 9.9.
The check (§5) fails a body under 9.9 CSS pixels.
Not taken:
- XAG 101's mobile size, 18 px at 100 DPI (0.18 inch of body). On a 390x844 iPhone (153 points an inch) that is 27.6 points of body: a pitch of 16 frame pixels upright and 12 sideways, a room of 120x67 and 160x90 pitches, where no gallery screen fits (Fog Signal's dialogue box alone is 80 pitches tall). XAG gives it for mobile and game streaming, and asks for a text size the player can set besides; that setting is "Not yet" (§8). L3 left XAG's console size aside for the same reason: the accepted games could not meet it.
- Lighthouse's 12 px (the "legible font sizes" audit): a page-level score for body copy (60% of a page's text), not a floor for every label. What shipped meets it for the slate face anyway (a 12 px em is a 10.8 body; the smallest is 11.0).
3. Options, measured
The text's size on the screen is its share of the frame times the frame's size there. The frame can't grow on a phone (the controls take the rest), so only the share can.
- Render the frame smaller on a small screen. With
pitchHeight(every gallery Style has it) the pitch follows the frame, so the UI keeps its share, give or take rounding. Measured on Fog Signal's pitch 3 at 1080 rows: rendered at the upright frame's own 658 device rows, the pitch rounds to 2 and the body is 6.0 CSS pixels (5.5 now); at the sideways frame's 849 rows it also rounds to 2, and the body falls to 6.0 (7.1 now); at a fixed 960x540 it is 7.3 and 9.4. Rounding can give at most half a pitch, and these Styles needed twice the share. Alone it never reaches 9.9, and it costs every view a frame whose size changes when the phone turns (a 2D game's pixel scale with it). - Draw the UI bigger when the frame is shown small. The pitch-3 Styles need 6 upright and 5 sideways (1.1 points over 0.203 and 0.262 points a frame pixel), the pitch-6 Styles nothing. A UI designed for a room of 640x360 pitches then has 320x180 or 384x216: the layout must give.
- Both. Rendering at the screen's size would make each glyph pixel whole device pixels instead of the 0.61x (upright) or 0.79x (sideways) downscale the touch layout gives the frame. The screenshots (§6) show the face reads crisply enough at those scales, so the cost buys nothing now.
Chosen: 2. It is the only one that reaches the floor, it changes nothing a desktop draws, and it keeps the touch layout's promise that the game renders as it does without the controls.
4. How it works
The frame on the screen. In a person's run the player works out where its frame server sits
on the window and how big (GwPlayer.frame_on_screen: the window's content scale and, while the
touch controls show, the frame's own) and so how many points one frame pixel takes
(frame_points, over the screen's scale, the page's devicePixelRatio). It sets it on the stage
every frame (GwStage.frame_points). A tool run (a tour, a replay, a shot) is drawn for no
screen (0), so its frames are what they were, unless --points P names one: tours then shoot what
that phone shows (0.2031 upright, 0.2620 sideways). Presentation only: nothing reaches the
simulation.
The reading pitch (kit.ui, GwUiTokens.read_at). The UI's pitch is the Style's, fitted to the
frame as before (style_pitch), or the least whole pitch that is 1.1 points on the screen, if that
is bigger (GwStage.READ_EM_PT over the face's ten-pitch em). Still whole pitches: crisp in the
frame's pixels. When the frame's size on the screen changes (the phone turns, a key hides the
controls, a window is resized) GwUiView reads it again and, if the pitch changed, rebuilds the
theme and lays every screen out again.
Compact layout. When the pitch is bigger than the Style's, the UI is compact: each screen has
fewer pitches of room than it was designed for (GwUiPlace). A person's Text size makes it
compact too: at 200% the pitch doubles.
- Scenery stays. A fill or bare frame with no title, or a sprite, that holds nothing but scenery keeps the Style's pitch and place: a backdrop is the same picture on every screen (Fog Signal's pier, lamp room and shore; its characters).
- Nothing leaves its parent. A widget too big for its parent shrinks to it, keeping its offset
(at least six pitches,
FIT_MARGIN) clear at each side; one past an edge moves in. - Text reflows. A label made narrower breaks its words over lines to the new width and grows as tall as they need, whether it wraps on a monitor or not (Nightcoast's two-line opening message on an upright phone; Thornwick's quest line at 200%). A title (a label with a scale) first steps its scale down until its text fits, never under 1: "THORNWICK" whole instead of "THORN…".
- A box holds its words. A box (a frame that is not scenery) whose wrapped words need more lines than it holds, or whose words grown down run into the hints on its foot, grows by that much along its anchor: a talk's box at the foot grows up, and what is in it is placed again in the room that gives (Thornwick's sign at 200%: four lines, "go on" under them).
- A stack stays a stack. A widget that grew moves the shown widgets after it on its side out of its way: below a top-anchored one, above a bottom-anchored one (the errand under the quest line), never out of its parent. Widgets drawn over each other on purpose stay so. The top's stack and the bottom's can still meet on a small frame; there, a compact place is the author's fix.
- A label made to wrap is as tall as its lines, never taller: the menu's note, a two-line box holding "←→ change", gives its second line back. It shrinks inside where its box was, so it runs into nothing it didn't.
- The answers join their question. A pane holding only a list (a talk's answers) that would run over a box beside it holding wrapped words (the question) joins that box: the list sits between the box's words and its hints, the box grows to hold its rows, and the pane drops its own glass and title. Two panes can't share the frame at 200%, where each one's padding doubles: on an upright phone Thornwick's answers covered the first lines of Maren's question; joined, her three lines, the three answers and "↑↓ choose", "OK say it" all fit, whole. A longer question leaves room for fewer rows, and the list scrolls; one too long to leave a row under it even in a box as tall as the frame (Fog Signal's lamp room, seven lines at 200%) keeps the pane where it was drawn.
- A list takes the room it has. A list that would run over the widgets above or below it (a label with nothing to say takes no room), or past its parent, starts under those above, no higher than it would rest, and ends over those below; it shows the rows whose words end inside it, a row fewer when the rest need the scroll mark under them, and scrolls to keep the chosen one there. The menu's Options page on an upright phone at 200% shows three rows under its heading and over its note, where its seven-row list had been pushed up over the heading ("OPTIONS" over "Volume"); the pause page shows all four of its rows.
- A list's row wraps. A row whose label is too long for the room its detail leaves breaks over more lines, up to three, and its lit bar grows to hold them; on a big screen it is cut as before. Fog Signal's "Tell him to wait for the fog to pass" at 200% on an upright phone reads whole over two lines under Tomas's question, where it showed "Tell him to wait for the …".
- A quieter line yields. A widget whose entry says
yieldsis hidden while it would run into a shown widget beside it that doesn't yield and has words to read, in it or in what it holds (an empty or faded line, or a row of hearts, takes no room), and comes back once there is room: on a big screen nothing yields. Thornwick's quest line, errand and eat hint yield, as do the adventure template's quest line and eat hint, so a message that grows up the frame on a phone at 200% is read whole and the reminder above it returns when it fades: 7 of Thornwick's 8 red frames at 200% went green. The speaker's plate yields too (Thornwick, Fog Signal and both talk templates), to a box its answers joined; on a plain line it stays, over the hearts. For reminders and names said just before, never for the only place something is said. - The slate is all glass. Its device is no bigger than the frame; on a phone it fills the frame edge to edge with no bezel, and its apps get the body that leaves.
- A compact place. Where the rules can't know the design (two panes side by side, a row of
hints), a Screen widget may say where it sits there:
compact: {offset, size}(schemas/v1/screen.schema.json), in pitches of the bigger pitch. The templates use it three times: action_survival's carry and make apps put the list (160 pitches) and the note pane (136) side by side, which on a desktop are 250 and 196; visual_novel's title brings its outer hints in from 40 pitches to 12.
Text in Godot's face. kit.grid's header (a game with no HUD) and kit.platformer's "paused"
banner draw at least GwStage.least_font_px() (11 pt in frame pixels), the header's band and the
banner's plate growing with it. They report their text now (GwView.plain_text_row), so L3's text
checks and this one see it.
What stays the same. A tool run is drawn for no screen, so a desktop shot is what it was (§6). A desktop window changes nothing for the gallery's Styles while the frame shows at least 424 points tall (a pitch of 3 at 1080 rows is 1.1 points from 396 points up; the 28 px banner from 424). The simulation never sees any of it: every proof tour ends on the same state hash as on main.
5. The check
On the web, when a check asks for it (--say-texts=on, passed by the harness's phone visit through
window.GAMEWRIGHT_TEST_ARGS; a person's visit leaves it off), the player says what the screen
shows of the game's text whenever it changes, looked at four times a second and at once when the
frame moves on the screen, each time after the frame is drawn (a view that writes its text as it
draws, kit.platformer's banner and kit.grid's header, has drawn this frame's by then: said before
the draw, Burrow Run's "paused" banner, sized for the layout a key had left, was measured against
the touch layout that came back, 8.5 CSS px, and failed a deploy one time in about fifteen):
gamewright text: {window, frame, points, texts}, each text with its screen,
widget, what is shown, px (its body in the frame's pixels), css (that body in CSS pixels),
rect (its ink on the screen), whole, lines, fading, and under when a pane drawn after it
covers it (GwScreenTexts, GwUiScreen.texts). tools/web/harness.mjs --touch (through
tools/web.sh --touch) reads every such line of the phone's page, upright, sideways and with keys,
and its touch text check fails any text that is under 9.9 CSS pixels, not drawn whole (cut, or a
line dropped), off the frame, over another text, or under a pane. A message line on its way out
(fading) is skipped, as at L3.
It was written first, against the engine without the change (commit "Measure the game's text on the phone's screen"): six of the seven games were red (Fog Signal, Tideline and Nightcoast at 5.5 and 7.1; Lamplighter's header at 6.1; Burrow Run's and Acorn Rooftops' banner at 7.7). With the change all seven are green.
6. Proof
Engine (
engine/tests/kits/ui/gw_ui_phone_test.gd): the pitch grows to what the screen can read (6 upright, 5 sideways, 4 at exactly 1.1 points, the Style's on a desktop or with no screen); on a screen big enough nothing moves; on a phone the box shrinks inside the frame, its line reflows and grows, scenery keeps its pitch and place, and every text is 9 pitches tall; a compact place replaces a widget's own only on a phone; the slate is all glass and inside the frame; the view follows the frame on screen and back. With the pitch never grown, five of its six tests fail.gw_ui_texts_test(text under a later pane),gw_grid_view_testandgw_platformer_view_test(their text reported, and never under 11 pt),gw_player_test(--points),engine/tests/player/gw_screen_texts_test.gd(the report in points).Browser, each gallery game,
tools/heavy.sh tools/web.sh --bundle=<baked> --no-export --no-native --touch(2026-10-01, GPU Chromium, three device pixels a CSS pixel). The smallest text, in CSS pixels of body:Game Upright before Upright after Sideways before Sideways after Keys, sideways, after Fog Signal 5.5 11.0 7.1 11.8 13.0 Tideline 5.5 11.0 7.1 11.8 13.0 Nightcoast 5.5 11.0 7.1 11.8 13.0 Lamplighter 6.1 11.2 7.9 11.0 11.2 Burrow Run 11.0 11.0 7.7 11.3 11.2 Acorn Rooftops 11.0 11.0 7.7 11.3 11.2 Pier Pressure 11.0 11.0 14.1 14.1 19.5 Every phone shot was looked at: the text reads at one CSS pixel a pixel, nothing overlaps the touch controls (they never sit on the frame) or other text, and nothing is cut. The slate apps were shot open both ways up through
--pointstoo (Tideline's and Nightcoast's carry and make).Desktop: every gallery game's look, poster and proof tours (nine tours) shot on main and with this change at 640x360, 1280x720 and 1920x1080, through
tools/play.shon llvmpipe: 273 frames (the shots and their measurement frames), 251 byte for byte the same, every run on the same state hash. The other 22 are Burrow Run's: its camera differs between two runs of main itself (kit.platformer eases its camera on the first drawn frame of each tick, and snaps when two ticks pass between frames, so a shot depends on the machine's speed). Every Burrow Run frame main draws the same twice is the same here, and its 31 text rows are identical in all 30 shots. kit.ui's own T5 shots (engine/tests/kits/ui/gw_ui_shot.gd: title, HUD, carry, make) are byte for byte the same.Determinism: every game's web proof tour (tools/play-origin/deploy.sh's GAMES) ends on the same state hash in the browser as on the desktop, and as on main's desktop player (Acorn Rooftops at tick 2113, Burrow Run 477, Fog Signal 91, Lamplighter 130, Nightcoast 1356, Pier Pressure 105, Tideline 115).
Fog Signal upright, before and after:

Tideline's slate sideways, before and after (all glass on a phone):


7. L3 on a phone
Built 2026-10-02. Until then only the web proof's phone visit saw a Screen that doesn't fit a phone, so a Director's new Screen learned it at deploy, after the person had approved it. Now the Director's own checks see it: L3 draws for a phone too.
What L3 draws. A tour whose shots show text is played a second time in the player with
--phone 390x844: a phone of 390x844 points held upright, the phone the web proof's phone visit
opens. The player renders the frame at the size a person's run renders it (the project's
1920x1080) and places it where the touch layout puts it beside the game's own controls
(GwTouchControls.frame_on_phone: GwTouchPlan.room() of the game's InputMap,
GwTouchLayout.arrange with the controls' size for that screen, the code a person's phone lays
out with). So the points are the layout's, kept nowhere else: upright the controls go below a
frame as wide as the phone, 0.2031 points a frame pixel for every game; sideways a column each
side leaves 503 points with a stick and two buttons a row (0.2619) and 528 with a platformer's
d-pad and lone jump (0.2749), the frames the browser measured (390x219, 503x283, 528x297; the
engine test gw_touch_test pins them). A tool run drawn for a phone carries the same report the
web says (GwScreenTexts) in each shot (files[].screen). Subjects are left out of that run
(shot NAME with … is shot NAME): measuring one draws the frame twice more at 1920x1080, and
this pass judges text.
The checks. Each shot of that run with text is a frame of its own, beside the monitor's,
saying which phone (phone: {window, frame, points}), with two checks judged by
core/src/verify/screen-texts.ts. The harness's touch text imports the same module, its phone
and its floor, so L3 and the web proof agree text for text: a body under 9.9 CSS pixels, not
drawn whole (cut, a line dropped), off the frame, over another text (half a point of rounding
forgiven) or under a pane; a line on its way out is skipped.
phone_text_fits: cut, lines, outside, over, under. The fix is the Screen's: wrap a long label, shorten it, or give the widget acompactplace (§4).phone_text_size: under 9.9. kit.ui can't draw under it on a phone (the reading pitch), and Godot's face in kit.grid and kit.platformer draws at leastleast_font_px, so this goes red only for a kit's own text that doesn't honour the floor: the fix is a KitRequest. Two checks, not one, because the fixes differ, and both namedphone_so a Director never takes them for the monitor's.- Contrast is not judged again: the colours are the Style's, the same as the monitor's frame,
which
text_contrastjudges, and the harness doesn't judge it either.
A red phone text says where it is (ui.hud goal on a phone held upright: "Gather driftwood and flint, then light the sign…" shows 61 of its 73 characters, …) and gives the phone's fixes; the
digest lists it apart from the monitor's, and a red phone frame comes back to the Director as its
own picture (the phone's frame, downscaled to L3's 640x360). The phone run must end on the
monitor run's state hash, or L3 is red: the simulation read the presentation.
Upright only. Upright the frame is the smallest a phone shows it, so the reading pitch is the
biggest and each Screen has the least room: 320x180 pitches at 1080 rows for every gallery Style
(pitch 6 upright; sideways 5 for the pitch-3 Styles, a room of 384x216, and the platformers' and
Pier Pressure's own 6, 320x180, with more points to it). The frame sideways is never smaller
whatever the controls (a game with none fills the height, 693 points). Measured on every gallery
game's L3 tours, drawn both ways (--phone 390x844 and 844x390), the smallest text in CSS
pixels of body and what screen-texts.ts finds:
| Game (L3's tours) | Shots with text | Upright: frame, points | Upright: smallest | Sideways: frame, points | Sideways: smallest | Problems either way |
|---|---|---|---|---|---|---|
| Tideline (proof) | 13 | 390x219, 0.2031 | 11.0 | 503x283, 0.2619 | 11.8 | none |
| Nightcoast (look) | 6 | 390x219, 0.2031 | 11.0 | 503x283, 0.2619 | 11.8 | none |
| Fog Signal (look, resume) | 11 | 390x219, 0.2031 | 11.0 | 503x283, 0.2619 | 11.8 | none |
| Burrow Run (look) | 10 | 390x219, 0.2031 | 11.0 | 528x297, 0.2749 | 14.8 | none |
| Acorn Rooftops (start) | 1 | 390x219, 0.2031 | 11.0 | 528x297, 0.2749 | 14.8 | none |
| Pier Pressure (look) | 4 | 390x219, 0.2031 | 11.0 | 503x283, 0.2619 | 14.1 | none |
| Lamplighter (start) | 1 | 390x219, 0.2031 | 11.2 | 503x283, 0.2619 | 11.0 | none |
Every gallery game is green on a phone; no Style, Screen or check was changed for it. The
smallest body is set by construction both ways (the reading pitch for kit.ui, least_font_px for
Godot's face), so sideways can be a little smaller in points (Lamplighter's header, 11.0 against
11.2, from rounding a whole frame pixel) but never under the floor; what can go wrong on a phone
is room, and upright has the least. The planted phone_label (below) is cut upright and whole
sideways in the browser too. Sideways adds nothing upright doesn't show, at the price of another
full run of every tour, so L3 draws upright; the web proof still visits both ways at every
deploy. Each run, both ways, ended on the same state hash as the monitor's.
Cost. One more player run of each tour that shows text, at 1920x1080, without subject measurements. Engine seconds of L3's player runs on this machine (2026-10-02, load 25 to 30 on 24 cores; heavy.sh's wait for a slot left out), the monitor's run and the phone's:
| Game | Tours | Monitor (as before) | Phone | L3 after |
|---|---|---|---|---|
| Tideline | 1 | 101.7 s | 107.4 s | 209.1 s |
| Fog Signal | 2 | 40.1 s | 43.2 s | 83.3 s |
| Nightcoast | 1 | 36.8 s | 26.0 s | 62.8 s |
| Burrow Run | 1 | 20.2 s | 22.0 s | 42.2 s |
| Pier Pressure | 1 | 3.5 s | 2.9 s | 6.4 s |
| Acorn Rooftops | 1 | 3.0 s | 3.8 s | 6.8 s |
| Lamplighter | 1 | 2.9 s | 4.3 s | 7.2 s |
L3 about doubles: a tour plays in real time at its tick rate in the player, and that is most of a run at either size (Burrow Run's look tour is 1014 ticks, 17 s); the phone's frames cost about what the monitor's subject measurements did. heavy.sh's wait adds about 20 s to each run on a quiet box (three readings of free memory, 10 s apart), so Tideline's L3 went from 123 s to 251 s of wall time (main measured 126 s the same day). Its 900 s budget holds that with room. The lever, if L3 must be shorter, is a tool run that ticks faster than real time, not dropping the phone. Built since (fast-runs.md): Tideline's L0–L3 inside one heavy slot went from 311 s to 140 s, with every frame the same.
Planted: phone_label (core/test/fixtures/planted, m3-verification.md decision 8): the
action_survival HUD with a goal line across the top, one line 400 pitches wide, as a Director
might add one. On the monitor it shows whole and every check is green; on the phone held
upright the UI draws twice as big in a room of 320, the widget shrinks to fit it, and the line
is cut: L3 frames ✖ 1 of 2 frames red; opening on a phone: phone_text_fits: …. Without the
phone pass (main's shots.ts), the same test failed: L3 was green and the bug went through. With
the label's wrap: true it breaks into two lines on the phone and grows to hold them: green. The
web proof's phone visit, with the harness reading screen-texts.ts, says the same of both packs
(tools/web.sh --touch on main's web export, GPU Chromium): touch text fails 390x844: ui.hud goal "Gather driftwood and flint, the…" is not drawn whole and finds nothing sideways; wrapped,
it passes both ways. On Tideline it passes with the numbers of §6 (11.0 upright, 11.8 sideways).

Since kit.ui reflows text (2026-10-21, "Compact layout" above), the goal wraps on the phone
without wrap: true, and a cut line is no longer a bug a Director can make this way. The planted
phone_label now adds a tip line above the middle of the frame: apart on the monitor, and on the
phone the goal's second line runs into it (phone_text_fits: ui.hud goal … overlaps ui.hud tip "The tide turns at dusk."). A compact place for the tip, lower in the phone's room, makes it
green.
The phone's words (2026-10-02). The phone L3 draws is played by touch alone, so its touch
controls always show and kit.ui's key hints name them, not keys: [OK] say it, [↑↓] choose,
[Use] talk (the player's GwToolScreen.name_touch_controls; why always, touch-controls.md §8).
Until then a --phone run's hints named keys ([ENT] say it, [W/S] choose), which no phone
shows, and a hint is as wide as its cap, so the pass judged layouts no phone draws. With the
words it catches what only the deploy's phone visit had caught: Pier Pressure's title, green in
L3 with [R] restart, is red with [Restart] restart running into [Next] next level
(phone_text_fits: ui.title restart on a phone held upright: "restart" overlaps ui.title next "next level"); kit.ui's fix, a control whose word is the hint's words standing alone, is
19fc2d1. Thornwick (14 phone frames), Fog Signal (11) and Tideline (13) stay green, and every
phone run ends on its monitor run's state hash: the words are presentation.

The monitor by the same rules (2026-10-02). phone_text_fits judged a text over another or
under a pane; the monitor's text_fits didn't, so two labels over each other on a monitor passed
L3. Worse, the phone's fix for an overlap is a compact place, which moves a widget on a phone
only: a Director that took it left the monitor's overlap where it was, and nothing said so. Now
text_fits asks screen-texts.ts too (frames.ts monitorScreen: the frame as a screen of one
point a pixel, its rects whole pixels, so the half point a phone forgives for rounding forgives
nothing but two texts that only touch). Over and under judge a monitor's frame exactly as they
judge a phone's, a pair listed once and a line on its way out skipped. It is the same check,
not a new text_clear: phone_text_fits already means cut, lines, off the frame, over and under,
so text_fits now means the same on the other screen, and the fixes are all the layout's. The
monitor's hint says where: the widget's offset or size, as a compact place moves it on a phone
only. The planted crossed_labels is that Director's HUD: a tide line 6 pitches under the goal
line, less than a line, with compact places a line apart. L3 was green on it; now its monitor
frame is red (text_fits: ui.hud goal: "Gather driftwood and flint for the signal fire." overlaps ui.hud tide "The tide turns at dusk.") and its phone frame green.

A phone in the Director's preview (2026-10-02). preview drew a monitor's frame only, so the
Director met a phone's layout first in L3's report, after proposing. phone: true draws the
preview as L3's phone pass draws it (--phone 390x844, the frame at a person's 1920x1080 beside
the touch controls, its hints naming them, no subject measured) and returns that frame, 960 wide,
with phone_text_fits and phone_text_size in its checks and the phone (window, frame, points)
beside them. The caption says it is a phone. It takes every other argument (what, level, hours,
changeId, before), so a pending Screen can be seen on a phone before and after. The playbook says
to look at one after any change to a Screen.

8. Not yet
A text size the person sets (XAG 101: scale to 200%).Done (2026-10-09): the menu every game has offers Text size (100, 125, 150 or 200%), kept per game on the device. kit.ui draws at the Style's pitch times it, rounded, and never under its times 1.1 points on the screen (GwUiTokens.read_at(points, scale)), laying out in the smaller room that leaves as on a phone; Godot's face in kit.grid and kit.platformer grows with it (GwStage.text_scale,least_font_px). A tool run draws at 100%, so no frame of L3 changes.- A Screen is designed for one room. The rules handle the gallery's; a new Screen with panes side by side, or a long one-line label, may need a compact place. L3's phone pass says so (§7).
A Screen no tour shows.Done (2026-10-02): L3 opens every app and title no tour shot, once on the monitor and once on the phone, and judges its text there; the Director'spreviewopens any Screen by id (screen: ui.carry), on a phone too (m3-verification.md decision 13). A HUD part gated by awhen(an end card) is still shot only where a tour reaches it.The Director'sDone:previewis a monitor's frame.phone: truedraws it on a phone held upright (§7).- The touch controls' own labels shrink to 10 pt before they give up room (
LABEL_MIN_PT); none of the gallery's do. On-screen key hints still name keys on a phone.Done: they name the touch control on screen (touch-controls.md §8).- kit.platformer's camera eases in
presentrather thanticked, so a tour shot of a moving platformer is not always the same frame (found while proving the desktop unchanged, §6).