Files that landed on disk via any route other than store() (legacy
data, SCP, rsync, manual drops) had no variant siblings under
_thumbs/<variant>/, so url($path, 'admin-thumb') returned null and
admin previews fell back to the full original.
Change url() to materialise the variant on demand when the original
exists, is a real image, and the variant is registered. One-time
resize per asset on first request; subsequent requests hit the
cached sibling. Existing test that asserts null for a non-image
file (the bytes 'x' at a.png) still passes — getimagesize() returns
false on non-images so the lazy path skips them.
Resizer failures (corrupt image, missing GD/Imagick, unwritable
_thumbs dir) are swallowed and surface as null so callers continue
to fall back to the original URL.
3 new tests: lazy materialisation succeeds, idempotent across
repeat calls, returns null on resizer throw.
S3 and CloudflareImages are unaffected — S3 still materialises
during store() (round-trip cost makes lazy gen expensive), and
CloudflareImages serves variants via URL transform with no
materialisation step.