/*
 * The fixture page's Lineups panel — two elevens on one full pitch
 * (MatchLineup.js).
 *
 * The player nodes standing on it are not styled here. They come out of
 * `PlayerNode` wearing `.season-stats__*` classes and are dressed entirely by
 * season-stats.css, which is deliberate: a player reads the same on any pitch
 * in this app, and a second set of rules saying so is a second set to keep in
 * sync. What this file owns is the pitch, the rows on it, and the two team
 * labels either side.
 *
 * This pitch is a drawn surface, not turf: the same pale ground and the same
 * centre-to-edge gradient the club and league pages' half pitches stand on,
 * which keeps a panel this size from reading as one flat rectangle. It was
 * green once — a turf gradient under a white grain overlay — and losing the
 * green is what collapsed the difference between the two pitches; the
 * gradient then went the other way, out of here and into season-stats.css,
 * so the club page's half pitch, the league page's and this one all draw the
 * same material at three different sizes. That is the win
 * here, not a loss: a player node now reads the same on either, so the pile
 * of white-on-turf contrast overrides this file used to carry (white names,
 * white shirt numbers, a white plate behind the rating pill, a darker edge on
 * the event chip) is gone, and season-stats.css dresses these nodes alone.
 * The greys come through --pitch / --pitch-deep in tokens.css rather than
 * being named here, per that file's rule that a component never reaches past
 * a semantic token.
 */

.match-lineup {
  padding: var(--space-4) var(--space-3);
  opacity: 1;
  transition: opacity var(--duration) var(--easing);
}

/* Both of these mirror `.fixture-details__panel-empty` — the panel this one
   replaces — because `panelVisibility` (FixtureDetails.js) drives every panel
   through the same `hidden` / `data-open` pair. A class beats the UA [hidden]
   rule, so the pair has to stay explicit. */
.match-lineup[hidden] {
  display: none;
}

.match-lineup[data-open="false"] {
  opacity: 0;
}

/* The pitch's own footprint, pulsing, while the lineup feed answers — see
   `Skeleton` in MatchLineup.js. The label rows above and below the pitch are
   not mocked: they are a line of text each, and a placeholder that tall is
   more distracting than the gap. */
.match-lineup__skeleton {
  width: 100%;
  aspect-ratio: 68 / 120;
  border-radius: var(--radius-lg);
}

.match-lineup__empty {
  padding: var(--space-8) var(--space-4);
  font-size: var(--text-sm);
  color: var(--text-muted);
  text-align: center;
}

/*
 * The line over a pitch whose elevens are each side's *last* lineup rather
 * than this match's — see `Caption` in MatchLineup.js. Quiet and centred,
 * like the empty line above, but it sits over real content, so it takes only
 * the space a caption needs and leaves the labels' own rhythm alone.
 */
.match-lineup__caption {
  margin: 0;
  padding: 0 var(--space-1) var(--space-2);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  color: var(--text-muted);
  text-align: center;
}

/* --- Team labels ------------------------------------------------------------- */

/*
 * Crest, club name, formation — one above the pitch for the away side, one
 * below it for the home side. Each sits beside the goal its team is
 * defending, which is what says whose eleven is whose; there is no "home" or
 * "away" word anywhere, because the position already carries it.
 */
.match-lineup__team {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-1);
}

.match-lineup__team-name {
  min-width: 0;
  /* Takes the slack, so the formation is pushed to the far edge and the two
     labels' formations line up with each other down the right-hand side
     rather than floating at whatever width each club name happens to be. */
  flex: 1;
  /* Two steps up the scale from the --text-sm this started at. These two
     labels are the only things naming the teams on the whole panel — there
     is no scoreline or header inside it — so they carry more weight than a
     caption under a card, which is what --text-sm sized them as. */
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  color: var(--text);
}

/* Muted and tabular: a formation is a reference number beside the name, not a
   second title competing with it. Tabular figures keep "4-2-3-1" and "4-3-3"
   setting from the same right edge in the two labels. */
.match-lineup__formation {
  flex: none;
  /* Up with the name, and still a clear step below it: the formation is a
     detail *about* the team, and the two going up together would flatten that
     into a pair of equal headings. */
  font-size: var(--text-md);
  font-weight: var(--weight-medium);
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary);
}

/* --- Pitch ------------------------------------------------------------------- */

