/* listing-card.css
 * The listing preview card (assets/js/shared/components/listing-card.js):
 * map popup, search grid, AI results and a listing shared into a DM.
 * The map's price markers stay in map-price-markers.css. */


.map-preview-card {
  position: absolute;
  z-index: 20;
  width: 288px;
  border-radius: 16px;
  background: var(--liquid-glass-surface, rgba(255,255,255,.72));
  border: 1px solid var(--liquid-glass-border, rgba(255,255,255,.14));
  box-shadow: var(--liquid-glass-shadow, 0 8px 32px rgba(0,0,0,.22));
  backdrop-filter: blur(18px) saturate(1.18);
  -webkit-backdrop-filter: blur(18px) saturate(1.18);
  overflow: hidden;
  font-family: "Montserrat", sans-serif;
  cursor: default;
  animation: mapPreviewCardIn 0.22s cubic-bezier(0.22, 1, 0.36, 1) both;
  transform-origin: bottom center;
}

.map-preview-card::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: var(--liquid-glass-top-sheen);
  z-index: 1;
}

/* Grid variant: the same card sitting in a results list instead of floating
   over a map. It gives up the popup's absolute placement, fixed width, drop
   shadow and entrance animation, and lets the grid own its size. */
.map-preview-card--grid {
  /* relative, NOT static: the card's full-surface link is absolute with
     inset:0, so the card must stay its containing block. Left static, that
     link resolves against the page and covers the whole viewport. */
  position: relative;
  z-index: auto;
  width: 100%;
  background: transparent;
  box-shadow: none;
  /* A card in a list, never a top surface: no backdrop-filter (CLAUDE.md). */
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  overflow: visible;
  animation: none;
}

.map-preview-card--grid .map-preview-card__gallery {
  border-radius: 16px;
  background: var(--liquid-glass-surface, #e8edf3);
}

.map-preview-card--grid .map-preview-card__body {
  padding: 10px 2px 0;
  color: var(--theme-text);
}

.map-preview-card--grid .map-preview-card__title,
.map-preview-card--grid .map-preview-card__subtitle {
  color: var(--theme-text);
}

@keyframes mapPreviewCardIn {
  from {
    opacity: 0;
    transform: translateY(6px) scale(0.97);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

.map-preview-card.is-hiding {
  animation: mapPreviewCardOut 0.16s ease both;
}

@keyframes mapPreviewCardOut {
  from {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
  to {
    opacity: 0;
    transform: translateY(4px) scale(0.97);
  }
}

/* Gallery */
.map-preview-card__gallery {
  position: relative;
  width: 100%;
  aspect-ratio: 20 / 19;
  overflow: hidden;
  background: #e8edf3;
}

.map-preview-card__gallery-track {
  display: flex;
  width: 100%;
  height: 100%;
  transition: transform 0.32s cubic-bezier(0.22, 1, 0.36, 1);
}

/* The floating card is glass (backdrop-filter). Promoted only while it moved,
   the track's layer was born on a swipe's first frame and died at the settle,
   and each birth/death re-sampled the glass behind the WHOLE card — on the
   phone the popup flickered at every swipe (CLAUDE.md, the ghost rectangle).
   Kept as a layer for the popup's life; grid cards have no glass, and a layer
   per card in a long list would only cost memory. */
.map-preview-card:not(.map-preview-card--grid) .map-preview-card__gallery-track {
  will-change: transform;
}

/* While the pointer is scrubbing across the photo the slide must land on the
   same frame as the mouse — the easing above would read as lag. */
.map-preview-card__gallery-track.is-instant {
  transition: none;
}

.map-preview-card__gallery-slide {
  position: relative;
  flex: 0 0 100%;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

.map-preview-card__gallery-slide--empty {
  background: var(--photo-skeleton-bg);
}

.map-preview-card__gallery-slide img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  user-select: none;
  -webkit-user-drag: none;
}

/* Loading state (floating map popup only): the shared .photo-skeleton from
   style.css, added per slide by wireGallery(). */

/* Dots */
.map-preview-card__dots {
  position: absolute;
  bottom: 8px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 4px;
  z-index: 2;
  pointer-events: none;
}

/* Ease the dots in rather than let them pop when the photos hydrate. */
.map-preview-card:not(.map-preview-card--grid) .map-preview-card__dots {
  animation: mapCardDotsIn 0.4s ease both;
}

@keyframes mapCardDotsIn {
  from {
    opacity: 0;
    transform: translateX(-50%) translateY(4px);
  }
  to {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
  }
}

/* While the extra photos are being fetched (home map), pulse three dots where
   the real ones will appear, so something is clearly happening the instant the
   card opens instead of a lone static cover. Removed as soon as the gallery
   hydrates (see buildCard), handing over to the real dots above. */
.map-preview-card__gallery.is-hydrating::after {
  content: "";
  position: absolute;
  bottom: 10px;
  left: 50%;
  width: 5px;
  height: 5px;
  margin-left: -2.5px;
  border-radius: 50%;
  background: #fff;
  box-shadow:
    -8px 0 0 #fff,
    8px 0 0 #fff;
  filter: drop-shadow(0 1px 1.5px rgba(0, 0, 0, 0.35));
  z-index: 3;
  pointer-events: none;
  animation: mapCardHydrateDots 1s ease-in-out infinite;
}

@keyframes mapCardHydrateDots {
  0%,
  100% {
    opacity: 0.35;
  }
  50% {
    opacity: 1;
  }
}

.map-preview-card__dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.65);
  transition:
    background 0.18s ease,
    transform 0.18s ease;
}

.map-preview-card__dot.is-active {
  background: #fff;
  transform: scale(1.25);
}

/* Heart / wishlist */
.map-preview-card__wish {
  --map-like-active-color: #ff151c;
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border: 0;
  border-radius: 50%;
  background: transparent;
  cursor: pointer;
  padding: 0;
  overflow: visible; /* let the burst dots escape the button */
}

.map-preview-card__wish img {
  width: 24px;
  height: 24px;
  object-fit: contain;
  transition:
    transform 0.16s ease,
    filter 0.18s ease;
  pointer-events: none;
}

/* Not liked: subtle white silhouette with a soft shadow for depth. */
.map-preview-card__wish:not(.is-liked) img {
  filter: brightness(0) invert(1) drop-shadow(0 1px 3px rgba(0, 0, 0, 0.35));
}

/* Liked: keep the filled icon's own colour, just add a soft shadow. */
.map-preview-card__wish.is-liked img {
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.4));
}

