/* Play it from here — web/static/js/play-here.js.
 *
 * Two surfaces. The setup is a page inside the board's right-click menu
 * (.ph-*), so it speaks the menu's vocabulary: the same labels, the same
 * slider and tick and segmented choice, at the menu's 300px. The game is the
 * Play panel (.gd-play*), which takes the review rail's place while a game is
 * on, and is built from the rail's own card: a warm panel, a thin divider
 * border, small corners.
 *
 * Opponents wear the colours their cards wear on the play pages
 * (bot-accents.css): a game against Jade looks like Jade's tile.
 */

/* ══ The setup page ═══════════════════════════════════════════════════ */

.ph-setup {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.ph-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: -2px 0 -4px -4px;
}
.ph-back {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-text-secondary);
  cursor: pointer;
  transition: background var(--dur-short) var(--ease-out),
              color var(--dur-short) var(--ease-out);
}
.ph-back:hover { background: var(--color-surface-overlay); color: var(--color-text); }
.ph-back:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--color-wood) 60%, transparent);
  outline-offset: -2px;
}
.ph-back svg {
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.ph-titles { min-width: 0; display: flex; flex-direction: column; }
.ph-title {
  font-size: 15px;
  font-weight: 600;
  line-height: 1.2;
  color: var(--color-text);
}
.ph-sub {
  font-size: 12px;
  line-height: 1.35;
  color: var(--color-text-tertiary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ph-field { display: flex; flex-direction: column; gap: var(--space-2); }
.ph-field[hidden] { display: none; }

/* How strong: − 5k + over the scale. The level reads big, because it is the
   one decision everything else here follows from. */
.ph-stepper {
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr) 34px;
  align-items: center;
  gap: var(--space-2);
}
.ph-step {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-sm);
  background: var(--color-surface-card);
  color: var(--color-text);
  cursor: pointer;
  transition: background var(--dur-short) var(--ease-out),
              border-color var(--dur-short) var(--ease-out),
              transform 100ms var(--ease-out);
}
.ph-step:hover:not(:disabled) {
  background: var(--color-surface-warm);
  border-color: color-mix(in srgb, var(--color-wood) 50%, var(--color-divider));
}
.ph-step:active:not(:disabled) { transform: scale(0.94); }
.ph-step:disabled { opacity: 0.35; cursor: default; }
.ph-step:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--color-wood) 60%, transparent);
  outline-offset: 1px;
}
.ph-step svg {
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
}
.ph-level {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 0;
  line-height: 1.1;
}
.ph-level-value {
  display: block;
  font-size: 22px;
  font-weight: 650;
  letter-spacing: -0.01em;
  color: var(--color-text);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
  min-height: 24px;
}
/* While the scale loads: a shimmer the size of the value it stands for. */
.ph-level-value.is-loading {
  width: 56px;
  border-radius: 6px;
  background: linear-gradient(90deg, var(--color-surface-warm) 20%,
              var(--color-surface-overlay) 50%, var(--color-surface-warm) 80%);
  background-size: 200% 100%;
  animation: ph-shimmer 1.1s linear infinite;
}
@keyframes ph-shimmer { to { background-position: -200% 0; } }
.ph-level-note {
  min-height: 14px;
  font-size: 11px;
  color: var(--color-text-tertiary);
}
/* The drawer's slider (board-controls.css); only its width is ours. */
.ph-scale { width: 100%; margin: 0; }
.ph-scale-ends {
  display: flex;
  justify-content: space-between;
  margin-top: -4px;
  font-size: 10.5px;
  letter-spacing: 0.02em;
  color: var(--color-text-tertiary);
}

/* Who, at that strength. Radio rows; the chosen one carries the tick the
   board list uses. */