.match-lineup__pitch {
  position: relative;
  /*
   * A real pitch is 68m x 105m and this is close to it — closer than the club
   * page's half-pitch card, which had one eleven's worth of content to fit
   * into a card-shaped box and stretched hard to do it. A full pitch in a
   * scrolling panel has the room to be nearly true, so it is: 68/120 against
   * a true 68/105 is a ~13% vertical elongation, enough to buy the clear air
   * between one line of players and the next that `lineDepth`
   * (MatchLineup.js) budgets for, and little enough that the centre circle
   * still reads as a circle under the `preserveAspectRatio="none"` stretch
   * rather than visibly flattening into an ellipse.
   *
   * A ratio, not a fixed height, so all of that holds at any panel width.
   * `lineDepth`'s spacing budget is derived from this number together with
   * `AVATAR_SIZE` — change either and re-check the other.
   */
  aspect-ratio: 68 / 120;
  padding: var(--space-2);
  /*
   * Lifted in the middle and falling away to the corners, so a panel this
   * large reads as a surface rather than as one flat grey block. The ellipse
   * is deliberately wider than the box (120% x 80%) so the falloff is gentle
   * across the middle and only gathers at the corners — a tighter one puts a
   * visible bright disc on the centre circle. The two steps are one rung
   * apart on the neutral ladder, which is as far as this can go: the markings
   * and the player type both have to hold over either end.
   */
  background: radial-gradient(120% 80% at 50% 50%, var(--pitch) 0%, var(--pitch-deep) 100%);
  border-radius: var(--radius-md);
  /* Its own stacking context, so the markings' z-index is compared only
     against this pitch's own rows and never something elsewhere on the page. */
  isolation: isolate;
}

/*
 * The markings — backdrop only. `z-index: 0` against the rows' `z-index: 1`
 * keeps them painting behind; `pointer-events: none` is what actually keeps a
 * tap on an overlap from landing on the pitch instead of the player node,
 * since paint order alone does nothing for hit-testing.
 *
 * `inset: 0` puts this on the padding box, which is the same box the rows'
 * own percentage `top` resolves against — that agreement is what makes a
 * defensive line land where the drawn box says it does.
 */
.match-lineup__pitch-lines {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  /* The half-pitch card's value, which is what this ground now is: a dark
     hairline on a pale panel, quiet enough that the avatars stay the focus.
     It was 0.62 while the lines were white on turf, where anything less
     disappeared. */
  opacity: 0.4;
}

.match-lineup__pitch-line {
  fill: none;
  stroke: var(--pitch-line);
  /* In CSS pixels, per `vector-effect` below — not in pitch metres. */
  stroke-width: 2;
  /* Keeps every line the same weight even though the `<svg>` stretches
     non-uniformly to fill the pitch; without it the strokes would stretch and
     skew along with the shapes they draw. */
  vector-effect: non-scaling-stroke;
}

.match-lineup__pitch-spot {
  fill: var(--pitch-line);
  stroke: none;
}

/*
 * Where the badges sit on this pitch, and how big they are.
 *
 * The photo here is larger than the half-pitch card's and everything hung off
 * it is smaller, which is the opposite of scaling the node as a unit. The
 * reason is that the two are read at different distances: on a phone, a
 * player's photo is the thing being identified and the badges are annotations
 * on it, and at the previous sizes the annotations were closer to swallowing
 * the photo than labelling it.
 *
 * The corners are reassigned to match:
 *
 *   bottom right   rating       (top right on the club pitch)
 *   bottom left    goal/assist  (unchanged)
 *   top right      booking      (top left on the club pitch)
 *
 * Moving the rating down is what frees the top right for a card, and the
 * bottom right is free to take it only because a match lineup carries no club
 * crests — both its teams are named beside the pitch instead. That
 * dependency is noted on `PlayerBadges` in PitchLineup.js too, since it is
 * the kind of thing a later pitch could quietly break.
 */
.match-lineup__pitch .season-stats__rating {
  top: auto;
  bottom: -1px;
  right: -4px;
  /* Sized by its two decimals and nothing else — a minimum width was
     holding it open past the number, and the number is all it says. The
     line box is tightened for the same reason: at this size the font's own
     leading was a third of the pill's height. */
  min-width: 0;
  padding: 0.5px 3.5px;
  border-width: 1.25px;
  font-size: 9px;
  line-height: 1.15;
}