.map-preview-card__wish:hover img,
.map-preview-card__wish:focus-visible img {
  transform: scale(1.12);
}

.map-preview-card__wish:focus-visible {
  outline: 2px solid rgba(255, 255, 255, 0.88);
  outline-offset: 3px;
}

/* Like animation — the same ripple ring + dot burst + spring pop as the
   search page's favorite button. */
.map-preview-card__wish::before,
.map-preview-card__wish::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  pointer-events: none;
  opacity: 0;
}

/* Ripple ring */
.map-preview-card__wish::before {
  width: 44px;
  height: 44px;
  border-radius: 999px;
  background: #ff151c29;
  transform: translate(-50%, -50%) scale(0.35);
}

/* Eight dots bursting outward */
.map-preview-card__wish::after {
  width: 4px;
  height: 4px;
  border-radius: 999px;
  background: var(--map-like-active-color);
  box-shadow:
    0 -22px 0 var(--map-like-active-color),
    16px -16px 0 var(--map-like-active-color),
    22px 0 0 var(--map-like-active-color),
    16px 16px 0 var(--map-like-active-color),
    0 22px 0 var(--map-like-active-color),
    -16px 16px 0 var(--map-like-active-color),
    -22px 0 0 var(--map-like-active-color),
    -16px -16px 0 var(--map-like-active-color);
  transform: translate(-50%, -50%) scale(0.2);
}

.map-preview-card__wish.is-like-animating img {
  animation: mapwish-pop 0.44s cubic-bezier(0.16, 1, 0.3, 1);
}
.map-preview-card__wish.is-like-animating::before {
  animation: mapwish-ripple 0.44s cubic-bezier(0.16, 1, 0.3, 1);
}
.map-preview-card__wish.is-like-animating::after {
  animation: mapwish-burst 0.52s ease-out;
}
.map-preview-card__wish.is-like-removing img {
  animation: mapwish-remove 0.24s ease-out;
}

@keyframes mapwish-pop {
  0% {
    transform: scale(0.72) rotate(-18deg);
  }
  48% {
    transform: scale(1.22) rotate(10deg);
  }
  72% {
    transform: scale(0.94) rotate(-4deg);
  }
  100% {
    transform: scale(1) rotate(0);
  }
}

@keyframes mapwish-ripple {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.3);
  }
  32% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(1.22);
  }
}

@keyframes mapwish-burst {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.2) rotate(0);
  }
  24% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(1.1) rotate(28deg);
  }
}

@keyframes mapwish-remove {
  0% {
    transform: scale(1);
  }
  45% {
    transform: scale(0.72);
  }
  100% {
    transform: scale(1);
  }
}

/* Body */
.map-preview-card__body {
  padding: 10px 12px 12px;
}

