/*
 * 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 in the open panel rises a beat after the one above it. Only the first
 * few — the stagger stops at the third card, which is the last one that can be
 * on screen under the hero and the tab row, and anything below it arrives
 * with the third rather than queueing. `both` is what holds a card invisible
 * until its turn.
 *
 * Counted within the panel, not the page: a panel that is `display: none`
 * has no animating cards in it, and one that comes back into layout on a tab
 * switch starts its cards' entrance fresh — so the arriving tab's cards rise
 * into the panel cross-fade the way the page's own first cards rise into its
 * open, and the two moves read as one.
 */
.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-panel > .player-card:nth-child(1) {
  --card-i: 0;
}

.player-panel > .player-card:nth-child(2) {
  --card-i: 1;
}

.player-panel > .player-card:nth-child(3) {
  --card-i: 2;
}

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

  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;
  }
}

/* --- 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(calc(4px * var(--scale)));
}

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

/* --- 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(calc(6px * var(--scale)));
}

.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 {
    animation: none;
  }
}
