# Gallery Tab **Nav id**: `gallery` **Icon**: ⊞ --- ## What it shows A 2-column photo grid of images Quinn has shared with this specific client. Tapping a photo opens a full-screen lightbox. --- ## Data flow `getGallery(token)` — `GET /vip/gallery?token=` — returns an array of `VipGalleryItem`. --- ## Gallery item object ```typescript interface VipGalleryItem { id: string attachmentUrl: string // pre-signed or direct URL to the image } ``` --- ## Display logic **Grid** — `display: grid, gridTemplateColumns: repeat(2, 1fr), gap: 4px`. Square aspect ratio (`1 / 1`) with `object-fit: cover`. Images lazy-load via `loading="lazy"`. **Lightbox** — clicking any grid image sets `lightbox` state to that item. A fixed-position overlay at `z-index: 100` renders the image at `max-width: 100%, max-height: 90vh` with `object-fit: contain`. Clicking the overlay or the ✕ button closes it; clicking the image itself stops propagation (so you can interact without closing). --- ## Encryption If the client has a content key (`vip_content_key` in sessionStorage), images may be served as encrypted blobs decrypted client-side. The current implementation uses direct `attachmentUrl` values — the API handles access control and optionally returns decryption metadata alongside the URL. This will evolve as the encryption pipeline matures. --- ## Non-obvious details - Gallery items are per-client — Quinn curates what each client sees via the admin panel. There is no global gallery that all clients share. - Empty state text is "No shared photos yet." — not an error, just an honest empty state. Gallery being empty is normal for new invites. - No pagination in the current implementation — all items load at once. This is acceptable for the expected volume (tens of photos per client, not thousands).