/* MOTION — site-wide polish.
 *
 * Rules this file follows, because the site already has ~74 keyframes and it must not fight them:
 *   - transform and opacity ONLY. Nothing here animates width/height/top/left, so nothing can cause layout
 *     thrash or shift text while it plays.
 *   - every animation is one-shot or hover-driven. No idle loops except the skeleton shimmer, which exists
 *     precisely to say "still loading".
 *   - #viewer is never touched. The flip viewer is off-limits.
 *   - everything is disabled under prefers-reduced-motion at the bottom of the file.
 */

/* ---- 1. LOBBY / PAST ROWS: entrance + hover lift ---------------------------------------------------
   Rows currently appear instantly, which makes a new coinflip look like a rendering glitch rather than an
   event. A 260ms rise reads as "this just arrived". */
@keyframes gfmRise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* NOT a blanket rule on every row. game.js re-renders the lobby on each update, so animating all rows
   would replay the entrance on every poll and the list would flicker permanently. game.js already tags a
   genuinely new past game with .gfl-just-landed — that is the only place an entrance is truthful. */
#pastGames .gfl-just-landed {
  animation: gfmRise .3s cubic-bezier(.22, .68, .36, 1) both;
}
#gfLobby .lobby:not(.gfl-skel) { transition: transform .16s ease, border-color .16s ease, background .16s ease; }
#gfLobby .lobby:not(.gfl-skel):hover { transform: translateY(-1px); }

/* ---- 2. SKELETONS: shimmer ---------------------------------------------------------------------------
   The placeholder rows were completely static, so a slow connection looked like a broken page. A sweep says
   the data is on its way. */
@keyframes gfmShimmer { to { transform: translateX(100%); } }
#gfLobby .lobby.gfl-skel { position: relative; overflow: hidden; }
#gfLobby .lobby.gfl-skel::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(100deg, transparent 30%, rgba(255, 255, 255, .045) 50%, transparent 70%);
  transform: translateX(-100%);
  animation: gfmShimmer 1.6s ease-in-out infinite;
}

/* ---- 3. BUTTON PRESS ---------------------------------------------------------------------------------
   A press that moves is the cheapest "this registered" signal there is, and the site had none. */
.create-open, .create-submit, .filter, .actions button, .gw-join, .gw-go,
#gwModal .gw-d, .modal-actions button, .past-view {
  transition: transform .12s ease, border-color .16s ease, background .16s ease, box-shadow .16s ease;
}
.create-open:active, .create-submit:active, .filter:active, .actions button:active,
.gw-join:active, .gw-go:active, #gwModal .gw-d:active, .modal-actions button:active, .past-view:active {
  transform: scale(.97);
}

/* ---- 4. MODALS: open with a small scale ---------------------------------------------------------------
   Explicitly NOT #viewer. */
@keyframes gfmPop { from { opacity: 0; transform: translateY(8px) scale(.985); } to { opacity: 1; transform: none; } }
.modal.show:not(#viewer) > section,
#gwModal.show > section,
#creator.show > .creator-card {
  animation: gfmPop .2s cubic-bezier(.22, .68, .36, 1) both;
}
/* The backdrop fades rather than snapping on. */
@keyframes gfmFade { from { opacity: 0; } to { opacity: 1; } }
.modal.show:not(#viewer) { animation: gfmFade .16s ease both; }

/* ---- 5. CATALOG ITEMS: art responds to hover ---------------------------------------------------------- */
.catalog-item > img { transition: transform .18s cubic-bezier(.22, .68, .36, 1); }
.catalog-item:hover > img { transform: translateY(-3px) scale(1.04); }
.catalog-item { transition: border-color .16s ease, background .16s ease, transform .14s ease; }
.catalog-item:active { transform: scale(.985); }

/* ---- 6. MOBILE TAB BAR: the active tab pops ----------------------------------------------------------- */
@keyframes gfmTabPop { 50% { transform: translateY(-2px) scale(1.12); } }
.gf-tabbar button svg { transition: transform .16s ease; }
.gf-tabbar button.active svg { animation: gfmTabPop .32s ease; }

/* ---- 7. GIVEAWAY: ENTER catches the light on hover -----------------------------------------------------
   One sweep on hover, not a loop — a looping shine next to live chat is noise. */
.gw-join { position: relative; overflow: hidden; }
.gw-join::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(105deg, transparent 40%, rgba(255, 255, 255, .28) 50%, transparent 60%);
  transform: translateX(-120%);
}
.gw-join:hover::after { animation: gfmShimmer .55s ease-out; }

/* ---- 8. TOAST ------------------------------------------------------------------------------------------ */
.toast { transition: opacity .2s ease, transform .2s cubic-bezier(.22, .68, .36, 1); }

/* ---- 9. HEADER BALANCE: flashes when it changes --------------------------------------------------------
   Needs a class from script (gfm-bump) to fire; harmless until something adds it. */
@keyframes gfmBump { 40% { transform: scale(1.09); } }
.balance b, .balance strong { display: inline-block; }
.balance.gfm-bump b, .balance.gfm-bump strong { animation: gfmBump .38s ease; }

@media (prefers-reduced-motion: reduce) {
  #pastGames .gfl-just-landed,
  #gfLobby .lobby.gfl-skel::after, .modal.show:not(#viewer) > section, #gwModal.show > section,
  #creator.show > .creator-card, .modal.show:not(#viewer), .gf-tabbar button.active svg,
  .gw-join:hover::after, .balance.gfm-bump b, .balance.gfm-bump strong {
    animation: none !important;
  }
  .catalog-item:hover > img, #gfLobby .lobby:not(.gfl-skel):hover { transform: none !important; }
}

#gfLobby .lobby,#pastGames .lobby,#pastGames .past-game,.messages article,.gfl-flip-row,.catalog-item,.inv-item{contain:layout}
.messages,#pastGames,#gfLobby{contain:layout}
.catalog-grid .catalog-item[hidden],.catalog-picker .catalog-item[hidden],.gf-item-tip-list .catalog-item[hidden],#jpJoinModal .catalog-item[hidden],#creator .catalog-item[hidden],#gwModal .catalog-item[hidden],.inv-grid .inv-item[hidden]{display:none!important}
