/* Stars — Map Select Cards
   Visual tokens from specs/Stars logo/handoff/README.md */

/* ============================================================
   Card — flip container
   Sits inside the existing .chapter-grid (horizontal scroll row).
   ============================================================ */
.ms-card {
  flex: 0 0 200px;
  width: 200px;
  height: 280px;
  scroll-snap-align: center;
  perspective: 1400px;
  cursor: pointer;
  outline: none;
}
.ms-card:focus-visible {
  outline: none;
}

.ms-card__inner {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transition: transform 900ms cubic-bezier(0.7, 0.05, 0.3, 1);
}
.ms-card.is-flipped .ms-card__inner {
  transform: rotateY(180deg);
}

.ms-card__face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  overflow: hidden;
  background: #0a0d1a;
  border: 1px solid rgba(255,255,255,0.09);
  transition: border-color 0.35s ease;
}

/* Front = orbit / locked face */
.ms-card__front {
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Back = revealed face (constellation + chrome) */
.ms-card__back {
  transform: rotateY(180deg);
}

/* ============================================================
   Card border states
   ============================================================ */
/* Selected (focused) */
.ms-card:focus .ms-card__face,
.ms-card.is-selected .ms-card__face {
  border-color: rgba(255,255,255,0.85);
  border-width: 1.5px;
}

/* Locked — dimmer border */
.ms-card.is-locked .ms-card__front {
  border-color: rgba(255,255,255,0.06);
}

/* ============================================================
   Category-tinted borders + card body tint
   Register outline (below): Par = crimson, Time = shard-blue, Training = white.
   Tint applies to both faces (front constellation + back orbit) so
   category reads identically whether the card is flipped or not.
   ============================================================ */
.ms-card--time .ms-card__face     { background: #0a0d1a;  border-color: rgba(170, 200, 255, 0.25); }
.ms-card--par  .ms-card__face     { background: #090b1c;  border-color: rgba(130, 160, 240, 0.30); }
.ms-card--training .ms-card__face { background: #0c0c10;  border-color: rgba(255, 255, 255, 0.15); }

.ms-card--time:focus .ms-card__face,
.ms-card--time.is-selected .ms-card__face     { border-color: rgba(170, 200, 255, 0.9); }
.ms-card--par:focus .ms-card__face,
.ms-card--par.is-selected .ms-card__face      { border-color: rgba(130, 160, 240, 0.9); }
.ms-card--training:focus .ms-card__face,
.ms-card--training.is-selected .ms-card__face { border-color: rgba(255, 255, 255, 0.85); }

/* Display register drives the card outline: Par = crimson, Time = shard-blue.
   Training stays neutral white regardless of its display register, so it's
   excluded from both rules. */
.ms-card--display-par:not(.ms-card--training) .ms-card__face { border-color: rgba(220, 20, 60, 0.55); }
.ms-card--display-par:not(.ms-card--training):focus .ms-card__face,
.ms-card--display-par:not(.ms-card--training).is-selected .ms-card__face { border-color: rgba(255, 70, 95, 0.95); }

.ms-card--display-time:not(.ms-card--training) .ms-card__face { border-color: rgba(170, 200, 255, 0.55); }
.ms-card--display-time:not(.ms-card--training):focus .ms-card__face,
.ms-card--display-time:not(.ms-card--training).is-selected .ms-card__face { border-color: rgba(170, 200, 255, 0.95); }

/* ============================================================
   Revealed-face chrome
   ============================================================ */
.ms-chrome-top {
  position: absolute;
  top: 14px;
  left: 16px;
  right: 16px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-family: var(--r-mono, 'JetBrains Mono', monospace);
  font-size: 9px;
  letter-spacing: 0.22em;
  color: rgba(255,255,255,0.32);
  pointer-events: none;
  z-index: 1;
}

.ms-chrome-top__check {
  color: rgba(77,168,255,0.75);
  letter-spacing: 0;
  font-size: 11px;
}

.ms-chrome-top__tier {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  display: inline-block;
  box-shadow: 0 0 6px currentColor;
}
.ms-tier-redDwarf    { background: rgb(255, 140, 100); color: rgb(255, 140, 100); }
.ms-tier-yellowDwarf { background: rgb(255, 220, 130); color: rgb(255, 220, 130); }
.ms-tier-blueGiant   { background: rgb(150, 200, 255); color: rgb(150, 200, 255); }
.ms-tier-magnetar    { background: rgb(220, 150, 255); color: rgb(220, 150, 255); }

.ms-chrome-bottom {
  position: absolute;
  left: 16px;
  right: 16px;
  bottom: 14px;
  pointer-events: none;
  z-index: 1;
}

.ms-map-name {
  font-family: var(--stars-font, 'Inter', sans-serif);
  font-weight: 300;
  font-size: 13px;
  letter-spacing: 0.01em;
  color: rgba(255,255,255,0.82);
  text-transform: lowercase;
  margin-bottom: 6px;
  transition: color 0.35s ease;
}

.ms-card:focus .ms-map-name,
.ms-card.is-selected .ms-map-name {
  color: #ffffff;
}

.ms-chrome-meta {
  display: flex;
  gap: 14px;
  font-family: var(--r-mono, 'JetBrains Mono', monospace);
  font-size: 10px;
  letter-spacing: 0.2em;
  color: rgba(255,255,255,0.32);
  text-transform: uppercase;
  align-items: center;
}

/* Time-map unlock telegraph — shipped builds only (see map-select.js
   LOCK_PROGRESSION). Amber, because it speaks to "you". */
.ms-gate-hint {
  margin-top: 5px;
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(232,200,121,0.72);
}

.ms-diff-pips {
  display: inline-flex;
  gap: 2px;
  align-items: center;
}
.ms-diff-pip {
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: rgba(255,255,255,0.08);
}
.ms-diff-pip.is-active {
  background: rgba(77,168,255,0.85);
  box-shadow: 0 0 4px rgba(77,168,255,0.5);
}

.ms-par-value {
  color: rgba(244,162,97,0.75);
}

.ms-time-value {
  color: rgba(244, 162, 97, 0.75);
}

.ms-time-unit {
  color: rgba(170, 200, 255, 0.75);
  margin-left: 1px;
}

/* Shard facet — blue-white to match the in-game crystalline shard render.
   margin-left:auto pushes it to the meta row's right edge so it sits in the
   bottom-right corner, mirroring the top-right tier/rank dot. */
.ms-shard-facet {
  margin-left: auto;
  color: rgba(200, 225, 255, 0.9);
  font-size: 10px;
  line-height: 1;
  letter-spacing: 0;
  text-shadow: 0 0 5px rgba(170, 200, 255, 0.55);
}

/* ============================================================
   Constellation SVG area
   ============================================================ */
.ms-constellation {
  position: absolute;
  top: 42px;
  left: 0;
  right: 0;
  bottom: 52px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.ms-constellation svg {
  overflow: visible;
}

/* ============================================================
   Orbit SVG animation
   ============================================================ */
.ms-orbit-svg {
  width: 165px;
  height: 165px;
  overflow: visible;
}

/* ============================================================
   Étude — the practice-room doorway (§1 entrance)
   A white wormhole adrift in the open margin, apart from the card
   filmstrip. Rest = thin white line-work (silence is white); hover
   blooms into color as a low tone fades in (sound = color). Not a
   card: no flip, no rank, no pips. Built by map-select.js.
   ============================================================ */
.etude-portal {
  position: absolute;
  top: 15%;
  right: max(6vw, 3rem);
  width: 132px;
  height: 132px;
  z-index: 2;
  cursor: pointer;
  outline: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* adrift: a slow, small wander so it never reads as pinned UI */
  animation: etude-drift 23s ease-in-out infinite alternate;
}
.etude-svg {
  width: 132px;
  height: 132px;
  overflow: visible;
  /* the bloom: none at rest, a soft colored glow when awake */
  filter: drop-shadow(0 0 0 rgba(120, 190, 255, 0));
  transition: filter 0.55s var(--stars-ease-out-expo, ease-out);
}
.etude-rings {
  transform-box: fill-box;
  transform-origin: center;
}
.etude-rings--cw  { animation: etude-churn 34s linear infinite; }
.etude-rings--ccw { animation: etude-churn 27s linear infinite reverse; }
.etude-rings ellipse,
.etude-core {
  fill: none;
  stroke: rgba(255, 255, 255, 0.42);   /* silence is white */
  stroke-width: 1;
  transition: stroke 0.55s var(--stars-ease-out-expo, ease-out);
}
.etude-core { fill: rgba(255, 255, 255, 0.55); stroke: none; }
.etude-caption {
  margin-top: 0.35rem;
  font-family: var(--stars-font);
  font-size: 0.72rem;
  font-weight: 300;
  letter-spacing: 0.28em;
  color: rgba(180, 214, 255, 0.9);
  opacity: 0;
  transition: opacity 0.5s var(--stars-ease-out-expo, ease-out);
  pointer-events: none;
}

/* awake — hover or keyboard focus: bloom + caption (tone starts in JS) */
.etude-portal.is-awake .etude-svg {
  filter: drop-shadow(0 0 7px rgba(120, 190, 255, 0.85));
}
.etude-portal.is-awake .etude-rings ellipse {
  stroke: hsl(205, 90%, 74%);
}
.etude-portal.is-awake .etude-core {
  fill: hsl(205, 95%, 82%);
}
.etude-portal.is-awake .etude-caption { opacity: 0.92; }

/* ---- the entrance card: a vertical Einstein–Rosen bridge you turn over ----
   One face; the BRIDGE flips (rotateX), so its poles swap — orange-up/blue-down
   = Étude, blue-up/orange-down = Choros. Body = enter the shown mode; the
   corner (or DOWN on the pad) turns the bridge. A normal flex card at the head
   of the secondary strip — same size and behaviour as every other card. */
.etude-portal.etude-card {
  position: relative;
  top: auto; right: auto; bottom: auto; left: auto;
  inset: auto;
  width: 200px;
  height: 280px;
  flex: 0 0 200px;
  z-index: auto;
  animation: none;          /* a row card holds still; it does not drift */
}
.etude-card__face {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 12px;
  background: rgba(9, 9, 16, 0.55);
  perspective: 820px;   /* depth for the bridge's turn */
  transition: border-color 0.5s var(--stars-ease-out-expo, ease-out),
              box-shadow 0.5s var(--stars-ease-out-expo, ease-out);
}
.etude-card.is-awake .etude-card__face {
  border-color: rgba(255, 255, 255, 0.5);
  box-shadow: 0 0 16px rgba(160, 190, 235, 0.22);
}
.etude-card .erb-svg { width: 132px; height: 132px; }

/* the bridge turns over — the rings/paths carry their colour with them, so
   rotateX(180) drops orange to the bottom and lifts blue = Choros */
.erb-wrap {
  transform-style: preserve-3d;
  transition: transform 760ms cubic-bezier(0.7, 0.05, 0.3, 1);
}
.etude-card.is-flipped .erb-wrap { transform: rotateX(180deg); }
.erb-ring, .erb-side {
  fill: none;
  stroke-width: 1.1;
  transition: stroke 0.55s var(--stars-ease-out-expo, ease-out);
}
/* the poles are always coloured but MUTED at rest (David: too loud) — a low
   saturation that reads without shouting; approach blooms them to full. */
.erb-top    { stroke: hsl(28, 38%, 50%); }
.erb-bot    { stroke: hsl(205, 35%, 52%); }
.erb-throat { stroke: rgba(255, 255, 255, 0.42); }
.etude-card.is-awake .erb-top    { stroke: hsl(28, 74%, 64%); }
.etude-card.is-awake .erb-bot    { stroke: hsl(205, 74%, 68%); }
.etude-card.is-awake .erb-throat { stroke: rgba(255, 255, 255, 0.8); }
.etude-card.is-awake .erb-svg { filter: drop-shadow(0 0 7px rgba(255, 255, 255, 0.3)); }

/* the flip corner — a soft ⇅ that turns the bridge; brightens on approach */
.etude-card__flip {
  position: absolute;
  right: 10px;
  bottom: 10px;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.05);
  color: rgba(255, 255, 255, 0.55);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  z-index: 4;
  transition: color 0.4s, background 0.4s, transform 0.4s;
}
.etude-card.is-awake .etude-card__flip { color: rgba(255, 255, 255, 0.85); background: rgba(255, 255, 255, 0.1); }
.etude-card__flip:hover { transform: scale(1.16); }

@keyframes etude-churn { to { transform: rotate(360deg); } }
@keyframes etude-drift {
  0%   { transform: translate(0, 0); }
  50%  { transform: translate(-7px, 9px); }
  100% { transform: translate(6px, -5px); }
}
@media (prefers-reduced-motion: reduce) {
  .etude-portal, .etude-rings--cw, .etude-rings--ccw { animation: none; }
}

/* Shorter viewports: the vertically-centered card band rises toward the top, so
   shrink the doorway and lift it to preserve the gap. Final responsive placement
   is David's visual-pass call; this is a safe first-pass mitigation. */
@media (max-height: 780px) {
  .etude-portal { top: 64px; width: 108px; }
  .etude-svg { width: 108px; height: 108px; }
}
@media (max-height: 640px) {
  .etude-portal { top: 48px; width: 88px; }
  .etude-svg { width: 88px; height: 88px; }
  .etude-caption { font-size: 0.64rem; }
}

/* ============================================================
   Acts — a little air where each act begins. Unlabelled.
   ============================================================ */
.ms-card.ms-act-start { margin-left: 2.25rem; }

/* ============================================================
   Overview sheet (#chapterSelect.is-overview) — the same cards as a grid,
   each with its name beneath. The practice room and the soloists run across
   the top; the campaign follows; the whole sheet scrolls down if it runs past
   the screen, under a header and status line that stay put.
   map-select.js sets --ov-cols (cards per row) and --ov-zoom (card scale).
   ============================================================ */
.chapter-select-actions { display: flex; align-items: center; gap: 0.75rem; }

/* One line beside the header buttons, present only while the save is at risk
   and one click would fix it (game.js, save-backup.js): what is wrong, said
   plainly, then the thing to press. Below ~1000px only the thing to press. */
.chapter-save-notice { display: flex; align-items: center; gap: 0.35rem; margin-right: 1.5rem; min-width: 0; }
.chapter-save-notice[hidden] { display: none; }
.chapter-save-notice__text {
  font-family: var(--stars-font);
  font-size: 0.72rem;
  font-weight: 300;
  letter-spacing: 0.08em;
  color: var(--stars-dim);
  white-space: nowrap;
}
.chapter-save-notice__act { opacity: 0.85; white-space: nowrap; }
@media (max-width: 1000px) {
  .chapter-save-notice { margin-right: 0.25rem; }
  .chapter-save-notice__text { display: none; }
}
.chapter-overview-btn[aria-pressed="true"] { opacity: 1; color: rgb(232, 200, 121); }
/* Reached by d-pad / arrows (UP from the top row of cards): a thin rule says
   which of the two is held, since "on" already takes the bright colour. */
.chapter-select-actions .chapter-back-btn:focus {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.5em;
}

#chapterSelect.is-overview { overflow-x: hidden; overflow-y: auto; }
#chapterSelect.is-overview::-webkit-scrollbar { width: 6px; }
#chapterSelect.is-overview::-webkit-scrollbar-track { background: transparent; }
#chapterSelect.is-overview::-webkit-scrollbar-thumb { background: var(--stars-border); border-radius: 3px; }
#chapterSelect.is-overview .chapter-select-header {
  position: sticky; top: 0; z-index: 3;
  flex: 0 0 auto;
  background: var(--stars-bg);
}
#chapterSelect.is-overview .chapter-status {
  order: 3;
  position: sticky; bottom: 0; z-index: 3;
  flex: 0 0 auto;
  margin-top: auto;
  background: var(--stars-bg);
  color: rgba(230, 239, 255, 0.78);
}
#chapterSelect.is-overview .chapter-grid {
  display: grid;
  /* fixed-width columns, so the short top row lines up with the campaign's left edge */
  grid-template-columns: repeat(var(--ov-cols, 15), calc(200px * var(--ov-zoom, 0.5)));
  justify-content: center;
  align-items: start;
  gap: var(--ov-row-gap, 42px) var(--ov-gap, 14px);
  flex: 0 0 auto;
  overflow: visible;
  scroll-snap-type: none;
}
/* bottom padding leaves room for the last row's names */
#chapterSelect.is-overview #soloistGrid { order: 1; padding: 1.75rem max(4vw, 2rem) 3.25rem; }
#chapterSelect.is-overview #chapterGrid { order: 2; padding: 0.25rem max(4vw, 2rem) 4rem; }
#chapterSelect.is-overview .ms-card,
#chapterSelect.is-overview .etude-card {
  position: relative;
  zoom: var(--ov-zoom, 0.5);
  scroll-snap-align: none;
  /* keyboard movement scrolls a card clear of the pinned header and status line */
  scroll-margin: calc(110px / var(--ov-zoom, 0.5)) 0 calc(90px / var(--ov-zoom, 0.5));
}
/* the grid keeps its columns: no act gaps here */
#chapterSelect.is-overview .ms-card.ms-act-start { margin-left: 0; }
/* At this size the card is its constellation; its name sits underneath. */
#chapterSelect.is-overview .ms-chrome-bottom,
#chapterSelect.is-overview .ms-chrome-top__index { visibility: hidden; }
#chapterSelect.is-overview .ms-card:focus .ms-card__face,
#chapterSelect.is-overview .ms-card:hover .ms-card__face { border-width: 2px; border-color: rgba(255,255,255,0.9); }