.match-lineup__pitch .season-stats__event {
  bottom: -3px;
  left: -5px;
  gap: 0;
  padding: 0.5px 3.5px;
  border-width: 1.25px;
}

.match-lineup__pitch .season-stats__event-count {
  font-size: 9px;
}

/* Into the corner the rating pill left. `left: auto` is required, not
   decorative — season-stats.css positions this from the left, and setting
   `right` alone would leave both edges pinned and stretch the card. The 3px
   is that file's own inset mirrored to this side; see its comment for why a
   card insets where the other badges outset. */
.match-lineup__pitch .season-stats__card {
  top: 2px;
  right: 3px;
  left: auto;
}

/*
 * The rating pill needs nothing of its own here beyond the placement above.
 * It once did, twice over: its fill was a 12% tint of the tier colour that
 * all but vanished over the pitch, so this file first painted a white plate
 * back under it, and then — when the pill moved down onto the photo and its
 * white ring went white-on-white against the plate — gave it a tier-coloured
 * edge to stop it fusing into the photo's silhouette. The fill is a solid
 * block of the tier colour now (season-stats.css), which reads on its own
 * over a photo, a pitch or a page, and the white ring it keeps is what parts
 * it from the plate.
 */

/*
 * One line of players, spanning the pitch and spreading its own across it —
 * count and spacing follow from however many players are in the row, and
 * `space-around` centres a goalkeeper's row of one for free.
 *
 * `top` is set inline per row (`TeamRows`, MatchLineup.js) from that line's
 * real-world depth, and `translateY(-50%)` is what makes that value the row's
 * *centre* rather than its top edge. Centred, because two teams meeting in
 * the middle need every node bounded at both ends — see the comment above
 * `lineDepth` for the spacing budget that depends on it.
 */
.match-lineup__row {
  position: absolute;
  z-index: 1;
  left: 0;
  right: 0;
  transform: translateY(-50%);
  display: flex;
  justify-content: space-around;
  align-items: center;
  gap: var(--space-1);
}

/* --- Squad lists ------------------------------------------------------------
 *
 * The tabbed coach, substitutions and injury lists under the pitch
 * (MatchSquad.js). They live in this file rather than one of their own because
 * they are only ever this panel's, and `season-stats.css` already sets the
 * precedent: a stylesheet here covers a surface, not a single JS module.
 *
 * Everything below the tabs sits in cards, which the pitch above does not.
 * That is the split the panel is built on: the pitch is one object filling its
 * own width, and these are stacked blocks of list, each needing an edge to say
 * where it starts. The card recipe — surface, hairline, large radius, card
 * shadow — is the one every other card in the fixture page's panels already
 * uses (see fixture-preview.css).
 */

.match-squad {
  padding-top: var(--space-5);
}

/* --- Team tabs ---------------------------------------------------------------
 *
 * A two-up segmented control: a sunken track with a raised pill sliding
 * between its halves. Crests only — see the note in MatchSquad.js for why this
 * is not `TabBar`, and why a crest says everything a club name would here.
 *
 * No `gap` between the tabs, which is load-bearing rather than a style choice:
 * the pill is sized and moved in percentages of the track, and a gap would
 * make each tab (100% - gap) / 2 wide while the pill stayed at 50%, so the two
 * would agree at one width and nowhere else.
 */
.match-squad__tabs {
  position: relative;
  display: flex;
  padding: var(--space-1);
  background: var(--surface-sunken);
  border-radius: var(--radius-full);
  /* Contains the pill's overshoot: `--spring` deliberately passes its target
     before settling, and without this its leading edge would break the track's
     rounded end on every switch. */
  overflow: hidden;
}

/*
 * The selected half, as one element that travels.
 *
 * `--spring` at `--spring-duration`, which is what INTERACTIONS.md asks for on
 * a tab indicator: it is a physical object moving between two places, not
 * something appearing. Transform only, so the whole move is one composited
 * property and no layout is touched.
 *
 * Its width matches a tab exactly. Percentages on an absolutely positioned box
 * resolve against the track's padding box, so 50% is half the *outer* width
 * and the inset has to come back off it — the same arithmetic that makes each
 * `flex: 1` tab (100% - 2 x inset) / 2 wide.
 */