.ph-who { overflow: hidden; }
.ph-bots {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.ph-bot {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 6px var(--space-2);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-text);
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  transition: background var(--dur-short) var(--ease-out),
              border-color var(--dur-short) var(--ease-out);
}
.ph-bot:hover { background: var(--color-surface-overlay); }
.ph-bot.is-selected {
  background: var(--color-surface-warm);
  border-color: color-mix(in srgb, var(--color-wood) 45%, var(--color-divider));
}
.ph-bot:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--color-wood) 60%, transparent);
  outline-offset: -2px;
}
.ph-bot .bm-tick svg { opacity: 0; }
.ph-bot.is-selected .bm-tick svg { opacity: 1; }
/* A lone opponent at its strength is not a choice: no tick, no hover. */
.ph-who.is-single .ph-bot { cursor: default; background: var(--color-surface-warm); border-color: var(--color-divider); }
.ph-who.is-single .bm-tick { display: none; }
.ph-bot-text {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.ph-bot-name {
  display: flex;
  align-items: baseline;
  gap: 6px;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.3;
}
.ph-bot-rank {
  font-size: 11px;
  font-weight: 500;
  color: var(--color-text-tertiary);
  font-variant-numeric: tabular-nums;
}
.ph-bot-line {
  font-size: 11.5px;
  line-height: 1.3;
  color: var(--color-text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ph-blurb {
  margin: 0;
  padding: 0 var(--space-2);
  font-size: 12px;
  line-height: 1.45;
  color: var(--color-text-secondary);
}
.ph-blurb[hidden] { display: none; }

/* The opponent's face: its mineral's colours, its initial. KataGo wears the
   robot instead of a letter, and Random a shuffle. */
.ph-avatar {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-sm);
  background: linear-gradient(150deg,
    var(--bot-accent-soft, var(--color-wood)) 0%,
    var(--bot-accent, var(--color-wood)) 55%,
    var(--bot-accent-deep, var(--color-wood-dark)) 100%);
  color: var(--bot-accent-ink, var(--color-on-wood));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25),
              0 1px 2px rgba(23, 10, 2, 0.16);
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
}
.ph-avatar--lg {
  width: 40px;
  height: 40px;
  font-size: 18px;
}
.ph-avatar svg {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.ph-avatar--engine {
  --bot-accent-soft: #5b6470;
  --bot-accent: #3a414b;
  --bot-accent-deep: #1f242b;
  --bot-accent-ink: #f4f6f8;
}
.ph-avatar--random {
  background: var(--color-surface-card);
  border: 1px dashed color-mix(in srgb, var(--color-text-tertiary) 70%, transparent);
  box-shadow: none;
  color: var(--color-text-secondary);
}

/* Which colour: the drawer's segmented choice, each side with its stone and
   "to play" beside the side whose turn it is. */
.ph-colors .kf-seg-opt { flex: 1 1 0; }
.ph-colors .kf-seg-text {
  gap: 7px;
  padding: 0.45rem 0.6rem;
}
.ph-color-note {
  font-size: 10.5px;
  font-weight: 500;
  opacity: 0.75;
}
.ph-color-note[hidden] { display: none; }
.ph-color-stone {
  flex: 0 0 auto;
  width: 12px;
  height: 12px;
  border-radius: 50%;
}

.ph-go {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  min-height: 40px;
  padding: 0 var(--space-4);
  border: 1px solid color-mix(in srgb, var(--color-wood) 80%, var(--color-wood-dark));
  border-radius: var(--radius-sm);
  background: var(--color-wood);
  color: var(--color-on-wood);
  font-family: inherit;
  font-size: 14px;
  font-weight: 650;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3), 0 1px 2px rgba(23, 10, 2, 0.12);
  transition: background var(--dur-short) var(--ease-out),
              box-shadow var(--dur-short) var(--ease-out),
              transform 100ms var(--ease-out);
}
.ph-go:hover:not(:disabled) {
  background: color-mix(in srgb, var(--color-wood) 88%, var(--color-wood-dark));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3), 0 3px 10px rgba(23, 10, 2, 0.16);
}
.ph-go:active:not(:disabled) { transform: scale(0.985); }
.ph-go:disabled { opacity: 0.5; cursor: default; }
.ph-go:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--color-wood-dark) 50%, transparent);
  outline-offset: 2px;
}
.ph-go svg {
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform var(--dur-short) var(--ease-out);
}
.ph-go:hover:not(:disabled) svg { transform: translateX(2px); }

.ph-note {
  margin: -6px 0 0;
  font-size: 12px;
  line-height: 1.4;
  color: var(--color-text-secondary);
  text-align: center;
}
.ph-note[hidden] { display: none; }