.ms-ov-name { display: none; }
#chapterSelect.is-overview .ms-ov-name {
  display: block;
  position: absolute;
  left: 0; right: 0; top: 100%;
  margin-top: calc(7px / var(--ov-zoom, 0.5));
  font-family: var(--stars-font, 'Inter', sans-serif);
  font-weight: 300;
  font-size: calc(11.5px / var(--ov-zoom, 0.5));
  line-height: 1.25;
  letter-spacing: 0.02em;
  text-transform: lowercase;
  color: rgba(230, 239, 255, 0.66);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
  transition: color 0.25s ease;
}
#chapterSelect.is-overview .ms-card.is-locked .ms-ov-name { color: rgba(230, 239, 255, 0.36); }
#chapterSelect.is-overview .ms-card:focus .ms-ov-name,
#chapterSelect.is-overview .ms-card:hover .ms-ov-name,
#chapterSelect.is-overview .etude-card:focus .ms-ov-name,
#chapterSelect.is-overview .etude-card:hover .ms-ov-name { color: #fff; }

/* ============================================================
   The finale — the void.
   The one card with nothing on it: no constellation, no name, no number.
   Blacker than the cards around it, and breathing: a faint light at its
   edges swells and thins on a slow, uneven cycle, so the dark at its centre
   seems to draw in and let go. Whatever is in there, the card does not say.
   (Opacity + transform only, so the pulse costs the compositor nothing; it
   runs only while the map select is showing, and not at all with reduced
   motion.)
   ============================================================ */