.map-preview-card__title {
  margin: 0 0 1px;
  color: #111;
  font-size: 14px;
  font-weight: 700;
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The space a unit belongs to («Pêra da Serra · 5 alojamentos»), above the
   unit's own name. */
.map-preview-card__space {
  margin: 0 0 2px;
  color: #6a6a6a;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.map-preview-card__subtitle {
  margin: 0 0 4px;
  color: #6a6a6a;
  font-size: 12px;
  font-weight: 500;
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.map-preview-card__rating {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  margin: 0 0 6px;
  color: #222;
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
}

.map-preview-card__rating svg {
  width: 11px;
  height: 11px;
  fill: #222;
}

.map-preview-card__rating span {
  font-weight: 500;
  color: #6a6a6a;
}

/* Capacity specs (guests / bedrooms / kitchens / bathrooms) */
.map-preview-card__specs {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 2px 0 1px;
}

.map-preview-card__spec {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: #555;
  font-size: 12px;
  font-weight: 600;
  line-height: 1;
}

.map-preview-card__spec svg {
  flex: 0 0 auto;
  width: 13px;
  height: 13px;
  opacity: 0.8;
}

.map-preview-card__divider {
  height: 1px;
  margin: 6px 0;
  background: #ededed;
}

.map-preview-card__price-row {
  margin: 0;
}

.map-preview-card__price-total {
  color: #111;
  font-size: 13px;
  font-weight: 700;
  line-height: 1.2;
}

.map-preview-card__price-total strong {
  font-weight: 800;
}

.map-preview-card__cancel {
  color: #7a2d4c;
  font-size: 11px;
  font-weight: 600;
  line-height: 1.2;
  margin: 3px 0 0;
}

/* No refund at all is information, not a selling point: neutral, not brand. */
.map-preview-card__cancel[data-policy="non_refundable"] {
  color: #717171;
}

/* Clickable overlay linking to the listing */
.map-preview-card__link {
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
  text-decoration: none;
  outline: none;
}

/* Reduced motion: the pulsing dots stop (the photo skeleton handles itself). */
@media (prefers-reduced-motion: reduce) {
  .map-preview-card:not(.map-preview-card--grid) .map-preview-card__dots,
  .map-preview-card__gallery.is-hydrating::after {
    animation: none;
  }
}

:root[data-theme="night"] .map-preview-card {
  background: var(--liquid-glass-surface, rgba(18,24,36,.72));
  border-color: var(--liquid-glass-border, rgba(255,255,255,.08));
  box-shadow: var(--liquid-glass-shadow, 0 8px 32px rgba(0,0,0,.55));
}

/* On the map the card is a floating panel; in the results grid it sits
   directly on the page, so it carries no surface of its own. This has to come
   AFTER the night rule above — same specificity, so source order decides. */
:root[data-theme="night"] .map-preview-card--grid,
.map-preview-card--grid {
  background: transparent;
  box-shadow: none;
}

:root[data-theme="night"] .map-preview-card__title,
:root[data-theme="night"] .map-preview-card__price-total {
  color: #f0f6ff;
}

:root[data-theme="night"] .map-preview-card__subtitle,
:root[data-theme="night"] .map-preview-card__space,
:root[data-theme="night"] .map-preview-card__rating span,
:root[data-theme="night"] .map-preview-card__spec {
  color: #a0b4cc;
}


:root[data-theme="night"] .map-preview-card__rating {
  color: #f0f6ff;
}
:root[data-theme="night"] .map-preview-card__rating svg {
  fill: #f0f6ff;
}

:root[data-theme="night"] .map-preview-card__divider {
  background: #2a3a50;
}

.map-preview-card__top {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 5px 9px;
  border-radius: 999px;
  background: linear-gradient(90deg, #f5a524, #f97316);
  color: #fff;
  font-family: "Montserrat", sans-serif;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.08em;
  line-height: 1;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.22);
  pointer-events: none;
}


/* Who is hosting, on top of the explore map's preview (listing-card.js
   hostLine): avatar, name, the badge in the host's tier, «★ 4,9 · 23». */
.map-preview-card__host {
  display: flex; align-items: center; gap: 6px; min-width: 0; margin: 0 0 6px;
  font-size: .8rem; font-weight: 600; line-height: 1.2;
}
.map-preview-card__host-avatar {
  display: inline-flex; flex: 0 0 22px; width: 22px; height: 22px; align-items: center; justify-content: center;
  overflow: hidden; border-radius: 50%; background: rgba(0, 0, 0, .08); font-size: .7rem; font-weight: 800;
}
.map-preview-card__host-avatar img { width: 100%; height: 100%; object-fit: cover; }
.map-preview-card__host-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.map-preview-card__host-badge { flex: 0 0 14px; width: 14px; height: 14px; }
.map-preview-card__host .host-rating { margin-left: 2px; }
