/* Emote playback — sprite sheet + CSS steps(). Injected by shared/emotes/emote.js.
 *
 * The whole animation is four custom properties and one keyframe. No library, no canvas,
 * no requestAnimationFrame: a paused tab, a background game and a phone that throttled
 * timers all keep behaving, and there is no per-emote JS to leak.
 *
 * ⚠️ THE STEP MATHS, because it is easy to get subtly wrong and the failure looks like
 * "the art is slightly off" rather than like a bug:
 *
 *   background-size is `frames * w` wide, so the strip is exactly `frames` element-widths.
 *   Frame k sits at background-position-x = -k * w.
 *   `steps(frames)` over 0 → -(frames * w) with jump-end yields -k * w for k = 0 … frames-1,
 *   which is every frame exactly once and never the blank past the end.
 *
 *   Do NOT re-spell this with percentages. A percentage background-position resolves
 *   against (element size - image size), not element size, so `0% → 100%` means "first
 *   frame → LAST frame" and steps(frames) over that range lands between cells. Lengths
 *   sidestep the whole trap.
 *
 * Frame count and frame size come from the catalogue entry, never from this file.
 */

.gg-emote {
  --gg-emote-frames: 1;
  --gg-emote-w: 64px;
  --gg-emote-h: 64px;
  --gg-emote-duration: 800ms;

  display: block;
  flex: none;
  width: var(--gg-emote-w);
  height: var(--gg-emote-h);
  background-image: var(--gg-emote-sheet);
  background-repeat: no-repeat;
  background-position: 0 0;
  background-size: calc(var(--gg-emote-frames) * var(--gg-emote-w)) var(--gg-emote-h);
  /* The sheets are pixel art. Without this the browser bilinear-filters them into mush,
     and it also stops a half-pixel sample bleeding the neighbouring frame in at the seam. */
  image-rendering: pixelated;
  animation: gg-emote-run var(--gg-emote-duration) steps(var(--gg-emote-frames)) infinite;
}

/* A static emote is still a legible emote — it shows frame 0. Motion is decoration here,
   and a looping face is exactly the kind of thing this media query exists for. */
@media (prefers-reduced-motion: reduce) {
  .gg-emote { animation: none; }
}

/* Paused rather than removed, so a card can stop animating on demand without reflowing. */
.gg-emote.is-paused { animation-play-state: paused; }

@keyframes gg-emote-run {
  from { background-position-x: 0px; }
  to   { background-position-x: calc(var(--gg-emote-frames) * var(--gg-emote-w) * -1); }
}

/* ── the in-game bubble ──────────────────────────────────────────────────────────────
 * What a game shows over a player. Self-contained: it borrows no token from site.css,
 * because games do not load site.css (that is the same gap that let a `hidden` D Coin
 * balance render on every game — CLAUDE.md).
 */
.gg-emote-bubble {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-radius: 14px;
  border: 1px solid rgb(245 241 232 / 0.16);
  background: rgb(17 22 15 / 0.92);
  box-shadow: 0 14px 30px -14px rgb(0 0 0 / 0.8);
  pointer-events: none;
  animation: gg-emote-bubble-in 160ms ease-out both;
}

.gg-emote-bubble::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -7px;
  width: 12px;
  height: 12px;
  transform: translateX(-50%) rotate(45deg);
  background: rgb(17 22 15 / 0.92);
  border-right: 1px solid rgb(245 241 232 / 0.16);
  border-bottom: 1px solid rgb(245 241 232 / 0.16);
}

.gg-emote-bubble .gg-emote-name {
  font: 600 12px/1 system-ui, -apple-system, "Segoe UI", sans-serif;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #b4bdaf;
  white-space: nowrap;
}

.gg-emote-bubble.is-leaving { animation: gg-emote-bubble-out 160ms ease-in both; }

@keyframes gg-emote-bubble-in {
  from { opacity: 0; transform: translateY(6px) scale(0.94); }
  to   { opacity: 1; transform: none; }
}

@keyframes gg-emote-bubble-out {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateY(-6px) scale(0.96); }
}

@media (prefers-reduced-motion: reduce) {
  .gg-emote-bubble,
  .gg-emote-bubble.is-leaving { animation: none; }
}

