/*
 * How the player page moves — the entrance of its cards and every re-cut of
 * every graphic on it, in one place.
 *
 * The rules pair with core/transitions.js: that file writes `data-state` and
 * `--i` on the nodes it moves, and this file is what those attributes do. They
 * are one sheet rather than a block per component because the whole point is
 * that the cards move *alike* — the heatmap, the shot map and the stat bars
 * re-cut with one duration and one curve, which is the difference between a
 * page and a collection of widgets. Timings are the tokens INTERACTIONS.md
 * sets out: `--duration` for anything that appears or disappears, `--spring`
 * for anything that arrives, `--duration-slow` for anything that travels.
 */

/* --- Cards arriving ------------------------------------------------------------
 *
 * The page rises and fades as a whole (player-details.css); on top of that each
 * card rises a beat after the one above it. Only the first few — the stagger
 * stops at the fourth card, which is the last one that can be on screen at
 * open, and anything below it arrives with the fourth rather than queueing.
 * `both` is what holds a card invisible until its turn.
 */
.player-details[data-open="true"] .player-card {
  animation: player-card-enter var(--duration-slow) var(--easing) both;
  animation-delay: calc(var(--stagger-step) * var(--card-i, 3));
}

.player-details[data-open="true"] .player-card:nth-child(2) {
  --card-i: 0;
}

.player-details[data-open="true"] .player-card:nth-child(3) {
  --card-i: 1;
}

.player-details[data-open="true"] .player-card:nth-child(4) {
  --card-i: 2;
}

