Zoom level is a dropdown (Off/Subtle/Moderate/Strong) controlling how far
the album art zooms in during its breathing effect; Off skips the
animation.Transformation entirely and just shows a static crop.
Color schemes now match SUB/WAVE's own web UI theme picker (Blueprint,
Classic Dark, Classic Light, Cyberpunk, Flare, Recon, Signal, Vinyl) so a
station can mirror its own site theme on the device, including a
background fill that was previously hardcoded to black.
The rest state used Scale(ART_SLOT/ART_SIZE, ...) (~0.71) on a 45x45
image to shrink it down and reveal the whole picture, but scaling below
1.0 turns out to be unsafe: empirically it reintroduces the black-border
bug well before the math would predict any gap (a visible gap opens up
below roughly 0.9-0.95, not just when the scaled image shrinks smaller
than the display slot). So the whole "shrink down from oversized to
reveal everything" design doesn't work.
Instead, keep the oversized render just barely larger (36x36 vs. the
32x32 slot) so the safe floor of scale = 1.0 already shows ~89% of the
image -- close enough to read as "fully zoomed out" -- while still
leaving headroom above 1.0 for a real zoom-in effect.
Swapped the keyframes so scale=1.0 (full frame) is the loop's start/end
state and the zoom-in happens at the midpoint, matching how it's meant to
read: settle on the full picture, breathe in, breathe back out.
At the old 60s round-trip, a fresh render (triggered by the server well
before a minute passes) kept restarting the animation from tick 0, so only
the outbound zoom-out was ever visible on-device -- it never appeared to
zoom back in. Shortening the loop to 10s total makes it far more likely a
full out-and-back cycle plays before the next refresh.
The corner-reveal zoom scaled down from 1.0 toward ~0.71 (via Translate
to shift toward a corner), which showed up on a real device as a black
border cutting into the art. Empirical testing against the actual Row
layout at native canvas size showed the outer Box auto-fits an oversized
child at Scale(1.0, 1.0), so scaling down from there shrinks the already-
fitted image further and exposes background outside its bounds. Scaling
up only ever crops further into the image, which is always safe.
This drops Translate and the per-track corner selection (animation.Scale
pivots at the top-left corner by default with no way to change it -- an
explicit origin parameter was tried and had no effect), so the effect now
always zooms into the top-left corner instead of a track-dependent one.