/* ══ Stones, drawn ════════════════════════════════════════════════════ */
/* A flat stone with a hint of light, for the panel and the colour choice. */
.gd-play-stone--B {
  background: radial-gradient(circle at 35% 30%, #5a5a5a 0%, #1c1c1c 55%, #0b0b0b 100%);
  box-shadow: 0 1px 1.5px rgba(0, 0, 0, 0.35);
}
.gd-play-stone--W {
  background: radial-gradient(circle at 35% 30%, #ffffff 0%, #f1efe9 55%, #cfcbc2 100%);
  box-shadow: 0 1px 1.5px rgba(0, 0, 0, 0.3), inset 0 0 0 0.5px rgba(0, 0, 0, 0.18);
}
/* On a dark panel a black stone needs its edge drawn, or it is a hole. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .gd-play-stone--B {
    box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.22), 0 1px 1.5px rgba(0, 0, 0, 0.5);
  }
}
html[data-theme="dark"] .gd-play-stone--B {
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.22), 0 1px 1.5px rgba(0, 0, 0, 0.5);
}

/* ══ The Play panel ═══════════════════════════════════════════════════ */

.gd-play[hidden] { display: none !important; }
.gd-play {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-width: 0;
}
/* Where the rail is: the same cell of the review's app-shell grid. The rail
   stays under it, invisible and inert, with all its state intact for when the
   game ends. */
@media (min-width: 1280px) {
  body.gd-review .gd-body > .gd-play {
    grid-area: panel;
    justify-self: stretch;
    min-height: 0;
    height: 100%;
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: none;
  }
  body.gd-review .gd-body > .gd-play::-webkit-scrollbar { width: 0; height: 0; display: none; }
  body.gd-review .gd-body > .gd-panel.is-under-play { visibility: hidden; }
}
@media (min-width: 1680px) {
  body.gd-review .gd-play { font-size: 15px; }
}

.gd-play-card {
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface-card);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-sm);
}

