One scalar now shrinks every surface together. The desktop canvas already
multiplied native pixels by scale; the CLI/TUI now derive their terminal
width from it via constants.resolve_cols() instead of a separate pinned
unicode_cols (which is now an optional override, default 0 = auto).
Two sizing bugs fixed along the way:
- kitty placement sized its c×r cell box from a native-aspect column count,
so small pets got upscaled ~2× to fill it. It now derives the box from the
scaled frame pixels (_cell_box, shared with the half-block frame() path),
so kitty tracks scale like the GUI does.
- half-blocks can't follow scale all the way down — a cell samples the sprite
at 1 horizontal + 2 vertical taps, so a tiny width turns the pet to mush.
cols_for_scale() clamps to a legibility floor (UNICODE_MIN_COLS) and only
grows above it, while kitty/GUI keep shrinking on true pixels.
Default scale lowered 0.7 -> 0.33 (glanceable corner sprite); the 0.7 literal
fallbacks in pets.py and the desktop sprite now reference the shared default.