GET /b/{name}/compare with the conjunction 404 (containment AND the review
ring), both sides recorded as seen, view state (side, link) mapped from a
closed set onto every link, side-keyed regions, and back=compare in
_mark_redirect. compare.html: two stages sharing one set of rows, the strip
as picker (the side active now), linked and per-side stepping, X/L/Z/A/B/C
keys under the review's guards, synced pan by fraction with an echo guard,
per-side blur reveals, JS-off parity.
The stage machinery moves out of view.html into _stage_js.html
(BoothMode.bind, BoothStage.attach), shared by the review and compare. The
review gains a Compare control and a C key. At phone width a full top bar
wraps.
Tables: r2c's 15 stage rows re-pointed to _stage_js.html; r2b's phone
top-bar row re-anchored (the wrap made it vacuous alone); new r3.toml. The
contract records the wrap, equal stages and C on the compare page.
- 1:1 start-aligns. The centred flex item overflowed both sides and the
start was unreachable; measured, a 3000px picture hid its leftmost
980px. Auto margins still centre a small picture.
- Drag lifecycle: a move with no button ends the drag, so a press
released outside the stage never pans on a later hover. Capture is now
load-bearing in a test. The threshold is 4px of total movement.
- A press on the stage's own scrollbar is never a pan. The arrows clamp
to the stage's client box, so they are never under a classic
scrollbar. The test runs a browser without --hide-scrollbars and
asserts the gutter exists.
- Stacked, the arrows' CSS spot is the stage's centre (30vh), set in
view.html because base.html lost to the page's later rule.
- The stage reveal is `hidden` until bound, and keeps Fit's drop shadow
when revealed. A blurred picture composes blur() drop-shadow().
- The mode follows another tab. A failed or unknown size returns the
arrows to their CSS spot.
- Tests: object-position, vertical centring, the Fit half of
aria-pressed, a storage read that throws, a large picture's toggle,
Fit forgetting 1:1, single-axis pan.
- Declared: the r2b reveal test reads "no blur" (the shadow stays), and
the r2_flow 360px-offset row is retired.
Mutation tables 137/137 across four. 810 passed.
The operator: "fit and 1:1 modes as well as moving the forward and back
arrows closer to the edge of the image ... mouse click and pan for 1:1
mode if it exceeds page width (defeat drag drop of image)". Ruled: "Fit
may enlarge."
- Fit: the picture's box is the stage's inner box, and object-fit: contain
draws it whole at the largest size that fits, up or down, never
cropped. It works with or without JS. 1:1 is natural pixels.
- The Fit | 1:1 toggle shows for every picture; the per-picture hide is
gone. It stays hidden without JS.
- The mode persists as `stage-one` on <html>, set by the head script
before the stage exists, so a 1:1 reel never paints a stage in Fit.
Anything stored but "one" reads as Fit. Storage never raises.
- The arrows sit wholly outside the DRAWN picture (near edge 8px),
clamped 8px inside the stage. They sit over the picture only when it
spans the stage, and never over the rail. They are re-placed on load,
resize, mode switch and 1:1 scroll, and keep their CSS spot until the
drawn box is known.
- 1:1 drag-to-pan when the picture overflows either axis: the picture
follows the pointer, a 4px threshold, pointer capture, grab/grabbing.
The picture is draggable=false. The stage's reveal button moves out of
the scrolled content to sit over the stage (a pan carried it off), so
no control is a pan source.
Contract docs/contracts/r2c_review_stage.contract.md (heid contract
panel 4/4 folded; it changed the no-flash mechanism). Declared test
changes: the Nyx stage-edge arrow test is replaced; the stage class and
the toggle's `hidden` are updated. tests/mutations/r2c.toml 16/16. 803
passed.