.ms-card--finale .ms-card__face {
  background: #000;
  border-color: rgba(150, 160, 235, 0.14);
}
.ms-card--finale .ms-card__face > * { visibility: hidden; }
.ms-card--finale .ms-card__face::before,
.ms-card--finale .ms-card__face::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
}
/* the breath at the edges */
.ms-card--finale .ms-card__face::before {
  background: radial-gradient(ellipse 78% 74% at 50% 50%,
    rgba(0, 0, 0, 0) 38%,
    rgba(70, 76, 150, 0.16) 78%,
    rgba(120, 128, 215, 0.30) 100%);
  opacity: 0.3;
}
/* the dark at the centre, which grows as the edges brighten */
.ms-card--finale .ms-card__face::after {
  background: radial-gradient(circle at 50% 50%, #000 0%, #000 22%, rgba(0, 0, 0, 0) 62%);
  transform: scale(0.92);
}
.ms-card--finale:focus .ms-card__face,
.ms-card--finale.is-selected .ms-card__face { border-color: rgba(190, 196, 255, 0.6); }
.ms-card--finale.is-locked .ms-card__face { border-color: rgba(150, 160, 235, 0.07); }
.ms-card--finale.is-locked .ms-card__face::before { opacity: 0.12; }

@keyframes ms-void-breath {
  0%   { opacity: 0.22; }
  31%  { opacity: 0.74; }
  44%  { opacity: 0.50; }
  63%  { opacity: 0.86; }
  100% { opacity: 0.22; }
}
@keyframes ms-void-draw {
  0%   { transform: scale(0.90); }
  31%  { transform: scale(1.10); }
  44%  { transform: scale(1.02); }
  63%  { transform: scale(1.16); }
  100% { transform: scale(0.90); }
}
#chapterSelect:not(.chapter-hidden) .ms-card--finale:not(.is-locked) .ms-card__face::before {
  animation: ms-void-breath 11s ease-in-out infinite;
}
#chapterSelect:not(.chapter-hidden) .ms-card--finale:not(.is-locked) .ms-card__face::after {
  animation: ms-void-draw 11s ease-in-out infinite;
}
/* still locked: the same breath, slower and fainter — it is there before it can be entered */
#chapterSelect:not(.chapter-hidden) .ms-card--finale.is-locked .ms-card__face::before {
  animation: ms-void-breath 17s ease-in-out infinite;
  filter: brightness(0.45);
}
@media (prefers-reduced-motion: reduce) {
  .ms-card--finale .ms-card__face::before,
  .ms-card--finale .ms-card__face::after { animation: none !important; }
}
.ms-card--finale.is-still .ms-card__face::before,
.ms-card--finale.is-still .ms-card__face::after { animation: none !important; }

