/*
 * Shape Pairs.
 *
 * Every colour here is one of the shared custom properties, or a mix of two of
 * them, so a buyer's palette in brand.config.js reaches the cards, the combo
 * meter and the focus ring without a single edit to this file. Sizes are
 * intrinsic — the grid is told how many columns it has and works the rest out
 * — so the same board fills a 360px phone and a desktop without a media query.
 *
 * Depth follows the shell: layered shadows and a light top edge rather than
 * heavy borders, and the cards sit in a sunken tray so they read as pieces on
 * a board rather than boxes on paper.
 */

.sp-stage {
  width: 100%;
  display: grid;
  gap: clamp(12px, 2.2vw, 18px);
  justify-items: center;

  /* One number drives both the stat rail and the tray, so the two line up
     exactly at every width. --sp-cols is set on this element by game.js. */
  --sp-board-w: calc(var(--sp-cols) * 104px);
  /* Trimmed from the shell tray's default: the six columns of the hard board
     have to clear a 44px tap target inside a 360px phone, and every pixel of
     padding here comes straight off the cards. */
  --sp-pad: clamp(5px, 1.4vw, 14px);

  /*
   * Local tones, mixed from the shared palette so nothing here is a colour in
   * its own right. Each is declared twice: the plain `transparent` above wins
   * on a browser without color-mix(), which loses the texture and keeps the
   * surfaces — far better than falling back to a colour that fights the brand.
   */
  --sp-shade: transparent;
  --sp-shade: color-mix(in srgb, var(--la-ink) 12%, transparent);
  --sp-shade-soft: transparent;
  --sp-shade-soft: color-mix(in srgb, var(--la-ink) 7%, transparent);
  --sp-gloss: transparent;
  --sp-gloss: color-mix(in srgb, var(--la-on-primary) 17%, transparent);
  --sp-gloss-soft: transparent;
  --sp-gloss-soft: color-mix(in srgb, var(--la-on-primary) 9%, transparent);
  --sp-hatch: transparent;
  --sp-hatch: color-mix(in srgb, var(--la-on-primary) 8%, transparent);
  --sp-foot: transparent;
  --sp-foot: color-mix(in srgb, var(--la-ink) 20%, transparent);
}

/* ---- Heads-up display -------------------------------------------------- */
.sp-hud {
  width: 100%;
  max-width: var(--sp-board-w);
  display: flex;
  align-items: center;
  /* Centred rather than pushed to the edges: on the standard board the meter
     and the figures do not fit on one line, and two left-aligned rows with a
     hole beside them is the look this pass exists to get rid of. */
  justify-content: center;
  gap: 10px clamp(16px, 4vw, 36px);
  flex-wrap: wrap;
}

/* ---- Combo meter -------------------------------------------------------
   A charging meter rather than a row of dots: a sunken track, pips that fill
   from the left, and the multiplier read out beside them in the numeric face
   so the number never carries the state on colour alone. */
.sp-combo {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 5px 11px;
  border-radius: 999px;
  background: var(--la-surface);
  border: 1px solid var(--la-line);
  box-shadow: var(--la-shadow-sm), var(--la-shadow-inset);
  transition:
    border-color 0.2s ease,
    box-shadow 0.2s ease;
}
.sp-combo.is-live {
  border-color: var(--la-line-strong);
  border-color: color-mix(in srgb, var(--la-primary) 46%, var(--la-line-strong));
  box-shadow: var(--la-shadow-sm), var(--la-shadow-inset), 0 0 0 3px var(--la-primary-tint);
}

.sp-combo__label {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  /* Soft ink, not faint: this label has to clear 4.5:1 on the panel. */
  color: var(--la-ink-soft);
}

.sp-combo__rail {
  display: flex;
  gap: 3px;
  padding: 3px;
  border-radius: 999px;
  background: var(--la-surface-sunken);
  box-shadow: inset 0 1px 2px var(--sp-shade-soft);
}
.sp-combo__seg {
  width: 15px;
  height: 7px;
  border-radius: 999px;
  background: var(--la-line);
  transition:
    background 0.2s ease,
    box-shadow 0.2s ease;
}
.sp-combo__seg.is-lit {
  background-image: linear-gradient(180deg, var(--la-primary), var(--la-primary-hover));
  box-shadow: 0 0 5px var(--la-primary-glow);
}