@keyframes player-card-enter {
  from {
    opacity: 0;
    transform: translateY(10px);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

/* --- Traits ---------------------------------------------------------------------
 *
 * The shape grows out of the centre of the hexagon and the vertices follow it.
 * `view-box` rather than `fill-box`: the origin has to be the chart's centre,
 * not the middle of whatever lopsided shape this player's six numbers make.
 */
.player-traits__shape,
.player-traits__vertex {
  transform-box: view-box;
  transform-origin: 50% 50%;
  animation: player-traits-grow var(--spring-duration) var(--spring) both;
}

.player-traits__vertex {
  animation-delay: calc(var(--duration) / 2);
}

@keyframes player-traits-grow {
  from {
    opacity: 0;
    transform: scale(0.2);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

/* --- Transfer value --------------------------------------------------------------
 *
 * The line draws itself from left to right — the career, told in order — then
 * the area under it fills and the transfer marks pop onto it. `pathLength="1"`
 * on the path (PlayerValueChart.js) is what lets one dash rule draw any length
 * of line: the dash and its offset are in the path's own units, so a ten-year
 * career and a two-year one both draw in `--duration-slow`.
 */
.player-value__line {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  animation: player-value-draw var(--duration-slow) var(--easing) both;
}

@keyframes player-value-draw {
  to {
    stroke-dashoffset: 0;
  }
}

.player-value__area {
  animation: player-fade-in var(--duration) var(--easing) both;
  animation-delay: calc(var(--duration-slow) / 2);
}

.player-value__mark {
  animation: player-pop var(--spring-duration) var(--spring) both;
  animation-delay: calc(var(--duration-slow) + var(--stagger-step) * var(--i, 0));
}

@keyframes player-fade-in {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

@keyframes player-pop {
  from {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.4);
  }

  to {
    opacity: 1;
    transform: translate(-50%, -50%);
  }
}

/* --- Heatmap -----------------------------------------------------------------------
 *
 * A re-cut cross-fades one cloud into the next (`crossfade` in transitions.js).
 * Two generations overlap for one `--duration`; the pitch under them never
 * moves, which is what makes the new season read as the same player somewhere
 * else rather than as a new picture.
 */
.player-heatmap__generation {
  transition: opacity var(--duration) var(--easing);
}

.player-heatmap__generation[data-state="enter"],
.player-heatmap__generation[data-state="leave"] {
  opacity: 0;
}

/* --- Shot map -----------------------------------------------------------------------
 *
 * Shots arrive one after another and leave together (`keyed` in
 * transitions.js) — by opacity alone. They used to scale in as well, and that
 * was the single most expensive thing on the page: a `transform` on an SVG
 * element is not a compositor property, it invalidates the SVG's layout, so
 * thirty shots scaling at once re-laid the whole map out on every frame for
 * half a second. Opacity on an SVG element repaints; it does not lay out. The
 * stagger is short for the same reason — every frame the reveal runs is a
 * frame the map repaints.
 */
.player-shots .shot-map__shot,
.player-shots .shot-map__mouth-mark {
  transition: opacity var(--duration) var(--easing);
  transition-delay: calc(var(--stagger-step) * var(--i, 0) / 2);
}

.player-shots .shot-map__shot[data-state="enter"],
.player-shots [data-state="enter"] > .shot-map__mouth-mark {
  opacity: 0;
}

.player-shots .shot-map__shot[data-state="leave"],
.player-shots [data-state="leave"] > .shot-map__mouth-mark {
  opacity: 0;
  transition-delay: 0ms;
}

.player-shots .shot-map__shot[data-state="leave"] {
  pointer-events: none;
}

/* The Pitch/Goal switch: the arriving view fades up while the slot glides to
   its height (`settleHeight`). */
.player-shots__graphic > .shot-map__pitch,
.player-shots__graphic > .shot-map__goal {
  transition: opacity var(--duration) var(--easing);
}

.player-shots__graphic > [data-state="enter"] {
  opacity: 0;
}

/* The selected shot's card, stepping to the next shot (`refresh`). Down and
   back rather than out and in: a dip the eye reads as "this changed" without
   the card ever going blank. */
.player-shots__card-body {
  transition:
    opacity var(--duration) var(--easing),
    transform var(--duration) var(--easing);
}

.player-shots__card-body[data-state="leave"] {
  opacity: 0;
  transform: translateY(4px);
}

.player-shots__card-body[data-state="enter"] {
  opacity: 0;
  transform: translateY(-4px);
}

/* --- Season performance -------------------------------------------------------------
 *
 * The bars glide by their clip (player-performance.css explains why not their
 * width), and the colour goes with them, so a bar that crosses a band on the
 * way to per-90 changes hue as it shrinks rather than after.
 */
.player-stat__fill {
  transition:
    clip-path var(--duration-slow) var(--easing),
    background-color var(--duration) var(--easing);
}

/* --- Career and matches -------------------------------------------------------------
 *
 * A season's competitions unfold under it and fold back the same way: the
 * drawer is shut to nothing here, and `settleHeight` (transitions.js) glides
 * it between nothing and its contents in either direction. `visibility` waits
 * for the close to finish so the rows do not vanish before the drawer has
 * shut; on the way open it flips at once so they are there to be revealed.
 */
.player-career__drawer {
  height: 0;
  overflow: hidden;
  visibility: hidden;
  transition: visibility 0s linear var(--duration-slow);
}

.player-career__drawer[data-open="true"] {
  height: auto;
  visibility: visible;
  transition-delay: 0s;
}

/* The Club/Season views and the match list's extra rows arrive the way the
   shots do: one row a beat after the last, inside a slot that glides to fit.
   On the way back the rows stay put and fade while the slot closes over them
   — a list that emptied itself and then shrank was a box collapsing on
   nothing. */
.player-career__view[data-state="enter"] {
  opacity: 0;
}

.player-career__view {
  transition: opacity var(--duration) var(--easing);
}

.player-matches__row[data-state="enter"],
.player-matches__group[data-state="enter"] {
  opacity: 0;
  transform: translateY(6px);
}

.player-matches__row[data-state="leave"],
.player-matches__group[data-state="leave"] {
  opacity: 0;
  transition-delay: 0ms;
  /* As long as the list's own glide, so the rows are still there, going, for
     the whole of the time the list is closing over them. */
  transition-duration: var(--duration-slow);
}

.player-matches__row,
.player-matches__group {
  transition:
    opacity var(--duration) var(--easing),
    transform var(--duration-slow) var(--easing);
  transition-delay: calc(var(--stagger-step) * var(--i, 0));
}

/* --- Reduced motion --------------------------------------------------------------------
 *
 * The tokens are already zero here, but an animation with a zero duration and
 * a `both` fill still snaps through its keyframes — and every entrance above
 * starts at `opacity: 0` — so the animations come off outright rather than
 * being shortened. fixture-commentary.css documents the same trap.
 */
@media (prefers-reduced-motion: reduce) {
  .player-details[data-open="true"] .player-card,
  .player-traits__shape,
  .player-traits__vertex,
  .player-value__line,
  .player-value__area,
  .player-value__mark {
    animation: none;
  }

  .player-value__line {
    stroke-dasharray: none;
    stroke-dashoffset: 0;
  }
}
