/* ==========================================================================
   NZEP 资料库 · assets/games/games-fx.css —— 棋牌界面的"动效层"
   --------------------------------------------------------------------------
   为什么单独一个文件：`games.css` 由另一位开发在改（人机对战那一摊），
   动效顺手塞进去容易撞车；这里只加 **gf- 前缀**的新类，一个都不覆盖既有类，
   所以两边可以各改各的，互不影响。

   纪律：
     ① **只做"看得见的一下"**：每项 0.16–0.6s，绝不拖住操作，也不做循环闪烁
        （唯一例外是"轮到你"的呼吸光，它必须持续到对方出手）；
     ② **时长与曲线写在类名的注释里**，实验页按钮上标同一组数字 —— 两边对不上就是 BUG；
     ③ 系统开了"减少动效"时全部关掉（页面底部统一 @media 兜底，不用 JS 判断）；
     ④ 不用 filter/backdrop-filter 做动效（手机上很贵），只用 transform / opacity / box-shadow。

   对照表（实验页 lab.html 的按钮文案与此一一对应）：
     gf-deal      发牌入场       .38s  cubic-bezier(.22,.8,.3,1)
     gf-drop      出牌落桌       .34s  cubic-bezier(.3,1.4,.5,1)   带一点回弹
     gf-lift      可出的牌抬起   .16s  cubic-bezier(.2,.8,.3,1)    悬停/聚焦才动
     gf-turn      轮到你呼吸光   1.4s  ease-in-out 无限循环
     gf-flash     对手行动闪一下 .5s   ease-out
     gf-shake     不能出（抖动） .3s   ease-in-out
     gf-reason    原因气泡       .22s  ease-out（淡入 + 轻抬）
     gf-banner    结束横幅       .5s   cubic-bezier(.2,.9,.3,1)
   ========================================================================== */

/* ---------------------------------------------------------------- 发牌入场
   新牌从上方 30px 外落下 + 轻微左旋，38% 处就已经基本到位（后半程只是收尾），
   所以连发 5 张时每张 60ms 错开也不会显得拖沓。 */
@keyframes gf-deal {
  from { opacity: 0; transform: translateY(-30px) rotate(-8deg); }
  to   { opacity: 1; transform: none; }
}
.gf-deal { animation: gf-deal .38s cubic-bezier(.22, .8, .3, 1) both; }

/* ---------------------------------------------------------------- 出牌落桌
   落下来时"压一下"再弹回 —— 关键是 65% 那一帧的 scale(.985)：
   没有它，牌看起来像贴上去的，不像"拍"在桌上。
   正式界面里目标位还会画一圈高亮（boards.js 的 lastMove 方框），两者叠一起就是"落桌"的观感。 */
@keyframes gf-drop {
  0%   { opacity: .65; transform: translateY(-26px) scale(1.10); }
  65%  { opacity: 1;   transform: translateY(2px) scale(.985); }
  100% { opacity: 1;   transform: none; }
}
.gf-drop { animation: gf-drop .34s cubic-bezier(.3, 1.4, .5, 1) both; }

/* ---------------------------------------------------------------- 可出的牌抬起
   比 games.css 里那圈绿描边更"活"的一点：鼠标/手指落到能出的牌上时，
   牌自己抬 8px 并加深投影。**不用 transform: scale** —— 缩放会糊掉牌面文字。 */
.gf-lift {
  transition: transform .16s cubic-bezier(.2, .8, .3, 1),
              box-shadow .16s ease-out;
  will-change: transform;
}
.gf-lift:hover,
.gf-lift:focus-visible {
  transform: translateY(-8px);
  box-shadow: 0 8px 16px rgba(0, 0, 0, .32);
}
.gf-lift:active { transform: translateY(-3px); }

/* 触屏没有 hover：给"能出的牌"一个更明确的静息态（抬起来一点 + 投影），
   这样手机上不用悬停也知道哪几张能出。宽度 ≤ 760px 才算触屏档。 */
