/* Product card catalog: the card grid and the full-size card viewer. Reuses
   product-pages.css tokens so the page sits in the same visual system as the
   six product pages. */

:root {
  --card-gutter: clamp(24px, 7vw, 44px);
  --card-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* The cards are the page. The h1 exists for structure and search but takes no
   room of its own. */
.card-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.card-item:focus-visible,
.card-viewer-close:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.card-grid-section {
  padding-top: 14px;
}

.card-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
  padding-bottom: 10px;
}

.card-item {
  display: block;
  width: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
  border: 0;
  background: transparent;
  text-align: left;
  cursor: pointer;
  transition: transform 0.18s var(--card-ease), box-shadow 0.18s ease;
}

.card-item:hover,
.card-item:focus-visible {
  transform: translateY(-3px);
}

.card-item:active {
  transform: scale(0.98);
}

.card-media {
  width: 100%;
  height: auto;
  background-position: center;
  background-size: cover;
  filter: blur(7px);
  transform: scale(1.015);
  transition: filter 0.3s ease, transform 0.3s ease;
}

/* The placeholder lives in each img's inline style attribute, so it cannot be
   cleared from here. product-cards.js drops it when the card settles. */
.card-media.is-loaded {
  filter: none;
  transform: none;
}

.card-viewer {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: grid;
  grid-template-rows: minmax(0, 1fr);
  padding-top: env(safe-area-inset-top, 0px);
  color: #fff;
  background: rgba(7, 15, 25, 0.97);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.22s ease, visibility 0s linear 0.22s;
}

.card-viewer.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition-delay: 0s;
}

.card-viewer-stage {
  /* Mobile geometry is the original side-by-side slide: neighbours sit one
     card width away at full size and full opacity. The desktop block below only
     swaps these numbers to turn the same layout into a coverflow. */
  --flow-gap: 15px;
  /* The phone thumbnail strip runs across the top and reserves a band for
     itself. The card is width-limited, not height-limited, so the band comes out
     of the black around it and costs the card nothing. */
  --strip-h: 72px;
  --flow-h: calc(100dvh - (2 * var(--flow-gap)) - var(--strip-h) - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px));
  --flow-w: min(calc(100vw - (2 * var(--card-gutter))), calc(var(--flow-h) * 0.6667));
  /* Spacing is one viewport width, not one card width: the card is narrower than
     the screen because of the gutter, so a card-width step left a ~27px sliver of
     each neighbour on screen. The standalone page spaced slides by the stage
     width, which put them exactly off screen. */
  --flow-step: 100vw;
  /* One thumbnail plus the 8px of air that keeps the enlarged centre thumb from
     crushing its neighbours. */
  --thumb-step: 34px;
  --flow-shrink: 0;
  --flow-dim: 0;
  --flow-dim-base: 0;
  /* Which position along the row is under the centre line, in cards. Slides
     derive their own placement from this one number (see .card-viewer-slide),
     so product-cards.js animates a coverflow by writing this and nothing else. */
  --pos: 0;
  position: relative;
  min-height: 0;
  overflow: hidden;
  touch-action: pan-y;
  user-select: none;
}

.card-viewer-slide {
  /* --i is this card's position in the row, set once when the slide is built.
     Every other number follows from --pos, including fractional ones, so the
     neighbours grow and recede continuously while the row travels instead of
     only at the end of the move. */
  --o: calc(var(--i, 0) - var(--pos));
  --ao: max(var(--o), calc(-1 * var(--o)));
  position: absolute;
  top: calc(var(--flow-gap) + var(--strip-h));
  bottom: var(--flow-gap);
  left: 50%;
  width: var(--flow-w);
  display: grid;
  place-items: center;
  z-index: calc(10 - var(--ao));
  /* Neighbours recede by darkening, not by going translucent: an opacity falloff
     lets the black surround read through the card itself. The falloff is a flat
     hit on every non-current card plus a gentle per-ring slope, because a pure
     slope either leaves ring one too bright or drives the outer half card to
     black. */
  filter: brightness(calc(1 - (min(1, var(--ao)) * var(--flow-dim-base)) - (var(--ao) * var(--flow-dim))));
  transform: translateX(calc(-50% + (var(--o) * var(--flow-step))))
    scale(calc(1 - (var(--ao) * var(--flow-shrink))));
  /* No transition: a fixed-duration ease-out cannot match the speed the finger
     already had, so every move started with a lurch. product-cards.js drives
     --pos through a spring instead, which begins at exactly the release velocity
     and decelerates into the landing. */
  will-change: transform;
}

/* Phones show no falloff at all, so skip the filter rather than rasterising
   three viewport-sized cards through an identity brightness(). */