/* ============================================================
   A soloist arriving (map-select.js, "the soloists arrive").
   It surfaces. Nothing is there; then a ripple opens at the middle of where
   the card will be and spreads, and the card is whatever the ripple has
   already passed — so its picture comes up first and its frame last, as the
   ripple reaches it. Two fainter ripples follow the first. The frame holds a
   glow where the ripple met it, and settles.
   ============================================================ */
@property --ms-rip { syntax: '<length>'; inherits: true; initial-value: 0px; }

.ms-card.ms-arriving { position: relative; }
.ms-card.ms-arriving--waiting { visibility: hidden; pointer-events: none; }

/* rising the last little way toward you as it comes up */
.ms-card.ms-arriving--go { animation: ms-surface-rise 2.4s cubic-bezier(0.2, 0.7, 0.25, 1) both; }

.ms-card.ms-arriving--go .ms-card__back {
  /* the card exists only inside the ripple; a soft edge, like a waterline */
  -webkit-mask-image: radial-gradient(circle at 50% 50%, #000 var(--ms-rip), transparent calc(var(--ms-rip) + 22px));
          mask-image: radial-gradient(circle at 50% 50%, #000 var(--ms-rip), transparent calc(var(--ms-rip) + 22px));
  /* unhurried: it reaches the frame's sides about a second in, its corners a little after */
  animation: ms-surface-spread 2.4s cubic-bezier(0.3, 0.42, 0.4, 1) both,
             ms-surface-settle 2.6s ease-out 1.05s both;
}
/* the ripples themselves: the leading one at the waterline, two behind it */
.ms-card.ms-arriving--go .ms-card__back::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 3;
  background:
    radial-gradient(circle at 50% 50%, transparent calc(var(--ms-rip) - 5px), rgba(255, 214, 150, 0.62) var(--ms-rip), transparent calc(var(--ms-rip) + 5px)),
    radial-gradient(circle at 50% 50%, transparent max(0px, calc(var(--ms-rip) - 47px)), rgba(255, 196, 120, 0.3) max(0px, calc(var(--ms-rip) - 42px)), transparent max(0px, calc(var(--ms-rip) - 37px))),
    radial-gradient(circle at 50% 50%, transparent max(0px, calc(var(--ms-rip) - 89px)), rgba(255, 190, 110, 0.16) max(0px, calc(var(--ms-rip) - 84px)), transparent max(0px, calc(var(--ms-rip) - 79px))),
    radial-gradient(circle at 50% 50%, rgba(255, 200, 130, 0.09), transparent var(--ms-rip));
  animation: ms-surface-fade 2.4s ease-out both;
}

/* far enough to clear the card's corners (it is 200 by 280), then a little */
@keyframes ms-surface-spread { from { --ms-rip: 0px; } to { --ms-rip: 250px; } }
@keyframes ms-surface-fade   { 0% { opacity: 0; } 12% { opacity: 1; } 62% { opacity: 1; } 100% { opacity: 0; } }
@keyframes ms-surface-rise   { from { transform: scale(0.955); } to { transform: scale(1); } }
@keyframes ms-surface-settle {
  0%   { box-shadow: 0 0 0 0 rgba(255, 179, 71, 0);          border-color: rgba(255, 214, 150, 1); }
  22%  { box-shadow: 0 0 24px 2px rgba(255, 179, 71, 0.5);   border-color: rgba(255, 214, 150, 1); }
  100% { box-shadow: 0 0 0 0 rgba(255, 179, 71, 0); }
}

/* Reduce Motion (the game's own setting, or the system's): it simply fades in. */
.ms-card.ms-arriving--calm { animation: ms-arrive-calm 0.5s ease-out both; }
@keyframes ms-arrive-calm { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .ms-card.ms-arriving--go { animation: ms-arrive-calm 0.5s ease-out both; }
  .ms-card.ms-arriving--go .ms-card__back { -webkit-mask-image: none; mask-image: none; animation: none; }
  .ms-card.ms-arriving--go .ms-card__back::after { display: none; }
}
