/**
 * @file
 * Game image wall — an angled, slowly-scrolling wall of game covers.
 *
 * Full-fidelity port of the legacy boardgames boxart-wall component, owned by
 * the module (the theme may refine via the same custom properties). GPU
 * transforms only — no scroll JS. Angle, speed, the background and fade colours,
 * fade thickness/intensity all arrive as inline CSS custom properties from the
 * block; per-row offset/direction from `--row-index`. Each strip holds the tile
 * set `--wall-repeat` times (rendered by the template) so the loop wraps
 * seamlessly.
 */

.game-image-wall {
  /* Overridden inline per block instance. */
  --wall-rotate: -30deg;
  --wall-speed: 60s;
  --wall-repeat: 3; /* how many times each row's set is duplicated (fill density) */
  --tile-crop: 50% 0%; /* object-position for the cover crop (default: top) */
  --wall-bg: #14100f; /* band background (behind the art) */
  --wall-fade-color: #000; /* colour the art dissolves into at the edges */
  --wall-fade-size: 16%; /* thickness of each edge fade */
  --wall-fade-intensity: 1; /* 0 = art hard to the edge, 1 = fully into the colour */
  /* How far the tilted plane spreads past the layer; larger pushes the art off
     the browser edges so it encapsulates the whole band. */
  --wall-overshoot: 155%;
  --tile-w: 15rem; /* 240px derivative */
  --tile-h: 8.4375rem; /* 135px derivative */
  --tile-gap: 0.75rem;

  position: relative;
  /* Full-bleed: break out of the content container and run to the browser edges. */
  width: 100vw;
  max-width: 100vw;
  margin-inline: calc(50% - 50vw);
  /* Explicit height (px/rem) from the block, else a responsive default. */
  height: var(--wall-height, clamp(18rem, 34vw, 30rem));
  overflow: hidden;
  perspective: 900px;
  perspective-origin: 50% 45%;
  background: var(--wall-bg);
}

.game-image-wall__viewport {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: var(--tile-gap);
  width: var(--wall-overshoot);
  transform: translate(-50%, -50%) rotateY(var(--wall-rotate)) rotateX(4deg);
  transform-style: preserve-3d;
}

.game-image-wall__strip {
  display: flex;
  width: max-content;
  will-change: transform;
  animation: game-image-wall-scroll var(--wall-speed) linear infinite;
  /* Stagger each row so they don't march in lockstep. */
  animation-delay: calc(var(--row-index, 0) * -8s);
}

/* Alternate direction row to row for a livelier wall. */
.game-image-wall__row:nth-child(even) .game-image-wall__strip {
  animation-direction: reverse;
}

.game-image-wall__tile {
  flex: 0 0 auto;
  width: var(--tile-w);
  height: var(--tile-h);
  /* Spacing as margin (not flex gap) so the tile pattern is perfectly periodic
     and the translateX(-50%) loop is seamless — no half-gap jump at the reset. */
  margin-inline-end: var(--tile-gap);
  overflow: hidden;
  border-radius: 0.375rem;
  box-shadow: 0 0.5rem 1.25rem rgb(0 0 0 / 45%);
}

.game-image-wall__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* The image style keeps the full cover; the tile crops it here, anchored per
     block (top keeps titles visible, which usually sit at the top). */
  object-position: var(--tile-crop);
}

/* Edge fade: an overlay of the chosen colour, opaque at each side and clearing
   toward the middle. Because it is a real colour overlay (not a transparency
   mask) the art dissolves into --wall-fade-color rather than revealing whatever
   sits behind the wall. --wall-fade-intensity scales the whole overlay. */
.game-image-wall::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  opacity: var(--wall-fade-intensity);
  background: linear-gradient(
    90deg,
    var(--wall-fade-color) 0%,
    transparent var(--wall-fade-size),
    transparent calc(100% - var(--wall-fade-size)),
    var(--wall-fade-color) 100%
  );
}

/* Layered content block, centred over the wall (above the tiles and the fade).
   The wall itself is decorative; this holds the real content, so it stays
   interactive. Give the layered block a transparent background so the art
   shows through; the optional text-shadow keeps copy legible over busy art. */
.game-image-wall__overlay {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: clamp(1rem, 4vw, 3rem);
  /* Sensible defaults so unstyled copy is legible over the dark wall. Kept to
     single-class specificity with no !important, so any CSS the layered block
     sets on its own content overrides these. The heading font falls back to
     inherit when the theme does not define --bo-font-heading. */
  color: #fbf6ef;
  font-family: var(--bo-font-heading, inherit);
  font-size: clamp(1.25rem, 3vw, 2rem);
  line-height: 1.15;
  text-align: center;
  text-shadow: var(--overlay-text-shadow, none);
}