.sp-combo__value {
  display: inline-block;
  min-width: 2.1em;
  text-align: right;
  font-family: var(--la-numeric);
  font-size: 13px;
  font-weight: 800;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  color: var(--la-ink-soft);
  transition: color 0.2s ease;
}
.sp-combo.is-live .sp-combo__value {
  color: var(--la-primary);
}

/* ---- Stat line ---------------------------------------------------------
   Pairs, flips and time as shell figures, evenly spaced and split by
   hairlines so the three read as one instrument. */
.sp-stats {
  display: flex;
  align-items: baseline;
  margin: 0;
  padding: 0;
}
.sp-stat {
  padding: 0 clamp(9px, 2vw, 15px);
}
.sp-stat + .sp-stat {
  border-left: 1px solid var(--la-line);
}
.sp-stat:first-child {
  padding-left: 0;
}
.sp-stat:last-child {
  padding-right: 0;
}
.sp-stats .la-stat {
  color: var(--la-ink-soft);
}
.sp-stats dd {
  margin: 0;
}
.sp-stats .la-stat__value {
  font-family: var(--la-numeric);
  font-size: 15.5px;
}

/* ---- The board ---------------------------------------------------------
   The cards live in a sunken tray, the way the pieces do in every other game
   in the pack. The tray's own padding is trimmed from the shell's default so
   six columns still clear a 44px tap target on a 360px phone. */
.sp-board {
  width: 100%;
  max-width: calc(var(--sp-board-w) + 2 * var(--sp-pad) + 2px);
  padding: var(--sp-pad);
  box-shadow: inset 0 2px 6px var(--sp-shade);
}

.sp-grid {
  width: 100%;
  display: grid;
  grid-template-columns: repeat(var(--sp-cols), 1fr);
  gap: clamp(4px, 1.15vw, 12px);
  /* One vanishing point for the whole board, so a flip reads as a card being
     turned on a table rather than each card spinning in its own little box. */
  perspective: 1200px;
  perspective-origin: 50% 42%;
}

.sp-card {
  position: relative;
  /* Slightly taller than wide reads as a card without making four rows an
     awkward scroll on a phone. */
  aspect-ratio: 5 / 6;
  padding: 0;
  border: 0;
  background: none;
  border-radius: 12px;
  cursor: pointer;
  /* The card joins the grid's 3D context rather than starting a flat one of
     its own — without this the pop on a match would flatten the flip. */
  transform-style: preserve-3d;
  -webkit-tap-highlight-color: transparent;
}
.sp-card[aria-disabled="true"] {
  cursor: default;
}

/* The ring is drawn in ink, not the brand colour: a primary-coloured ring on
   a primary-coloured card back would be invisible on half the board. */
.sp-card:focus-visible {
  outline: 3px solid var(--la-ink);
  outline-offset: 2px;
  z-index: 3;
}

.sp-card__inner {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  /* Long enough to read as a turn, short enough that a fast player is never
     waiting on it; the curve does almost all its work early and coasts in. */
  transition: transform 320ms cubic-bezier(0.16, 0.86, 0.28, 1);
}
.sp-card.is-up .sp-card__inner {
  transform: rotateY(180deg);
}
.sp-card:not(.is-up):not([aria-disabled="true"]):hover .sp-card__inner {
  transform: translate3d(0, -3px, 8px);
}

.sp-face {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  border-radius: 12px;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  overflow: hidden;
}

/*
 * The back of a card, not a coloured box: a fine mesh of two hatchings, a
 * medallion of light behind the motif, a sheen off the top-left corner and a
 * shade gathering at the foot. Every layer is a gradient, so it costs nothing
 * to ship and re-colours itself with the brand.
 */
