feat(ui): green map pin for applied flats, hide map reject after apply, lightbox image viewer

Map: replace Leaflet's default marker with a divIcon SVG pin coloured
per state — green when the user has already successfully applied
(status.chip === "ok"), brand-blue otherwise. Same condition also hides
the action buttons in the popup, matching the list view, which already
hid both Bewerben and Ablehnen on success — so the only remaining
action on an applied flat is opening the original ad link.

Image gallery: clicks now open a global lightbox modal instead of a new
tab. The viewer fits each image into the viewport via max-width/height
+ object-fit: contain (uniform sizing regardless of source aspect),
shows × top-right, prev/next arrows on the sides, ←/→/Esc keyboard
nav, and click-on-backdrop to close. Prev arrow is hidden on the first
image and next on the last. Tile changes from <a target="_blank"> to
<button> since the new-tab fallback is no longer wanted.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
EiSiMo 2026-04-23 12:37:15 +02:00
parent fe43a402d8
commit 787f848aba
5 changed files with 173 additions and 21 deletions

View file

@ -128,11 +128,48 @@ body:has(#v_map:checked) .view-map { display: block; }
gap: 8px; }
.flat-gallery-tile { aspect-ratio: 4 / 3; overflow: hidden;
border-radius: 8px; border: 1px solid var(--border);
background: #f0f5fa; display: block; }
background: #f0f5fa; display: block;
padding: 0; cursor: zoom-in; }
.flat-gallery-tile img { width: 100%; height: 100%; object-fit: cover;
display: block; transition: transform .3s; }
.flat-gallery-tile:hover img { transform: scale(1.04); }
/* Image lightbox full-viewport overlay with a centered, uniformly sized
image. Prev arrow is hidden on the first image; next arrow on the last. */
.lightbox { position: fixed; inset: 0; z-index: 1000;
background: rgba(8, 18, 32, .92);
display: flex; align-items: center; justify-content: center;
animation: lightbox-fade .15s ease-out; }
.lightbox.hidden { display: none; }
.lightbox-image { max-width: 92vw; max-height: 88vh; object-fit: contain;
border-radius: 8px; box-shadow: 0 10px 40px rgba(0,0,0,.5);
background: #0c1726; }
.lightbox button { background: rgba(255,255,255,.08); color: #fff; border: 0;
border-radius: 9999px; cursor: pointer;
display: inline-flex; align-items: center; justify-content: center;
transition: background .15s, transform .05s; padding: 0; }
.lightbox button:hover { background: rgba(255,255,255,.22); }
.lightbox button:active { transform: scale(.96); }
.lightbox-close { position: absolute; top: 1.25rem; right: 1.25rem;
width: 2.5rem; height: 2.5rem; }
.lightbox-prev, .lightbox-next { position: absolute; top: 50%;
transform: translateY(-50%);
width: 3rem; height: 3rem; }
.lightbox-prev:active, .lightbox-next:active { transform: translateY(-50%) scale(.96); }
.lightbox-prev { left: 1.25rem; }
.lightbox-next { right: 1.25rem; }
.lightbox-prev[hidden], .lightbox-next[hidden] { display: none; }
.lightbox-counter { position: absolute; bottom: 1.25rem; left: 50%;
transform: translateX(-50%); color: rgba(255,255,255,.7);
font-size: .85rem; font-variant-numeric: tabular-nums;
letter-spacing: .02em; pointer-events: none; }
body.lightbox-open { overflow: hidden; }
@keyframes lightbox-fade { from { opacity: 0 } to { opacity: 1 } }
/* Map pin — divIcon default class has a white box; clear it so the SVG sits clean. */
.lazyflat-pin { background: transparent; border: 0; }
.lazyflat-pin svg { display: block; filter: drop-shadow(0 1px 2px rgba(0,0,0,.25)); }
/* Leaflet popup — match site visual */
.leaflet-popup-content-wrapper { border-radius: 12px; box-shadow: 0 6px 20px rgba(16,37,63,.15); }
.leaflet-popup-content { margin: 12px 14px; min-width: 220px; color: var(--text); }