/* ══ THE EMOTE DECK — the reserved top-right corner ═════════════════════════════════════
 *
 * ⚠️ THESE FOUR TOKENS ARE THE RESERVATION, and they are the ONLY place its size is
 * written down. The deck's button is positioned from them, `reservedCornerRect()` in
 * ./deck.js measures the button and pads it by `--gg-emote-corner-gutter`, and every game's
 * top band insets itself with `right: var(--gg-emote-corner, 0px)`. One number, three
 * consumers — because a second copy of "the corner is 60px wide" is a second copy that goes
 * stale, and the symptom of THAT is a gate measuring a rectangle nothing occupies.
 *
 * A game may not override these. If the deck needs to be bigger, it gets bigger here and
 * every band follows.
 */
:root {
  --gg-emote-corner-inset: 12px;   /* from the viewport's right edge */
  --gg-emote-corner-size: 40px;    /* the collapsed button, square */
  --gg-emote-corner-gutter: 8px;   /* clear air around it, on every side */
  /* What a game must keep clear on its right: inset + button + gutter. */
  --gg-emote-corner: calc(var(--gg-emote-corner-inset) + var(--gg-emote-corner-size) + var(--gg-emote-corner-gutter));
}

.gg-emote-deck {
  position: fixed;
  /* --gg-ui-top is published by shared/drawer/drawer.js from the visual viewport; on iOS
     in-app browsers it is what stops fixed UI hiding under the host app's chrome. The deck
     shares it with the drawer handle so the two corners sit on the same line. */
  top: calc(var(--gg-ui-top, 0px) + env(safe-area-inset-top, 0px) + 12px);
  right: calc(var(--gg-emote-corner-inset) + env(safe-area-inset-right, 0px));
  z-index: 470;   /* under the drawer's backdrop (498) — opening the menu covers the deck */
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  /* The wrapper is as tall as its open tray, so it must not take pointer events: a closed
     deck that swallowed clicks 200px below the button is the failure the side drawer already
     shipped once, and it cost Guild Delve its back button. Children opt back in. */
  pointer-events: none;
}

.gg-emote-deck-btn,
.gg-emote-pick { pointer-events: auto; }

.gg-emote-deck-btn {
  width: var(--gg-emote-corner-size);
  height: var(--gg-emote-corner-size);
  display: grid;
  place-items: center;
  padding: 0;
  font: 400 21px/1 system-ui, -apple-system, "Segoe UI", sans-serif;
  color: #d9d3c8;
  cursor: pointer;
  border-radius: 13px;
  border: 1px solid rgb(255 255 255 / .12);
  background: rgb(17 22 15 / .82);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: background .18s, color .18s;
}

.gg-emote-deck-btn:hover,
.gg-emote-deck.is-open .gg-emote-deck-btn { background: rgb(30 38 26 / .92); color: #f5f1e8; }

/* A picked emote is on cooldown. Dimming the button is the whole feedback — a toast for
   "you are emoting too fast" is a second thing on screen for a non-event. */
.gg-emote-deck.is-cooling .gg-emote-deck-btn { opacity: .45; }

.gg-emote-tray {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 4px;
  padding: 6px;
  border-radius: 14px;
  border: 1px solid rgb(255 255 255 / .12);
  background: rgb(13 17 11 / .94);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: 0 18px 40px -18px rgb(0 0 0 / .9);
}

/* ⚠️ `[hidden]` is a USER-AGENT style and any author `display` beats it. This tray is a
   grid, so without this line it would be `hidden === true` in the DOM and fully on screen —
   which is exactly how a D Coin balance shipped under "Not signed in" on every game.
   Scoped rather than global: games do not load site.css, and a `!important` on every
   `[hidden]` from an injected stylesheet is not this component's call to make. */
.gg-emote-tray[hidden] { display: none !important; }

.gg-emote-pick {
  display: grid;
  place-items: center;
  gap: 2px;
  width: 60px;
  padding: 6px 2px 5px;
  cursor: pointer;
  border-radius: 10px;
  border: 1px solid transparent;
  background: none;
  color: #b4bdaf;
}

.gg-emote-pick:hover { background: rgb(255 255 255 / .07); border-color: rgb(255 255 255 / .12); }

.gg-emote-pick-name {
  font: 600 9px/1 system-ui, -apple-system, "Segoe UI", sans-serif;
  letter-spacing: .06em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* Where a played emote lands. Below the corner, never inside it, and never clickable. */
.gg-emote-stack {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
  pointer-events: none;
}