.match-squad__tabs-pill {
  position: absolute;
  top: var(--space-1);
  bottom: var(--space-1);
  left: var(--space-1);
  width: calc(50% - var(--space-1));
  background: var(--surface);
  border-radius: var(--radius-full);
  box-shadow: var(--shadow-card);
  transition: transform var(--spring-duration) var(--spring);
}

/* One tab's width, which is exactly this element's own — so the move is 100%
   of itself rather than a length that would have to be recomputed. */
.match-squad__tabs[data-active="away"] .match-squad__tabs-pill {
  transform: translateX(100%);
}

.match-squad__tab {
  /* Above the pill, which is the whole reason it can slide *under* the crests
     rather than carrying them along with it. */
  position: relative;
  z-index: 1;
  display: flex;
  flex: 1;
  align-items: center;
  justify-content: center;
  padding: var(--space-2);
  background: none;
  border: none;
  border-radius: var(--radius-full);
}

/*
 * The unselected crest steps back without disappearing — it is still the way
 * to reach that team, so it has to stay legible as a target.
 *
 * On `--easing`, not the spring: this one is appearing and disappearing, and
 * an overshoot on opacity would flare past full before settling.
 */
.match-squad__tab .logo {
  transition: opacity var(--duration) var(--easing);
}

.match-squad__tab[aria-selected="false"] .logo {
  opacity: 0.55;
}

/* --- Panels ------------------------------------------------------------------
 *
 * The two teams' panels are stacked in one grid cell rather than following one
 * another down the page. That is what lets the outgoing team stay in place
 * while the incoming one arrives over it — a cross-dissolve needs both halves
 * on screen at once — and it is also why nothing jumps: the cell is as tall as
 * the taller of the two, so switching teams never changes the height of
 * anything above or below.
 *
 * The trade-off is real and worth stating. A team with the shorter injury list
 * leaves blank space at the bottom of its panel, because the taller team is
 * still setting the height. Sizing the cell to whichever panel is showing
 * would mean animating height, which INTERACTIONS.md rules out outright, and
 * snapping it instead would put a jump on every switch.
 */
.match-squad__panels {
  display: grid;
  padding-top: var(--space-3);
}

/*
 * `visibility` is what actually retires the outgoing panel: it takes it out of
 * the accessibility tree and out of reach of a tap, neither of which
 * `opacity: 0` does on its own. It is delayed by the length of the fade on the
 * way out, so the panel is still drawn while it fades, and immediate on the
 * way in — which is why the two states spell out their transitions separately
 * instead of sharing one.
 */
.match-squad__panel {
  grid-area: 1 / 1;
  transition:
    opacity var(--duration) var(--easing),
    visibility 0s;
}

.match-squad__panel[data-open="false"] {
  opacity: 0;
  visibility: hidden;
  transition:
    opacity var(--duration) var(--easing),
    visibility 0s linear var(--duration);
}

/* --- Cards ------------------------------------------------------------------- */

/*
 * Each card lifts into place a beat after the one above it. `--card` is set per
 * card in MatchSquad.js and `--stagger-step` is the app's own 40ms, so three
 * cards finish 80ms apart. Deliberately small: the panel is cross-fading
 * underneath them, and a long cascade on top of that reads as two animations
 * arguing rather than one arrival. The whole thing lands inside
 * `--duration-max`.
 *
 * The delay applies on the way in only — see the leaving state below. A
 * staggered exit makes a reader wait to see the thing they just asked for.
 */
.match-squad__card {
  padding: var(--space-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  transition:
    opacity var(--duration) var(--easing) calc(var(--card, 0) * var(--stagger-step)),
    transform var(--duration) var(--easing) calc(var(--card, 0) * var(--stagger-step));
}

.match-squad__card + .match-squad__card {
  margin-top: var(--space-3);
}

/*
 * Where a card comes from, and where it goes back to. A short rise, not a
 * slide from the side: the panel it belongs to is not going anywhere
 * horizontally, and 10px is enough to read as movement without the card
 * appearing to travel.
 */
.match-squad__panel[data-open="false"] .match-squad__card {
  opacity: 0;
  transform: translateY(10px);
  transition-delay: 0s;
}

/* Centred, which is the one place this panel departs from the app's usual
   left-aligned card titles: these cards are a list under a label rather than a
   block of content with a heading over it, and the label sits over the middle
   of what it names. */
.match-squad__title {
  margin: 0 0 var(--space-2);
  font-size: var(--text-md);
  font-weight: var(--weight-bold);
  color: var(--text);
  text-align: center;
}

/* --- The coach --------------------------------------------------------------- */

.match-squad__coach {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.match-squad__coach-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.match-squad__coach-name {
  font-size: var(--text-md);
  font-weight: var(--weight-bold);
  color: var(--text);
}

/* The accent, not --text-secondary. It is the only coloured word on the card
   and it is doing what a caption does — saying what the name above it is — so
   it takes the app's own accent rather than reading as a second, quieter name. */
.match-squad__coach-role {
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--accent);
}

/* --- One player --------------------------------------------------------------- */

.match-squad__rows {
  display: flex;
  flex-direction: column;
}

.match-squad__row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding-block: var(--space-2);
  background: none;
  border: none;
  text-align: left;
}