/* The game: where it came from. */
.gd-play-head {
  display: flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
  padding: 2px 2px 0;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-tertiary);
}
.gd-play-eyebrow-icon {
  flex: 0 0 auto;
  fill: none;
  stroke: var(--color-wood);
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.gd-play-from { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Buttons: small corners, the rail's quiet secondary, and one primary. */
.gd-play-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 32px;
  padding: 0 var(--space-3);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-sm);
  background: var(--color-surface-card);
  color: var(--color-text);
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: background var(--dur-short) var(--ease-out),
              border-color var(--dur-short) var(--ease-out),
              transform 100ms var(--ease-out),
              opacity var(--dur-short) var(--ease-out);
}
.gd-play-btn:hover:not(:disabled) {
  background: var(--color-surface-warm);
  border-color: color-mix(in srgb, var(--color-wood) 45%, var(--color-divider));
}
.gd-play-btn:active:not(:disabled) { transform: scale(0.97); }
.gd-play-btn:disabled { opacity: 0.4; cursor: default; }
.gd-play-btn:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--color-wood) 60%, transparent);
  outline-offset: 1px;
}
.gd-play-btn[hidden] { display: none; }
.gd-play-btn svg {
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Inside a card a button can't take the card's own colour: it gets the
   warm band's fill and a wood-tinted edge, so it reads as something to press
   without shouting like the primary. */
.gd-play-card .gd-play-btn:not(.gd-play-btn--primary) {
  background: var(--color-surface-warm);
  border-color: color-mix(in srgb, var(--color-wood) 28%, var(--color-divider));
}
.gd-play-card .gd-play-btn:not(.gd-play-btn--primary):hover:not(:disabled) {
  background: color-mix(in srgb, var(--color-wood) 14%, var(--color-surface-warm));
  border-color: color-mix(in srgb, var(--color-wood) 55%, var(--color-divider));
}
.gd-play-btn--primary {
  border-color: color-mix(in srgb, var(--color-wood) 80%, var(--color-wood-dark));
  background: var(--color-wood);
  color: var(--color-on-wood);
  font-weight: 600;
}
.gd-play-btn--primary:hover:not(:disabled) {
  background: color-mix(in srgb, var(--color-wood) 88%, var(--color-wood-dark));
  border-color: color-mix(in srgb, var(--color-wood) 70%, var(--color-wood-dark));
}

/* Who plays which colour, whose turn it is, and the prisoners. One grid for
   both rows, so the names, the turn mark and the counts line up whatever the
   names' lengths. */
.gd-play-match {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  column-gap: 10px;
  row-gap: 2px;
  padding: var(--space-2);
}
.gd-play-side {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: subgrid;
  align-items: center;
  min-height: 38px;
  padding: 0 var(--space-2);
  border-radius: 6px;
  transition: background var(--dur-medium) var(--ease-out);
}
.gd-play-side.is-turn { background: var(--color-surface-warm); }
.gd-play-stone {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  transition: transform var(--dur-medium) var(--ease-out);
}
.gd-play-side.is-turn .gd-play-stone { transform: scale(1.12); }
.gd-play-who {
  min-width: 0;
  font-size: 14px;
  font-weight: 500;
  color: var(--color-text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: color var(--dur-medium) var(--ease-out);
}
.gd-play-side.is-turn .gd-play-who { color: var(--color-text); font-weight: 600; }
/* The opponent at work: three dots breathing in its row. */
.gd-play-turn { display: flex; align-items: center; justify-content: flex-end; min-width: 22px; }
.gd-play-dots { display: none; gap: 3px; }
.gd-play-side.is-thinking .gd-play-dots { display: inline-flex; }
.gd-play-dots i {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--color-wood);
  animation: gd-play-breathe 1.1s var(--ease-out) infinite;
}
.gd-play-dots i:nth-child(2) { animation-delay: 0.16s; }
.gd-play-dots i:nth-child(3) { animation-delay: 0.32s; }
@keyframes gd-play-breathe {
  0%, 80%, 100% { opacity: 0.25; transform: translateY(0); }
  40% { opacity: 1; transform: translateY(-2px); }
}
/* Stones taken: a small stone of the captured colour and the count. */
.gd-play-caps {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  font-weight: 600;
  color: var(--color-text-secondary);
  font-variant-numeric: tabular-nums;
}
.gd-play-caps[hidden] { visibility: hidden; display: inline-flex; }
.gd-play-caps-stone { width: 9px; height: 9px; border-radius: 50%; }

/* What happens now, and the one thing to do about it when there is one. */
.gd-play-status {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  min-height: 40px;
  margin-top: 4px;
  padding: 6px var(--space-2) 2px;
  border-top: 1px solid var(--color-divider);
}
.gd-play-status-text {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 13.5px;
  line-height: 1.4;
  color: var(--color-text);
}
.gd-play.is-over .gd-play-status-text { font-weight: 600; }

.gd-play-actions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2);
}

/* The opponent, as its card on the play pages has it. */
.gd-play-opp { display: flex; flex-direction: column; gap: var(--space-2); }
.gd-play-opp-top { display: flex; align-items: center; gap: var(--space-3); }
.gd-play-opp-avatar { display: contents; }
.gd-play-opp-text { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.gd-play-opp-name {
  display: flex;
  align-items: baseline;
  gap: 7px;
  font-size: 15px;
  font-weight: 600;
  line-height: 1.25;
  color: var(--color-text);
}
.gd-play-rank {
  font-size: 12px;
  font-weight: 500;
  color: var(--color-text-tertiary);
  font-variant-numeric: tabular-nums;
}
.gd-play-opp-line {
  font-size: 12px;
  color: var(--color-text-secondary);
}
.gd-play-opp-line[hidden],
.gd-play-opp-blurb[hidden] { display: none; }
.gd-play-opp-blurb {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--color-text-secondary);
}
/* Someone else, or no one: the two ways out of this game, at the card's
   end. Stop's square is the one touch of colour. */
.gd-play-opp-acts {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--space-2);
  margin-top: 2px;
}
.gd-play-stop svg { fill: var(--color-danger-soft); stroke: none; }

