refactor: shared ExifDisplayData model for feed card (#8)
* refactor: replace GrainExif dependency with ExifDisplayData in ExifInfoView
Introduces ExifDisplayData, a display-only struct that both GrainExif (feed)
and ExifSummary (create flow) map to via .displayData extensions. ExifInfoView
now accepts this shared type plus a style param for per-call dimming.
ExifSettingsRow: removes dot separators, adds smooth position-shift animation
(.animation(.smooth, value: tokens)) for digit-count changes, suppresses text
content crossfades via .contentTransition(.identity), and snaps
appearance/disappearance via zero-duration transition. VStack-level opacity
with .easeInOut(0.2) fades all rows together when the whole exif block
appears or disappears.
* feat: wire ExifDisplayData into GalleryCardView feed card
Passes currentExif?.displayData to ExifInfoView instead of the raw GrainExif,
so the feed card uses the same shared component as the create flow.
* fix: block inherited animations from reaching ExifInfoView in feed
Add .transaction { \$0.animation = nil } to the ExifInfoView call in
GalleryCardView so copiedToast's spring and the bare withAnimation in the
media-warning reveal can't bleed into the EXIF rows.
Remove the Form-level .animation(.smooth, value: selectedPhotoID) from
CreateGalleryView — it was the primary source of inherited smooth transactions
reaching ExifInfoView on every photo tap.
* fix: ForEach indices ambiguity and add StringExtensions
* fix: center X button on thumbnail corner and even vertical padding in strip