/* If the layered block is a Big Hero band, strip its own background, scrim and
   media so the scrolling wall behind shows through — only its copy remains. */
.game-image-wall__overlay .bo-hero {
  height: auto;
  min-height: 0;
  background: none;
}

.game-image-wall__overlay .bo-hero__scrim,
.game-image-wall__overlay .bo-hero__media {
  display: none;
}

@keyframes game-image-wall-scroll {
  from {
    transform: translateX(0);
  }

  to {
    /* Shift by exactly one set (100% ÷ number of repeated sets). The remaining
       (repeat − 1) sets keep the viewport filled throughout the loop, so there
       are no trailing gaps and the reset is seamless. */
    transform: translateX(calc(-100% / var(--wall-repeat, 3)));
  }
}

/* Respect reduced-motion: a static angled wall, no scroll. */
@media (prefers-reduced-motion: reduce) {
  .game-image-wall__strip {
    animation: none;
  }
}

/* ===== Interactive (JS-driven) ===== */

/* The CSS animation always drives the ambient scroll (compositor-driven, so the
   speed never changes). JS only pauses it (the button / press-to-freeze set
   animation-play-state inline) or lifts a single strip off the animation during
   a manual drag, then re-syncs it. This rule is the button's explicit pause. */
.game-image-wall.is-paused .game-image-wall__strip {
  animation-play-state: paused;
}

/* The wall is a 3D (rotateY) plane, so the container layers can hit-test on top
   of their own tiles on the receding half, and the optional overlay sits on top
   of the whole wall. Make the container layers and the overlay click-through so
   pointer events reach the tiles and the pause button; re-enable interactive
   elements inside the overlay (so a layered CTA still works). */
.game-image-wall__viewport,
.game-image-wall__row,
.game-image-wall__strip,
.game-image-wall__overlay {
  pointer-events: none;
}

.game-image-wall__tile,
.game-image-wall__pause,
.game-image-wall__overlay a,
.game-image-wall__overlay button,
.game-image-wall__overlay input,
.game-image-wall__overlay select,
.game-image-wall__overlay textarea,
.game-image-wall__overlay [role="button"] {
  pointer-events: auto;
}

/* Dragging the wall must not start a text selection or an image/link drag-ghost. */
.game-image-wall {
  user-select: none;
  -webkit-user-select: none;
}

.game-image-wall__img {
  -webkit-user-drag: none;
  user-drag: none;
}

/* Clickable tiles. */
.game-image-wall__tile[href],
a.game-image-wall__tile {
  cursor: pointer;
  display: inline-block;
}

/* When paused with drag enabled, rows invite grabbing. */
.game-image-wall.is-paused.is-interactive[data-allow-drag="1"] .game-image-wall__row {
  cursor: grab;
}

.game-image-wall.is-dragging .game-image-wall__row {
  cursor: grabbing;
}

.game-image-wall.is-dragging .game-image-wall__tile {
  /* Don't let a drag start a tile click/drag-ghost. */
  pointer-events: none;
}

/* ===== Pause / play button ===== */

.game-image-wall__pause {
  position: absolute;
  z-index: 5;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  border: none;
  border-radius: 9999px;
  background: rgb(0 0 0 / 55%);
  color: #fff;
  font-size: 1.1rem;
  cursor: pointer;
  transition: background 0.15s ease, transform 0.15s ease;
}

.game-image-wall__pause:hover {
  background: rgb(0 0 0 / 75%);
}

.game-image-wall__pause:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

.game-image-wall__pause:active {
  transform: scale(0.94);
}

.game-image-wall__pause--top-left {
  top: 0.75rem;
  left: 0.75rem;
}

.game-image-wall__pause--top-right {
  top: 0.75rem;
  right: 0.75rem;
}

.game-image-wall__pause--bottom-left {
  bottom: 0.75rem;
  left: 0.75rem;
}

.game-image-wall__pause--bottom-right {
  bottom: 0.75rem;
  right: 0.75rem;
}

.game-image-wall__pause--bottom-center {
  bottom: 0.75rem;
  left: 50%;
  transform: translateX(-50%);
}

.game-image-wall__pause--bottom-center:active {
  transform: translateX(-50%) scale(0.94);
}

/* Icon swap by state — the fa-pause/fa-play classes survive the module's SVG
   replacement, so toggling on .is-paused works without touching the markup. */
.game-image-wall__pause .fa-play {
  display: none;
}

.game-image-wall.is-paused .game-image-wall__pause .fa-pause {
  display: none;
}

.game-image-wall.is-paused .game-image-wall__pause .fa-play {
  display: inline-block;
}