/* Hairlines between rows and never above the first, so the list is divided
   rather than boxed — the card's own edge is already doing that job. */
.match-squad__row + .match-squad__row {
  border-top: 1px solid var(--border);
}

.match-squad__text {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.match-squad__name {
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--text);
}

/* Muted and tabular, the same relationship the shirt number has to the name on
   the pitch above: a row of single- and double-digit numbers sets the names
   beside them from one left edge. */
.match-squad__number {
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* --- The substitution line ---------------------------------------------------- */

.match-squad__sub {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  min-width: 0;
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
}

/* A filled disc with a white arrow in it, which is what makes this read as an
   arrival rather than as a bullet before a time. */
.match-squad__sub-arrow {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  background: var(--sub-on);
  border-radius: var(--radius-full);
  color: var(--text-inverse);
}

.match-squad__sub-minute {
  flex: none;
  color: var(--sub-on);
  font-weight: var(--weight-bold);
  font-variant-numeric: tabular-nums;
}

/* "Out:" and the name after it are both quiet — the minute is the fact, and
   who came off is the detail hanging off it. The label is a shade lighter
   again so the name is the readable half of the pair. */
.match-squad__sub-label {
  flex: none;
  color: var(--text-muted);
}

.match-squad__sub-name {
  min-width: 0;
  color: var(--text-secondary);
}

/* --- The absence line ---------------------------------------------------------- */

/*
 * Coloured by status rather than by kind: the question this line answers is
 * whether the player is available, and "Injured" and "Doubtful" are the two
 * answers. A suspension is always out, so it takes the same red the injured
 * do without needing a rule of its own.
 */
.match-squad__absence {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  min-width: 0;
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
  color: var(--injury);
}

.match-squad__absence[data-status="doubtful"] {
  color: var(--doubtful);
}

.match-squad__absence-label {
  flex: none;
  font-weight: var(--weight-bold);
}

/* The body part, in the neutral text colour — it is a detail, and two coloured
   strings on one line would compete for the same glance. A suspension has none
   and the line simply ends. */
.match-squad__absence-reason {
  min-width: 0;
  color: var(--text-secondary);
}

/* The card itself, at row scale — the pitch badge's shape (season-stats.css)
   without its white plating, which exists there to lift it off a photo and has
   nothing to lift it off here. */
.match-squad__card-mark {
  flex: none;
  width: 7px;
  height: 10px;
  background: var(--card-red);
  border-radius: 1.5px;
}

/* --- The rating ---------------------------------------------------------------- */

/*
 * A solid block of colour with white type, where the pitch's own pill is a
 * tinted wash with coloured type. The two are read differently: the pill sits
 * among ten others on a busy pitch and has to stay quiet, and this ends a row
 * and is what the eye travels along the row to reach.
 *
 * The fills are their own tokens (--rating-*-solid) rather than the tinted
 * pair, because a bright amber cannot carry white text — see the palette note
 * in tokens.css. One decimal, not the pitch's two: up there a fixed width keeps
 * a column of pills aligned, down here a rating is read on its own and "6.8" is
 * what anyone would say.
 */
.match-squad__rating {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  padding: 3px var(--space-1);
  border-radius: var(--radius-sm);
  color: var(--text-inverse);
  font-size: var(--text-2xs);
  font-weight: var(--weight-bold);
  font-variant-numeric: tabular-nums;
}

.match-squad__rating[data-tier="high"] {
  background: var(--rating-high-solid);
}

.match-squad__rating[data-tier="mid"] {
  background: var(--rating-mid-solid);
}

.match-squad__rating[data-tier="low"] {
  background: var(--rating-low-solid);
}