.sp-face--back {
  color: var(--la-on-primary);
  background-color: var(--la-primary);
  background-image:
    linear-gradient(148deg, var(--sp-gloss) 0%, transparent 46%),
    repeating-linear-gradient(45deg, var(--sp-hatch) 0 1px, transparent 1px 7px),
    repeating-linear-gradient(-45deg, var(--sp-hatch) 0 1px, transparent 1px 7px),
    radial-gradient(58% 46% at 50% 44%, var(--sp-gloss-soft), transparent 72%),
    radial-gradient(122% 78% at 50% 118%, var(--sp-foot), transparent 62%),
    linear-gradient(158deg, var(--la-primary), var(--la-primary-hover));
  box-shadow:
    inset 0 1px 0 var(--sp-gloss),
    inset 0 0 0 1px var(--sp-shade),
    var(--la-shadow-sm);
}

.sp-face--front {
  transform: rotateY(180deg);
  background-color: var(--la-surface);
  background-image: linear-gradient(180deg, var(--la-surface) 0%, var(--la-surface-sunken) 100%);
  border: 1px solid var(--la-line-strong);
  box-shadow: var(--la-shadow-inset), var(--la-shadow-sm);
}

.sp-art {
  width: 66%;
  height: 66%;
  display: block;
  transition: transform 0.24s ease;
}
.sp-art--back {
  width: 46%;
  height: 46%;
  opacity: 0.55;
}

/* ---- Matched ------------------------------------------------------------
   A cleared pair settles: the face goes flat and sunken, the shadow comes off
   so it stops floating, the mark shrinks a little — and a tick appears, so
   the board's progress is legible without reading the colour. */
.sp-card.is-matched .sp-face--front {
  background-image: none;
  /* A shade above the tray it is sitting in, not level with it: matched flat
     to the tray colour reads as a hole in the board rather than a card that
     has been turned over and left there. */
  background-color: var(--la-surface-sunken);
  background-color: color-mix(in srgb, var(--la-surface) 45%, var(--la-surface-sunken));
  border-color: var(--la-line-strong);
  border-color: color-mix(in srgb, var(--la-primary) 34%, var(--la-line-strong));
  box-shadow: none;
}
.sp-card.is-matched .sp-art {
  transform: scale(0.9);
}

.sp-tick {
  position: absolute;
  top: 6%;
  right: 6%;
  width: 26%;
  min-width: 12px;
  max-width: 20px;
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: var(--la-primary);
  color: var(--la-on-primary);
  box-shadow: 0 0 0 2px var(--la-surface-sunken);
  opacity: 0;
  transform: scale(0.4);
  transition:
    opacity 0.18s ease,
    transform 0.26s cubic-bezier(0.2, 1.3, 0.4, 1);
}
.sp-card.is-matched .sp-tick {
  opacity: 1;
  transform: scale(1);
}
.sp-tick svg {
  width: 64%;
  height: 64%;
  display: block;
}

/* ---- Mark inks --------------------------------------------------------
   Four inks mixed from the two brand colours. The fallback on the line above
   each mix keeps older browsers on a real brand colour rather than black. */
.sp-tone-0 {
  color: var(--la-primary);
}
.sp-tone-1 {
  color: var(--la-ink);
}
.sp-tone-2 {
  color: var(--la-primary-hover);
  color: color-mix(in srgb, var(--la-primary) 58%, var(--la-ink));
}
.sp-tone-3 {
  color: var(--la-ink-soft);
  color: color-mix(in srgb, var(--la-ink) 62%, var(--la-primary));
}

.sp-note {
  /* .la-hint carries the size and centring; the colour is lifted off the
     shell's faint ink so this line clears 4.5:1 on the board. */
  color: var(--la-ink-soft);
}