/* The engine's read: off unless asked for. */
.gd-play-ai { padding: 0; overflow: hidden; }
.gd-play-ai[hidden] { display: none; }
.gd-play-toggles { border: none; background: transparent; }
.gd-play-toggles .cz-toggle { border-bottom: none; }
.gd-play-ai-label { display: inline-flex; align-items: center; gap: 8px; }
/* Turns while the engine reads a new position with the last read still up. */
.gd-play-ai-spin {
  flex: 0 0 auto;
  opacity: 0;
  transition: opacity var(--dur-medium) var(--ease-out);
}
.gd-play-ai-spin.is-on { opacity: 1; }
.gd-play-eval[hidden],
.gd-play-eval-wait[hidden],
.gd-play-facts [hidden] { display: none; }
.gd-play-eval {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: 0 var(--space-4) var(--space-4);
  animation: gd-play-in var(--dur-medium) var(--ease-out);
}
/* A read of the position before: still the best there is, said quietly
   until the new one lands. The last move is current and stays crisp. */
.gd-play-odds,
.gd-play-wr,
.gd-play-facts dd:first-of-type {
  transition: opacity var(--dur-medium) var(--ease-out);
}
.gd-play-eval.is-stale .gd-play-odds,
.gd-play-eval.is-stale .gd-play-wr,
.gd-play-eval.is-stale .gd-play-facts dd:first-of-type { opacity: 0.5; }
@keyframes gd-play-in {
  from { opacity: 0; transform: translateY(-3px); }
  to { opacity: 1; transform: none; }
}
.gd-play-odds {
  display: flex;
  justify-content: space-between;
  gap: var(--space-2);
  font-size: 14px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
}
.gd-play-odds-them {
  color: var(--color-text-secondary);
  font-weight: 500;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The reader's share of the bar in their colour, the rest in the opponent's:
   a black and a white stone's colours, flat, readable in either theme. The
   edge is drawn over both halves (::after) so neither colour bleeds into the
   card. */
.gd-play-wr {
  --wr-black: #1f1d1b;
  --wr-white: #ece8e0;
  position: relative;
  height: 12px;
  margin-top: -2px;
  border-radius: 3px;
  overflow: hidden;
  background: var(--wr-white);
}
.gd-play-wr::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.16);
  pointer-events: none;
}
.gd-play-wr[data-you="W"] { background: var(--wr-black); }
.gd-play-wr-fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 50%;
  background: var(--wr-black);
  transition: width 520ms var(--ease-out);
}
.gd-play-wr[data-you="W"] .gd-play-wr-fill { background: var(--wr-white); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .gd-play-wr::after { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2); }
}
html[data-theme="dark"] .gd-play-wr::after { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2); }
.gd-play-lm-stone { width: 10px; height: 10px; border-radius: 50%; }
.gd-play-facts {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: var(--space-3);
  row-gap: 6px;
  margin: 0;
  font-size: 14px;
}
.gd-play-facts dt { color: var(--color-text-tertiary); }
.gd-play-facts dd {
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
  color: var(--color-text);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.gd-play-coord { color: var(--color-text-secondary); }
/* The cost of the last move, in the board's candidate colours. */
.gd-play-loss {
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 12.5px;
  background: var(--color-surface-warm);
  font-weight: 600;
}
.gd-play-loss[data-heat="9"] { background: var(--color-heat-best); color: #fff; }
.gd-play-loss[data-heat="7"],
.gd-play-loss[data-heat="8"] { background: var(--color-heat-good); color: #fff; }
.gd-play-loss[data-heat="5"] { background: var(--color-heat-ok); color: #2a1f12; }
.gd-play-loss[data-heat="4"],
.gd-play-loss[data-heat="3"] { background: var(--color-mq-mistake); color: #fff; }
.gd-play-eval-wait {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 var(--space-4) var(--space-4);
  font-size: 14px;
  color: var(--color-text-tertiary);
}
.gd-play-spin {
  width: 12px;
  height: 12px;
  border: 1.5px solid color-mix(in srgb, var(--color-wood) 35%, transparent);
  border-top-color: var(--color-wood);
  border-radius: 50%;
  animation: bm-spin 0.7s linear infinite;
}
@keyframes bm-spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .gd-play-dots i { animation: none; opacity: 0.7; }
  .gd-play-spin, .ph-level-value.is-loading { animation-duration: 2.4s; }
  .gd-play-eval { animation: none; }
  .gd-play *, .ph-setup * { transition-duration: 1ms !important; }
}