@media (max-width: 760px) {
  .gf-lift { transform: translateY(-4px); box-shadow: 0 4px 10px rgba(0, 0, 0, .26); }
  .gf-lift:active { transform: translateY(0); }
}

/* ---------------------------------------------------------------- 轮到你：呼吸光
   唯一允许的无限循环。用 box-shadow 扩散而不是改边框：边框会推动布局，
   整排玩家会跟着抖。 */
@keyframes gf-turn {
  0%, 100% { box-shadow: 0 0 0 0 rgba(127, 209, 185, 0); }
  50%      { box-shadow: 0 0 0 4px rgba(127, 209, 185, .38); }
}
.gf-turn { animation: gf-turn 1.4s ease-in-out infinite; }

/* ---------------------------------------------------------------- 对手行动闪一下
   对手那一行短促地亮一次（0.5s 后回到透明）。慢半拍的人也知道"刚才谁动了"。 */
@keyframes gf-flash {
  0%   { background: rgba(47, 107, 92, .85); }
  100% { background: transparent; }
}
.gf-flash { animation: gf-flash .5s ease-out; }

/* ---------------------------------------------------------------- 不能出：抖动
   左右各 5px、两次来回，正好 .3s。抖完必须回到原位（100% transform:none），
   否则牌会停在偏 5px 的地方。 */
@keyframes gf-shake {
  0%, 100% { transform: translateX(0); }
  20%      { transform: translateX(-5px); }
  60%      { transform: translateX(5px); }
}
.gf-shake { animation: gf-shake .3s ease-in-out; }

/* 原因气泡：在被打回的牌上方浮出来，1.8s 后自己消失（JS 负责摘掉节点）。
   宿主元素要有定位（.gf-host），不然气泡会跑到页面左上角去。 */
@keyframes gf-reason-in {
  from { opacity: 0; transform: translate(-50%, 4px); }
  to   { opacity: 1; transform: translate(-50%, -6px); }
}
.gf-host { position: relative; }
.gf-reason {
  position: absolute;
  left: 50%;
  bottom: 100%;
  margin-bottom: .35rem;
  z-index: 40;
  max-width: min(20rem, 76vw);
  padding: .3rem .5rem;
  background: #20242b;
  color: #ffe9a8;
  border: 1px solid #4a4f58;
  border-radius: 3px;
  font-size: .78rem;
  line-height: 1.45;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
  animation: gf-reason-in .22s ease-out both;
}
.gf-reason.out { opacity: 0; transition: opacity .25s ease-out; }

/* ---------------------------------------------------------------- 结束横幅 */
@keyframes gf-banner {
  from { opacity: 0; transform: scale(.94); }
  to   { opacity: 1; transform: none; }
}
.gf-banner { animation: gf-banner .5s cubic-bezier(.2, .9, .3, 1) both; }

/* ---------------------------------------------------------------- 减少动效
   系统级偏好与实验页的手动开关（给 <html> 加 .gf-reduce）都走这一段。
   注意：这里连 gf-lift 的 transition 也一起关掉 —— "减少动效"要的是"别动"，
   留个 0.16s 的位移同样会让人不适。 */
@media (prefers-reduced-motion: reduce) {
  .gf-deal, .gf-drop, .gf-turn, .gf-flash, .gf-shake, .gf-reason, .gf-banner {
    animation: none !important;
  }
  .gf-lift, .gf-lift:hover, .gf-lift:focus-visible, .gf-lift:active {
    transition: none !important;
    transform: none !important;
  }
}
html.gf-reduce .gf-deal,
html.gf-reduce .gf-drop,
html.gf-reduce .gf-turn,
html.gf-reduce .gf-flash,
html.gf-reduce .gf-shake,
html.gf-reduce .gf-reason,
html.gf-reduce .gf-banner { animation: none !important; }
html.gf-reduce .gf-lift,
html.gf-reduce .gf-lift:hover,
html.gf-reduce .gf-lift:focus-visible,
html.gf-reduce .gf-lift:active { transition: none !important; transform: none !important; }