@media (max-width: 899px) {
  .card-viewer-slide {
    filter: none;
  }
}

.card-viewer-slide img {
  width: 100%;
  height: auto;
  max-height: 100%;
  object-fit: contain;
}

/* Sized and coloured to match .shared-dialog-close in the map and video
   dialogs, so every close control on the site reads the same. */
.card-viewer-close {
  display: none;
  position: absolute;
  z-index: 3;
  place-items: center;
  top: calc(10px + env(safe-area-inset-top, 0px));
  right: 12px;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.12);
  color: #ffffff;
  font-size: 24px;
  font-weight: 700;
  line-height: 1;
}

.card-viewer-close:hover {
  background: rgba(255, 255, 255, 0.22);
  border-color: rgba(255, 255, 255, 0.3);
}

.card-strip {
  position: absolute;
  z-index: 3;
  top: 0;
  right: 0;
  left: 0;
  height: var(--strip-h);
  /* Only the thumbs are tappable. The band they sit in belongs to the stage, so
     a tap between two of them still counts as "tap anywhere to go back". */
  pointer-events: none;
}

/* A thumbnail-sized copy of the coverflow: each thumb carries its own --i and
   places itself against the stage's --pos, so the row slides, recedes and dips
   under the finger in step with the cards, instead of repainting in place. */
.card-strip-item {
  --t: calc(var(--i, -999) - var(--pos));
  --tao: max(var(--t), calc(-1 * var(--t)));
  --tscale: max(0.7, calc(1.6 - (var(--tao) * 0.16)));
  position: absolute;
  /* Every thumb scales around this line, so the row is symmetric the way the
     coverflow is: the open card sticks out above and below its neighbours rather
     than hanging from a level top edge. */
  top: 16px;
  left: 50%;
  width: 26px;
  height: 39px;
  padding: 0;
  overflow: hidden;
  border: 0;
  border-radius: 6px;
  background: transparent;
  opacity: calc(1 - (min(var(--tao), 5) * 0.135));
  pointer-events: auto;
  cursor: pointer;
  transform: translateX(calc(-50% + (var(--t) * var(--thumb-step)))) scale(var(--tscale));
}

.card-strip-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The open thumb is 41.6px wide on a 34px step, so it overlaps 3.8px into each
   neighbour and paint order has to say which one is in front. */
.card-strip-item.is-active {
  z-index: 5;
}

/* Thumbs past the end of the catalog keep their slot and step out of the way.
   visibility, not display: the row is positioned from the centre, so removing a
   box would move nothing, and display none would drop it out of the tab order
   anyway. */
.card-strip-item.is-parked {
  visibility: hidden;
}

.card-strip-item:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.85);
  opacity: 1;
}

.card-viewer-hint {
  position: absolute;
  z-index: 3;
  bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  right: 16px;
  left: 16px;
  margin: 0;
  color: rgba(255, 255, 255, 0.34);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-align: center;
  pointer-events: none;
  white-space: nowrap;
}

.card-viewer-hint-desktop {
  display: none;
}

@media (prefers-reduced-motion: reduce) {
  .card-item,
  .card-media,
  .card-viewer {
    transition-duration: 0.01ms !important;
  }
}

@media (min-width: 620px) {
  .card-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 14px;
  }
}

@media (min-width: 900px) {
  .card-grid {
    grid-template-columns: repeat(5, minmax(0, 1fr));
    gap: 16px;
  }

  .card-media {
    filter: blur(6px);
  }

  /* Coverflow only from 900px: neighbours pull in over each other, shrink, and
     fade so the open card stays the only thing in focus. The step is a quarter
     of half the viewport rather than a fraction of the card, which puts the
     fourth card's centre exactly on the screen edge: three and a half per side,
     and the row uses the full browser width at any window size. */
  .card-viewer-stage {
    --flow-step: calc(50vw / 4);
    --flow-shrink: 0.18;
    --flow-dim: 0.12;
    --flow-dim-base: 0.34;
    --strip-h: 0px;
  }

  /* The coverflow already shows there is more either side, so the strip would
     only take height the card is using. */
  .card-strip {
    display: none;
  }

  .card-viewer-close {
    display: grid;
  }

  .card-viewer-hint-touch {
    display: none;
  }

  /* Only phones centre the hint. On desktop the card is centred too, so a line
     in the middle of the screen would be painted over by it. */
  .card-viewer-hint {
    right: auto;
    text-align: left;
  }

  .card-viewer-hint-desktop {
    display: block;
  }
}

/* Placed after the 900px block so the higher density is not undone by it. */
@media (min-width: 1200px) {
  .card-grid {
    grid-template-columns: repeat(6, minmax(0, 1fr));
    gap: 14px;
  }
}