/* ---- Movement ---------------------------------------------------------- */
@keyframes sp-pop {
  0% {
    transform: scale3d(1, 1, 1);
  }
  45% {
    transform: scale3d(1.07, 1.07, 1);
  }
  100% {
    transform: scale3d(1, 1, 1);
  }
}
@keyframes sp-nudge {
  0%,
  100% {
    transform: translate3d(0, 0, 0);
  }
  25% {
    transform: translate3d(-4px, 0, 0);
  }
  75% {
    transform: translate3d(4px, 0, 0);
  }
}
@keyframes sp-charge {
  0% {
    transform: scale(1);
  }
  40% {
    transform: scale(1.45, 1.3);
  }
  100% {
    transform: scale(1);
  }
}
@keyframes sp-value-bump {
  0% {
    transform: scale(1);
  }
  35% {
    transform: scale(1.3);
  }
  100% {
    transform: scale(1);
  }
}
@keyframes sp-rail-flash {
  0% {
    box-shadow: inset 0 1px 2px var(--sp-shade-soft), 0 0 0 0 var(--la-primary-glow);
  }
  40% {
    box-shadow: inset 0 1px 2px var(--sp-shade-soft), 0 0 0 6px var(--la-primary-tint);
  }
  100% {
    box-shadow: inset 0 1px 2px var(--sp-shade-soft), 0 0 0 0 transparent;
  }
}
/* The meter draining, rather than simply dimming: the streak is gone and the
   row should look like it lost something. */
@keyframes sp-drain {
  0% {
    opacity: 1;
    transform: translateX(0);
  }
  30% {
    opacity: 0.4;
    transform: translateX(-2px);
  }
  65% {
    transform: translateX(2px);
  }
  100% {
    opacity: 1;
    transform: translateX(0);
  }
}

.sp-card.is-hit {
  animation: sp-pop 0.34s ease;
}
.sp-card.is-wrong {
  animation: sp-nudge 0.32s ease;
}
.sp-combo__seg.is-fresh {
  animation: sp-charge 0.42s cubic-bezier(0.2, 0.9, 0.3, 1);
}
.sp-combo.is-bump .sp-combo__value {
  animation: sp-value-bump 0.42s ease;
}
.sp-combo.is-bump .sp-combo__rail {
  animation: sp-rail-flash 0.5s ease;
}
.sp-combo.is-lost .sp-combo__rail {
  animation: sp-drain 0.44s ease;
}

/*
 * Reduced motion: the flip is the one piece of movement that carries meaning,
 * so it is replaced with a cross-fade between the two faces rather than being
 * slowed down. The shell clamps every transition to a hair under a
 * millisecond, so in practice this lands as a clean swap — which is the point.
 */
@media (prefers-reduced-motion: reduce) {
  /* The hover selector below repeats every part of the rule it is cancelling.
     Drop the :not([aria-disabled="true"]) and it loses on specificity and does
     nothing at all. */
  .sp-card__inner,
  .sp-card.is-up .sp-card__inner,
  .sp-card:not(.is-up):not([aria-disabled="true"]):hover .sp-card__inner {
    transition: none;
    transform: none;
  }
  .sp-grid {
    perspective: none;
  }
  .sp-face {
    backface-visibility: visible;
    -webkit-backface-visibility: visible;
    transition: opacity 0.15s linear;
  }
  .sp-face--front {
    transform: none;
    opacity: 0;
  }
  .sp-card.is-up .sp-face--front {
    opacity: 1;
  }
  .sp-card.is-up .sp-face--back {
    opacity: 0;
  }
  .sp-art,
  .sp-tick {
    transition: none;
  }
  .sp-card.is-hit,
  .sp-card.is-wrong,
  .sp-combo__seg.is-fresh,
  .sp-combo.is-bump .sp-combo__value,
  .sp-combo.is-bump .sp-combo__rail,
  .sp-combo.is-lost .sp-combo__rail {
    animation: none;
  }
}

/* A wrong pair is outlined as well as nudged, so the miss is not carried by
   movement alone for anyone who has turned movement off. */
.sp-card.is-wrong .sp-face--front {
  border: 2px dashed var(--la-ink);
  box-shadow: none;
}

@media (max-width: 460px) {
  .sp-hud {
    justify-content: center;
  }
  .sp-stats {
    width: 100%;
    justify-content: center;
  }
  .sp-art {
    width: 70%;
    height: 70%;
  }
}
