Round 3 · still swap

Still-swap agent · 2026-10-13 · files changed: prototype/mind.html (the #head slot and its CSS) and prototype/standard.html (the .hero-head slot and its CSS). Nothing else was edited: index.html, shared.css, server.py, img/, data/, brief/ and research/ are untouched, and no earlier shot or report was overwritten.

Method: headless Chrome for Testing (chromium-1234) through the Playwright library, 1440x900 at DPR 2 and 390x844 at DPR 3 with isMobile and hasTouch, served by python3 -I -m http.server 8705 --bind 127.0.0.1 from prototype/ (PID 77660, stopped by that PID at the end, port 8705 confirmed free; 8777 was never listening and never contacted). Both pages were loaded with ?demo=1, so neither page calls /api/; a context route aborted and counted every request to port 8777 or to /api/, and both counters ended at 0. Shot prefix r3-still-. Every capture cited below was read as pixels before it was judged. Analysis ran through uv run --script in scratch (r3-still), removed at the end.

Summary

Item Result
Stills (step 1) img/head-still.png 800x924 and img/head-still@2x.png 1600x1848 present, straight-alpha RGBA, read as pixels next to the opening's own frame r3-opening-1440x900.png: the same matte mannequin cranium, soft dishes, bridge and fogged dissolve
Swap (step 2) Both slots now hold one <img>; the interim SVG and its private defs are gone; the 24 px gHead glyph is still SVG
Geometry The still is taller per unit width than the SVG, so each slot keeps its old height, top and bottom and narrows by 0.743; every new slot box sits inside the old one (heights equal to within 0.3 px)
Placement mind: centred above the opener, 35 px clear (1440) and 33 px clear (390); standard: head x 1006-1356 inside the right third, dissolve ending on the hero's lower edge; phone: copy block at 40 % of the slot, about 62 % of the head behind the lockup
Edges zero change outside the slot box in all four frozen pairs; no rectangle on any slot edge; no fringe; the only lighter-than-background ring is the lit crown, +2.5 to +4.1 levels
Fades mind after one demo answer: #head opacity 0 at both widths, glyph shown; standard with the dock open: .hero-head opacity 0
Console and network 8 loads, 0 console errors, 0 page errors, 0 failed requests, 0 HTTP 4xx/5xx, 0 requests to 8777, 0 /api/ requests; server log 61 requests, all 200

1. The stills (step 1)

Measured on the files (alpha > 0 extent, uv run --script):

File Bytes Head extent Opaque (centre column) Alpha reaches 0 by Transparent foot
img/head-still.png 800x924 155,258 x 26-773 (748 wide), y 26-896 rows 28-742 row 896 rows 897-923 = 27 rows = 3.375 % of the width
img/head-still@2x.png 1600x1848 432,139 x 52-1547 (1496 wide), y 52-1794 rows 55-1486 row 1794 rows 1795-1847 = 53 rows = 3.31 % of the width

Darkest opaque pixel L 170.4 / 170.2 (the opening's shading floor), widest row at 55 % of the height. Read as pixels: both files show the round-3 head (shallow soft dishes well inside the silhouette, brow, bridge, white dissolve) and match the head in r3-opening-1440x900.png and r3-opening-head-1440.png. Not a skull, helmet, capsule, ball with holes, goggles or sleep mask.

2. The swap (step 2)

Markup

In both slots the inline 240x200 SVG (with its private mSkin, mHollow, mFade, mMask, mClip, mBlur, mBlur2 defs, which nothing else referenced; grep across all four prototype files) is replaced by one element inside the unchanged wrapper:

<img src="img/head-still.png" srcset="img/head-still@2x.png 2x" width="800" height="924" alt="" decoding="async" draggable="false">

CSS: #head img and .hero-head img are display:block; width:100%; height:auto; margin-bottom:-3.3%. The width/height attributes only supply the aspect ratio before decode, so the slot (and the standard mask) has its final height from the first frame. The wrappers keep aria-hidden (standard) or sit in the aria-hidden realm (mind); alt="" makes the image decorative either way. The gHead symbol and its three <use> sites (mind top bar, dock trigger, dock head) are untouched.

The -3.3% foot: percent margins resolve against the slot's width, and the still's transparent foot is 3.375 % (1x) / 3.31 % (2x) of its width, so the slot's box ends where the dissolve ends. Measured on the frozen captures: the last row the still changes by 1 level or more is 237.0 against a slot bottom of 237.0 (mind 1440) and 237.7 against 238.3 (mind 390); on the standard hero the slot bottom is the hero's lower edge (868 at 1440) and the mask plus dissolve fall under 1 level by 857.5.

Geometry: why the slot widths changed

The SVG slot was 0.8333 x its width tall. The still, once its foot is trimmed, is 1.122 x its width. At the old widths every slot would have grown 35 % taller: on the mind page the head would have ended at about 300 px, on top of the opener's h1 (272) at 1440 and at 390, and on the phone hero 71 % of the head would have gone behind the copy. The smallest change that keeps everything the mind-standard fixer measured valid is to keep each slot's height, top, bottom and anchor and narrow its width by 0.8333 / 1.122 = 0.743:

Slot Before (SVG) After (still)
mind #head, desktop clamp(150px,22vmin,230px) clamp(111px,16.34vmin,171px)
mind #head, max-width 560 clamp(170px,50vw,230px) clamp(126px,37.14vw,171px)
standard .hero-head, desktop min(35%,520px) min(26%,386px)
standard .hero-head, max-width 720 width:var(--head-w) width:calc(var(--head-w) * .743)

Both clamp ends scale by the same factor, so at every viewport each new box has the same top, bottom and height as the old one (to within 0.3 px at the rounded clamp ends) and is narrower around the same anchor (centre for mind and phone, right 5 % for the desktop hero): it sits inside the old box. Nothing that cleared the SVG can collide with the still. The phone hero's padding-top: calc(var(--head-w) * .3333) and --head-w: min(62vw,260px) are unchanged and still mean what their comments say (the token's comment now reads "the phone head box: .8333 x --head-w tall (the still at .743 x --head-w wide)"); the slot comments in both pages explain the factor and the foot.

Resulting heads (footprint = pixels the still changes by 1 level or more, frozen captures):

Slot Head footprint (CSS px) Against the design
mind 1440 x 650.5-789.0 (138.5 wide), y 76.5-237.0; slot centred on x 720 38.7 % of the opening's 358 px head: design 4.1's "38 % of its opening scale"; 35 px above the opener's h1 (272)
mind 390 x 127.0-263.0 (136 wide), y 80.7-237.7; centred on x 195 33 px above the h1 (272); the crown sits just under the top bar's fade as the SVG's did
standard 1440 x 1006-1356 (350 wide), y 460.5-857.5 inside the right third (x 960 and up; the SVG reached x 863); the opening's scale (350 vs 358); slot bottom on the hero's lower edge; 178 px clear of the copy column's right edge (827.5)
standard 390 x 110.7-279.7 (169 wide), y 63-246; slot 57-259.2 copy block at 137.6 = 40 % of the slot, 38 % down the visible head (crown at 63): about 62 % of the head behind the eyebrow, the mark and the theme line, on the copy's white gradient; about two thirds of the opening's phone head (about 250 px in r3-opening-390x844.png)

Kept as built

The standard masks (55 % desktop, 64 % phone), the phone copy's white gradient, the mind slot's ::after contact shadow, both breathe animations and their transform origins, the reduced-motion rules, the mind fade (body:not([data-state="empty"]) #head), the dock-open fade and its max-width 820 reset, and the .facts rules. Diff against the pre-swap files: width values, the img rules replacing the dead svg rules, the slot markup, and comments; nothing else.

3. Verification (step 3)

Captures (all in prototype/shots/, read as pixels)

Capture Verdict
r3-still-mind-empty-1440x900.png The opening's matte mannequin cranium, soft grey dishes, bridge, fogged dissolve and a faint contact shadow, centred above "Ask me about ETS27." with clear air; no rectangle, no halo
r3-still-mind-after1-1440x900.png After one demo answer (chip "who's speaking", 8-speaker grid): no trace of the head; #head computed opacity 0, the glyph shows in the top bar
r3-still-standard-top-1440x900.png Head at the right third, dissolving into the hero's lower edge; nothing crosses the copy, the rule or the numerals; no slot edge over the hero gradient
r3-still-standard-dock-open-1440x900.png Dock open: .hero-head opacity 0, no ghost of the head, the facts span the column, Register clear of the dock
r3-still-mind-empty-390x844.png Same cranium, centred above the opener, dissolve and shadow end above the h1; no rectangle
r3-still-mind-after1-390x844.png Head gone (opacity 0), glyph in the top bar, the grid in three columns
r3-still-standard-top-390x844.png The dome rises above the eyebrow, the dishes show faintly between the eyebrow and the mark, the rest sits behind ETS27 on the white gradient
r3-still-mind-head-1440.png, r3-still-mind-head-390.png Device-resolution crops: a smooth matte cranium read, dishes are soft hollows with a gradient, not sockets; the silhouette is clean
r3-still-standard-head-1440.png, r3-still-standard-head-390.png Device-resolution crops: same head under the slot mask; on the phone the veil keeps the dishes present but quiet
r3-still-edge-zoom-1440.png 4x nearest-neighbour panels (top row) and the same panels stretched L 225-255 to 0-255 (bottom row): mind left silhouette, mind crown, mind dissolve end, standard slot top-left corner, standard slot bottom-left at the hero edge, standard left silhouette. No slot corner or edge appears even stretched; the silhouettes are clean antialiased edges with the still's own Fresnel rim; the dissolve fades into the realm with no line

One thing visible only in the stretched crown panel: a faint horizontal contour at CSS y 64-68 above the crown. It is identical with and without the still (row means differ by 0.00): the top bar's fade and the realm glow's banding, not the head.

Edge checks (frozen pairs)

Method: a second load of each page, every animation paused at t = 0 (document.getAnimations()), captured with the still and again with img{visibility:hidden}; dL is the luminance difference. Bands are 3 device px just inside each slot edge and 4 px just outside it; the ring is the 4 device px just inside the head's footprint in the top 80 % of the slot (above the dissolve), over a light background (text excluded).

Case Inside bands L / R / T / B (max abs dL) Outside bands, and the whole frame outside the slot Ring px lighter than the background by more than 1.5 (share on the top arc, max) Outer ring max abs dL Darkest head pixel over a light background
mind 1440 0 / 0 / 0 / 2.0 0 / 0 1,352 (89 %, +4.07) under 1 173.4
mind 390 0 / 0 / 0 / 1.0 0 / 0 2,720 (91 %, +3.86) under 1 173.4
standard 1440 0 / 0 / 0 / 0 0 / 0 7,153 (100 %, +2.78) under 1 182.2
standard 390 0 / 0 / 0 / 0 0 / 0 1,071 (93 %, +2.49) under 1 197.0

Console and network

Eight loads (mind and standard, normal and frozen, at both sizes): 0 console errors, 0 page errors, 0 failed requests, 0 responses of 400 or more, 0 requests to port 8777, 0 requests to /api/. currentSrc is img/head-still@2x.png at DPR 2 and 3 (naturalWidth reports the density-corrected 800). Server log: 61 requests, all 200; the 1x file was fetched only by the curl check, the browser always took the 2x.

4. DOM before and after (slot box incl. the breathe transform, CSS px)

Case Before (SVG) After (still)
mind 1440 #head x 620.6, y 71.7, 198.8 x 165.6, bottom 237.3 x 646.2, y 71.7, 147.6 x 165.6, bottom 237.3
standard 1440 .hero-head x 862.8, y 446, 506.4 x 422, bottom 868 x 992.8, y 446.1, 376 x 421.9, bottom 868
mind 390 #head x 97.2, y 75.7, 195.7 x 163.1, bottom 238.7 x 122.3, y 75.7, 145.4 x 163.1, bottom 238.8
standard 390 .hero-head x 73.8, y 57, 242.5 x 202, bottom 259 x 104.9, y 57, 180.2 x 202.2, bottom 259.2

Opener h1 (mind) 272 at both sizes and the hero copy, eyebrow, mark, theme and facts boxes (standard) are identical before and after.

5. Not done, notes